diff --git a/data/boards/test.json b/data/boards/board-test.json
similarity index 100%
rename from data/boards/test.json
rename to data/boards/board-test.json
diff --git a/data/www/ata-boothifier-upgradeV3.html b/data/www/ata-boothifier-upgradeV3.html
index 642085d..bce43fd 100644
--- a/data/www/ata-boothifier-upgradeV3.html
+++ b/data/www/ata-boothifier-upgradeV3.html
@@ -357,7 +357,7 @@
async function connectToBle(){
if(!navigator.bluetooth){ logMessage('Web Bluetooth not supported.'); return; }
try{
- bleDevice = await navigator.bluetooth.requestDevice({
+ bleDevice = await navigator.bluetooth.requestDevice({
filters:[{ name: el.inDeviceName.value || BLE_SERVER_NAME }],
optionalServices:[BLE_SERVICE_UUID]
});
@@ -369,20 +369,21 @@
bleCharacteristic2.addEventListener('characteristicvaluechanged', e => {
try{
const txt = new TextDecoder().decode(e.target.value);
+ console.log('--> ' + txt);
logMessage('--> ' + txt.trim());
}catch(_){ /* ignore */ }
});
bleConnected=true;
bleDevice.addEventListener('gattserverdisconnected', onDisconnect);
- const connectedName = bleDevice.name || el.inDeviceName.value || 'Device';
- logMessage('Connected to ' + connectedName);
- const nameLabel = document.getElementById('ble-device-name');
- if(nameLabel){ nameLabel.textContent = 'Device Name: ' + connectedName; }
- await readPacket();
- updateUI();
- }catch(err){
- logMessage( err.message.includes('cancel') ? 'Connection cancelled.' : ('Connection failed: '+err.message) );
- }
+ const connectedName = bleDevice.name || el.inDeviceName.value || 'Device';
+ logMessage('Connected to ' + connectedName);
+ const nameLabel = document.getElementById('ble-device-name');
+ if(nameLabel){ nameLabel.textContent = 'Device Name: ' + connectedName; }
+ await readPacket();
+ updateUI();
+ }catch(err){
+ logMessage( err.message.includes('cancel') ? 'Connection cancelled.' : ('Connection failed: '+err.message) );
+ }
}
function onDisconnect(){
diff --git a/firmware_update/Tools/UploadToGoogle.py b/firmware_update/UploadToGoogle.py
similarity index 100%
rename from firmware_update/Tools/UploadToGoogle.py
rename to firmware_update/UploadToGoogle.py
diff --git a/firmware_update/UploadToMinio_direct.py b/firmware_update/UploadToMinio_direct.py
new file mode 100644
index 0000000..b4f76e4
--- /dev/null
+++ b/firmware_update/UploadToMinio_direct.py
@@ -0,0 +1,456 @@
+#!/usr/bin/env python3
+"""Upload firmware, manifest, and data assets to a MinIO (S3-compatible) bucket.
+
+Features preserved from original GCS script:
+ - Optional backup (copies existing objects under destination prefix to timestamped folder under backups/)
+ - Upload firmware.bin, update.json, and recursively mirror a data directory
+ - Cache-Control set to disable caching on clients
+
+Switches from google.cloud.storage to boto3 (S3 API) for MinIO compatibility.
+"""
+import os
+import sys
+import datetime
+import hashlib
+import json
+from pathlib import Path
+
+try:
+ import boto3
+ from botocore.exceptions import ClientError
+ from botocore.config import Config
+except ImportError:
+ print("ERROR: boto3 is required. Install with: pip install boto3")
+ sys.exit(1)
+
+# =============================================================================
+# CONFIGURATION CONSTANTS (edit as needed or supply via environment variables)
+# =============================================================================
+
+CREATE_BACKUP = False
+UPLOAD_FIRMWARE = True
+UPDATE_MANIFEST = True
+UPLOAD_DATA = True
+
+DIR_SKIP_LIST = [
+ "system",
+ "booths"
+]
+
+FIlES_SKIP_LIST = [
+
+]
+
+# Bucket / endpoint configuration
+BUCKET_NAME = os.getenv('MINIO_BUCKET', 'boothifier')
+DESTINATION_DIR = os.getenv('MINIO_DEST_PREFIX', 'latest') # prefix inside bucket
+BACKUPS_DIR = os.getenv('MINIO_BACKUPS_PREFIX', 'backups')
+
+PROJECT_ROOT_PATH = Path(__file__).parent.parent.resolve()
+LOCAL_ROOT_PATH = Path(__file__).parent.resolve()
+
+# Optional service account style JSON key (generated by MinIO Console). Expected fields:
+# {"url":"https://minio.example.com/api/v1/service-account-credentials","accessKey":"...","secretKey":"...","api":"s3v4","path":"auto"}
+MINIO_KEY_FILE = LOCAL_ROOT_PATH / 'minio-boothifier-key.json'
+
+# Defaults before loading file / env
+_json_access = None
+_json_secret = None
+_json_url = None
+
+def _load_json_key():
+ global _json_access, _json_secret, _json_url
+ try:
+ if MINIO_KEY_FILE.is_file():
+ with open(MINIO_KEY_FILE, 'r', encoding='utf-8') as fh:
+ data = json.load(fh)
+ _json_access = data.get('accessKey') or None
+ _json_secret = data.get('secretKey') or None
+ _json_url = data.get('url') or None
+ except Exception as e:
+ print(f"WARN: Failed to load MinIO key file '{MINIO_KEY_FILE.name}': {e}")
+
+_load_json_key()
+
+def _derive_endpoint(url_value: str) -> str:
+ if not url_value:
+ return 'https://s3-minio.boothwizard.com'
+ # Remove known API suffix if present (/api/...)
+ # e.g. https://s3-minio.boothwizard.com/api/v1/service-account-credentials -> https://s3-minio.boothwizard.com
+ parts = url_value.split('/api/')
+ return parts[0] if parts else url_value
+
+# MinIO credentials with precedence: ENV > JSON file > fallback
+MINIO_ENDPOINT = os.getenv('MINIO_ENDPOINT') or _derive_endpoint(_json_url)
+MINIO_ACCESS_KEY = os.getenv('MINIO_ACCESS_KEY') or _json_access or 'CHANGE_ME_ACCESS'
+MINIO_SECRET_KEY = os.getenv('MINIO_SECRET_KEY') or _json_secret or 'CHANGE_ME_SECRET'
+MINIO_REGION = os.getenv('MINIO_REGION', 'us-east-1') # MinIO ignores but boto3 wants some value
+
+ # Addressing / SSL options
+MINIO_ADDRESSING = os.getenv('MINIO_ADDRESSING_STYLE', 'path').lower() # 'path' or 'virtual'
+MINIO_VERIFY_SSL = os.getenv('MINIO_TLS_VERIFY', '1') not in ('0','false','no')
+MINIO_DEBUG = os.getenv('MINIO_DEBUG', '0') in ('1','true','yes')
+MINIO_ALLOW_VARIANTS = os.getenv('MINIO_ALLOW_ENDPOINT_VARIANTS', '0') in ('1','true','yes') # normally false with nginx redirect
+
+LOCAL_FIRMWARE_PATH = str(PROJECT_ROOT_PATH / '.pio' / 'build' / 'esp32s3dev' / 'firmware.bin')
+LOCAL_DATA_DIRECTORY = str(PROJECT_ROOT_PATH / 'data')
+MANIFEST_LOCAL_PATH = str(LOCAL_ROOT_PATH / 'manifest-local.json') # source of version/description/changelog
+MANIFEST_FILENAME = os.getenv('MANIFEST_FILENAME', 'manifest.json') # destination manifest name
+
+# =============================================================================
+# HELPERS
+# =============================================================================
+
+def s3_client():
+ """Create an S3 client pointed at MinIO endpoint, forcing path-style unless overridden, with short timeouts."""
+ addressing = 'path' if MINIO_ADDRESSING not in ('virtual','auto') else 'virtual'
+ cfg = Config(
+ s3={'addressing_style': addressing},
+ signature_version='s3v4',
+ connect_timeout=3,
+ read_timeout=5,
+ retries={'max_attempts': 2}
+ )
+ if MINIO_DEBUG:
+ masked_key = (MINIO_ACCESS_KEY[:3] + '...' + MINIO_ACCESS_KEY[-3:]) if MINIO_ACCESS_KEY else 'None'
+ print(f"[DEBUG] Creating client: endpoint={MINIO_ENDPOINT} addressing={addressing} verifySSL={MINIO_VERIFY_SSL} region={MINIO_REGION} accessKey={masked_key}")
+ return boto3.client(
+ 's3',
+ endpoint_url=MINIO_ENDPOINT,
+ aws_access_key_id=MINIO_ACCESS_KEY,
+ aws_secret_access_key=MINIO_SECRET_KEY,
+ region_name=MINIO_REGION,
+ verify=MINIO_VERIFY_SSL,
+ config=cfg,
+ )
+
+def _endpoint_variants(base: str):
+ """Return endpoint variants only if explicitly allowed; otherwise just the base (nginx handles forwarding)."""
+ if not MINIO_ALLOW_VARIANTS:
+ return [base]
+ # Fallback to previous expanded logic if variants are enabled
+ try:
+ variants = []
+ if not base:
+ return variants
+ base = base.rstrip('/')
+ proto_sep = '://'
+ if proto_sep in base:
+ scheme, rest = base.split(proto_sep,1)
+ else:
+ scheme, rest = 'https', base
+ host_port = rest
+ if ':' in host_port:
+ host, port = host_port.split(':',1)
+ else:
+ host, port = host_port, ''
+ variants.append(f"{scheme}://{host_port}")
+ common_ports = ['9000','443','80']
+ for p in common_ports:
+ if port != p:
+ variants.append(f"{scheme}://{host}:{p}")
+ alt_scheme = 'http' if scheme == 'https' else 'https'
+ variants.append(f"{alt_scheme}://{host_port}")
+ for p in common_ports:
+ if port != p:
+ variants.append(f"{alt_scheme}://{host}:{p}")
+ seen = set()
+ uniq = []
+ for v in variants:
+ if v not in seen:
+ uniq.append(v)
+ seen.add(v)
+ return uniq
+ except Exception:
+ return [base]
+
+def create_validated_client():
+ """Validate (or create) client using only provided endpoint unless variants enabled."""
+ global MINIO_ENDPOINT
+ primary = MINIO_ENDPOINT
+ variants = _endpoint_variants(primary) or [primary]
+ errors = []
+ probe_bucket = BUCKET_NAME # we will head the target bucket directly
+ for candidate in variants:
+ saved = MINIO_ENDPOINT
+ MINIO_ENDPOINT = candidate
+ if MINIO_DEBUG:
+ print(f"[DEBUG] Probing endpoint candidate: {candidate}")
+ try:
+ c = s3_client()
+ try:
+ c.head_bucket(Bucket=probe_bucket)
+ if MINIO_DEBUG:
+ print(f"[DEBUG] head_bucket succeeded on {candidate} for '{probe_bucket}'.")
+ return c
+ except ClientError as e:
+ msg = str(e)
+ # Acceptable if bucket not found (we can create later)
+ if any(code in msg for code in ('404', 'NoSuchBucket', 'NotFound')):
+ if MINIO_DEBUG:
+ print(f"[DEBUG] Bucket not found on {candidate} (expected if first deploy). Using this endpoint.")
+ return c
+ if 'API Requests must be made to API port' in msg:
+ errors.append(f"{candidate}: wrong port (console endpoint)")
+ else:
+ errors.append(f"{candidate}: {msg}")
+ MINIO_ENDPOINT = saved
+ except Exception as ex:
+ errors.append(f"{candidate}: {ex}")
+ MINIO_ENDPOINT = saved
+ continue
+ print("ERROR: Could not validate any endpoint candidate.")
+ for e in errors:
+ print(' - ' + e)
+ print("Provide correct API endpoint (e.g. https://host:9000) via MINIO_ENDPOINT env var.")
+ sys.exit(3)
+
+def list_objects(client, prefix: str):
+ """Generator yielding object keys under a prefix (non-recursive listing with pagination)."""
+ kwargs = {'Bucket': BUCKET_NAME, 'Prefix': prefix}
+ while True:
+ resp = client.list_objects_v2(**kwargs)
+ for obj in resp.get('Contents', []):
+ yield obj['Key']
+ if not resp.get('IsTruncated'):
+ break
+ kwargs['ContinuationToken'] = resp['NextContinuationToken']
+
+def normalize_prefix(p: str) -> str:
+ p = p.strip('/')
+ return p
+
+def join_key(*parts: str) -> str:
+ parts_clean = [p.strip('/') for p in parts if p is not None and p != '']
+ return '/'.join(parts_clean)
+
+def backup_existing_files(client, destination_prefix: str, backups_prefix: str, backup_folder: str):
+ if not destination_prefix:
+ prefix = ''
+ else:
+ prefix = destination_prefix + '/'
+ print(f"Scanning existing objects under '{prefix}' for backup...")
+ for key in list_objects(client, prefix):
+ if backups_prefix and key.startswith(backups_prefix + '/'): # Skip prior backups
+ continue
+ # relative path within destination
+ relative = key[len(prefix):] if prefix and key.startswith(prefix) else key
+ backup_key = join_key(backups_prefix, backup_folder, relative)
+ print(f"Backup copy: {key} -> {backup_key}")
+ client.copy_object(
+ Bucket=BUCKET_NAME,
+ CopySource={'Bucket': BUCKET_NAME, 'Key': key},
+ Key=backup_key,
+ MetadataDirective='COPY'
+ )
+
+def upload_file(client, local_path: str, key: str, cache_control: str = 'private, max-age=0, no-transform'):
+ if not os.path.isfile(local_path):
+ print(f"WARN: File missing, skipping: {local_path}")
+ return
+ print(f"Upload: {local_path} -> s3://{BUCKET_NAME}/{key}")
+ extra_args = { 'CacheControl': cache_control }
+ client.upload_file(local_path, BUCKET_NAME, key, ExtraArgs=extra_args)
+
+def upload_directory(client, local_directory: str, destination_prefix: str):
+ if not os.path.isdir(local_directory):
+ print(f"WARN: Data directory missing: {local_directory}")
+ return
+ for root, _, files in os.walk(local_directory):
+ for fname in files:
+ full = os.path.join(root, fname)
+ rel = os.path.relpath(full, local_directory).replace('\\','/') # force forward slashes for S3 keys
+ key = join_key(destination_prefix, rel)
+ upload_file(client, full, key)
+
+def ensure_bucket(client):
+ """Ensure bucket exists; provide diagnostics if HeadBucket returns 400/other errors."""
+ try:
+ client.head_bucket(Bucket=BUCKET_NAME)
+ if MINIO_DEBUG:
+ print(f"[DEBUG] Bucket '{BUCKET_NAME}' exists.")
+ return
+ except ClientError as e:
+ code = e.response.get('Error', {}).get('Code')
+ status = e.response.get('ResponseMetadata', {}).get('HTTPStatusCode')
+ print(f"HeadBucket failed (code={code}, status={status}).")
+
+ # List buckets for diagnostics
+ try:
+ resp = client.list_buckets()
+ bucket_names = [b['Name'] for b in resp.get('Buckets', [])]
+ print(f"Available buckets: {bucket_names or 'None'}")
+ except Exception as le:
+ print(f"WARN: list_buckets failed: {le}")
+
+ if code in ('404', 'NoSuchBucket', 'NotFound'):
+ print(f"Bucket '{BUCKET_NAME}' not found. Attempting to create...")
+ try:
+ client.create_bucket(Bucket=BUCKET_NAME)
+ print(f"Created bucket '{BUCKET_NAME}'.")
+ return
+ except ClientError as ce:
+ print(f"ERROR: Cannot create bucket: {ce}")
+ sys.exit(2)
+
+ if status == 400:
+ print("HINTS: \n - Verify endpoint URL (MINIO_ENDPOINT).\n - Ensure no trailing slash in endpoint.\n - Check that TLS verify matches server cert (set MINIO_TLS_VERIFY=0 to test).\n - Confirm bucket name is correct and DNS compatible.\n - Credentials may lack permission: verify access key policies.")
+
+ # Retry once forcing path style if not already
+ if MINIO_ADDRESSING != 'path':
+ print("Retrying with path-style addressing...")
+ os.environ['MINIO_ADDRESSING_STYLE'] = 'path'
+ new_client = s3_client()
+ try:
+ new_client.head_bucket(Bucket=BUCKET_NAME)
+ print("Second attempt succeeded with path-style addressing.")
+ return
+ except ClientError as e2:
+ print(f"Second HeadBucket attempt failed: {e2}")
+ print(f"ERROR: head_bucket ultimately failed: {e}")
+ sys.exit(2)
+
+# =============================================================================
+# MAIN
+# =============================================================================
+
+def main():
+ dest_prefix = normalize_prefix(DESTINATION_DIR)
+ backups_prefix = normalize_prefix(BACKUPS_DIR) if BACKUPS_DIR else ''
+ client = create_validated_client()
+ if MINIO_DEBUG:
+ print("[DEBUG] Starting ensure_bucket phase...")
+ ensure_bucket(client)
+
+ # Create backup if needed
+ if CREATE_BACKUP:
+ ts = datetime.datetime.now().strftime('%Y%m%d_%H%M%S')
+ backup_folder = f"backup_{ts}"
+ print(f"Creating backup under '{backups_prefix}/{backup_folder}' from prefix '{dest_prefix}'")
+ backup_existing_files(client, dest_prefix, backups_prefix, backup_folder)
+ print("Backup complete.")
+ else:
+ print("Backup creation skipped.")
+
+ # Firmware
+ if UPLOAD_FIRMWARE:
+ firmware_key = join_key(dest_prefix, 'firmware.bin') if dest_prefix else 'firmware.bin'
+ upload_file(client, LOCAL_FIRMWARE_PATH, firmware_key)
+ print("Firmware upload complete.")
+ else:
+ print("Firmware upload skipped.")
+
+ # Data directory
+ if UPLOAD_DATA:
+ data_prefix = join_key(dest_prefix, 'data') if dest_prefix else 'data'
+ upload_directory(client, LOCAL_DATA_DIRECTORY, data_prefix)
+ print("All uploads complete.")
+ else:
+ print("Data upload skipped.")
+
+ # Manifest
+ if UPDATE_MANIFEST:
+ manifest_key = join_key(dest_prefix, MANIFEST_FILENAME) if dest_prefix else MANIFEST_FILENAME
+ try:
+ manifest_doc = build_and_write_manifest(client, dest_prefix)
+ upload_manifest_json(client, manifest_doc, manifest_key)
+ print(f"Manifest upload complete: s3://{BUCKET_NAME}/{manifest_key}")
+ except Exception as e:
+ print(f"ERROR: Manifest generation/upload failed: {e}")
+ sys.exit(4)
+ else:
+ print("Manifest upload skipped.")
+
+# ================= Manifest Support =================
+
+def md5_hex(path: str, chunk_size: int = 65536) -> str:
+ h = hashlib.md5()
+ with open(path, 'rb') as f:
+ while True:
+ chunk = f.read(chunk_size)
+ if not chunk:
+ break
+ h.update(chunk)
+ return h.hexdigest()
+
+def collect_data_files(data_root: str):
+ files = []
+ if not os.path.isdir(data_root):
+ return files
+ for root, _, filenames in os.walk(data_root):
+ for fname in filenames:
+ full = os.path.join(root, fname)
+ rel = os.path.relpath(full, data_root).replace('\\','/')
+ entry = {
+ 'path': f"data/{rel}",
+ 'md5': md5_hex(full),
+ 'size': os.path.getsize(full)
+ }
+ files.append(entry)
+ files.sort(key=lambda x: x['path'])
+ return files
+
+def read_local_manifest(local_path: str):
+ if not os.path.isfile(local_path):
+ raise FileNotFoundError(f"manifest-local file not found: {local_path}")
+ with open(local_path, 'r', encoding='utf-8') as fh:
+ data = json.load(fh)
+ # Basic validation
+ if 'version' not in data:
+ raise ValueError('manifest-local missing version section')
+ ver = data['version']
+ for k in ('major','minor','patch'):
+ if k not in ver:
+ raise ValueError(f"manifest-local version missing '{k}'")
+ data.setdefault('description', '')
+ data.setdefault('changelog', [])
+ if not isinstance(data['changelog'], list):
+ raise ValueError('changelog must be an array')
+ return data
+
+def build_and_write_manifest(client, dest_prefix: str):
+ # Read local manifest-local.json
+ base_info = read_local_manifest(MANIFEST_LOCAL_PATH)
+ # Timestamp
+ now = datetime.datetime.now()
+ release_date = now.strftime('%Y-%m-%d')
+ release_time = now.strftime('%H:%M:%S')
+ # Firmware info
+ fw_path_local = LOCAL_FIRMWARE_PATH
+ if not os.path.isfile(fw_path_local):
+ raise FileNotFoundError(f"Firmware file not found: {fw_path_local}")
+ fw_md5 = md5_hex(fw_path_local)
+ fw_size = os.path.getsize(fw_path_local)
+ # Data files
+ data_files = collect_data_files(LOCAL_DATA_DIRECTORY)
+ manifest = {
+ 'version': {
+ 'major': int(base_info['version']['major']),
+ 'minor': int(base_info['version']['minor']),
+ 'patch': int(base_info['version']['patch'])
+ },
+ 'release_date': release_date,
+ 'release_time': release_time,
+ 'description': base_info.get('description',''),
+ 'changelog': base_info.get('changelog', []),
+ 'firmware': {
+ 'path': 'firmware.bin',
+ 'md5': fw_md5,
+ 'size': fw_size
+ },
+ 'files': data_files
+ }
+ return manifest
+
+def upload_manifest_json(client, manifest_obj: dict, key: str):
+ body = json.dumps(manifest_obj, indent=4).encode('utf-8')
+ client.put_object(
+ Bucket=BUCKET_NAME,
+ Key=key,
+ Body=body,
+ ContentType='application/json',
+ CacheControl='private, max-age=0, no-transform'
+ )
+
+if __name__ == '__main__':
+ main()
diff --git a/firmware_update/UploadToMinio_direct_V2.py b/firmware_update/UploadToMinio_direct_V2.py
new file mode 100644
index 0000000..acd2219
--- /dev/null
+++ b/firmware_update/UploadToMinio_direct_V2.py
@@ -0,0 +1,497 @@
+#!/usr/bin/env python3
+"""Upload firmware, manifest, and data assets to a MinIO (S3-compatible) bucket.
+
+Features preserved from original GCS script:
+ - Optional backup (copies existing objects under destination prefix to timestamped folder under backups/)
+ - Upload firmware.bin, update.json, and recursively mirror a data directory
+ - Cache-Control set to disable caching on clients
+
+Switches from google.cloud.storage to boto3 (S3 API) for MinIO compatibility.
+"""
+import os
+import sys
+import datetime
+import hashlib
+import json
+from pathlib import Path
+
+try:
+ import boto3
+ from botocore.exceptions import ClientError
+ from botocore.config import Config
+except ImportError:
+ print("ERROR: boto3 is required. Install with: pip install boto3")
+ sys.exit(1)
+
+# =============================================================================
+# CONFIGURATION CONSTANTS (edit as needed or supply via environment variables)
+# =============================================================================
+
+CREATE_BACKUP = False
+UPLOAD_FIRMWARE = True
+UPDATE_MANIFEST = True
+UPLOAD_DATA = True
+
+DIR_SKIP_LIST = [
+ "data/system/**/*",
+ "data/booths/**/*"
+]
+
+FILES_SKIP_LIST = [
+ # Add base filenames to skip regardless of directory, e.g. "readme.txt"
+]
+
+# Bucket / endpoint configuration
+BUCKET_NAME = os.getenv('MINIO_BUCKET', 'boothifier')
+DESTINATION_DIR = os.getenv('MINIO_DEST_PREFIX', 'latest') # prefix inside bucket
+BACKUPS_DIR = os.getenv('MINIO_BACKUPS_PREFIX', 'backups')
+
+PROJECT_ROOT_PATH = Path(__file__).parent.parent.resolve()
+LOCAL_ROOT_PATH = Path(__file__).parent.resolve()
+
+# Optional service account style JSON key (generated by MinIO Console). Expected fields:
+# {"url":"https://minio.example.com/api/v1/service-account-credentials","accessKey":"...","secretKey":"...","api":"s3v4","path":"auto"}
+MINIO_KEY_FILE = LOCAL_ROOT_PATH / 'minio-boothifier-key.json'
+
+# Defaults before loading file / env
+_json_access = None
+_json_secret = None
+_json_url = None
+
+def _load_json_key():
+ global _json_access, _json_secret, _json_url
+ try:
+ if MINIO_KEY_FILE.is_file():
+ with open(MINIO_KEY_FILE, 'r', encoding='utf-8') as fh:
+ data = json.load(fh)
+ _json_access = data.get('accessKey') or None
+ _json_secret = data.get('secretKey') or None
+ _json_url = data.get('url') or None
+ except Exception as e:
+ print(f"WARN: Failed to load MinIO key file '{MINIO_KEY_FILE.name}': {e}")
+
+_load_json_key()
+
+def _derive_endpoint(url_value: str) -> str:
+ if not url_value:
+ return 'https://s3-minio.boothwizard.com'
+ # Remove known API suffix if present (/api/...)
+ # e.g. https://s3-minio.boothwizard.com/api/v1/service-account-credentials -> https://s3-minio.boothwizard.com
+ parts = url_value.split('/api/')
+ return parts[0] if parts else url_value
+
+# MinIO credentials with precedence: ENV > JSON file > fallback
+MINIO_ENDPOINT = os.getenv('MINIO_ENDPOINT') or _derive_endpoint(_json_url)
+MINIO_ACCESS_KEY = os.getenv('MINIO_ACCESS_KEY') or _json_access or 'CHANGE_ME_ACCESS'
+MINIO_SECRET_KEY = os.getenv('MINIO_SECRET_KEY') or _json_secret or 'CHANGE_ME_SECRET'
+MINIO_REGION = os.getenv('MINIO_REGION', 'us-east-1') # MinIO ignores but boto3 wants some value
+
+ # Addressing / SSL options
+MINIO_ADDRESSING = os.getenv('MINIO_ADDRESSING_STYLE', 'path').lower() # 'path' or 'virtual'
+MINIO_VERIFY_SSL = os.getenv('MINIO_TLS_VERIFY', '1') not in ('0','false','no')
+MINIO_DEBUG = os.getenv('MINIO_DEBUG', '0') in ('1','true','yes')
+MINIO_ALLOW_VARIANTS = os.getenv('MINIO_ALLOW_ENDPOINT_VARIANTS', '0') in ('1','true','yes') # normally false with nginx redirect
+
+LOCAL_FIRMWARE_PATH = str(PROJECT_ROOT_PATH / '.pio' / 'build' / 'esp32s3dev' / 'firmware.bin')
+LOCAL_DATA_DIRECTORY = str(PROJECT_ROOT_PATH / 'data')
+MANIFEST_LOCAL_PATH = str(LOCAL_ROOT_PATH / 'manifest-local.json') # source of version/description/changelog
+MANIFEST_FILENAME = os.getenv('MANIFEST_FILENAME', 'manifest.json') # destination manifest name
+
+# =============================================================================
+# HELPERS
+# =============================================================================
+
+def s3_client():
+ """Create an S3 client pointed at MinIO endpoint, forcing path-style unless overridden, with short timeouts."""
+ addressing = 'path' if MINIO_ADDRESSING not in ('virtual','auto') else 'virtual'
+ cfg = Config(
+ s3={'addressing_style': addressing},
+ signature_version='s3v4',
+ connect_timeout=3,
+ read_timeout=5,
+ retries={'max_attempts': 2}
+ )
+ if MINIO_DEBUG:
+ masked_key = (MINIO_ACCESS_KEY[:3] + '...' + MINIO_ACCESS_KEY[-3:]) if MINIO_ACCESS_KEY else 'None'
+ print(f"[DEBUG] Creating client: endpoint={MINIO_ENDPOINT} addressing={addressing} verifySSL={MINIO_VERIFY_SSL} region={MINIO_REGION} accessKey={masked_key}")
+ return boto3.client(
+ 's3',
+ endpoint_url=MINIO_ENDPOINT,
+ aws_access_key_id=MINIO_ACCESS_KEY,
+ aws_secret_access_key=MINIO_SECRET_KEY,
+ region_name=MINIO_REGION,
+ verify=MINIO_VERIFY_SSL,
+ config=cfg,
+ )
+
+def _endpoint_variants(base: str):
+ """Return endpoint variants only if explicitly allowed; otherwise just the base (nginx handles forwarding)."""
+ if not MINIO_ALLOW_VARIANTS:
+ return [base]
+ # Fallback to previous expanded logic if variants are enabled
+ try:
+ variants = []
+ if not base:
+ return variants
+ base = base.rstrip('/')
+ proto_sep = '://'
+ if proto_sep in base:
+ scheme, rest = base.split(proto_sep,1)
+ else:
+ scheme, rest = 'https', base
+ host_port = rest
+ if ':' in host_port:
+ host, port = host_port.split(':',1)
+ else:
+ host, port = host_port, ''
+ variants.append(f"{scheme}://{host_port}")
+ common_ports = ['9000','443','80']
+ for p in common_ports:
+ if port != p:
+ variants.append(f"{scheme}://{host}:{p}")
+ alt_scheme = 'http' if scheme == 'https' else 'https'
+ variants.append(f"{alt_scheme}://{host_port}")
+ for p in common_ports:
+ if port != p:
+ variants.append(f"{alt_scheme}://{host}:{p}")
+ seen = set()
+ uniq = []
+ for v in variants:
+ if v not in seen:
+ uniq.append(v)
+ seen.add(v)
+ return uniq
+ except Exception:
+ return [base]
+
+def create_validated_client():
+ """Validate (or create) client using only provided endpoint unless variants enabled."""
+ global MINIO_ENDPOINT
+ primary = MINIO_ENDPOINT
+ variants = _endpoint_variants(primary) or [primary]
+ errors = []
+ probe_bucket = BUCKET_NAME # we will head the target bucket directly
+ for candidate in variants:
+ saved = MINIO_ENDPOINT
+ MINIO_ENDPOINT = candidate
+ if MINIO_DEBUG:
+ print(f"[DEBUG] Probing endpoint candidate: {candidate}")
+ try:
+ c = s3_client()
+ try:
+ c.head_bucket(Bucket=probe_bucket)
+ if MINIO_DEBUG:
+ print(f"[DEBUG] head_bucket succeeded on {candidate} for '{probe_bucket}'.")
+ return c
+ except ClientError as e:
+ msg = str(e)
+ # Acceptable if bucket not found (we can create later)
+ if any(code in msg for code in ('404', 'NoSuchBucket', 'NotFound')):
+ if MINIO_DEBUG:
+ print(f"[DEBUG] Bucket not found on {candidate} (expected if first deploy). Using this endpoint.")
+ return c
+ if 'API Requests must be made to API port' in msg:
+ errors.append(f"{candidate}: wrong port (console endpoint)")
+ else:
+ errors.append(f"{candidate}: {msg}")
+ MINIO_ENDPOINT = saved
+ except Exception as ex:
+ errors.append(f"{candidate}: {ex}")
+ MINIO_ENDPOINT = saved
+ continue
+ print("ERROR: Could not validate any endpoint candidate.")
+ for e in errors:
+ print(' - ' + e)
+ print("Provide correct API endpoint (e.g. https://host:9000) via MINIO_ENDPOINT env var.")
+ sys.exit(3)
+
+def list_objects(client, prefix: str):
+ """Generator yielding object keys under a prefix (non-recursive listing with pagination)."""
+ kwargs = {'Bucket': BUCKET_NAME, 'Prefix': prefix}
+ while True:
+ resp = client.list_objects_v2(**kwargs)
+ for obj in resp.get('Contents', []):
+ yield obj['Key']
+ if not resp.get('IsTruncated'):
+ break
+ kwargs['ContinuationToken'] = resp['NextContinuationToken']
+
+def normalize_prefix(p: str) -> str:
+ p = p.strip('/')
+ return p
+
+def join_key(*parts: str) -> str:
+ parts_clean = [p.strip('/') for p in parts if p is not None and p != '']
+ return '/'.join(parts_clean)
+
+def backup_existing_files(client, destination_prefix: str, backups_prefix: str, backup_folder: str):
+ if not destination_prefix:
+ prefix = ''
+ else:
+ prefix = destination_prefix + '/'
+ print(f"Scanning existing objects under '{prefix}' for backup...")
+ for key in list_objects(client, prefix):
+ if backups_prefix and key.startswith(backups_prefix + '/'): # Skip prior backups
+ continue
+ # relative path within destination
+ relative = key[len(prefix):] if prefix and key.startswith(prefix) else key
+ backup_key = join_key(backups_prefix, backup_folder, relative)
+ print(f"Backup copy: {key} -> {backup_key}")
+ client.copy_object(
+ Bucket=BUCKET_NAME,
+ CopySource={'Bucket': BUCKET_NAME, 'Key': key},
+ Key=backup_key,
+ MetadataDirective='COPY'
+ )
+
+def upload_file(client, local_path: str, key: str, cache_control: str = 'private, max-age=0, no-transform'):
+ if not os.path.isfile(local_path):
+ print(f"WARN: File missing, skipping: {local_path}")
+ return
+ print(f"Upload: {local_path} -> s3://{BUCKET_NAME}/{key}")
+ extra_args = { 'CacheControl': cache_control }
+ client.upload_file(local_path, BUCKET_NAME, key, ExtraArgs=extra_args)
+
+def upload_directory(client, local_directory: str, destination_prefix: str):
+ if not os.path.isdir(local_directory):
+ print(f"WARN: Data directory missing: {local_directory}")
+ return
+ skip_dirs = set(DIR_SKIP_LIST)
+ skip_files = set(FILES_SKIP_LIST)
+ for root, dirs, files in os.walk(local_directory):
+ rel_dir = os.path.relpath(root, local_directory).replace('\\','/')
+ if rel_dir == '.':
+ rel_dir = ''
+ # Prune directories in-place if their TOP-LEVEL relative segment matches skip list
+ pruned = []
+ for d in list(dirs):
+ seg = d # immediate subdir name
+ if seg in skip_dirs:
+ if MINIO_DEBUG:
+ print(f"[DEBUG] Skipping directory subtree: {os.path.join(root,d)}")
+ dirs.remove(d)
+ pruned.append(d)
+ for fname in files:
+ if fname in skip_files:
+ if MINIO_DEBUG:
+ print(f"[DEBUG] Skipping file by name: {os.path.join(root,fname)}")
+ continue
+ full = os.path.join(root, fname)
+ rel = os.path.relpath(full, local_directory).replace('\\','/') # force forward slashes for S3 keys
+ # If top-level directory of this file is in skip list, skip (covers deeper nested finds if any slipped through)
+ top_level = rel.split('/',1)[0]
+ if top_level in skip_dirs:
+ if MINIO_DEBUG:
+ print(f"[DEBUG] Skipping file in skipped dir: {rel}")
+ continue
+ key = join_key(destination_prefix, rel)
+ upload_file(client, full, key)
+
+def ensure_bucket(client):
+ """Ensure bucket exists; provide diagnostics if HeadBucket returns 400/other errors."""
+ try:
+ client.head_bucket(Bucket=BUCKET_NAME)
+ if MINIO_DEBUG:
+ print(f"[DEBUG] Bucket '{BUCKET_NAME}' exists.")
+ return
+ except ClientError as e:
+ code = e.response.get('Error', {}).get('Code')
+ status = e.response.get('ResponseMetadata', {}).get('HTTPStatusCode')
+ print(f"HeadBucket failed (code={code}, status={status}).")
+
+ # List buckets for diagnostics
+ try:
+ resp = client.list_buckets()
+ bucket_names = [b['Name'] for b in resp.get('Buckets', [])]
+ print(f"Available buckets: {bucket_names or 'None'}")
+ except Exception as le:
+ print(f"WARN: list_buckets failed: {le}")
+
+ if code in ('404', 'NoSuchBucket', 'NotFound'):
+ print(f"Bucket '{BUCKET_NAME}' not found. Attempting to create...")
+ try:
+ client.create_bucket(Bucket=BUCKET_NAME)
+ print(f"Created bucket '{BUCKET_NAME}'.")
+ return
+ except ClientError as ce:
+ print(f"ERROR: Cannot create bucket: {ce}")
+ sys.exit(2)
+
+ if status == 400:
+ print("HINTS: \n - Verify endpoint URL (MINIO_ENDPOINT).\n - Ensure no trailing slash in endpoint.\n - Check that TLS verify matches server cert (set MINIO_TLS_VERIFY=0 to test).\n - Confirm bucket name is correct and DNS compatible.\n - Credentials may lack permission: verify access key policies.")
+
+ # Retry once forcing path style if not already
+ if MINIO_ADDRESSING != 'path':
+ print("Retrying with path-style addressing...")
+ os.environ['MINIO_ADDRESSING_STYLE'] = 'path'
+ new_client = s3_client()
+ try:
+ new_client.head_bucket(Bucket=BUCKET_NAME)
+ print("Second attempt succeeded with path-style addressing.")
+ return
+ except ClientError as e2:
+ print(f"Second HeadBucket attempt failed: {e2}")
+ print(f"ERROR: head_bucket ultimately failed: {e}")
+ sys.exit(2)
+
+# =============================================================================
+# MAIN
+# =============================================================================
+
+def main():
+ dest_prefix = normalize_prefix(DESTINATION_DIR)
+ backups_prefix = normalize_prefix(BACKUPS_DIR) if BACKUPS_DIR else ''
+ client = create_validated_client()
+ if MINIO_DEBUG:
+ print("[DEBUG] Starting ensure_bucket phase...")
+ ensure_bucket(client)
+
+ # Create backup if needed
+ if CREATE_BACKUP:
+ ts = datetime.datetime.now().strftime('%Y%m%d_%H%M%S')
+ backup_folder = f"backup_{ts}"
+ print(f"Creating backup under '{backups_prefix}/{backup_folder}' from prefix '{dest_prefix}'")
+ backup_existing_files(client, dest_prefix, backups_prefix, backup_folder)
+ print("Backup complete.")
+ else:
+ print("Backup creation skipped.")
+
+ # Firmware
+ if UPLOAD_FIRMWARE:
+ firmware_key = join_key(dest_prefix, 'firmware.bin') if dest_prefix else 'firmware.bin'
+ upload_file(client, LOCAL_FIRMWARE_PATH, firmware_key)
+ print("Firmware upload complete.")
+ else:
+ print("Firmware upload skipped.")
+
+ # Data directory
+ if UPLOAD_DATA:
+ data_prefix = join_key(dest_prefix, 'data') if dest_prefix else 'data'
+ upload_directory(client, LOCAL_DATA_DIRECTORY, data_prefix)
+ print("All uploads complete.")
+ else:
+ print("Data upload skipped.")
+
+ # Manifest
+ if UPDATE_MANIFEST:
+ manifest_key = join_key(dest_prefix, MANIFEST_FILENAME) if dest_prefix else MANIFEST_FILENAME
+ try:
+ manifest_doc = build_and_write_manifest(client, dest_prefix)
+ upload_manifest_json(client, manifest_doc, manifest_key)
+ print(f"Manifest upload complete: s3://{BUCKET_NAME}/{manifest_key}")
+ except Exception as e:
+ print(f"ERROR: Manifest generation/upload failed: {e}")
+ sys.exit(4)
+ else:
+ print("Manifest upload skipped.")
+
+# ================= Manifest Support =================
+
+def md5_hex(path: str, chunk_size: int = 65536) -> str:
+ h = hashlib.md5()
+ with open(path, 'rb') as f:
+ while True:
+ chunk = f.read(chunk_size)
+ if not chunk:
+ break
+ h.update(chunk)
+ return h.hexdigest()
+
+def collect_data_files(data_root: str):
+ files = []
+ if not os.path.isdir(data_root):
+ return files
+ skip_dirs = set(DIR_SKIP_LIST)
+ skip_files = set(FILES_SKIP_LIST)
+ for root, dirs, filenames in os.walk(data_root):
+ # Prune dirs
+ for d in list(dirs):
+ if d in skip_dirs:
+ dirs.remove(d)
+ if MINIO_DEBUG:
+ print(f"[DEBUG] (manifest) Pruned dir: {os.path.join(root,d)}")
+ for fname in filenames:
+ if fname in skip_files:
+ if MINIO_DEBUG:
+ print(f"[DEBUG] (manifest) Skipped file: {os.path.join(root,fname)}")
+ continue
+ full = os.path.join(root, fname)
+ rel = os.path.relpath(full, data_root).replace('\\','/')
+ top_level = rel.split('/',1)[0]
+ if top_level in skip_dirs:
+ if MINIO_DEBUG:
+ print(f"[DEBUG] (manifest) Skipped by top-level dir: {rel}")
+ continue
+ entry = {
+ 'path': f"data/{rel}",
+ 'md5': md5_hex(full),
+ 'size': os.path.getsize(full)
+ }
+ files.append(entry)
+ files.sort(key=lambda x: x['path'])
+ return files
+
+def read_local_manifest(local_path: str):
+ if not os.path.isfile(local_path):
+ raise FileNotFoundError(f"manifest-local file not found: {local_path}")
+ with open(local_path, 'r', encoding='utf-8') as fh:
+ data = json.load(fh)
+ # Basic validation
+ if 'version' not in data:
+ raise ValueError('manifest-local missing version section')
+ ver = data['version']
+ for k in ('major','minor','patch'):
+ if k not in ver:
+ raise ValueError(f"manifest-local version missing '{k}'")
+ data.setdefault('description', '')
+ data.setdefault('changelog', [])
+ if not isinstance(data['changelog'], list):
+ raise ValueError('changelog must be an array')
+ return data
+
+def build_and_write_manifest(client, dest_prefix: str):
+ # Read local manifest-local.json
+ base_info = read_local_manifest(MANIFEST_LOCAL_PATH)
+ # Timestamp
+ now = datetime.datetime.now()
+ release_date = now.strftime('%Y-%m-%d')
+ release_time = now.strftime('%H:%M:%S')
+ # Firmware info
+ fw_path_local = LOCAL_FIRMWARE_PATH
+ if not os.path.isfile(fw_path_local):
+ raise FileNotFoundError(f"Firmware file not found: {fw_path_local}")
+ fw_md5 = md5_hex(fw_path_local)
+ fw_size = os.path.getsize(fw_path_local)
+ # Data files
+ data_files = collect_data_files(LOCAL_DATA_DIRECTORY)
+ manifest = {
+ 'version': {
+ 'major': int(base_info['version']['major']),
+ 'minor': int(base_info['version']['minor']),
+ 'patch': int(base_info['version']['patch'])
+ },
+ 'release_date': release_date,
+ 'release_time': release_time,
+ 'description': base_info.get('description',''),
+ 'changelog': base_info.get('changelog', []),
+ 'firmware': {
+ 'path': 'firmware.bin',
+ 'md5': fw_md5,
+ 'size': fw_size
+ },
+ 'files': data_files
+ }
+ return manifest
+
+def upload_manifest_json(client, manifest_obj: dict, key: str):
+ body = json.dumps(manifest_obj, indent=4).encode('utf-8')
+ client.put_object(
+ Bucket=BUCKET_NAME,
+ Key=key,
+ Body=body,
+ ContentType='application/json',
+ CacheControl='private, max-age=0, no-transform'
+ )
+
+if __name__ == '__main__':
+ main()
diff --git a/firmware_update/UploadToMinio_direct_V3..py b/firmware_update/UploadToMinio_direct_V3..py
new file mode 100644
index 0000000..04f8c9b
--- /dev/null
+++ b/firmware_update/UploadToMinio_direct_V3..py
@@ -0,0 +1,549 @@
+#!/usr/bin/env python3
+"""Upload firmware, manifest, and data assets to a MinIO (S3-compatible) bucket.
+
+Features preserved from original GCS script:
+ - Optional backup (copies existing objects under destination prefix to timestamped folder under backups/)
+ - Upload firmware.bin, update.json, and recursively mirror a data directory
+ - Cache-Control set to disable caching on clients
+
+Switches from google.cloud.storage to boto3 (S3 API) for MinIO compatibility.
+"""
+import os
+import sys
+import datetime
+import hashlib
+import json
+import re
+from pathlib import Path
+
+try:
+ import boto3
+ from botocore.exceptions import ClientError
+ from botocore.config import Config
+except ImportError:
+ print("ERROR: boto3 is required. Install with: pip install boto3")
+ sys.exit(1)
+
+# =============================================================================
+# CONFIGURATION CONSTANTS (edit as needed or supply via environment variables)
+# =============================================================================
+
+CREATE_BACKUP = False
+UPLOAD_FIRMWARE = True
+UPDATE_MANIFEST = True
+UPLOAD_DATA = True
+
+DIR_SKIP_LIST = [
+ "data/system/**/*",
+ "data/booths/**/*"
+]
+
+FILES_SKIP_LIST = [
+ # Add base filenames to skip regardless of directory, e.g. "readme.txt"
+]
+
+# Bucket / endpoint configuration
+BUCKET_NAME = os.getenv('MINIO_BUCKET', 'boothifier')
+DESTINATION_DIR = os.getenv('MINIO_DEST_PREFIX', 'latest') # prefix inside bucket
+BACKUPS_DIR = os.getenv('MINIO_BACKUPS_PREFIX', 'backups')
+
+PROJECT_ROOT_PATH = Path(__file__).parent.parent.resolve()
+LOCAL_ROOT_PATH = Path(__file__).parent.resolve()
+
+# Optional service account style JSON key (generated by MinIO Console). Expected fields:
+# {"url":"https://minio.example.com/api/v1/service-account-credentials","accessKey":"...","secretKey":"...","api":"s3v4","path":"auto"}
+MINIO_KEY_FILE = LOCAL_ROOT_PATH / 'minio-boothifier-key.json'
+
+# Defaults before loading file / env
+_json_access = None
+_json_secret = None
+_json_url = None
+
+def _load_json_key():
+ global _json_access, _json_secret, _json_url
+ try:
+ if MINIO_KEY_FILE.is_file():
+ with open(MINIO_KEY_FILE, 'r', encoding='utf-8') as fh:
+ data = json.load(fh)
+ _json_access = data.get('accessKey') or None
+ _json_secret = data.get('secretKey') or None
+ _json_url = data.get('url') or None
+ except Exception as e:
+ print(f"WARN: Failed to load MinIO key file '{MINIO_KEY_FILE.name}': {e}")
+
+_load_json_key()
+
+def _derive_endpoint(url_value: str) -> str:
+ if not url_value:
+ return 'https://s3-minio.boothwizard.com'
+ # Remove known API suffix if present (/api/...)
+ # e.g. https://s3-minio.boothwizard.com/api/v1/service-account-credentials -> https://s3-minio.boothwizard.com
+ parts = url_value.split('/api/')
+ return parts[0] if parts else url_value
+
+# MinIO credentials with precedence: ENV > JSON file > fallback
+MINIO_ENDPOINT = os.getenv('MINIO_ENDPOINT') or _derive_endpoint(_json_url)
+MINIO_ACCESS_KEY = os.getenv('MINIO_ACCESS_KEY') or _json_access or 'CHANGE_ME_ACCESS'
+MINIO_SECRET_KEY = os.getenv('MINIO_SECRET_KEY') or _json_secret or 'CHANGE_ME_SECRET'
+MINIO_REGION = os.getenv('MINIO_REGION', 'us-east-1') # MinIO ignores but boto3 wants some value
+
+ # Addressing / SSL options
+MINIO_ADDRESSING = os.getenv('MINIO_ADDRESSING_STYLE', 'path').lower() # 'path' or 'virtual'
+MINIO_VERIFY_SSL = os.getenv('MINIO_TLS_VERIFY', '1') not in ('0','false','no')
+MINIO_DEBUG = os.getenv('MINIO_DEBUG', '0') in ('1','true','yes')
+MINIO_ALLOW_VARIANTS = os.getenv('MINIO_ALLOW_ENDPOINT_VARIANTS', '0') in ('1','true','yes') # normally false with nginx redirect
+
+LOCAL_FIRMWARE_PATH = str(PROJECT_ROOT_PATH / '.pio' / 'build' / 'esp32s3dev' / 'firmware.bin')
+LOCAL_DATA_DIRECTORY = str(PROJECT_ROOT_PATH / 'data')
+VERSION_HEADER_PATH = str(PROJECT_ROOT_PATH / 'include' / 'version.h') # source of version/description/changelog
+MANIFEST_FILENAME = os.getenv('MANIFEST_FILENAME', 'manifest.json') # destination manifest name
+
+# =============================================================================
+# HELPERS
+# =============================================================================
+
+def s3_client():
+ """Create an S3 client pointed at MinIO endpoint, forcing path-style unless overridden, with short timeouts."""
+ addressing = 'path' if MINIO_ADDRESSING not in ('virtual','auto') else 'virtual'
+ cfg = Config(
+ s3={'addressing_style': addressing},
+ signature_version='s3v4',
+ connect_timeout=3,
+ read_timeout=5,
+ retries={'max_attempts': 2}
+ )
+ if MINIO_DEBUG:
+ masked_key = (MINIO_ACCESS_KEY[:3] + '...' + MINIO_ACCESS_KEY[-3:]) if MINIO_ACCESS_KEY else 'None'
+ print(f"[DEBUG] Creating client: endpoint={MINIO_ENDPOINT} addressing={addressing} verifySSL={MINIO_VERIFY_SSL} region={MINIO_REGION} accessKey={masked_key}")
+ return boto3.client(
+ 's3',
+ endpoint_url=MINIO_ENDPOINT,
+ aws_access_key_id=MINIO_ACCESS_KEY,
+ aws_secret_access_key=MINIO_SECRET_KEY,
+ region_name=MINIO_REGION,
+ verify=MINIO_VERIFY_SSL,
+ config=cfg,
+ )
+
+def _endpoint_variants(base: str):
+ """Return endpoint variants only if explicitly allowed; otherwise just the base (nginx handles forwarding)."""
+ if not MINIO_ALLOW_VARIANTS:
+ return [base]
+ # Fallback to previous expanded logic if variants are enabled
+ try:
+ variants = []
+ if not base:
+ return variants
+ base = base.rstrip('/')
+ proto_sep = '://'
+ if proto_sep in base:
+ scheme, rest = base.split(proto_sep,1)
+ else:
+ scheme, rest = 'https', base
+ host_port = rest
+ if ':' in host_port:
+ host, port = host_port.split(':',1)
+ else:
+ host, port = host_port, ''
+ variants.append(f"{scheme}://{host_port}")
+ common_ports = ['9000','443','80']
+ for p in common_ports:
+ if port != p:
+ variants.append(f"{scheme}://{host}:{p}")
+ alt_scheme = 'http' if scheme == 'https' else 'https'
+ variants.append(f"{alt_scheme}://{host_port}")
+ for p in common_ports:
+ if port != p:
+ variants.append(f"{alt_scheme}://{host}:{p}")
+ seen = set()
+ uniq = []
+ for v in variants:
+ if v not in seen:
+ uniq.append(v)
+ seen.add(v)
+ return uniq
+ except Exception:
+ return [base]
+
+def create_validated_client():
+ """Validate (or create) client using only provided endpoint unless variants enabled."""
+ global MINIO_ENDPOINT
+ primary = MINIO_ENDPOINT
+ variants = _endpoint_variants(primary) or [primary]
+ errors = []
+ probe_bucket = BUCKET_NAME # we will head the target bucket directly
+ for candidate in variants:
+ saved = MINIO_ENDPOINT
+ MINIO_ENDPOINT = candidate
+ if MINIO_DEBUG:
+ print(f"[DEBUG] Probing endpoint candidate: {candidate}")
+ try:
+ c = s3_client()
+ try:
+ c.head_bucket(Bucket=probe_bucket)
+ if MINIO_DEBUG:
+ print(f"[DEBUG] head_bucket succeeded on {candidate} for '{probe_bucket}'.")
+ return c
+ except ClientError as e:
+ msg = str(e)
+ # Acceptable if bucket not found (we can create later)
+ if any(code in msg for code in ('404', 'NoSuchBucket', 'NotFound')):
+ if MINIO_DEBUG:
+ print(f"[DEBUG] Bucket not found on {candidate} (expected if first deploy). Using this endpoint.")
+ return c
+ if 'API Requests must be made to API port' in msg:
+ errors.append(f"{candidate}: wrong port (console endpoint)")
+ else:
+ errors.append(f"{candidate}: {msg}")
+ MINIO_ENDPOINT = saved
+ except Exception as ex:
+ errors.append(f"{candidate}: {ex}")
+ MINIO_ENDPOINT = saved
+ continue
+ print("ERROR: Could not validate any endpoint candidate.")
+ for e in errors:
+ print(' - ' + e)
+ print("Provide correct API endpoint (e.g. https://host:9000) via MINIO_ENDPOINT env var.")
+ sys.exit(3)
+
+def list_objects(client, prefix: str):
+ """Generator yielding object keys under a prefix (non-recursive listing with pagination)."""
+ kwargs = {'Bucket': BUCKET_NAME, 'Prefix': prefix}
+ while True:
+ resp = client.list_objects_v2(**kwargs)
+ for obj in resp.get('Contents', []):
+ yield obj['Key']
+ if not resp.get('IsTruncated'):
+ break
+ kwargs['ContinuationToken'] = resp['NextContinuationToken']
+
+def normalize_prefix(p: str) -> str:
+ p = p.strip('/')
+ return p
+
+def join_key(*parts: str) -> str:
+ parts_clean = [p.strip('/') for p in parts if p is not None and p != '']
+ return '/'.join(parts_clean)
+
+def backup_existing_files(client, destination_prefix: str, backups_prefix: str, backup_folder: str):
+ if not destination_prefix:
+ prefix = ''
+ else:
+ prefix = destination_prefix + '/'
+ print(f"Scanning existing objects under '{prefix}' for backup...")
+ for key in list_objects(client, prefix):
+ if backups_prefix and key.startswith(backups_prefix + '/'): # Skip prior backups
+ continue
+ # relative path within destination
+ relative = key[len(prefix):] if prefix and key.startswith(prefix) else key
+ backup_key = join_key(backups_prefix, backup_folder, relative)
+ print(f"Backup copy: {key} -> {backup_key}")
+ client.copy_object(
+ Bucket=BUCKET_NAME,
+ CopySource={'Bucket': BUCKET_NAME, 'Key': key},
+ Key=backup_key,
+ MetadataDirective='COPY'
+ )
+
+def upload_file(client, local_path: str, key: str, cache_control: str = 'private, max-age=0, no-transform'):
+ if not os.path.isfile(local_path):
+ print(f"WARN: File missing, skipping: {local_path}")
+ return
+ print(f"Upload: {local_path} -> s3://{BUCKET_NAME}/{key}")
+ extra_args = { 'CacheControl': cache_control }
+ client.upload_file(local_path, BUCKET_NAME, key, ExtraArgs=extra_args)
+
+def upload_directory(client, local_directory: str, destination_prefix: str):
+ if not os.path.isdir(local_directory):
+ print(f"WARN: Data directory missing: {local_directory}")
+ return
+ skip_dirs = set(DIR_SKIP_LIST)
+ skip_files = set(FILES_SKIP_LIST)
+ for root, dirs, files in os.walk(local_directory):
+ rel_dir = os.path.relpath(root, local_directory).replace('\\','/')
+ if rel_dir == '.':
+ rel_dir = ''
+ # Prune directories in-place if their TOP-LEVEL relative segment matches skip list
+ pruned = []
+ for d in list(dirs):
+ seg = d # immediate subdir name
+ if seg in skip_dirs:
+ if MINIO_DEBUG:
+ print(f"[DEBUG] Skipping directory subtree: {os.path.join(root,d)}")
+ dirs.remove(d)
+ pruned.append(d)
+ for fname in files:
+ if fname in skip_files:
+ if MINIO_DEBUG:
+ print(f"[DEBUG] Skipping file by name: {os.path.join(root,fname)}")
+ continue
+ full = os.path.join(root, fname)
+ rel = os.path.relpath(full, local_directory).replace('\\','/') # force forward slashes for S3 keys
+ # If top-level directory of this file is in skip list, skip (covers deeper nested finds if any slipped through)
+ top_level = rel.split('/',1)[0]
+ if top_level in skip_dirs:
+ if MINIO_DEBUG:
+ print(f"[DEBUG] Skipping file in skipped dir: {rel}")
+ continue
+ key = join_key(destination_prefix, rel)
+ upload_file(client, full, key)
+
+def ensure_bucket(client):
+ """Ensure bucket exists; provide diagnostics if HeadBucket returns 400/other errors."""
+ try:
+ client.head_bucket(Bucket=BUCKET_NAME)
+ if MINIO_DEBUG:
+ print(f"[DEBUG] Bucket '{BUCKET_NAME}' exists.")
+ return
+ except ClientError as e:
+ code = e.response.get('Error', {}).get('Code')
+ status = e.response.get('ResponseMetadata', {}).get('HTTPStatusCode')
+ print(f"HeadBucket failed (code={code}, status={status}).")
+
+ # List buckets for diagnostics
+ try:
+ resp = client.list_buckets()
+ bucket_names = [b['Name'] for b in resp.get('Buckets', [])]
+ print(f"Available buckets: {bucket_names or 'None'}")
+ except Exception as le:
+ print(f"WARN: list_buckets failed: {le}")
+
+ if code in ('404', 'NoSuchBucket', 'NotFound'):
+ print(f"Bucket '{BUCKET_NAME}' not found. Attempting to create...")
+ try:
+ client.create_bucket(Bucket=BUCKET_NAME)
+ print(f"Created bucket '{BUCKET_NAME}'.")
+ return
+ except ClientError as ce:
+ print(f"ERROR: Cannot create bucket: {ce}")
+ sys.exit(2)
+
+ if status == 400:
+ print("HINTS: \n - Verify endpoint URL (MINIO_ENDPOINT).\n - Ensure no trailing slash in endpoint.\n - Check that TLS verify matches server cert (set MINIO_TLS_VERIFY=0 to test).\n - Confirm bucket name is correct and DNS compatible.\n - Credentials may lack permission: verify access key policies.")
+
+ # Retry once forcing path style if not already
+ if MINIO_ADDRESSING != 'path':
+ print("Retrying with path-style addressing...")
+ os.environ['MINIO_ADDRESSING_STYLE'] = 'path'
+ new_client = s3_client()
+ try:
+ new_client.head_bucket(Bucket=BUCKET_NAME)
+ print("Second attempt succeeded with path-style addressing.")
+ return
+ except ClientError as e2:
+ print(f"Second HeadBucket attempt failed: {e2}")
+ print(f"ERROR: head_bucket ultimately failed: {e}")
+ sys.exit(2)
+
+# =============================================================================
+# MAIN
+# =============================================================================
+
+def main():
+ dest_prefix = normalize_prefix(DESTINATION_DIR)
+ backups_prefix = normalize_prefix(BACKUPS_DIR) if BACKUPS_DIR else ''
+ client = create_validated_client()
+ if MINIO_DEBUG:
+ print("[DEBUG] Starting ensure_bucket phase...")
+ ensure_bucket(client)
+
+ # Create backup if needed
+ if CREATE_BACKUP:
+ ts = datetime.datetime.now().strftime('%Y%m%d_%H%M%S')
+ backup_folder = f"backup_{ts}"
+ print(f"Creating backup under '{backups_prefix}/{backup_folder}' from prefix '{dest_prefix}'")
+ backup_existing_files(client, dest_prefix, backups_prefix, backup_folder)
+ print("Backup complete.")
+ else:
+ print("Backup creation skipped.")
+
+ # Firmware
+ if UPLOAD_FIRMWARE:
+ firmware_key = join_key(dest_prefix, 'firmware.bin') if dest_prefix else 'firmware.bin'
+ upload_file(client, LOCAL_FIRMWARE_PATH, firmware_key)
+ print("Firmware upload complete.")
+ else:
+ print("Firmware upload skipped.")
+
+ # Data directory
+ if UPLOAD_DATA:
+ data_prefix = join_key(dest_prefix, 'data') if dest_prefix else 'data'
+ upload_directory(client, LOCAL_DATA_DIRECTORY, data_prefix)
+ print("All uploads complete.")
+ else:
+ print("Data upload skipped.")
+
+ # Manifest
+ if UPDATE_MANIFEST:
+ manifest_key = join_key(dest_prefix, MANIFEST_FILENAME) if dest_prefix else MANIFEST_FILENAME
+ try:
+ manifest_doc = build_and_write_manifest(client, dest_prefix)
+ upload_manifest_json(client, manifest_doc, manifest_key)
+ print(f"Manifest upload complete: s3://{BUCKET_NAME}/{manifest_key}")
+ except Exception as e:
+ print(f"ERROR: Manifest generation/upload failed: {e}")
+ sys.exit(4)
+ else:
+ print("Manifest upload skipped.")
+
+# ================= Manifest Support =================
+
+def md5_hex(path: str, chunk_size: int = 65536) -> str:
+ h = hashlib.md5()
+ with open(path, 'rb') as f:
+ while True:
+ chunk = f.read(chunk_size)
+ if not chunk:
+ break
+ h.update(chunk)
+ return h.hexdigest()
+
+def collect_data_files(data_root: str):
+ files = []
+ if not os.path.isdir(data_root):
+ return files
+ skip_dirs = set(DIR_SKIP_LIST)
+ skip_files = set(FILES_SKIP_LIST)
+ for root, dirs, filenames in os.walk(data_root):
+ # Prune dirs
+ for d in list(dirs):
+ if d in skip_dirs:
+ dirs.remove(d)
+ if MINIO_DEBUG:
+ print(f"[DEBUG] (manifest) Pruned dir: {os.path.join(root,d)}")
+ for fname in filenames:
+ if fname in skip_files:
+ if MINIO_DEBUG:
+ print(f"[DEBUG] (manifest) Skipped file: {os.path.join(root,fname)}")
+ continue
+ full = os.path.join(root, fname)
+ rel = os.path.relpath(full, data_root).replace('\\','/')
+ top_level = rel.split('/',1)[0]
+ if top_level in skip_dirs:
+ if MINIO_DEBUG:
+ print(f"[DEBUG] (manifest) Skipped by top-level dir: {rel}")
+ continue
+ entry = {
+ 'path': f"data/{rel}",
+ 'md5': md5_hex(full),
+ 'size': os.path.getsize(full)
+ }
+ files.append(entry)
+ files.sort(key=lambda x: x['path'])
+ return files
+
+def parse_version_header(path: str):
+ """Parse version.h to extract version numbers, description, and changelog lines."""
+ if not os.path.isfile(path):
+ raise FileNotFoundError(f"version header not found: {path}")
+ with open(path, 'r', encoding='utf-8') as f:
+ lines = f.readlines()
+
+ major = minor = patch = None
+ desc_lines = []
+ changelog_lines = []
+ state = None # None | DESC | CHANGELOG
+
+ re_major = re.compile(r'#define\s+FIRMWARE_VERSION_MAJOR\s+(\d+)')
+ re_minor = re.compile(r'#define\s+FIRMWARE_VERSION_MINOR\s+(\d+)')
+ re_patch = re.compile(r'#define\s+FIRMWARE_VERSION_PATCH\s+(\d+)')
+ re_quote = re.compile(r'^\s*"(.*)"\\?\s*$')
+
+ for raw in lines:
+ line = raw.rstrip('\n')
+ if major is None:
+ m = re_major.match(line)
+ if m:
+ major = int(m.group(1))
+ continue
+ if minor is None:
+ m = re_minor.match(line)
+ if m:
+ minor = int(m.group(1))
+ continue
+ if patch is None:
+ m = re_patch.match(line)
+ if m:
+ patch = int(m.group(1))
+ continue
+ if line.startswith('#define FIRMWARE_DESCRIPTION'):
+ state = 'DESC'
+ continue
+ if line.startswith('#define FIRMWARE_CHANGELOG'):
+ state = 'CHANGELOG'
+ continue
+ if line.startswith('#define') and state in ('DESC','CHANGELOG'):
+ state = None
+ if state in ('DESC','CHANGELOG'):
+ mq = re_quote.match(line.strip())
+ if mq:
+ text = mq.group(1).replace('\\n', '\n')
+ if state == 'DESC':
+ desc_lines.append(text)
+ else:
+ changelog_lines.append(text)
+ else:
+ state = None
+
+ if None in (major, minor, patch):
+ raise ValueError('Failed to parse version numbers from version.h')
+
+ description = '\n'.join([t for t in desc_lines if t]).strip()
+ changelog = []
+ for seg in changelog_lines:
+ for part in seg.split('\n'):
+ part = part.strip()
+ if part:
+ changelog.append(part)
+ return {
+ 'version': {'major': major, 'minor': minor, 'patch': patch},
+ 'description': description,
+ 'changelog': changelog
+ }
+
+def build_and_write_manifest(client, dest_prefix: str):
+ # Parse version.h instead of manifest-local.json
+ base_info = parse_version_header(VERSION_HEADER_PATH)
+ # Timestamp
+ now = datetime.datetime.now()
+ release_date = now.strftime('%Y-%m-%d')
+ release_time = now.strftime('%H:%M:%S')
+ # Firmware info
+ fw_path_local = LOCAL_FIRMWARE_PATH
+ if not os.path.isfile(fw_path_local):
+ raise FileNotFoundError(f"Firmware file not found: {fw_path_local}")
+ fw_md5 = md5_hex(fw_path_local)
+ fw_size = os.path.getsize(fw_path_local)
+ # Data files
+ data_files = collect_data_files(LOCAL_DATA_DIRECTORY)
+ manifest = {
+ 'version': {
+ 'major': int(base_info['version']['major']),
+ 'minor': int(base_info['version']['minor']),
+ 'patch': int(base_info['version']['patch'])
+ },
+ 'release_date': release_date,
+ 'release_time': release_time,
+ 'description': base_info.get('description',''),
+ 'changelog': base_info.get('changelog', []),
+ 'firmware': {
+ 'path': 'firmware.bin',
+ 'md5': fw_md5,
+ 'size': fw_size
+ },
+ 'files': data_files
+ }
+ return manifest
+
+def upload_manifest_json(client, manifest_obj: dict, key: str):
+ body = json.dumps(manifest_obj, indent=4).encode('utf-8')
+ client.put_object(
+ Bucket=BUCKET_NAME,
+ Key=key,
+ Body=body,
+ ContentType='application/json',
+ CacheControl='private, max-age=0, no-transform'
+ )
+
+if __name__ == '__main__':
+ main()
diff --git a/firmware_update/latest/data/ata-boothifier-upgrade.html b/firmware_update/latest/data/ata-boothifier-upgrade.html
deleted file mode 100644
index 3327522..0000000
--- a/firmware_update/latest/data/ata-boothifier-upgrade.html
+++ /dev/null
@@ -1,508 +0,0 @@
-
-
-
-
-
- ATA Firmware Update
-
-
-
-
- ATA Firmware Update
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/firmware_update/latest/data/ata-boothifier-upgradeV2.html b/firmware_update/latest/data/ata-boothifier-upgradeV2.html
deleted file mode 100644
index d95fd0b..0000000
--- a/firmware_update/latest/data/ata-boothifier-upgradeV2.html
+++ /dev/null
@@ -1,547 +0,0 @@
-
-
-
-
-
- ATA Firmware Update
-
-
-
-
- ATA Firmware Update
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/firmware_update/latest/data/ata-boothifier-upgradeV3.html b/firmware_update/latest/data/ata-boothifier-upgradeV3.html
deleted file mode 100644
index 8f500d2..0000000
--- a/firmware_update/latest/data/ata-boothifier-upgradeV3.html
+++ /dev/null
@@ -1,476 +0,0 @@
-
-
-
-
-
- ATA Firmware Update
-
-
-
-
- ATA Firmware Update
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/firmware_update/latest/data/css/global-style.css b/firmware_update/latest/data/css/global-style.css
deleted file mode 100644
index a4edce2..0000000
--- a/firmware_update/latest/data/css/global-style.css
+++ /dev/null
@@ -1,79 +0,0 @@
-body {
- /*background-color: #f7f7f7;*/
- font-family: Tahoma, Arial, sans-serif;
- font-size: small;
- margin: 0;
- display: flex;
- min-height: 100vh;
-}
-.main-container {
- display: flex;
- flex-direction: column;
- align-items: center;
- width: 100%;
- padding: 20px;
-}
-h1 {
- margin-top: 0;
-}
-input{
- cursor:pointer;
-}
-input[type="number"]{
- width: 100px;
-}
-#submit {
- width:120px;
-}
-select{
- width:160px;
-}
-#select-path {
- width:250px;
-}
-#select-dir {
- width:90px;
-}
-#spacer-50 {
- height: 50px;
-}
-#spacer-20 {
- height: 20px;
-}
-#spacer-10 {
- height: 10px;
-}
-table {
- /*background-color: #dddddd;*/
- border-collapse: collapse;
- width:600px;
- margin: 0 auto;
- overflow: visible;
-}
-td, th {
- /*border: 1px solid #dddddd;*/
- text-align: left;
- padding: 2px;
-}
-#first_td_th {
- width:400px;
-}
-fieldset {
- width:620px;
- /*background-color: #f7f7f7;*/
- border-radius: 10px;
- border-color: blue;
-}
-#format-notice {
- color: #ff0000;
-}
-legend {
- display: flex;
- justify-content: center;
- background-color:white;
- background-blend-mode: darken;
- border-radius: 10px;
- padding: 1px 8px 2px 8px;
- border-style: solid;
- border-width: 1.0;
-}
\ No newline at end of file
diff --git a/firmware_update/latest/data/css/nav.css b/firmware_update/latest/data/css/nav.css
deleted file mode 100644
index f74f175..0000000
--- a/firmware_update/latest/data/css/nav.css
+++ /dev/null
@@ -1,72 +0,0 @@
-.navbar {
- width: 100%;
- background-color: black;
- border-bottom: 2px solid white;
- display: flex;
- justify-content: flex-start;
- align-items: center;
- position: sticky;
- top: 0;
- z-index: 1000;
- }
- .navbar-left {
- padding: 0 10px;
- }
- .navbar ul {
- list-style-type: none;
- margin: 0;
- padding: 0;
- display: flex;
- align-items: center;
- position: relative; /* Ensure relative positioning for the submenu */
- }
- .navbar ul li {
- float: left;
- position: relative; /* Ensure relative positioning for the submenu */
- }
- .navbar ul li a {
- display: block;
- color: white;
- text-align: center;
- padding: 14px 16px;
- text-decoration: none;
- border-right: 1px solid white;
- }
- .navbar ul li a:hover {
- background-color: grey;
- }
- .navbar ul li a.disabled {
- color: grey;
- pointer-events: none;
- }
- .navbar ul .submenu {
- display: none;
- position: absolute;
- top: 100%;
- left: 0;
- background-color: black;
- list-style-type: none;
- margin: 0;
- padding: 0;
- border-top: 2px solid white;
- z-index: 1000;
- }
- .navbar ul .submenu li {
- float: none;
- border-right: none;
- }
- .navbar ul .submenu li a {
- padding: 10px 16px;
- border-bottom: 1px solid white;
- }
- .navbar ul .submenu li a:hover {
- background-color: grey;
- }
- .navbar ul li:hover > .submenu {
- display: block;
- }
- .nav-image {
- height: 40px;
- width: auto;
- }
-
\ No newline at end of file
diff --git a/firmware_update/latest/data/favicon.ico b/firmware_update/latest/data/favicon.ico
deleted file mode 100644
index 3030261..0000000
Binary files a/firmware_update/latest/data/favicon.ico and /dev/null differ
diff --git a/firmware_update/latest/data/flashstik-reg.html b/firmware_update/latest/data/flashstik-reg.html
deleted file mode 100644
index 7cef51f..0000000
--- a/firmware_update/latest/data/flashstik-reg.html
+++ /dev/null
@@ -1,472 +0,0 @@
-
-
-
-
-
- ATA Light Stick Reg
-
-
-
-
- ATA Flash-Stick Link/Registration
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/firmware_update/latest/data/images/atalogo.png b/firmware_update/latest/data/images/atalogo.png
deleted file mode 100644
index 40f3ee6..0000000
Binary files a/firmware_update/latest/data/images/atalogo.png and /dev/null differ
diff --git a/firmware_update/latest/data/images/favicon-32x32.png b/firmware_update/latest/data/images/favicon-32x32.png
deleted file mode 100644
index 5118fd1..0000000
Binary files a/firmware_update/latest/data/images/favicon-32x32.png and /dev/null differ
diff --git a/firmware_update/latest/data/js/event-box.js b/firmware_update/latest/data/js/event-box.js
deleted file mode 100644
index cb01b1b..0000000
--- a/firmware_update/latest/data/js/event-box.js
+++ /dev/null
@@ -1,442 +0,0 @@
-class EventBox extends HTMLElement {
- constructor() {
- super();
- this.attachShadow({ mode: 'open' });
- this.shadowRoot.innerHTML = `
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- `;
- this.Title = this.shadowRoot.querySelector('#center-text');
- this.hueSelect = this.shadowRoot.querySelector('#hue-selector');
- this.AnimationList = this.shadowRoot.getElementById('animation-list');
- this.Speed = this.shadowRoot.getElementById('speed');
- this.HueRange = this.shadowRoot.querySelector('#huerange');
- this.Param1 = this.shadowRoot.querySelector('#param1');
- this.Param2 = this.shadowRoot.querySelector('#param2');
- this.Check1 = this.shadowRoot.querySelector('#check1');
- this.Check2 = this.shadowRoot.querySelector('#check2');
- this.Check3 = this.shadowRoot.querySelector('#check3');
- this.Check4 = this.shadowRoot.querySelector('#check4');
-
- this.AnimationListLabel = this.shadowRoot.querySelector('#list-label');
- this.SpeedLabel = this.shadowRoot.querySelector('#speed-label');
- this.HueRangeLabel = this.shadowRoot.querySelector('#huerange-label');
- this.Param1Label = this.shadowRoot.getElementById('param1-label');
- this.Param2Label = this.shadowRoot.getElementById('param2-label');
- this.Check1Label = this.shadowRoot.getElementById('check1-label');
- this.Check2Label = this.shadowRoot.getElementById('check2-label');
- this.Check3Label = this.shadowRoot.getElementById('check3-label');
- this.Check4Label = this.shadowRoot.getElementById('check4-label');
-
- this.AnimationPropsJson;
-
- this.SpeedCaption = "";
- this.HueRangeCaption = "";
- this.Param1Caption = "";
- this.Param2Caption = "";
- this.Check1Caption = "";
- this.Check2Caption = "";
- this.Check3Caption = "";
-
- this.setSpeedCaption(`Speed: `);
- this.Speed.min = 0;
- this.Speed.max = 100;
-
- this.setHueRangeCaption(`Hue Range: `);
- this.HueRange.min = 0;
- this.HueRange.max = 360;
-
- this.setParam1Caption(`Param1: `);
- this.Param1.min = 0;
- this.Param1.max = 100;
-
- this.setParam2Caption(`Param2: `);
- this.Param2.min = 0;
- this.Param2.max = 100;
-
- this.setCheck1Caption(`Check1`);
- this.setCheck2Caption(`Check2`);
- this.setCheck3Caption(`Check3`);
- this.setCheck4Caption(`50% Lum`);
-
- this.Index = 0;
-
- this.Speed.addEventListener('input', () => { this.updateSpeedLabel(); });
- this.HueRange.addEventListener('input', () => { this.updateHueRangeLabel(); });
- this.Param1.addEventListener('input', () => { this.updateParam1Label(); });
- this.Param2.addEventListener('input', () => { this.updateParam2Label(); });
-
- this.TryButton = this.shadowRoot.querySelector('#try-button');
- this.TryButton.addEventListener('click', () => {
- this.handleTryButtonClick();
- });
-
- this.AnimationList.addEventListener('change', this.handleAnimationListChange.bind(this));
- }
-
- setIndex(i){ this.Index = i; }
- getIndex(){ return this.Index; }
- updateSpeedLabel(){
- if(!this.Speed.hidden){
- this.SpeedLabel.innerHTML = this.SpeedCaption + this.getSpeedValue();
- }
- }
- updateHueRangeLabel(){
- if(!this.HueRange.hidden){
- this.HueRangeLabel.innerHTML = this.HueRangeCaption + this.getHueRangeValue();
- }
- }
- updateParam1Label(){
- if(!this.Param1.hidden){
- this.Param1Label.innerHTML = this.Param1Caption + this.getParam1Value();
- }
- }
- updateParam2Label(){
- if(!this.Param2.hidden){
- this.Param2Label.innerHTML = this.Param2Caption + this.getParam2Value();
- }
- }
- setTitle(text){
- this.Title.textContent = text;
- }
- addOptionToList(value, text){
- const optionElement = document.createElement('option');
- optionElement.value = value;
- optionElement.textContent = text;
- this.AnimationList.appendChild(optionElement);
- }
- setHidden(hidden){ this.hidden = hidden; }
- getHidden(){ return this.hidden; }
- setHueValue(value){ this.hueSelect.setHue(value); }
- getHueValue(){ return this.hueSelect.getSelectedHue(); }
- setSpeedValue(value){
- this.Speed.value = value;
- this.updateSpeedLabel();
- }
- setSpeedCaption(caption){
- let hid = false;
- if(caption.trim() === ""){
- hid = true;
- }
- this.SpeedCaption = caption;
- this.SpeedLabel.innerHTML = caption;
- this.Speed.hidden = hid;
- this.SpeedLabel.hidden = hid;
- }
- getSpeedValue(){ return this.Speed.value; }
- setHueRangeValue(value){
- this.HueRange.value = value;
- this.updateHueRangeLabel()
- }
- setHueRangeCaption(caption){
- let hid = false;
- if(caption.trim() === ""){
- hid = true;
- }
- this.HueRangeCaption = caption;
- this.HueRangeLabel.innerHTML = caption;
- this.HueRange.hidden = hid;
- this.HueRangeLabel.hidden = hid;
- }
- getHueRangeValue(){ return this.HueRange.value; }
- setParam1Value(value){
- this.Param1.value = value;
- this.updateParam1Label()
- }
- setParam1Caption(caption){
- let hid = false;
- if(caption.trim() === ""){
- hid = true;
- }
- this.Param1Caption = caption;
- this.Param1Label.innerHTML = caption;
- this.Param1.hidden = hid;
- this.Param1Label.hidden = hid;
- }
- getParam1Value(){ return this.Param1.value; }
- setParam2Value(value){
- this.Param2.value = value;
- this.updateParam2Label()
- }
- setParam2Caption(caption){
- let hid = false;
- if(caption.trim() === ""){
- hid = true;
- }
- this.Param2Caption = caption;
- this.Param2Label.innerHTML = caption;
- this.Param2.hidden = hid;
- this.Param2Label.hidden = hid;
- }
- getParam2Value(){ return this.Param2.value; }
-
- setCheck1Value(value){ this.Check1.checked = value; }
- setCheck1Caption(caption){
- let hid = false;
- if(caption.trim() === ""){
- hid = true;
- }
- this.Check1Label.innerHTML = caption;
- this.Check1.hidden = hid;
- this.Check1Label.hidden = hid;
- }
- getCheck1Value(){ return this.Check1.checked; }
-
- setCheck2Value(value){ this.Check2.checked = value; }
- setCheck2Caption(caption){
- let hid = false;
- if(caption.trim() === ""){
- hid = true;
- }
- this.Check2Label.innerHTML = caption;
- this.Check2.hidden = hid;
- this.Check2Label.hidden = hid;
- }
- getCheck2Value(){ return this.Check2.checked; }
-
- setCheck3Value(value){ this.Check3.checked = value; }
- setCheck3Caption(caption){
- let hid = false;
- if(caption.trim() === ""){
- hid = true;
- }
- this.Check3Label.innerHTML = caption;
- this.Check3.hidden = hid;
- this.Check3Label.hidden = hid;
- }
- getCheck3Value(){ return this.Check3.checked; }
-
- setCheck4Value(value){ this.Check4.checked = value; }
- setCheck4Caption(caption){
- let hid = false;
- if(caption.trim() === ""){
- hid = true;
- }
- this.Check4Label.innerHTML = caption;
- this.Check4.hidden = hid;
- this.Check4Label.hidden = hid;
- }
- getCheck4Value(){ return this.Check4.checked; }
-
- setAnimationIndex(index){
- this.AnimationList.selectedIndex = index;
- const changeEvent = new Event('change');
- this.AnimationList.dispatchEvent(changeEvent);
- }
- getAnimationIndex(){
- return this.AnimationList.selectedIndex;
- }
-
- handleTryButtonClick() {
- const eventIndexValue = this.getIndex();
- const animIndexValue = this.getAnimationIndex();
- const hueValue = this.getHueValue();
- const speedValue = this.getSpeedValue();
- const colorRangeValue = this.getHueRangeValue();
- const param1Value = this.getParam1Value();
- const param2Value = this.getParam2Value();
- const check1Value = this.getCheck1Value();
- const check2Value = this.getCheck2Value();
- const check3Value = this.getCheck3Value();
- const check4Value = this.getCheck4Value();
-
- this.dispatchEvent(new CustomEvent('tryClick', {
- detail: {
- eventIndex: eventIndexValue,
- animIndex: animIndexValue,
- hue: hueValue,
- speed: speedValue,
- colorRange: colorRangeValue,
- param1: param1Value,
- param2: param2Value,
- check1: check1Value,
- check2: check2Value,
- check3: check3Value,
- check4: check4Value
- }
- }));
- }
-
- setAnimationCaptions(propsJson){
- this.AnimationPropsJson = propsJson;
-
- //add options to list
- let x = 0;
- this.AnimationPropsJson.forEach(props => {
- this.addOptionToList(x, props.name);
- x++;
- });
-
- }
-
- handleAnimationListChange(){
- const selectedIndex = this.getAnimationIndex();
- const selectedProps = this.AnimationPropsJson[selectedIndex];
-
- this.updateControlProps(selectedProps);
- }
-
- updateControlProps(props){
- this.setSpeedCaption(props.speed);
- let s = props['hue-range'];
- this.setHueRangeCaption(s || "");
- this.setParam1Caption(props.param1 || "");
- this.setParam2Caption(props.param2 || "");
- this.setCheck1Caption(props.check1 || "");
- this.setCheck2Caption(props.check2 || "");
- this.setCheck3Caption(props.check3 || "");
- this.setCheck4Caption(props.check4 || "");
-
- this.updateSpeedLabel();
- this.updateHueRangeLabel();
- this.updateParam1Label();
- this.updateParam2Label();
- }
-
-}
-
-customElements.define('event-box', EventBox);
-
\ No newline at end of file
diff --git a/firmware_update/latest/data/js/fwUoload.js b/firmware_update/latest/data/js/fwUoload.js
deleted file mode 100644
index 446f3b4..0000000
--- a/firmware_update/latest/data/js/fwUoload.js
+++ /dev/null
@@ -1,64 +0,0 @@
-async function uploadFile(event) {
- event.preventDefault();
-
- const file = fileInput.files[0];
- if (!file) {
- alert("Please select a file.");
- return;
- }
-
- // Existing file validation code...
-
- const formData = new FormData();
- formData.append('file', file);
-
- // Create a custom reader function
- const reader = file.stream().getReader();
- const totalLength = file.size;
-
- let uploaded = 0;
-
- // Create a readable stream and use the custom reader function
- const uploadStream = new ReadableStream({
- async start(controller) {
- while (true) {
- const { done, value } = await reader.read();
- if (done) break;
-
- uploaded += value.length;
- let percentUploaded = (uploaded / totalLength * 100).toFixed(2);
-
- // Update progress bar and label
- progressBar.value = percentUploaded;
- progressLabel.innerHTML = `${percentUploaded}%`;
-
- controller.enqueue(value);
- }
- controller.close();
- },
- });
-
- // Create a new Request with the readable stream as body
- const req = new Request('/update', {
- method: 'POST',
- body: uploadStream,
- headers: {
- // Add any relevant headers here
- },
- });
-
- try {
- const response = await fetch(req);
-
- if (response.ok) {
- alert('Upload completed!');
- progressLabel.innerHTML = "Completed!";
- } else {
- alert('An error occurred during the upload.');
- progressLabel.innerHTML = "Error!";
- }
- } catch (error) {
- console.error('Upload failed:', error);
- }
- }
-
\ No newline at end of file
diff --git a/firmware_update/latest/data/js/hue-select.js b/firmware_update/latest/data/js/hue-select.js
deleted file mode 100644
index ca067d1..0000000
--- a/firmware_update/latest/data/js/hue-select.js
+++ /dev/null
@@ -1,244 +0,0 @@
-
-class HueSelect extends HTMLElement {
- constructor() {
- super();
- this.attachShadow({ mode: 'open' });
- this.shadowRoot.innerHTML = `
-
-
- `;
-
- this.currentHue = 0;
- this.colorList;
- this.hueLabel = this.shadowRoot.getElementById('hue-label');
-
- const selectedColorDiv = this.shadowRoot.getElementById('selectedColor');
- const colorPatch = selectedColorDiv.querySelector('.color-patch');
-
- selectedColorDiv.addEventListener('click', () => {
- const dropdownContent = this.shadowRoot.querySelector('.dropdown-content');
- dropdownContent.style.display = dropdownContent.style.display === 'block' ? 'none' : 'block';
- });
-
- this.createColorOptions();
- this.setHue(0);
- }
-
- generateColors() {
- const colors = [];
- for (let hue = 0; hue <= 360; hue += 10) {
- if (hue == 360) { hue = 359; }
- colors.push(hue);
- }
-
- colors.push(-1);
- colors.push(-2);
- return colors;
- }
-
- createColorOption(hue) {
- const colorOption = document.createElement('div');
- colorOption.classList.add('color-option');
-
- const colorPatch = document.createElement('span');
- colorPatch.classList.add('color-patch');
-
- const colorText = document.createElement('span');
- colorText.classList.add('color-text');
- colorText.textContent = hue;
-
- const rgbHex = document.createElement('span');
- rgbHex.classList.add('rgb-hex');
-
- if (hue === -2) {
- colorPatch.style.backgroundColor = 'rgb(0,0,0)';
- rgbHex.innerHTML = ' #000000';
- } else if (hue === -1) {
- colorPatch.style.backgroundColor = 'rgb(255,255,255)';
- rgbHex.innerHTML = ' #FFFFFF';
- } else {
- colorPatch.style.backgroundColor = `hsl(${hue}, 100%, 50%)`;
- const hexColor = this.hslToRgb(hue, 100, 50).toUpperCase();
- rgbHex.innerHTML = ` ${hexColor}`;
- }
-
- colorOption.appendChild(colorPatch);
- colorOption.appendChild(colorText);
- colorOption.appendChild(rgbHex);
-
- colorOption.addEventListener('click', () => this.handleColorSelection(hue));
-
- return colorOption;
- }
-
- createColorOptions() {
- const dropdownContent = this.shadowRoot.querySelector('.dropdown-content');
- this.colorList = this.generateColors();
-
- this.colorList.forEach(hue => {
- const colorOption = this.createColorOption(hue);
- dropdownContent.appendChild(colorOption);
- });
- }
-
- // Function to convert HSL to RGB
- hslToRgb(h, s, l) {
- h /= 360;
- s /= 100;
- l /= 100;
- let r, g, b;
- if (s === 0) {
- r = g = b = l; // achromatic
- } else {
- const hue2rgb = (p, q, t) => {
- if (t < 0) t += 1;
- if (t > 1) t -= 1;
- if (t < 1 / 6) return p + (q - p) * 6 * t;
- if (t < 1 / 2) return q;
- if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;
- return p;
- };
- const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
- const p = 2 * l - q;
- r = hue2rgb(p, q, h + 1 / 3);
- g = hue2rgb(p, q, h);
- b = hue2rgb(p, q, h - 1 / 3);
- }
- const toHex = (x) => {
- const hex = Math.round(x * 255).toString(16);
- return hex.length === 1 ? '0' + hex : hex;
- };
- return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
- }
-
- handleColorSelection(hue) {
- this.currentHue = hue;
- const selectedColorDiv = this.shadowRoot.getElementById('selectedColor');
- const colorPatch = selectedColorDiv.querySelector('.color-patch');
-
- // Update the color patch and hue label
- const rgb = this.getRGBfromHue(hue);
- colorPatch.style.backgroundColor = rgb;
- this.setHueLabel(hue);
- this.hideDropdown();
-
- this.dispatchEvent(new CustomEvent('change', { detail: { hue, rgb } }));
- }
-
- // Method to get the hue value of the selected item
- getSelectedHue() {
- return this.currentHue;
- }
-
- getSelectedRGB() {
- const selectedColorText = this.shadowRoot.getElementById('selectedColor').textContent.trim();
- return parseFloat(selectedColorText);
- }
-
- getRGBfromHue(hue){
- if(hue == -1){
- return '#FFFFFF';
- }else if(hue == -2){
- return '#000000';
- }else{
- return this.hslToRgb(hue, 100, 50);;
- }
- }
-
- // Method to get the RGB value of the selected item
- getSelectedRgb() {
- const selectedHue = this.getSelectedHue();
- return getRGBfromHue(selectedHue);
- }
-
- setHue(hue) {
- // Round the input hue to the nearest 10th
- let roundedHue = hue;
- if(hue === -1 || hue === -2){
- roundedHue = hue;
- }else{
- roundedHue = Math.round(hue / 10) * 10;
- if (roundedHue >= 360) {
- roundedHue = 359;
- }else if (roundedHue < 0) {
- roundedHue = 0;
- }
- }
-
- this.currentHue = roundedHue;
-
- this.colorList.forEach(colorVal => {
- if (colorVal === roundedHue) {
- this.handleColorSelection(roundedHue);
- }
- });
-
- this.hideDropdown();
- }
-
- hideDropdown() {
- const dropdownContent = this.shadowRoot.querySelector('.dropdown-content');
- dropdownContent.style.display = 'none';
- }
-
- setHueLabel(value){
- this.hueLabel.textContent = "Hue: " + value;
- }
-
-
- }
-
- customElements.define('hue-select', HueSelect);
-
\ No newline at end of file
diff --git a/firmware_update/latest/data/js/jquery-3.7.1.js b/firmware_update/latest/data/js/jquery-3.7.1.js
deleted file mode 100644
index f7d84ab..0000000
--- a/firmware_update/latest/data/js/jquery-3.7.1.js
+++ /dev/null
@@ -1,8673 +0,0 @@
-( function( global, factory ) {
-
- "use strict";
-
- if ( typeof module === "object" && typeof module.exports === "object" ) {
-
- module.exports = global.document ?
- factory( global, true ) :
- function( w ) {
- if ( !w.document ) {
- throw new Error( "jQuery requires a window with a document" );
- }
- return factory( w );
- };
- } else {
- factory( global );
- }
-
-} )( typeof window !== "undefined" ? window : this, function( window, noGlobal ) {
-
-"use strict";
-
-var arr = [];
-
-var getProto = Object.getPrototypeOf;
-
-var slice = arr.slice;
-
-var flat = arr.flat ? function( array ) {
- return arr.flat.call( array );
-} : function( array ) {
- return arr.concat.apply( [], array );
-};
-
-var push = arr.push;
-
-var indexOf = arr.indexOf;
-
-var class2type = {};
-
-var toString = class2type.toString;
-
-var hasOwn = class2type.hasOwnProperty;
-
-var fnToString = hasOwn.toString;
-
-var ObjectFunctionString = fnToString.call( Object );
-
-var support = {};
-
-var isFunction = function isFunction( obj ) {
-
- return typeof obj === "function" && typeof obj.nodeType !== "number" &&
- typeof obj.item !== "function";
- };
-
-var isWindow = function isWindow( obj ) {
- return obj != null && obj === obj.window;
- };
-
-var document = window.document;
-
- var preservedScriptAttributes = {
- type: true,
- src: true,
- nonce: true,
- noModule: true
- };
-
- function DOMEval( code, node, doc ) {
- doc = doc || document;
-
- var i, val,
- script = doc.createElement( "script" );
-
- script.text = code;
- if ( node ) {
- for ( i in preservedScriptAttributes ) {
-
- val = node[ i ] || node.getAttribute && node.getAttribute( i );
- if ( val ) {
- script.setAttribute( i, val );
- }
- }
- }
- doc.head.appendChild( script ).parentNode.removeChild( script );
- }
-
-function toType( obj ) {
- if ( obj == null ) {
- return obj + "";
- }
-
- return typeof obj === "object" || typeof obj === "function" ?
- class2type[ toString.call( obj ) ] || "object" :
- typeof obj;
-}
-
-var version = "3.7.1",
-
- rhtmlSuffix = /HTML$/i,
-
- jQuery = function( selector, context ) {
-
- return new jQuery.fn.init( selector, context );
- };
-
-jQuery.fn = jQuery.prototype = {
-
- jquery: version,
-
- constructor: jQuery,
-
- length: 0,
-
- toArray: function() {
- return slice.call( this );
- },
-
- get: function( num ) {
-
- if ( num == null ) {
- return slice.call( this );
- }
-
- return num < 0 ? this[ num + this.length ] : this[ num ];
- },
-
- pushStack: function( elems ) {
-
- var ret = jQuery.merge( this.constructor(), elems );
-
- ret.prevObject = this;
-
- return ret;
- },
-
- each: function( callback ) {
- return jQuery.each( this, callback );
- },
-
- map: function( callback ) {
- return this.pushStack( jQuery.map( this, function( elem, i ) {
- return callback.call( elem, i, elem );
- } ) );
- },
-
- slice: function() {
- return this.pushStack( slice.apply( this, arguments ) );
- },
-
- first: function() {
- return this.eq( 0 );
- },
-
- last: function() {
- return this.eq( -1 );
- },
-
- even: function() {
- return this.pushStack( jQuery.grep( this, function( _elem, i ) {
- return ( i + 1 ) % 2;
- } ) );
- },
-
- odd: function() {
- return this.pushStack( jQuery.grep( this, function( _elem, i ) {
- return i % 2;
- } ) );
- },
-
- eq: function( i ) {
- var len = this.length,
- j = +i + ( i < 0 ? len : 0 );
- return this.pushStack( j >= 0 && j < len ? [ this[ j ] ] : [] );
- },
-
- end: function() {
- return this.prevObject || this.constructor();
- },
-
- push: push,
- sort: arr.sort,
- splice: arr.splice
-};
-
-jQuery.extend = jQuery.fn.extend = function() {
- var options, name, src, copy, copyIsArray, clone,
- target = arguments[ 0 ] || {},
- i = 1,
- length = arguments.length,
- deep = false;
-
- if ( typeof target === "boolean" ) {
- deep = target;
-
- target = arguments[ i ] || {};
- i++;
- }
-
- if ( typeof target !== "object" && !isFunction( target ) ) {
- target = {};
- }
-
- if ( i === length ) {
- target = this;
- i--;
- }
-
- for ( ; i < length; i++ ) {
-
- if ( ( options = arguments[ i ] ) != null ) {
-
- for ( name in options ) {
- copy = options[ name ];
-
- if ( name === "__proto__" || target === copy ) {
- continue;
- }
-
- if ( deep && copy && ( jQuery.isPlainObject( copy ) ||
- ( copyIsArray = Array.isArray( copy ) ) ) ) {
- src = target[ name ];
-
- if ( copyIsArray && !Array.isArray( src ) ) {
- clone = [];
- } else if ( !copyIsArray && !jQuery.isPlainObject( src ) ) {
- clone = {};
- } else {
- clone = src;
- }
- copyIsArray = false;
-
- target[ name ] = jQuery.extend( deep, clone, copy );
-
- } else if ( copy !== undefined ) {
- target[ name ] = copy;
- }
- }
- }
- }
-
- return target;
-};
-
-jQuery.extend( {
-
- expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ),
-
- isReady: true,
-
- error: function( msg ) {
- throw new Error( msg );
- },
-
- noop: function() {},
-
- isPlainObject: function( obj ) {
- var proto, Ctor;
-
- if ( !obj || toString.call( obj ) !== "[object Object]" ) {
- return false;
- }
-
- proto = getProto( obj );
-
- if ( !proto ) {
- return true;
- }
-
- Ctor = hasOwn.call( proto, "constructor" ) && proto.constructor;
- return typeof Ctor === "function" && fnToString.call( Ctor ) === ObjectFunctionString;
- },
-
- isEmptyObject: function( obj ) {
- var name;
-
- for ( name in obj ) {
- return false;
- }
- return true;
- },
-
- globalEval: function( code, options, doc ) {
- DOMEval( code, { nonce: options && options.nonce }, doc );
- },
-
- each: function( obj, callback ) {
- var length, i = 0;
-
- if ( isArrayLike( obj ) ) {
- length = obj.length;
- for ( ; i < length; i++ ) {
- if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) {
- break;
- }
- }
- } else {
- for ( i in obj ) {
- if ( callback.call( obj[ i ], i, obj[ i ] ) === false ) {
- break;
- }
- }
- }
-
- return obj;
- },
-
- text: function( elem ) {
- var node,
- ret = "",
- i = 0,
- nodeType = elem.nodeType;
-
- if ( !nodeType ) {
-
- while ( ( node = elem[ i++ ] ) ) {
-
- ret += jQuery.text( node );
- }
- }
- if ( nodeType === 1 || nodeType === 11 ) {
- return elem.textContent;
- }
- if ( nodeType === 9 ) {
- return elem.documentElement.textContent;
- }
- if ( nodeType === 3 || nodeType === 4 ) {
- return elem.nodeValue;
- }
-
- return ret;
- },
-
- makeArray: function( arr, results ) {
- var ret = results || [];
-
- if ( arr != null ) {
- if ( isArrayLike( Object( arr ) ) ) {
- jQuery.merge( ret,
- typeof arr === "string" ?
- [ arr ] : arr
- );
- } else {
- push.call( ret, arr );
- }
- }
-
- return ret;
- },
-
- inArray: function( elem, arr, i ) {
- return arr == null ? -1 : indexOf.call( arr, elem, i );
- },
-
- isXMLDoc: function( elem ) {
- var namespace = elem && elem.namespaceURI,
- docElem = elem && ( elem.ownerDocument || elem ).documentElement;
-
- return !rhtmlSuffix.test( namespace || docElem && docElem.nodeName || "HTML" );
- },
-
- merge: function( first, second ) {
- var len = +second.length,
- j = 0,
- i = first.length;
-
- for ( ; j < len; j++ ) {
- first[ i++ ] = second[ j ];
- }
-
- first.length = i;
-
- return first;
- },
-
- grep: function( elems, callback, invert ) {
- var callbackInverse,
- matches = [],
- i = 0,
- length = elems.length,
- callbackExpect = !invert;
-
- for ( ; i < length; i++ ) {
- callbackInverse = !callback( elems[ i ], i );
- if ( callbackInverse !== callbackExpect ) {
- matches.push( elems[ i ] );
- }
- }
-
- return matches;
- },
-
- map: function( elems, callback, arg ) {
- var length, value,
- i = 0,
- ret = [];
-
- if ( isArrayLike( elems ) ) {
- length = elems.length;
- for ( ; i < length; i++ ) {
- value = callback( elems[ i ], i, arg );
-
- if ( value != null ) {
- ret.push( value );
- }
- }
-
- } else {
- for ( i in elems ) {
- value = callback( elems[ i ], i, arg );
-
- if ( value != null ) {
- ret.push( value );
- }
- }
- }
-
- return flat( ret );
- },
-
- guid: 1,
-
- support: support
-} );
-
-if ( typeof Symbol === "function" ) {
- jQuery.fn[ Symbol.iterator ] = arr[ Symbol.iterator ];
-}
-
-jQuery.each( "Boolean Number String Function Array Date RegExp Object Error Symbol".split( " " ),
- function( _i, name ) {
- class2type[ "[object " + name + "]" ] = name.toLowerCase();
- } );
-
-function isArrayLike( obj ) {
-
- var length = !!obj && "length" in obj && obj.length,
- type = toType( obj );
-
- if ( isFunction( obj ) || isWindow( obj ) ) {
- return false;
- }
-
- return type === "array" || length === 0 ||
- typeof length === "number" && length > 0 && ( length - 1 ) in obj;
-}
-
-function nodeName( elem, name ) {
-
- return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase();
-
-}
-var pop = arr.pop;
-
-var sort = arr.sort;
-
-var splice = arr.splice;
-
-var whitespace = "[\\x20\\t\\r\\n\\f]";
-
-var rtrimCSS = new RegExp(
- "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$",
- "g"
-);
-
-jQuery.contains = function( a, b ) {
- var bup = b && b.parentNode;
-
- return a === bup || !!( bup && bup.nodeType === 1 && (
-
- a.contains ?
- a.contains( bup ) :
- a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16
- ) );
-};
-
-var rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\x80-\uFFFF\w-]/g;
-
-function fcssescape( ch, asCodePoint ) {
- if ( asCodePoint ) {
-
- if ( ch === "\0" ) {
- return "\uFFFD";
- }
-
- return ch.slice( 0, -1 ) + "\\" + ch.charCodeAt( ch.length - 1 ).toString( 16 ) + " ";
- }
-
- return "\\" + ch;
-}
-
-jQuery.escapeSelector = function( sel ) {
- return ( sel + "" ).replace( rcssescape, fcssescape );
-};
-
-var preferredDoc = document,
- pushNative = push;
-
-( function() {
-
-var i,
- Expr,
- outermostContext,
- sortInput,
- hasDuplicate,
- push = pushNative,
-
- document,
- documentElement,
- documentIsHTML,
- rbuggyQSA,
- matches,
-
- expando = jQuery.expando,
- dirruns = 0,
- done = 0,
- classCache = createCache(),
- tokenCache = createCache(),
- compilerCache = createCache(),
- nonnativeSelectorCache = createCache(),
- sortOrder = function( a, b ) {
- if ( a === b ) {
- hasDuplicate = true;
- }
- return 0;
- },
-
- booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|" +
- "loop|multiple|open|readonly|required|scoped",
-
- identifier = "(?:\\\\[\\da-fA-F]{1,6}" + whitespace +
- "?|\\\\[^\\r\\n\\f]|[\\w-]|[^\0-\\x7f])+",
-
- attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace +
-
- "*([*^$|!~]?=)" + whitespace +
-
- "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" +
- whitespace + "*\\]",
-
- pseudos = ":(" + identifier + ")(?:\\((" +
-
- "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" +
-
- "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" +
-
- ".*" +
- ")\\)|)",
-
- rwhitespace = new RegExp( whitespace + "+", "g" ),
-
- rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ),
- rleadingCombinator = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" +
- whitespace + "*" ),
- rdescend = new RegExp( whitespace + "|>" ),
-
- rpseudo = new RegExp( pseudos ),
- ridentifier = new RegExp( "^" + identifier + "$" ),
-
- matchExpr = {
- ID: new RegExp( "^#(" + identifier + ")" ),
- CLASS: new RegExp( "^\\.(" + identifier + ")" ),
- TAG: new RegExp( "^(" + identifier + "|[*])" ),
- ATTR: new RegExp( "^" + attributes ),
- PSEUDO: new RegExp( "^" + pseudos ),
- CHILD: new RegExp(
- "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" +
- whitespace + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" +
- whitespace + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ),
- bool: new RegExp( "^(?:" + booleans + ")$", "i" ),
-
- needsContext: new RegExp( "^" + whitespace +
- "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + whitespace +
- "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" )
- },
-
- rinputs = /^(?:input|select|textarea|button)$/i,
- rheader = /^h\d$/i,
-
- rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,
-
- rsibling = /[+~]/,
-
- runescape = new RegExp( "\\\\[\\da-fA-F]{1,6}" + whitespace +
- "?|\\\\([^\\r\\n\\f])", "g" ),
- funescape = function( escape, nonHex ) {
- var high = "0x" + escape.slice( 1 ) - 0x10000;
-
- if ( nonHex ) {
-
- return nonHex;
- }
-
- return high < 0 ?
- String.fromCharCode( high + 0x10000 ) :
- String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 );
- },
-
- unloadHandler = function() {
- setDocument();
- },
-
- inDisabledFieldset = addCombinator(
- function( elem ) {
- return elem.disabled === true && nodeName( elem, "fieldset" );
- },
- { dir: "parentNode", next: "legend" }
- );
-
-function safeActiveElement() {
- try {
- return document.activeElement;
- } catch ( err ) { }
-}
-
-try {
- push.apply(
- ( arr = slice.call( preferredDoc.childNodes ) ),
- preferredDoc.childNodes
- );
-
- arr[ preferredDoc.childNodes.length ].nodeType;
-} catch ( e ) {
- push = {
- apply: function( target, els ) {
- pushNative.apply( target, slice.call( els ) );
- },
- call: function( target ) {
- pushNative.apply( target, slice.call( arguments, 1 ) );
- }
- };
-}
-
-function find( selector, context, results, seed ) {
- var m, i, elem, nid, match, groups, newSelector,
- newContext = context && context.ownerDocument,
-
- nodeType = context ? context.nodeType : 9;
-
- results = results || [];
-
- if ( typeof selector !== "string" || !selector ||
- nodeType !== 1 && nodeType !== 9 && nodeType !== 11 ) {
-
- return results;
- }
-
- if ( !seed ) {
- setDocument( context );
- context = context || document;
-
- if ( documentIsHTML ) {
-
- if ( nodeType !== 11 && ( match = rquickExpr.exec( selector ) ) ) {
-
- if ( ( m = match[ 1 ] ) ) {
-
- if ( nodeType === 9 ) {
- if ( ( elem = context.getElementById( m ) ) ) {
-
- if ( elem.id === m ) {
- push.call( results, elem );
- return results;
- }
- } else {
- return results;
- }
-
- } else {
-
- if ( newContext && ( elem = newContext.getElementById( m ) ) &&
- find.contains( context, elem ) &&
- elem.id === m ) {
-
- push.call( results, elem );
- return results;
- }
- }
-
- } else if ( match[ 2 ] ) {
- push.apply( results, context.getElementsByTagName( selector ) );
- return results;
-
- } else if ( ( m = match[ 3 ] ) && context.getElementsByClassName ) {
- push.apply( results, context.getElementsByClassName( m ) );
- return results;
- }
- }
-
- if ( !nonnativeSelectorCache[ selector + " " ] &&
- ( !rbuggyQSA || !rbuggyQSA.test( selector ) ) ) {
-
- newSelector = selector;
- newContext = context;
-
- if ( nodeType === 1 &&
- ( rdescend.test( selector ) || rleadingCombinator.test( selector ) ) ) {
-
- newContext = rsibling.test( selector ) && testContext( context.parentNode ) ||
- context;
-
- if ( newContext != context || !support.scope ) {
-
- if ( ( nid = context.getAttribute( "id" ) ) ) {
- nid = jQuery.escapeSelector( nid );
- } else {
- context.setAttribute( "id", ( nid = expando ) );
- }
- }
-
- groups = tokenize( selector );
- i = groups.length;
- while ( i-- ) {
- groups[ i ] = ( nid ? "#" + nid : ":scope" ) + " " +
- toSelector( groups[ i ] );
- }
- newSelector = groups.join( "," );
- }
-
- try {
- push.apply( results,
- newContext.querySelectorAll( newSelector )
- );
- return results;
- } catch ( qsaError ) {
- nonnativeSelectorCache( selector, true );
- } finally {
- if ( nid === expando ) {
- context.removeAttribute( "id" );
- }
- }
- }
- }
- }
-
- return select( selector.replace( rtrimCSS, "$1" ), context, results, seed );
-}
-
-function createCache() {
- var keys = [];
-
- function cache( key, value ) {
-
- if ( keys.push( key + " " ) > Expr.cacheLength ) {
-
- delete cache[ keys.shift() ];
- }
- return ( cache[ key + " " ] = value );
- }
- return cache;
-}
-
-function markFunction( fn ) {
- fn[ expando ] = true;
- return fn;
-}
-
-function assert( fn ) {
- var el = document.createElement( "fieldset" );
-
- try {
- return !!fn( el );
- } catch ( e ) {
- return false;
- } finally {
-
- if ( el.parentNode ) {
- el.parentNode.removeChild( el );
- }
-
- el = null;
- }
-}
-
-function createInputPseudo( type ) {
- return function( elem ) {
- return nodeName( elem, "input" ) && elem.type === type;
- };
-}
-
-function createButtonPseudo( type ) {
- return function( elem ) {
- return ( nodeName( elem, "input" ) || nodeName( elem, "button" ) ) &&
- elem.type === type;
- };
-}
-
-function createDisabledPseudo( disabled ) {
-
- return function( elem ) {
-
- if ( "form" in elem ) {
-
- if ( elem.parentNode && elem.disabled === false ) {
-
- if ( "label" in elem ) {
- if ( "label" in elem.parentNode ) {
- return elem.parentNode.disabled === disabled;
- } else {
- return elem.disabled === disabled;
- }
- }
-
- return elem.isDisabled === disabled ||
-
- elem.isDisabled !== !disabled &&
- inDisabledFieldset( elem ) === disabled;
- }
-
- return elem.disabled === disabled;
-
- } else if ( "label" in elem ) {
- return elem.disabled === disabled;
- }
-
- return false;
- };
-}
-
-function createPositionalPseudo( fn ) {
- return markFunction( function( argument ) {
- argument = +argument;
- return markFunction( function( seed, matches ) {
- var j,
- matchIndexes = fn( [], seed.length, argument ),
- i = matchIndexes.length;
-
- while ( i-- ) {
- if ( seed[ ( j = matchIndexes[ i ] ) ] ) {
- seed[ j ] = !( matches[ j ] = seed[ j ] );
- }
- }
- } );
- } );
-}
-
-function testContext( context ) {
- return context && typeof context.getElementsByTagName !== "undefined" && context;
-}
-
-function setDocument( node ) {
- var subWindow,
- doc = node ? node.ownerDocument || node : preferredDoc;
-
- if ( doc == document || doc.nodeType !== 9 || !doc.documentElement ) {
- return document;
- }
-
- document = doc;
- documentElement = document.documentElement;
- documentIsHTML = !jQuery.isXMLDoc( document );
-
- matches = documentElement.matches ||
- documentElement.webkitMatchesSelector ||
- documentElement.msMatchesSelector;
-
- if ( documentElement.msMatchesSelector &&
-
- preferredDoc != document &&
- ( subWindow = document.defaultView ) && subWindow.top !== subWindow ) {
-
- subWindow.addEventListener( "unload", unloadHandler );
- }
-
- support.getById = assert( function( el ) {
- documentElement.appendChild( el ).id = jQuery.expando;
- return !document.getElementsByName ||
- !document.getElementsByName( jQuery.expando ).length;
- } );
-
- support.disconnectedMatch = assert( function( el ) {
- return matches.call( el, "*" );
- } );
-
- support.scope = assert( function() {
- return document.querySelectorAll( ":scope" );
- } );
-
- support.cssHas = assert( function() {
- try {
- document.querySelector( ":has(*,:jqfake)" );
- return false;
- } catch ( e ) {
- return true;
- }
- } );
-
- if ( support.getById ) {
- Expr.filter.ID = function( id ) {
- var attrId = id.replace( runescape, funescape );
- return function( elem ) {
- return elem.getAttribute( "id" ) === attrId;
- };
- };
- Expr.find.ID = function( id, context ) {
- if ( typeof context.getElementById !== "undefined" && documentIsHTML ) {
- var elem = context.getElementById( id );
- return elem ? [ elem ] : [];
- }
- };
- } else {
- Expr.filter.ID = function( id ) {
- var attrId = id.replace( runescape, funescape );
- return function( elem ) {
- var node = typeof elem.getAttributeNode !== "undefined" &&
- elem.getAttributeNode( "id" );
- return node && node.value === attrId;
- };
- };
-
- Expr.find.ID = function( id, context ) {
- if ( typeof context.getElementById !== "undefined" && documentIsHTML ) {
- var node, i, elems,
- elem = context.getElementById( id );
-
- if ( elem ) {
-
- node = elem.getAttributeNode( "id" );
- if ( node && node.value === id ) {
- return [ elem ];
- }
-
- elems = context.getElementsByName( id );
- i = 0;
- while ( ( elem = elems[ i++ ] ) ) {
- node = elem.getAttributeNode( "id" );
- if ( node && node.value === id ) {
- return [ elem ];
- }
- }
- }
-
- return [];
- }
- };
- }
-
- Expr.find.TAG = function( tag, context ) {
- if ( typeof context.getElementsByTagName !== "undefined" ) {
- return context.getElementsByTagName( tag );
-
- } else {
- return context.querySelectorAll( tag );
- }
- };
-
- Expr.find.CLASS = function( className, context ) {
- if ( typeof context.getElementsByClassName !== "undefined" && documentIsHTML ) {
- return context.getElementsByClassName( className );
- }
- };
-
- rbuggyQSA = [];
-
- assert( function( el ) {
-
- var input;
-
- documentElement.appendChild( el ).innerHTML =
- "" +
- "";
-
- if ( !el.querySelectorAll( "[selected]" ).length ) {
- rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" );
- }
-
- if ( !el.querySelectorAll( "[id~=" + expando + "-]" ).length ) {
- rbuggyQSA.push( "~=" );
- }
-
- if ( !el.querySelectorAll( "a#" + expando + "+*" ).length ) {
- rbuggyQSA.push( ".#.+[+~]" );
- }
-
- if ( !el.querySelectorAll( ":checked" ).length ) {
- rbuggyQSA.push( ":checked" );
- }
-
- input = document.createElement( "input" );
- input.setAttribute( "type", "hidden" );
- el.appendChild( input ).setAttribute( "name", "D" );
-
- documentElement.appendChild( el ).disabled = true;
- if ( el.querySelectorAll( ":disabled" ).length !== 2 ) {
- rbuggyQSA.push( ":enabled", ":disabled" );
- }
-
- input = document.createElement( "input" );
- input.setAttribute( "name", "" );
- el.appendChild( input );
- if ( !el.querySelectorAll( "[name='']" ).length ) {
- rbuggyQSA.push( "\\[" + whitespace + "*name" + whitespace + "*=" +
- whitespace + "*(?:''|\"\")" );
- }
- } );
-
- if ( !support.cssHas ) {
-
- rbuggyQSA.push( ":has" );
- }
-
- rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join( "|" ) );
-
- sortOrder = function( a, b ) {
-
- if ( a === b ) {
- hasDuplicate = true;
- return 0;
- }
-
- var compare = !a.compareDocumentPosition - !b.compareDocumentPosition;
- if ( compare ) {
- return compare;
- }
-
- compare = ( a.ownerDocument || a ) == ( b.ownerDocument || b ) ?
- a.compareDocumentPosition( b ) :
-
- 1;
-
- if ( compare & 1 ||
- ( !support.sortDetached && b.compareDocumentPosition( a ) === compare ) ) {
-
- if ( a === document || a.ownerDocument == preferredDoc &&
- find.contains( preferredDoc, a ) ) {
- return -1;
- }
-
- if ( b === document || b.ownerDocument == preferredDoc &&
- find.contains( preferredDoc, b ) ) {
- return 1;
- }
-
- return sortInput ?
- ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) :
- 0;
- }
-
- return compare & 4 ? -1 : 1;
- };
-
- return document;
-}
-
-find.matches = function( expr, elements ) {
- return find( expr, null, null, elements );
-};
-
-find.matchesSelector = function( elem, expr ) {
- setDocument( elem );
-
- if ( documentIsHTML &&
- !nonnativeSelectorCache[ expr + " " ] &&
- ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) {
-
- try {
- var ret = matches.call( elem, expr );
-
- if ( ret || support.disconnectedMatch ||
-
- elem.document && elem.document.nodeType !== 11 ) {
- return ret;
- }
- } catch ( e ) {
- nonnativeSelectorCache( expr, true );
- }
- }
-
- return find( expr, document, null, [ elem ] ).length > 0;
-};
-
-find.contains = function( context, elem ) {
-
- if ( ( context.ownerDocument || context ) != document ) {
- setDocument( context );
- }
- return jQuery.contains( context, elem );
-};
-
-find.attr = function( elem, name ) {
-
- if ( ( elem.ownerDocument || elem ) != document ) {
- setDocument( elem );
- }
-
- var fn = Expr.attrHandle[ name.toLowerCase() ],
-
- val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ?
- fn( elem, name, !documentIsHTML ) :
- undefined;
-
- if ( val !== undefined ) {
- return val;
- }
-
- return elem.getAttribute( name );
-};
-
-find.error = function( msg ) {
- throw new Error( "Syntax error, unrecognized expression: " + msg );
-};
-
-jQuery.uniqueSort = function( results ) {
- var elem,
- duplicates = [],
- j = 0,
- i = 0;
-
- hasDuplicate = !support.sortStable;
- sortInput = !support.sortStable && slice.call( results, 0 );
- sort.call( results, sortOrder );
-
- if ( hasDuplicate ) {
- while ( ( elem = results[ i++ ] ) ) {
- if ( elem === results[ i ] ) {
- j = duplicates.push( i );
- }
- }
- while ( j-- ) {
- splice.call( results, duplicates[ j ], 1 );
- }
- }
-
- sortInput = null;
-
- return results;
-};
-
-jQuery.fn.uniqueSort = function() {
- return this.pushStack( jQuery.uniqueSort( slice.apply( this ) ) );
-};
-
-Expr = jQuery.expr = {
-
- cacheLength: 50,
-
- createPseudo: markFunction,
-
- match: matchExpr,
-
- attrHandle: {},
-
- find: {},
-
- relative: {
- ">": { dir: "parentNode", first: true },
- " ": { dir: "parentNode" },
- "+": { dir: "previousSibling", first: true },
- "~": { dir: "previousSibling" }
- },
-
- preFilter: {
- ATTR: function( match ) {
- match[ 1 ] = match[ 1 ].replace( runescape, funescape );
-
- match[ 3 ] = ( match[ 3 ] || match[ 4 ] || match[ 5 ] || "" )
- .replace( runescape, funescape );
-
- if ( match[ 2 ] === "~=" ) {
- match[ 3 ] = " " + match[ 3 ] + " ";
- }
-
- return match.slice( 0, 4 );
- },
-
- CHILD: function( match ) {
-
- match[ 1 ] = match[ 1 ].toLowerCase();
-
- if ( match[ 1 ].slice( 0, 3 ) === "nth" ) {
-
- if ( !match[ 3 ] ) {
- find.error( match[ 0 ] );
- }
-
- match[ 4 ] = +( match[ 4 ] ?
- match[ 5 ] + ( match[ 6 ] || 1 ) :
- 2 * ( match[ 3 ] === "even" || match[ 3 ] === "odd" )
- );
- match[ 5 ] = +( ( match[ 7 ] + match[ 8 ] ) || match[ 3 ] === "odd" );
-
- } else if ( match[ 3 ] ) {
- find.error( match[ 0 ] );
- }
-
- return match;
- },
-
- PSEUDO: function( match ) {
- var excess,
- unquoted = !match[ 6 ] && match[ 2 ];
-
- if ( matchExpr.CHILD.test( match[ 0 ] ) ) {
- return null;
- }
-
- if ( match[ 3 ] ) {
- match[ 2 ] = match[ 4 ] || match[ 5 ] || "";
-
- } else if ( unquoted && rpseudo.test( unquoted ) &&
-
- ( excess = tokenize( unquoted, true ) ) &&
-
- ( excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length ) ) {
-
- match[ 0 ] = match[ 0 ].slice( 0, excess );
- match[ 2 ] = unquoted.slice( 0, excess );
- }
-
- return match.slice( 0, 3 );
- }
- },
-
- filter: {
-
- TAG: function( nodeNameSelector ) {
- var expectedNodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase();
- return nodeNameSelector === "*" ?
- function() {
- return true;
- } :
- function( elem ) {
- return nodeName( elem, expectedNodeName );
- };
- },
-
- CLASS: function( className ) {
- var pattern = classCache[ className + " " ];
-
- return pattern ||
- ( pattern = new RegExp( "(^|" + whitespace + ")" + className +
- "(" + whitespace + "|$)" ) ) &&
- classCache( className, function( elem ) {
- return pattern.test(
- typeof elem.className === "string" && elem.className ||
- typeof elem.getAttribute !== "undefined" &&
- elem.getAttribute( "class" ) ||
- ""
- );
- } );
- },
-
- ATTR: function( name, operator, check ) {
- return function( elem ) {
- var result = find.attr( elem, name );
-
- if ( result == null ) {
- return operator === "!=";
- }
- if ( !operator ) {
- return true;
- }
-
- result += "";
-
- if ( operator === "=" ) {
- return result === check;
- }
- if ( operator === "!=" ) {
- return result !== check;
- }
- if ( operator === "^=" ) {
- return check && result.indexOf( check ) === 0;
- }
- if ( operator === "*=" ) {
- return check && result.indexOf( check ) > -1;
- }
- if ( operator === "$=" ) {
- return check && result.slice( -check.length ) === check;
- }
- if ( operator === "~=" ) {
- return ( " " + result.replace( rwhitespace, " " ) + " " )
- .indexOf( check ) > -1;
- }
- if ( operator === "|=" ) {
- return result === check || result.slice( 0, check.length + 1 ) === check + "-";
- }
-
- return false;
- };
- },
-
- CHILD: function( type, what, _argument, first, last ) {
- var simple = type.slice( 0, 3 ) !== "nth",
- forward = type.slice( -4 ) !== "last",
- ofType = what === "of-type";
-
- return first === 1 && last === 0 ?
-
- function( elem ) {
- return !!elem.parentNode;
- } :
-
- function( elem, _context, xml ) {
- var cache, outerCache, node, nodeIndex, start,
- dir = simple !== forward ? "nextSibling" : "previousSibling",
- parent = elem.parentNode,
- name = ofType && elem.nodeName.toLowerCase(),
- useCache = !xml && !ofType,
- diff = false;
-
- if ( parent ) {
-
- if ( simple ) {
- while ( dir ) {
- node = elem;
- while ( ( node = node[ dir ] ) ) {
- if ( ofType ?
- nodeName( node, name ) :
- node.nodeType === 1 ) {
-
- return false;
- }
- }
-
- start = dir = type === "only" && !start && "nextSibling";
- }
- return true;
- }
-
- start = [ forward ? parent.firstChild : parent.lastChild ];
-
- if ( forward && useCache ) {
-
- outerCache = parent[ expando ] || ( parent[ expando ] = {} );
- cache = outerCache[ type ] || [];
- nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ];
- diff = nodeIndex && cache[ 2 ];
- node = nodeIndex && parent.childNodes[ nodeIndex ];
-
- while ( ( node = ++nodeIndex && node && node[ dir ] ||
-
- ( diff = nodeIndex = 0 ) || start.pop() ) ) {
-
- if ( node.nodeType === 1 && ++diff && node === elem ) {
- outerCache[ type ] = [ dirruns, nodeIndex, diff ];
- break;
- }
- }
-
- } else {
-
- if ( useCache ) {
- outerCache = elem[ expando ] || ( elem[ expando ] = {} );
- cache = outerCache[ type ] || [];
- nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ];
- diff = nodeIndex;
- }
-
- if ( diff === false ) {
-
- while ( ( node = ++nodeIndex && node && node[ dir ] ||
- ( diff = nodeIndex = 0 ) || start.pop() ) ) {
-
- if ( ( ofType ?
- nodeName( node, name ) :
- node.nodeType === 1 ) &&
- ++diff ) {
-
- if ( useCache ) {
- outerCache = node[ expando ] ||
- ( node[ expando ] = {} );
- outerCache[ type ] = [ dirruns, diff ];
- }
-
- if ( node === elem ) {
- break;
- }
- }
- }
- }
- }
-
- diff -= last;
- return diff === first || ( diff % first === 0 && diff / first >= 0 );
- }
- };
- },
-
- PSEUDO: function( pseudo, argument ) {
-
- var args,
- fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] ||
- find.error( "unsupported pseudo: " + pseudo );
-
- if ( fn[ expando ] ) {
- return fn( argument );
- }
-
- if ( fn.length > 1 ) {
- args = [ pseudo, pseudo, "", argument ];
- return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ?
- markFunction( function( seed, matches ) {
- var idx,
- matched = fn( seed, argument ),
- i = matched.length;
- while ( i-- ) {
- idx = indexOf.call( seed, matched[ i ] );
- seed[ idx ] = !( matches[ idx ] = matched[ i ] );
- }
- } ) :
- function( elem ) {
- return fn( elem, 0, args );
- };
- }
-
- return fn;
- }
- },
-
- pseudos: {
-
- not: markFunction( function( selector ) {
-
- var input = [],
- results = [],
- matcher = compile( selector.replace( rtrimCSS, "$1" ) );
-
- return matcher[ expando ] ?
- markFunction( function( seed, matches, _context, xml ) {
- var elem,
- unmatched = matcher( seed, null, xml, [] ),
- i = seed.length;
-
- while ( i-- ) {
- if ( ( elem = unmatched[ i ] ) ) {
- seed[ i ] = !( matches[ i ] = elem );
- }
- }
- } ) :
- function( elem, _context, xml ) {
- input[ 0 ] = elem;
- matcher( input, null, xml, results );
-
- input[ 0 ] = null;
- return !results.pop();
- };
- } ),
-
- has: markFunction( function( selector ) {
- return function( elem ) {
- return find( selector, elem ).length > 0;
- };
- } ),
-
- contains: markFunction( function( text ) {
- text = text.replace( runescape, funescape );
- return function( elem ) {
- return ( elem.textContent || jQuery.text( elem ) ).indexOf( text ) > -1;
- };
- } ),
-
- lang: markFunction( function( lang ) {
-
- if ( !ridentifier.test( lang || "" ) ) {
- find.error( "unsupported lang: " + lang );
- }
- lang = lang.replace( runescape, funescape ).toLowerCase();
- return function( elem ) {
- var elemLang;
- do {
- if ( ( elemLang = documentIsHTML ?
- elem.lang :
- elem.getAttribute( "xml:lang" ) || elem.getAttribute( "lang" ) ) ) {
-
- elemLang = elemLang.toLowerCase();
- return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0;
- }
- } while ( ( elem = elem.parentNode ) && elem.nodeType === 1 );
- return false;
- };
- } ),
-
- target: function( elem ) {
- var hash = window.location && window.location.hash;
- return hash && hash.slice( 1 ) === elem.id;
- },
-
- root: function( elem ) {
- return elem === documentElement;
- },
-
- focus: function( elem ) {
- return elem === safeActiveElement() &&
- document.hasFocus() &&
- !!( elem.type || elem.href || ~elem.tabIndex );
- },
-
- enabled: createDisabledPseudo( false ),
- disabled: createDisabledPseudo( true ),
-
- checked: function( elem ) {
-
- return ( nodeName( elem, "input" ) && !!elem.checked ) ||
- ( nodeName( elem, "option" ) && !!elem.selected );
- },
-
- selected: function( elem ) {
-
- if ( elem.parentNode ) {
-
- elem.parentNode.selectedIndex;
- }
-
- return elem.selected === true;
- },
-
- empty: function( elem ) {
-
- for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
- if ( elem.nodeType < 6 ) {
- return false;
- }
- }
- return true;
- },
-
- parent: function( elem ) {
- return !Expr.pseudos.empty( elem );
- },
-
- header: function( elem ) {
- return rheader.test( elem.nodeName );
- },
-
- input: function( elem ) {
- return rinputs.test( elem.nodeName );
- },
-
- button: function( elem ) {
- return nodeName( elem, "input" ) && elem.type === "button" ||
- nodeName( elem, "button" );
- },
-
- text: function( elem ) {
- var attr;
- return nodeName( elem, "input" ) && elem.type === "text" &&
-
- ( ( attr = elem.getAttribute( "type" ) ) == null ||
- attr.toLowerCase() === "text" );
- },
-
- first: createPositionalPseudo( function() {
- return [ 0 ];
- } ),
-
- last: createPositionalPseudo( function( _matchIndexes, length ) {
- return [ length - 1 ];
- } ),
-
- eq: createPositionalPseudo( function( _matchIndexes, length, argument ) {
- return [ argument < 0 ? argument + length : argument ];
- } ),
-
- even: createPositionalPseudo( function( matchIndexes, length ) {
- var i = 0;
- for ( ; i < length; i += 2 ) {
- matchIndexes.push( i );
- }
- return matchIndexes;
- } ),
-
- odd: createPositionalPseudo( function( matchIndexes, length ) {
- var i = 1;
- for ( ; i < length; i += 2 ) {
- matchIndexes.push( i );
- }
- return matchIndexes;
- } ),
-
- lt: createPositionalPseudo( function( matchIndexes, length, argument ) {
- var i;
-
- if ( argument < 0 ) {
- i = argument + length;
- } else if ( argument > length ) {
- i = length;
- } else {
- i = argument;
- }
-
- for ( ; --i >= 0; ) {
- matchIndexes.push( i );
- }
- return matchIndexes;
- } ),
-
- gt: createPositionalPseudo( function( matchIndexes, length, argument ) {
- var i = argument < 0 ? argument + length : argument;
- for ( ; ++i < length; ) {
- matchIndexes.push( i );
- }
- return matchIndexes;
- } )
- }
-};
-
-Expr.pseudos.nth = Expr.pseudos.eq;
-
-for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) {
- Expr.pseudos[ i ] = createInputPseudo( i );
-}
-for ( i in { submit: true, reset: true } ) {
- Expr.pseudos[ i ] = createButtonPseudo( i );
-}
-
-function setFilters() {}
-setFilters.prototype = Expr.filters = Expr.pseudos;
-Expr.setFilters = new setFilters();
-
-function tokenize( selector, parseOnly ) {
- var matched, match, tokens, type,
- soFar, groups, preFilters,
- cached = tokenCache[ selector + " " ];
-
- if ( cached ) {
- return parseOnly ? 0 : cached.slice( 0 );
- }
-
- soFar = selector;
- groups = [];
- preFilters = Expr.preFilter;
-
- while ( soFar ) {
-
- if ( !matched || ( match = rcomma.exec( soFar ) ) ) {
- if ( match ) {
-
- soFar = soFar.slice( match[ 0 ].length ) || soFar;
- }
- groups.push( ( tokens = [] ) );
- }
-
- matched = false;
-
- if ( ( match = rleadingCombinator.exec( soFar ) ) ) {
- matched = match.shift();
- tokens.push( {
- value: matched,
-
- type: match[ 0 ].replace( rtrimCSS, " " )
- } );
- soFar = soFar.slice( matched.length );
- }
-
- for ( type in Expr.filter ) {
- if ( ( match = matchExpr[ type ].exec( soFar ) ) && ( !preFilters[ type ] ||
- ( match = preFilters[ type ]( match ) ) ) ) {
- matched = match.shift();
- tokens.push( {
- value: matched,
- type: type,
- matches: match
- } );
- soFar = soFar.slice( matched.length );
- }
- }
-
- if ( !matched ) {
- break;
- }
- }
-
- if ( parseOnly ) {
- return soFar.length;
- }
-
- return soFar ?
- find.error( selector ) :
-
- tokenCache( selector, groups ).slice( 0 );
-}
-
-function toSelector( tokens ) {
- var i = 0,
- len = tokens.length,
- selector = "";
- for ( ; i < len; i++ ) {
- selector += tokens[ i ].value;
- }
- return selector;
-}
-
-function addCombinator( matcher, combinator, base ) {
- var dir = combinator.dir,
- skip = combinator.next,
- key = skip || dir,
- checkNonElements = base && key === "parentNode",
- doneName = done++;
-
- return combinator.first ?
-
- function( elem, context, xml ) {
- while ( ( elem = elem[ dir ] ) ) {
- if ( elem.nodeType === 1 || checkNonElements ) {
- return matcher( elem, context, xml );
- }
- }
- return false;
- } :
-
- function( elem, context, xml ) {
- var oldCache, outerCache,
- newCache = [ dirruns, doneName ];
-
- if ( xml ) {
- while ( ( elem = elem[ dir ] ) ) {
- if ( elem.nodeType === 1 || checkNonElements ) {
- if ( matcher( elem, context, xml ) ) {
- return true;
- }
- }
- }
- } else {
- while ( ( elem = elem[ dir ] ) ) {
- if ( elem.nodeType === 1 || checkNonElements ) {
- outerCache = elem[ expando ] || ( elem[ expando ] = {} );
-
- if ( skip && nodeName( elem, skip ) ) {
- elem = elem[ dir ] || elem;
- } else if ( ( oldCache = outerCache[ key ] ) &&
- oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) {
-
- return ( newCache[ 2 ] = oldCache[ 2 ] );
- } else {
-
- outerCache[ key ] = newCache;
-
- if ( ( newCache[ 2 ] = matcher( elem, context, xml ) ) ) {
- return true;
- }
- }
- }
- }
- }
- return false;
- };
-}
-
-function elementMatcher( matchers ) {
- return matchers.length > 1 ?
- function( elem, context, xml ) {
- var i = matchers.length;
- while ( i-- ) {
- if ( !matchers[ i ]( elem, context, xml ) ) {
- return false;
- }
- }
- return true;
- } :
- matchers[ 0 ];
-}
-
-function multipleContexts( selector, contexts, results ) {
- var i = 0,
- len = contexts.length;
- for ( ; i < len; i++ ) {
- find( selector, contexts[ i ], results );
- }
- return results;
-}
-
-function condense( unmatched, map, filter, context, xml ) {
- var elem,
- newUnmatched = [],
- i = 0,
- len = unmatched.length,
- mapped = map != null;
-
- for ( ; i < len; i++ ) {
- if ( ( elem = unmatched[ i ] ) ) {
- if ( !filter || filter( elem, context, xml ) ) {
- newUnmatched.push( elem );
- if ( mapped ) {
- map.push( i );
- }
- }
- }
- }
-
- return newUnmatched;
-}
-
-function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) {
- if ( postFilter && !postFilter[ expando ] ) {
- postFilter = setMatcher( postFilter );
- }
- if ( postFinder && !postFinder[ expando ] ) {
- postFinder = setMatcher( postFinder, postSelector );
- }
- return markFunction( function( seed, results, context, xml ) {
- var temp, i, elem, matcherOut,
- preMap = [],
- postMap = [],
- preexisting = results.length,
-
- elems = seed ||
- multipleContexts( selector || "*",
- context.nodeType ? [ context ] : context, [] ),
-
- matcherIn = preFilter && ( seed || !selector ) ?
- condense( elems, preMap, preFilter, context, xml ) :
- elems;
-
- if ( matcher ) {
-
- matcherOut = postFinder || ( seed ? preFilter : preexisting || postFilter ) ?
-
- [] :
-
- results;
-
- matcher( matcherIn, matcherOut, context, xml );
- } else {
- matcherOut = matcherIn;
- }
-
- if ( postFilter ) {
- temp = condense( matcherOut, postMap );
- postFilter( temp, [], context, xml );
-
- i = temp.length;
- while ( i-- ) {
- if ( ( elem = temp[ i ] ) ) {
- matcherOut[ postMap[ i ] ] = !( matcherIn[ postMap[ i ] ] = elem );
- }
- }
- }
-
- if ( seed ) {
- if ( postFinder || preFilter ) {
- if ( postFinder ) {
-
- temp = [];
- i = matcherOut.length;
- while ( i-- ) {
- if ( ( elem = matcherOut[ i ] ) ) {
-
- temp.push( ( matcherIn[ i ] = elem ) );
- }
- }
- postFinder( null, ( matcherOut = [] ), temp, xml );
- }
-
- i = matcherOut.length;
- while ( i-- ) {
- if ( ( elem = matcherOut[ i ] ) &&
- ( temp = postFinder ? indexOf.call( seed, elem ) : preMap[ i ] ) > -1 ) {
-
- seed[ temp ] = !( results[ temp ] = elem );
- }
- }
- }
-
- } else {
- matcherOut = condense(
- matcherOut === results ?
- matcherOut.splice( preexisting, matcherOut.length ) :
- matcherOut
- );
- if ( postFinder ) {
- postFinder( null, results, matcherOut, xml );
- } else {
- push.apply( results, matcherOut );
- }
- }
- } );
-}
-
-function matcherFromTokens( tokens ) {
- var checkContext, matcher, j,
- len = tokens.length,
- leadingRelative = Expr.relative[ tokens[ 0 ].type ],
- implicitRelative = leadingRelative || Expr.relative[ " " ],
- i = leadingRelative ? 1 : 0,
-
- matchContext = addCombinator( function( elem ) {
- return elem === checkContext;
- }, implicitRelative, true ),
- matchAnyContext = addCombinator( function( elem ) {
- return indexOf.call( checkContext, elem ) > -1;
- }, implicitRelative, true ),
- matchers = [ function( elem, context, xml ) {
-
- var ret = ( !leadingRelative && ( xml || context != outermostContext ) ) || (
- ( checkContext = context ).nodeType ?
- matchContext( elem, context, xml ) :
- matchAnyContext( elem, context, xml ) );
-
- checkContext = null;
- return ret;
- } ];
-
- for ( ; i < len; i++ ) {
- if ( ( matcher = Expr.relative[ tokens[ i ].type ] ) ) {
- matchers = [ addCombinator( elementMatcher( matchers ), matcher ) ];
- } else {
- matcher = Expr.filter[ tokens[ i ].type ].apply( null, tokens[ i ].matches );
-
- if ( matcher[ expando ] ) {
-
- j = ++i;
- for ( ; j < len; j++ ) {
- if ( Expr.relative[ tokens[ j ].type ] ) {
- break;
- }
- }
- return setMatcher(
- i > 1 && elementMatcher( matchers ),
- i > 1 && toSelector(
-
- tokens.slice( 0, i - 1 )
- .concat( { value: tokens[ i - 2 ].type === " " ? "*" : "" } )
- ).replace( rtrimCSS, "$1" ),
- matcher,
- i < j && matcherFromTokens( tokens.slice( i, j ) ),
- j < len && matcherFromTokens( ( tokens = tokens.slice( j ) ) ),
- j < len && toSelector( tokens )
- );
- }
- matchers.push( matcher );
- }
- }
-
- return elementMatcher( matchers );
-}
-
-function matcherFromGroupMatchers( elementMatchers, setMatchers ) {
- var bySet = setMatchers.length > 0,
- byElement = elementMatchers.length > 0,
- superMatcher = function( seed, context, xml, results, outermost ) {
- var elem, j, matcher,
- matchedCount = 0,
- i = "0",
- unmatched = seed && [],
- setMatched = [],
- contextBackup = outermostContext,
-
- elems = seed || byElement && Expr.find.TAG( "*", outermost ),
-
- dirrunsUnique = ( dirruns += contextBackup == null ? 1 : Math.random() || 0.1 ),
- len = elems.length;
-
- if ( outermost ) {
-
- outermostContext = context == document || context || outermost;
- }
-
- for ( ; i !== len && ( elem = elems[ i ] ) != null; i++ ) {
- if ( byElement && elem ) {
- j = 0;
-
- if ( !context && elem.ownerDocument != document ) {
- setDocument( elem );
- xml = !documentIsHTML;
- }
- while ( ( matcher = elementMatchers[ j++ ] ) ) {
- if ( matcher( elem, context || document, xml ) ) {
- push.call( results, elem );
- break;
- }
- }
- if ( outermost ) {
- dirruns = dirrunsUnique;
- }
- }
-
- if ( bySet ) {
-
- if ( ( elem = !matcher && elem ) ) {
- matchedCount--;
- }
-
- if ( seed ) {
- unmatched.push( elem );
- }
- }
- }
-
- matchedCount += i;
-
- if ( bySet && i !== matchedCount ) {
- j = 0;
- while ( ( matcher = setMatchers[ j++ ] ) ) {
- matcher( unmatched, setMatched, context, xml );
- }
-
- if ( seed ) {
-
- if ( matchedCount > 0 ) {
- while ( i-- ) {
- if ( !( unmatched[ i ] || setMatched[ i ] ) ) {
- setMatched[ i ] = pop.call( results );
- }
- }
- }
-
- setMatched = condense( setMatched );
- }
-
- push.apply( results, setMatched );
-
- if ( outermost && !seed && setMatched.length > 0 &&
- ( matchedCount + setMatchers.length ) > 1 ) {
-
- jQuery.uniqueSort( results );
- }
- }
-
- if ( outermost ) {
- dirruns = dirrunsUnique;
- outermostContext = contextBackup;
- }
-
- return unmatched;
- };
-
- return bySet ?
- markFunction( superMatcher ) :
- superMatcher;
-}
-
-function compile( selector, match ) {
- var i,
- setMatchers = [],
- elementMatchers = [],
- cached = compilerCache[ selector + " " ];
-
- if ( !cached ) {
-
- if ( !match ) {
- match = tokenize( selector );
- }
- i = match.length;
- while ( i-- ) {
- cached = matcherFromTokens( match[ i ] );
- if ( cached[ expando ] ) {
- setMatchers.push( cached );
- } else {
- elementMatchers.push( cached );
- }
- }
-
- cached = compilerCache( selector,
- matcherFromGroupMatchers( elementMatchers, setMatchers ) );
-
- cached.selector = selector;
- }
- return cached;
-}
-
-function select( selector, context, results, seed ) {
- var i, tokens, token, type, find,
- compiled = typeof selector === "function" && selector,
- match = !seed && tokenize( ( selector = compiled.selector || selector ) );
-
- results = results || [];
-
- if ( match.length === 1 ) {
-
- tokens = match[ 0 ] = match[ 0 ].slice( 0 );
- if ( tokens.length > 2 && ( token = tokens[ 0 ] ).type === "ID" &&
- context.nodeType === 9 && documentIsHTML && Expr.relative[ tokens[ 1 ].type ] ) {
-
- context = ( Expr.find.ID(
- token.matches[ 0 ].replace( runescape, funescape ),
- context
- ) || [] )[ 0 ];
- if ( !context ) {
- return results;
-
- } else if ( compiled ) {
- context = context.parentNode;
- }
-
- selector = selector.slice( tokens.shift().value.length );
- }
-
- i = matchExpr.needsContext.test( selector ) ? 0 : tokens.length;
- while ( i-- ) {
- token = tokens[ i ];
-
- if ( Expr.relative[ ( type = token.type ) ] ) {
- break;
- }
- if ( ( find = Expr.find[ type ] ) ) {
-
- if ( ( seed = find(
- token.matches[ 0 ].replace( runescape, funescape ),
- rsibling.test( tokens[ 0 ].type ) &&
- testContext( context.parentNode ) || context
- ) ) ) {
-
- tokens.splice( i, 1 );
- selector = seed.length && toSelector( tokens );
- if ( !selector ) {
- push.apply( results, seed );
- return results;
- }
-
- break;
- }
- }
- }
- }
-
- ( compiled || compile( selector, match ) )(
- seed,
- context,
- !documentIsHTML,
- results,
- !context || rsibling.test( selector ) && testContext( context.parentNode ) || context
- );
- return results;
-}
-
-support.sortStable = expando.split( "" ).sort( sortOrder ).join( "" ) === expando;
-
-setDocument();
-
-support.sortDetached = assert( function( el ) {
-
- return el.compareDocumentPosition( document.createElement( "fieldset" ) ) & 1;
-} );
-
-jQuery.find = find;
-
-jQuery.expr[ ":" ] = jQuery.expr.pseudos;
-jQuery.unique = jQuery.uniqueSort;
-
-find.compile = compile;
-find.select = select;
-find.setDocument = setDocument;
-find.tokenize = tokenize;
-
-find.escape = jQuery.escapeSelector;
-find.getText = jQuery.text;
-find.isXML = jQuery.isXMLDoc;
-find.selectors = jQuery.expr;
-find.support = jQuery.support;
-find.uniqueSort = jQuery.uniqueSort;
-
-} )();
-
-var dir = function( elem, dir, until ) {
- var matched = [],
- truncate = until !== undefined;
-
- while ( ( elem = elem[ dir ] ) && elem.nodeType !== 9 ) {
- if ( elem.nodeType === 1 ) {
- if ( truncate && jQuery( elem ).is( until ) ) {
- break;
- }
- matched.push( elem );
- }
- }
- return matched;
-};
-
-var siblings = function( n, elem ) {
- var matched = [];
-
- for ( ; n; n = n.nextSibling ) {
- if ( n.nodeType === 1 && n !== elem ) {
- matched.push( n );
- }
- }
-
- return matched;
-};
-
-var rneedsContext = jQuery.expr.match.needsContext;
-
-var rsingleTag = ( /^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i );
-
-function winnow( elements, qualifier, not ) {
- if ( isFunction( qualifier ) ) {
- return jQuery.grep( elements, function( elem, i ) {
- return !!qualifier.call( elem, i, elem ) !== not;
- } );
- }
-
- if ( qualifier.nodeType ) {
- return jQuery.grep( elements, function( elem ) {
- return ( elem === qualifier ) !== not;
- } );
- }
-
- if ( typeof qualifier !== "string" ) {
- return jQuery.grep( elements, function( elem ) {
- return ( indexOf.call( qualifier, elem ) > -1 ) !== not;
- } );
- }
-
- return jQuery.filter( qualifier, elements, not );
-}
-
-jQuery.filter = function( expr, elems, not ) {
- var elem = elems[ 0 ];
-
- if ( not ) {
- expr = ":not(" + expr + ")";
- }
-
- if ( elems.length === 1 && elem.nodeType === 1 ) {
- return jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [];
- }
-
- return jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) {
- return elem.nodeType === 1;
- } ) );
-};
-
-jQuery.fn.extend( {
- find: function( selector ) {
- var i, ret,
- len = this.length,
- self = this;
-
- if ( typeof selector !== "string" ) {
- return this.pushStack( jQuery( selector ).filter( function() {
- for ( i = 0; i < len; i++ ) {
- if ( jQuery.contains( self[ i ], this ) ) {
- return true;
- }
- }
- } ) );
- }
-
- ret = this.pushStack( [] );
-
- for ( i = 0; i < len; i++ ) {
- jQuery.find( selector, self[ i ], ret );
- }
-
- return len > 1 ? jQuery.uniqueSort( ret ) : ret;
- },
- filter: function( selector ) {
- return this.pushStack( winnow( this, selector || [], false ) );
- },
- not: function( selector ) {
- return this.pushStack( winnow( this, selector || [], true ) );
- },
- is: function( selector ) {
- return !!winnow(
- this,
-
- typeof selector === "string" && rneedsContext.test( selector ) ?
- jQuery( selector ) :
- selector || [],
- false
- ).length;
- }
-} );
-
-var rootjQuery,
-
- rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/,
-
- init = jQuery.fn.init = function( selector, context, root ) {
- var match, elem;
-
- if ( !selector ) {
- return this;
- }
-
- root = root || rootjQuery;
-
- if ( typeof selector === "string" ) {
- if ( selector[ 0 ] === "<" &&
- selector[ selector.length - 1 ] === ">" &&
- selector.length >= 3 ) {
-
- match = [ null, selector, null ];
-
- } else {
- match = rquickExpr.exec( selector );
- }
-
- if ( match && ( match[ 1 ] || !context ) ) {
-
- if ( match[ 1 ] ) {
- context = context instanceof jQuery ? context[ 0 ] : context;
-
- jQuery.merge( this, jQuery.parseHTML(
- match[ 1 ],
- context && context.nodeType ? context.ownerDocument || context : document,
- true
- ) );
-
- if ( rsingleTag.test( match[ 1 ] ) && jQuery.isPlainObject( context ) ) {
- for ( match in context ) {
-
- if ( isFunction( this[ match ] ) ) {
- this[ match ]( context[ match ] );
-
- } else {
- this.attr( match, context[ match ] );
- }
- }
- }
-
- return this;
-
- } else {
- elem = document.getElementById( match[ 2 ] );
-
- if ( elem ) {
-
- this[ 0 ] = elem;
- this.length = 1;
- }
- return this;
- }
-
- } else if ( !context || context.jquery ) {
- return ( context || root ).find( selector );
-
- } else {
- return this.constructor( context ).find( selector );
- }
-
- } else if ( selector.nodeType ) {
- this[ 0 ] = selector;
- this.length = 1;
- return this;
-
- } else if ( isFunction( selector ) ) {
- return root.ready !== undefined ?
- root.ready( selector ) :
-
- selector( jQuery );
- }
-
- return jQuery.makeArray( selector, this );
- };
-
-init.prototype = jQuery.fn;
-
-rootjQuery = jQuery( document );
-
-var rparentsprev = /^(?:parents|prev(?:Until|All))/,
-
- guaranteedUnique = {
- children: true,
- contents: true,
- next: true,
- prev: true
- };
-
-jQuery.fn.extend( {
- has: function( target ) {
- var targets = jQuery( target, this ),
- l = targets.length;
-
- return this.filter( function() {
- var i = 0;
- for ( ; i < l; i++ ) {
- if ( jQuery.contains( this, targets[ i ] ) ) {
- return true;
- }
- }
- } );
- },
-
- closest: function( selectors, context ) {
- var cur,
- i = 0,
- l = this.length,
- matched = [],
- targets = typeof selectors !== "string" && jQuery( selectors );
-
- if ( !rneedsContext.test( selectors ) ) {
- for ( ; i < l; i++ ) {
- for ( cur = this[ i ]; cur && cur !== context; cur = cur.parentNode ) {
-
- if ( cur.nodeType < 11 && ( targets ?
- targets.index( cur ) > -1 :
-
- cur.nodeType === 1 &&
- jQuery.find.matchesSelector( cur, selectors ) ) ) {
-
- matched.push( cur );
- break;
- }
- }
- }
- }
-
- return this.pushStack( matched.length > 1 ? jQuery.uniqueSort( matched ) : matched );
- },
-
- index: function( elem ) {
-
- if ( !elem ) {
- return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1;
- }
-
- if ( typeof elem === "string" ) {
- return indexOf.call( jQuery( elem ), this[ 0 ] );
- }
-
- return indexOf.call( this,
-
- elem.jquery ? elem[ 0 ] : elem
- );
- },
-
- add: function( selector, context ) {
- return this.pushStack(
- jQuery.uniqueSort(
- jQuery.merge( this.get(), jQuery( selector, context ) )
- )
- );
- },
-
- addBack: function( selector ) {
- return this.add( selector == null ?
- this.prevObject : this.prevObject.filter( selector )
- );
- }
-} );
-
-function sibling( cur, dir ) {
- while ( ( cur = cur[ dir ] ) && cur.nodeType !== 1 ) {}
- return cur;
-}
-
-jQuery.each( {
- parent: function( elem ) {
- var parent = elem.parentNode;
- return parent && parent.nodeType !== 11 ? parent : null;
- },
- parents: function( elem ) {
- return dir( elem, "parentNode" );
- },
- parentsUntil: function( elem, _i, until ) {
- return dir( elem, "parentNode", until );
- },
- next: function( elem ) {
- return sibling( elem, "nextSibling" );
- },
- prev: function( elem ) {
- return sibling( elem, "previousSibling" );
- },
- nextAll: function( elem ) {
- return dir( elem, "nextSibling" );
- },
- prevAll: function( elem ) {
- return dir( elem, "previousSibling" );
- },
- nextUntil: function( elem, _i, until ) {
- return dir( elem, "nextSibling", until );
- },
- prevUntil: function( elem, _i, until ) {
- return dir( elem, "previousSibling", until );
- },
- siblings: function( elem ) {
- return siblings( ( elem.parentNode || {} ).firstChild, elem );
- },
- children: function( elem ) {
- return siblings( elem.firstChild );
- },
- contents: function( elem ) {
- if ( elem.contentDocument != null &&
-
- getProto( elem.contentDocument ) ) {
-
- return elem.contentDocument;
- }
-
- if ( nodeName( elem, "template" ) ) {
- elem = elem.content || elem;
- }
-
- return jQuery.merge( [], elem.childNodes );
- }
-}, function( name, fn ) {
- jQuery.fn[ name ] = function( until, selector ) {
- var matched = jQuery.map( this, fn, until );
-
- if ( name.slice( -5 ) !== "Until" ) {
- selector = until;
- }
-
- if ( selector && typeof selector === "string" ) {
- matched = jQuery.filter( selector, matched );
- }
-
- if ( this.length > 1 ) {
-
- if ( !guaranteedUnique[ name ] ) {
- jQuery.uniqueSort( matched );
- }
-
- if ( rparentsprev.test( name ) ) {
- matched.reverse();
- }
- }
-
- return this.pushStack( matched );
- };
-} );
-var rnothtmlwhite = ( /[^\x20\t\r\n\f]+/g );
-
-function createOptions( options ) {
- var object = {};
- jQuery.each( options.match( rnothtmlwhite ) || [], function( _, flag ) {
- object[ flag ] = true;
- } );
- return object;
-}
-
-jQuery.Callbacks = function( options ) {
-
- options = typeof options === "string" ?
- createOptions( options ) :
- jQuery.extend( {}, options );
-
- var
- firing,
-
- memory,
-
- fired,
-
- locked,
-
- list = [],
-
- queue = [],
-
- firingIndex = -1,
-
- fire = function() {
-
- locked = locked || options.once;
-
- fired = firing = true;
- for ( ; queue.length; firingIndex = -1 ) {
- memory = queue.shift();
- while ( ++firingIndex < list.length ) {
-
- if ( list[ firingIndex ].apply( memory[ 0 ], memory[ 1 ] ) === false &&
- options.stopOnFalse ) {
-
- firingIndex = list.length;
- memory = false;
- }
- }
- }
-
- if ( !options.memory ) {
- memory = false;
- }
-
- firing = false;
-
- if ( locked ) {
-
- if ( memory ) {
- list = [];
-
- } else {
- list = "";
- }
- }
- },
-
- self = {
-
- add: function() {
- if ( list ) {
-
- if ( memory && !firing ) {
- firingIndex = list.length - 1;
- queue.push( memory );
- }
-
- ( function add( args ) {
- jQuery.each( args, function( _, arg ) {
- if ( isFunction( arg ) ) {
- if ( !options.unique || !self.has( arg ) ) {
- list.push( arg );
- }
- } else if ( arg && arg.length && toType( arg ) !== "string" ) {
-
- add( arg );
- }
- } );
- } )( arguments );
-
- if ( memory && !firing ) {
- fire();
- }
- }
- return this;
- },
-
- remove: function() {
- jQuery.each( arguments, function( _, arg ) {
- var index;
- while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) {
- list.splice( index, 1 );
-
- if ( index <= firingIndex ) {
- firingIndex--;
- }
- }
- } );
- return this;
- },
-
- has: function( fn ) {
- return fn ?
- jQuery.inArray( fn, list ) > -1 :
- list.length > 0;
- },
-
- empty: function() {
- if ( list ) {
- list = [];
- }
- return this;
- },
-
- disable: function() {
- locked = queue = [];
- list = memory = "";
- return this;
- },
- disabled: function() {
- return !list;
- },
-
- lock: function() {
- locked = queue = [];
- if ( !memory && !firing ) {
- list = memory = "";
- }
- return this;
- },
- locked: function() {
- return !!locked;
- },
-
- fireWith: function( context, args ) {
- if ( !locked ) {
- args = args || [];
- args = [ context, args.slice ? args.slice() : args ];
- queue.push( args );
- if ( !firing ) {
- fire();
- }
- }
- return this;
- },
-
- fire: function() {
- self.fireWith( this, arguments );
- return this;
- },
-
- fired: function() {
- return !!fired;
- }
- };
-
- return self;
-};
-
-function Identity( v ) {
- return v;
-}
-function Thrower( ex ) {
- throw ex;
-}
-
-function adoptValue( value, resolve, reject, noValue ) {
- var method;
-
- try {
-
- if ( value && isFunction( ( method = value.promise ) ) ) {
- method.call( value ).done( resolve ).fail( reject );
-
- } else if ( value && isFunction( ( method = value.then ) ) ) {
- method.call( value, resolve, reject );
-
- } else {
-
- resolve.apply( undefined, [ value ].slice( noValue ) );
- }
-
- } catch ( value ) {
-
- reject.apply( undefined, [ value ] );
- }
-}
-
-jQuery.extend( {
-
- Deferred: function( func ) {
- var tuples = [
-
- [ "notify", "progress", jQuery.Callbacks( "memory" ),
- jQuery.Callbacks( "memory" ), 2 ],
- [ "resolve", "done", jQuery.Callbacks( "once memory" ),
- jQuery.Callbacks( "once memory" ), 0, "resolved" ],
- [ "reject", "fail", jQuery.Callbacks( "once memory" ),
- jQuery.Callbacks( "once memory" ), 1, "rejected" ]
- ],
- state = "pending",
- promise = {
- state: function() {
- return state;
- },
- always: function() {
- deferred.done( arguments ).fail( arguments );
- return this;
- },
- "catch": function( fn ) {
- return promise.then( null, fn );
- },
-
- pipe: function( ) {
- var fns = arguments;
-
- return jQuery.Deferred( function( newDefer ) {
- jQuery.each( tuples, function( _i, tuple ) {
-
- var fn = isFunction( fns[ tuple[ 4 ] ] ) && fns[ tuple[ 4 ] ];
-
- deferred[ tuple[ 1 ] ]( function() {
- var returned = fn && fn.apply( this, arguments );
- if ( returned && isFunction( returned.promise ) ) {
- returned.promise()
- .progress( newDefer.notify )
- .done( newDefer.resolve )
- .fail( newDefer.reject );
- } else {
- newDefer[ tuple[ 0 ] + "With" ](
- this,
- fn ? [ returned ] : arguments
- );
- }
- } );
- } );
- fns = null;
- } ).promise();
- },
- then: function( onFulfilled, onRejected, onProgress ) {
- var maxDepth = 0;
- function resolve( depth, deferred, handler, special ) {
- return function() {
- var that = this,
- args = arguments,
- mightThrow = function() {
- var returned, then;
-
- if ( depth < maxDepth ) {
- return;
- }
-
- returned = handler.apply( that, args );
-
- if ( returned === deferred.promise() ) {
- throw new TypeError( "Thenable self-resolution" );
- }
-
- then = returned &&
-
- ( typeof returned === "object" ||
- typeof returned === "function" ) &&
- returned.then;
-
- if ( isFunction( then ) ) {
-
- if ( special ) {
- then.call(
- returned,
- resolve( maxDepth, deferred, Identity, special ),
- resolve( maxDepth, deferred, Thrower, special )
- );
-
- } else {
-
- maxDepth++;
-
- then.call(
- returned,
- resolve( maxDepth, deferred, Identity, special ),
- resolve( maxDepth, deferred, Thrower, special ),
- resolve( maxDepth, deferred, Identity,
- deferred.notifyWith )
- );
- }
-
- } else {
-
- if ( handler !== Identity ) {
- that = undefined;
- args = [ returned ];
- }
-
- ( special || deferred.resolveWith )( that, args );
- }
- },
-
- process = special ?
- mightThrow :
- function() {
- try {
- mightThrow();
- } catch ( e ) {
-
- if ( jQuery.Deferred.exceptionHook ) {
- jQuery.Deferred.exceptionHook( e,
- process.error );
- }
-
- if ( depth + 1 >= maxDepth ) {
-
- if ( handler !== Thrower ) {
- that = undefined;
- args = [ e ];
- }
-
- deferred.rejectWith( that, args );
- }
- }
- };
-
- if ( depth ) {
- process();
- } else {
-
- if ( jQuery.Deferred.getErrorHook ) {
- process.error = jQuery.Deferred.getErrorHook();
-
- } else if ( jQuery.Deferred.getStackHook ) {
- process.error = jQuery.Deferred.getStackHook();
- }
- window.setTimeout( process );
- }
- };
- }
-
- return jQuery.Deferred( function( newDefer ) {
-
- tuples[ 0 ][ 3 ].add(
- resolve(
- 0,
- newDefer,
- isFunction( onProgress ) ?
- onProgress :
- Identity,
- newDefer.notifyWith
- )
- );
-
- tuples[ 1 ][ 3 ].add(
- resolve(
- 0,
- newDefer,
- isFunction( onFulfilled ) ?
- onFulfilled :
- Identity
- )
- );
-
- tuples[ 2 ][ 3 ].add(
- resolve(
- 0,
- newDefer,
- isFunction( onRejected ) ?
- onRejected :
- Thrower
- )
- );
- } ).promise();
- },
-
- promise: function( obj ) {
- return obj != null ? jQuery.extend( obj, promise ) : promise;
- }
- },
- deferred = {};
-
- jQuery.each( tuples, function( i, tuple ) {
- var list = tuple[ 2 ],
- stateString = tuple[ 5 ];
-
- promise[ tuple[ 1 ] ] = list.add;
-
- if ( stateString ) {
- list.add(
- function() {
-
- state = stateString;
- },
-
- tuples[ 3 - i ][ 2 ].disable,
-
- tuples[ 3 - i ][ 3 ].disable,
-
- tuples[ 0 ][ 2 ].lock,
-
- tuples[ 0 ][ 3 ].lock
- );
- }
-
- list.add( tuple[ 3 ].fire );
-
- deferred[ tuple[ 0 ] ] = function() {
- deferred[ tuple[ 0 ] + "With" ]( this === deferred ? undefined : this, arguments );
- return this;
- };
-
- deferred[ tuple[ 0 ] + "With" ] = list.fireWith;
- } );
-
- promise.promise( deferred );
-
- if ( func ) {
- func.call( deferred, deferred );
- }
-
- return deferred;
- },
-
- when: function( singleValue ) {
- var
-
- remaining = arguments.length,
-
- i = remaining,
-
- resolveContexts = Array( i ),
- resolveValues = slice.call( arguments ),
-
- primary = jQuery.Deferred(),
-
- updateFunc = function( i ) {
- return function( value ) {
- resolveContexts[ i ] = this;
- resolveValues[ i ] = arguments.length > 1 ? slice.call( arguments ) : value;
- if ( !( --remaining ) ) {
- primary.resolveWith( resolveContexts, resolveValues );
- }
- };
- };
-
- if ( remaining <= 1 ) {
- adoptValue( singleValue, primary.done( updateFunc( i ) ).resolve, primary.reject,
- !remaining );
-
- if ( primary.state() === "pending" ||
- isFunction( resolveValues[ i ] && resolveValues[ i ].then ) ) {
-
- return primary.then();
- }
- }
-
- while ( i-- ) {
- adoptValue( resolveValues[ i ], updateFunc( i ), primary.reject );
- }
-
- return primary.promise();
- }
-} );
-
-var rerrorNames = /^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;
-
-jQuery.Deferred.exceptionHook = function( error, asyncError ) {
-
- if ( window.console && window.console.warn && error && rerrorNames.test( error.name ) ) {
- window.console.warn( "jQuery.Deferred exception: " + error.message,
- error.stack, asyncError );
- }
-};
-
-jQuery.readyException = function( error ) {
- window.setTimeout( function() {
- throw error;
- } );
-};
-
-var readyList = jQuery.Deferred();
-
-jQuery.fn.ready = function( fn ) {
-
- readyList
- .then( fn )
-
- .catch( function( error ) {
- jQuery.readyException( error );
- } );
-
- return this;
-};
-
-jQuery.extend( {
-
- isReady: false,
-
- readyWait: 1,
-
- ready: function( wait ) {
-
- if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) {
- return;
- }
-
- jQuery.isReady = true;
-
- if ( wait !== true && --jQuery.readyWait > 0 ) {
- return;
- }
-
- readyList.resolveWith( document, [ jQuery ] );
- }
-} );
-
-jQuery.ready.then = readyList.then;
-
-function completed() {
- document.removeEventListener( "DOMContentLoaded", completed );
- window.removeEventListener( "load", completed );
- jQuery.ready();
-}
-
-if ( document.readyState === "complete" ||
- ( document.readyState !== "loading" && !document.documentElement.doScroll ) ) {
-
- window.setTimeout( jQuery.ready );
-
-} else {
-
- document.addEventListener( "DOMContentLoaded", completed );
-
- window.addEventListener( "load", completed );
-}
-
-var access = function( elems, fn, key, value, chainable, emptyGet, raw ) {
- var i = 0,
- len = elems.length,
- bulk = key == null;
-
- if ( toType( key ) === "object" ) {
- chainable = true;
- for ( i in key ) {
- access( elems, fn, i, key[ i ], true, emptyGet, raw );
- }
-
- } else if ( value !== undefined ) {
- chainable = true;
-
- if ( !isFunction( value ) ) {
- raw = true;
- }
-
- if ( bulk ) {
-
- if ( raw ) {
- fn.call( elems, value );
- fn = null;
-
- } else {
- bulk = fn;
- fn = function( elem, _key, value ) {
- return bulk.call( jQuery( elem ), value );
- };
- }
- }
-
- if ( fn ) {
- for ( ; i < len; i++ ) {
- fn(
- elems[ i ], key, raw ?
- value :
- value.call( elems[ i ], i, fn( elems[ i ], key ) )
- );
- }
- }
- }
-
- if ( chainable ) {
- return elems;
- }
-
- if ( bulk ) {
- return fn.call( elems );
- }
-
- return len ? fn( elems[ 0 ], key ) : emptyGet;
-};
-
-var rmsPrefix = /^-ms-/,
- rdashAlpha = /-([a-z])/g;
-
-function fcamelCase( _all, letter ) {
- return letter.toUpperCase();
-}
-
-function camelCase( string ) {
- return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase );
-}
-var acceptData = function( owner ) {
-
- return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType );
-};
-
-function Data() {
- this.expando = jQuery.expando + Data.uid++;
-}
-
-Data.uid = 1;
-
-Data.prototype = {
-
- cache: function( owner ) {
-
- var value = owner[ this.expando ];
-
- if ( !value ) {
- value = {};
-
- if ( acceptData( owner ) ) {
-
- if ( owner.nodeType ) {
- owner[ this.expando ] = value;
-
- } else {
- Object.defineProperty( owner, this.expando, {
- value: value,
- configurable: true
- } );
- }
- }
- }
-
- return value;
- },
- set: function( owner, data, value ) {
- var prop,
- cache = this.cache( owner );
-
- if ( typeof data === "string" ) {
- cache[ camelCase( data ) ] = value;
-
- } else {
-
- for ( prop in data ) {
- cache[ camelCase( prop ) ] = data[ prop ];
- }
- }
- return cache;
- },
- get: function( owner, key ) {
- return key === undefined ?
- this.cache( owner ) :
-
- owner[ this.expando ] && owner[ this.expando ][ camelCase( key ) ];
- },
- access: function( owner, key, value ) {
-
- if ( key === undefined ||
- ( ( key && typeof key === "string" ) && value === undefined ) ) {
-
- return this.get( owner, key );
- }
-
- this.set( owner, key, value );
-
- return value !== undefined ? value : key;
- },
- remove: function( owner, key ) {
- var i,
- cache = owner[ this.expando ];
-
- if ( cache === undefined ) {
- return;
- }
-
- if ( key !== undefined ) {
-
- if ( Array.isArray( key ) ) {
-
- key = key.map( camelCase );
- } else {
- key = camelCase( key );
-
- key = key in cache ?
- [ key ] :
- ( key.match( rnothtmlwhite ) || [] );
- }
-
- i = key.length;
-
- while ( i-- ) {
- delete cache[ key[ i ] ];
- }
- }
-
- if ( key === undefined || jQuery.isEmptyObject( cache ) ) {
-
- if ( owner.nodeType ) {
- owner[ this.expando ] = undefined;
- } else {
- delete owner[ this.expando ];
- }
- }
- },
- hasData: function( owner ) {
- var cache = owner[ this.expando ];
- return cache !== undefined && !jQuery.isEmptyObject( cache );
- }
-};
-var dataPriv = new Data();
-
-var dataUser = new Data();
-
-var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,
- rmultiDash = /[A-Z]/g;
-
-function getData( data ) {
- if ( data === "true" ) {
- return true;
- }
-
- if ( data === "false" ) {
- return false;
- }
-
- if ( data === "null" ) {
- return null;
- }
-
- if ( data === +data + "" ) {
- return +data;
- }
-
- if ( rbrace.test( data ) ) {
- return JSON.parse( data );
- }
-
- return data;
-}
-
-function dataAttr( elem, key, data ) {
- var name;
-
- if ( data === undefined && elem.nodeType === 1 ) {
- name = "data-" + key.replace( rmultiDash, "-$&" ).toLowerCase();
- data = elem.getAttribute( name );
-
- if ( typeof data === "string" ) {
- try {
- data = getData( data );
- } catch ( e ) {}
-
- dataUser.set( elem, key, data );
- } else {
- data = undefined;
- }
- }
- return data;
-}
-
-jQuery.extend( {
- hasData: function( elem ) {
- return dataUser.hasData( elem ) || dataPriv.hasData( elem );
- },
-
- data: function( elem, name, data ) {
- return dataUser.access( elem, name, data );
- },
-
- removeData: function( elem, name ) {
- dataUser.remove( elem, name );
- },
-
- _data: function( elem, name, data ) {
- return dataPriv.access( elem, name, data );
- },
-
- _removeData: function( elem, name ) {
- dataPriv.remove( elem, name );
- }
-} );
-
-jQuery.fn.extend( {
- data: function( key, value ) {
- var i, name, data,
- elem = this[ 0 ],
- attrs = elem && elem.attributes;
-
- if ( key === undefined ) {
- if ( this.length ) {
- data = dataUser.get( elem );
-
- if ( elem.nodeType === 1 && !dataPriv.get( elem, "hasDataAttrs" ) ) {
- i = attrs.length;
- while ( i-- ) {
-
- if ( attrs[ i ] ) {
- name = attrs[ i ].name;
- if ( name.indexOf( "data-" ) === 0 ) {
- name = camelCase( name.slice( 5 ) );
- dataAttr( elem, name, data[ name ] );
- }
- }
- }
- dataPriv.set( elem, "hasDataAttrs", true );
- }
- }
-
- return data;
- }
-
- if ( typeof key === "object" ) {
- return this.each( function() {
- dataUser.set( this, key );
- } );
- }
-
- return access( this, function( value ) {
- var data;
-
- if ( elem && value === undefined ) {
-
- data = dataUser.get( elem, key );
- if ( data !== undefined ) {
- return data;
- }
-
- data = dataAttr( elem, key );
- if ( data !== undefined ) {
- return data;
- }
-
- return;
- }
-
- this.each( function() {
-
- dataUser.set( this, key, value );
- } );
- }, null, value, arguments.length > 1, null, true );
- },
-
- removeData: function( key ) {
- return this.each( function() {
- dataUser.remove( this, key );
- } );
- }
-} );
-
-jQuery.extend( {
- queue: function( elem, type, data ) {
- var queue;
-
- if ( elem ) {
- type = ( type || "fx" ) + "queue";
- queue = dataPriv.get( elem, type );
-
- if ( data ) {
- if ( !queue || Array.isArray( data ) ) {
- queue = dataPriv.access( elem, type, jQuery.makeArray( data ) );
- } else {
- queue.push( data );
- }
- }
- return queue || [];
- }
- },
-
- dequeue: function( elem, type ) {
- type = type || "fx";
-
- var queue = jQuery.queue( elem, type ),
- startLength = queue.length,
- fn = queue.shift(),
- hooks = jQuery._queueHooks( elem, type ),
- next = function() {
- jQuery.dequeue( elem, type );
- };
-
- if ( fn === "inprogress" ) {
- fn = queue.shift();
- startLength--;
- }
-
- if ( fn ) {
-
- if ( type === "fx" ) {
- queue.unshift( "inprogress" );
- }
-
- delete hooks.stop;
- fn.call( elem, next, hooks );
- }
-
- if ( !startLength && hooks ) {
- hooks.empty.fire();
- }
- },
-
- _queueHooks: function( elem, type ) {
- var key = type + "queueHooks";
- return dataPriv.get( elem, key ) || dataPriv.access( elem, key, {
- empty: jQuery.Callbacks( "once memory" ).add( function() {
- dataPriv.remove( elem, [ type + "queue", key ] );
- } )
- } );
- }
-} );
-
-jQuery.fn.extend( {
- queue: function( type, data ) {
- var setter = 2;
-
- if ( typeof type !== "string" ) {
- data = type;
- type = "fx";
- setter--;
- }
-
- if ( arguments.length < setter ) {
- return jQuery.queue( this[ 0 ], type );
- }
-
- return data === undefined ?
- this :
- this.each( function() {
- var queue = jQuery.queue( this, type, data );
-
- jQuery._queueHooks( this, type );
-
- if ( type === "fx" && queue[ 0 ] !== "inprogress" ) {
- jQuery.dequeue( this, type );
- }
- } );
- },
- dequeue: function( type ) {
- return this.each( function() {
- jQuery.dequeue( this, type );
- } );
- },
- clearQueue: function( type ) {
- return this.queue( type || "fx", [] );
- },
-
- promise: function( type, obj ) {
- var tmp,
- count = 1,
- defer = jQuery.Deferred(),
- elements = this,
- i = this.length,
- resolve = function() {
- if ( !( --count ) ) {
- defer.resolveWith( elements, [ elements ] );
- }
- };
-
- if ( typeof type !== "string" ) {
- obj = type;
- type = undefined;
- }
- type = type || "fx";
-
- while ( i-- ) {
- tmp = dataPriv.get( elements[ i ], type + "queueHooks" );
- if ( tmp && tmp.empty ) {
- count++;
- tmp.empty.add( resolve );
- }
- }
- resolve();
- return defer.promise( obj );
- }
-} );
-var pnum = ( /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/ ).source;
-
-var rcssNum = new RegExp( "^(?:([+-])=|)(" + pnum + ")([a-z%]*)$", "i" );
-
-var cssExpand = [ "Top", "Right", "Bottom", "Left" ];
-
-var documentElement = document.documentElement;
-
- var isAttached = function( elem ) {
- return jQuery.contains( elem.ownerDocument, elem );
- },
- composed = { composed: true };
-
- if ( documentElement.getRootNode ) {
- isAttached = function( elem ) {
- return jQuery.contains( elem.ownerDocument, elem ) ||
- elem.getRootNode( composed ) === elem.ownerDocument;
- };
- }
-var isHiddenWithinTree = function( elem, el ) {
-
- elem = el || elem;
-
- return elem.style.display === "none" ||
- elem.style.display === "" &&
-
- isAttached( elem ) &&
-
- jQuery.css( elem, "display" ) === "none";
- };
-
-function adjustCSS( elem, prop, valueParts, tween ) {
- var adjusted, scale,
- maxIterations = 20,
- currentValue = tween ?
- function() {
- return tween.cur();
- } :
- function() {
- return jQuery.css( elem, prop, "" );
- },
- initial = currentValue(),
- unit = valueParts && valueParts[ 3 ] || ( jQuery.cssNumber[ prop ] ? "" : "px" ),
-
- initialInUnit = elem.nodeType &&
- ( jQuery.cssNumber[ prop ] || unit !== "px" && +initial ) &&
- rcssNum.exec( jQuery.css( elem, prop ) );
-
- if ( initialInUnit && initialInUnit[ 3 ] !== unit ) {
-
- initial = initial / 2;
-
- unit = unit || initialInUnit[ 3 ];
-
- initialInUnit = +initial || 1;
-
- while ( maxIterations-- ) {
-
- jQuery.style( elem, prop, initialInUnit + unit );
- if ( ( 1 - scale ) * ( 1 - ( scale = currentValue() / initial || 0.5 ) ) <= 0 ) {
- maxIterations = 0;
- }
- initialInUnit = initialInUnit / scale;
-
- }
-
- initialInUnit = initialInUnit * 2;
- jQuery.style( elem, prop, initialInUnit + unit );
-
- valueParts = valueParts || [];
- }
-
- if ( valueParts ) {
- initialInUnit = +initialInUnit || +initial || 0;
-
- adjusted = valueParts[ 1 ] ?
- initialInUnit + ( valueParts[ 1 ] + 1 ) * valueParts[ 2 ] :
- +valueParts[ 2 ];
- if ( tween ) {
- tween.unit = unit;
- tween.start = initialInUnit;
- tween.end = adjusted;
- }
- }
- return adjusted;
-}
-
-var defaultDisplayMap = {};
-
-function getDefaultDisplay( elem ) {
- var temp,
- doc = elem.ownerDocument,
- nodeName = elem.nodeName,
- display = defaultDisplayMap[ nodeName ];
-
- if ( display ) {
- return display;
- }
-
- temp = doc.body.appendChild( doc.createElement( nodeName ) );
- display = jQuery.css( temp, "display" );
-
- temp.parentNode.removeChild( temp );
-
- if ( display === "none" ) {
- display = "block";
- }
- defaultDisplayMap[ nodeName ] = display;
-
- return display;
-}
-
-function showHide( elements, show ) {
- var display, elem,
- values = [],
- index = 0,
- length = elements.length;
-
- for ( ; index < length; index++ ) {
- elem = elements[ index ];
- if ( !elem.style ) {
- continue;
- }
-
- display = elem.style.display;
- if ( show ) {
-
- if ( display === "none" ) {
- values[ index ] = dataPriv.get( elem, "display" ) || null;
- if ( !values[ index ] ) {
- elem.style.display = "";
- }
- }
- if ( elem.style.display === "" && isHiddenWithinTree( elem ) ) {
- values[ index ] = getDefaultDisplay( elem );
- }
- } else {
- if ( display !== "none" ) {
- values[ index ] = "none";
-
- dataPriv.set( elem, "display", display );
- }
- }
- }
-
- for ( index = 0; index < length; index++ ) {
- if ( values[ index ] != null ) {
- elements[ index ].style.display = values[ index ];
- }
- }
-
- return elements;
-}
-
-jQuery.fn.extend( {
- show: function() {
- return showHide( this, true );
- },
- hide: function() {
- return showHide( this );
- },
- toggle: function( state ) {
- if ( typeof state === "boolean" ) {
- return state ? this.show() : this.hide();
- }
-
- return this.each( function() {
- if ( isHiddenWithinTree( this ) ) {
- jQuery( this ).show();
- } else {
- jQuery( this ).hide();
- }
- } );
- }
-} );
-var rcheckableType = ( /^(?:checkbox|radio)$/i );
-
-var rtagName = ( /<([a-z][^\/\0>\x20\t\r\n\f]*)/i );
-
-var rscriptType = ( /^$|^module$|\/(?:java|ecma)script/i );
-
-( function() {
- var fragment = document.createDocumentFragment(),
- div = fragment.appendChild( document.createElement( "div" ) ),
- input = document.createElement( "input" );
-
- input.setAttribute( "type", "radio" );
- input.setAttribute( "checked", "checked" );
- input.setAttribute( "name", "t" );
-
- div.appendChild( input );
-
- support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked;
-
- div.innerHTML = "";
- support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue;
-
- div.innerHTML = "";
- support.option = !!div.lastChild;
-} )();
-
-var wrapMap = {
-
- thead: [ 1, "" ],
- col: [ 2, "" ],
- tr: [ 2, "" ],
- td: [ 3, "" ],
-
- _default: [ 0, "", "" ]
-};
-
-wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
-wrapMap.th = wrapMap.td;
-
-if ( !support.option ) {
- wrapMap.optgroup = wrapMap.option = [ 1, "" ];
-}
-
-function getAll( context, tag ) {
-
- var ret;
-
- if ( typeof context.getElementsByTagName !== "undefined" ) {
- ret = context.getElementsByTagName( tag || "*" );
-
- } else if ( typeof context.querySelectorAll !== "undefined" ) {
- ret = context.querySelectorAll( tag || "*" );
-
- } else {
- ret = [];
- }
-
- if ( tag === undefined || tag && nodeName( context, tag ) ) {
- return jQuery.merge( [ context ], ret );
- }
-
- return ret;
-}
-
-function setGlobalEval( elems, refElements ) {
- var i = 0,
- l = elems.length;
-
- for ( ; i < l; i++ ) {
- dataPriv.set(
- elems[ i ],
- "globalEval",
- !refElements || dataPriv.get( refElements[ i ], "globalEval" )
- );
- }
-}
-
-var rhtml = /<|?\w+;/;
-
-function buildFragment( elems, context, scripts, selection, ignored ) {
- var elem, tmp, tag, wrap, attached, j,
- fragment = context.createDocumentFragment(),
- nodes = [],
- i = 0,
- l = elems.length;
-
- for ( ; i < l; i++ ) {
- elem = elems[ i ];
-
- if ( elem || elem === 0 ) {
-
- if ( toType( elem ) === "object" ) {
-
- jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem );
-
- } else if ( !rhtml.test( elem ) ) {
- nodes.push( context.createTextNode( elem ) );
-
- } else {
- tmp = tmp || fragment.appendChild( context.createElement( "div" ) );
-
- tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase();
- wrap = wrapMap[ tag ] || wrapMap._default;
- tmp.innerHTML = wrap[ 1 ] + jQuery.htmlPrefilter( elem ) + wrap[ 2 ];
-
- j = wrap[ 0 ];
- while ( j-- ) {
- tmp = tmp.lastChild;
- }
-
- jQuery.merge( nodes, tmp.childNodes );
-
- tmp = fragment.firstChild;
-
- tmp.textContent = "";
- }
- }
- }
-
- fragment.textContent = "";
-
- i = 0;
- while ( ( elem = nodes[ i++ ] ) ) {
-
- if ( selection && jQuery.inArray( elem, selection ) > -1 ) {
- if ( ignored ) {
- ignored.push( elem );
- }
- continue;
- }
-
- attached = isAttached( elem );
-
- tmp = getAll( fragment.appendChild( elem ), "script" );
-
- if ( attached ) {
- setGlobalEval( tmp );
- }
-
- if ( scripts ) {
- j = 0;
- while ( ( elem = tmp[ j++ ] ) ) {
- if ( rscriptType.test( elem.type || "" ) ) {
- scripts.push( elem );
- }
- }
- }
- }
-
- return fragment;
-}
-
-var rtypenamespace = /^([^.]*)(?:\.(.+)|)/;
-
-function returnTrue() {
- return true;
-}
-
-function returnFalse() {
- return false;
-}
-
-function on( elem, types, selector, data, fn, one ) {
- var origFn, type;
-
- if ( typeof types === "object" ) {
-
- if ( typeof selector !== "string" ) {
-
- data = data || selector;
- selector = undefined;
- }
- for ( type in types ) {
- on( elem, type, selector, data, types[ type ], one );
- }
- return elem;
- }
-
- if ( data == null && fn == null ) {
-
- fn = selector;
- data = selector = undefined;
- } else if ( fn == null ) {
- if ( typeof selector === "string" ) {
-
- fn = data;
- data = undefined;
- } else {
-
- fn = data;
- data = selector;
- selector = undefined;
- }
- }
- if ( fn === false ) {
- fn = returnFalse;
- } else if ( !fn ) {
- return elem;
- }
-
- if ( one === 1 ) {
- origFn = fn;
- fn = function( event ) {
-
- jQuery().off( event );
- return origFn.apply( this, arguments );
- };
-
- fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );
- }
- return elem.each( function() {
- jQuery.event.add( this, types, fn, data, selector );
- } );
-}
-
-jQuery.event = {
-
- global: {},
-
- add: function( elem, types, handler, data, selector ) {
-
- var handleObjIn, eventHandle, tmp,
- events, t, handleObj,
- special, handlers, type, namespaces, origType,
- elemData = dataPriv.get( elem );
-
- if ( !acceptData( elem ) ) {
- return;
- }
-
- if ( handler.handler ) {
- handleObjIn = handler;
- handler = handleObjIn.handler;
- selector = handleObjIn.selector;
- }
-
- if ( selector ) {
- jQuery.find.matchesSelector( documentElement, selector );
- }
-
- if ( !handler.guid ) {
- handler.guid = jQuery.guid++;
- }
-
- if ( !( events = elemData.events ) ) {
- events = elemData.events = Object.create( null );
- }
- if ( !( eventHandle = elemData.handle ) ) {
- eventHandle = elemData.handle = function( e ) {
-
- return typeof jQuery !== "undefined" && jQuery.event.triggered !== e.type ?
- jQuery.event.dispatch.apply( elem, arguments ) : undefined;
- };
- }
-
- types = ( types || "" ).match( rnothtmlwhite ) || [ "" ];
- t = types.length;
- while ( t-- ) {
- tmp = rtypenamespace.exec( types[ t ] ) || [];
- type = origType = tmp[ 1 ];
- namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort();
-
- if ( !type ) {
- continue;
- }
-
- special = jQuery.event.special[ type ] || {};
-
- type = ( selector ? special.delegateType : special.bindType ) || type;
-
- special = jQuery.event.special[ type ] || {};
-
- handleObj = jQuery.extend( {
- type: type,
- origType: origType,
- data: data,
- handler: handler,
- guid: handler.guid,
- selector: selector,
- needsContext: selector && jQuery.expr.match.needsContext.test( selector ),
- namespace: namespaces.join( "." )
- }, handleObjIn );
-
- if ( !( handlers = events[ type ] ) ) {
- handlers = events[ type ] = [];
- handlers.delegateCount = 0;
-
- if ( !special.setup ||
- special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
-
- if ( elem.addEventListener ) {
- elem.addEventListener( type, eventHandle );
- }
- }
- }
-
- if ( special.add ) {
- special.add.call( elem, handleObj );
-
- if ( !handleObj.handler.guid ) {
- handleObj.handler.guid = handler.guid;
- }
- }
-
- if ( selector ) {
- handlers.splice( handlers.delegateCount++, 0, handleObj );
- } else {
- handlers.push( handleObj );
- }
-
- jQuery.event.global[ type ] = true;
- }
-
- },
-
- remove: function( elem, types, handler, selector, mappedTypes ) {
-
- var j, origCount, tmp,
- events, t, handleObj,
- special, handlers, type, namespaces, origType,
- elemData = dataPriv.hasData( elem ) && dataPriv.get( elem );
-
- if ( !elemData || !( events = elemData.events ) ) {
- return;
- }
-
- types = ( types || "" ).match( rnothtmlwhite ) || [ "" ];
- t = types.length;
- while ( t-- ) {
- tmp = rtypenamespace.exec( types[ t ] ) || [];
- type = origType = tmp[ 1 ];
- namespaces = ( tmp[ 2 ] || "" ).split( "." ).sort();
-
- if ( !type ) {
- for ( type in events ) {
- jQuery.event.remove( elem, type + types[ t ], handler, selector, true );
- }
- continue;
- }
-
- special = jQuery.event.special[ type ] || {};
- type = ( selector ? special.delegateType : special.bindType ) || type;
- handlers = events[ type ] || [];
- tmp = tmp[ 2 ] &&
- new RegExp( "(^|\\.)" + namespaces.join( "\\.(?:.*\\.|)" ) + "(\\.|$)" );
-
- origCount = j = handlers.length;
- while ( j-- ) {
- handleObj = handlers[ j ];
-
- if ( ( mappedTypes || origType === handleObj.origType ) &&
- ( !handler || handler.guid === handleObj.guid ) &&
- ( !tmp || tmp.test( handleObj.namespace ) ) &&
- ( !selector || selector === handleObj.selector ||
- selector === "**" && handleObj.selector ) ) {
- handlers.splice( j, 1 );
-
- if ( handleObj.selector ) {
- handlers.delegateCount--;
- }
- if ( special.remove ) {
- special.remove.call( elem, handleObj );
- }
- }
- }
-
- if ( origCount && !handlers.length ) {
- if ( !special.teardown ||
- special.teardown.call( elem, namespaces, elemData.handle ) === false ) {
-
- jQuery.removeEvent( elem, type, elemData.handle );
- }
-
- delete events[ type ];
- }
- }
-
- if ( jQuery.isEmptyObject( events ) ) {
- dataPriv.remove( elem, "handle events" );
- }
- },
-
- dispatch: function( nativeEvent ) {
-
- var i, j, ret, matched, handleObj, handlerQueue,
- args = new Array( arguments.length ),
-
- event = jQuery.event.fix( nativeEvent ),
-
- handlers = (
- dataPriv.get( this, "events" ) || Object.create( null )
- )[ event.type ] || [],
- special = jQuery.event.special[ event.type ] || {};
-
- args[ 0 ] = event;
-
- for ( i = 1; i < arguments.length; i++ ) {
- args[ i ] = arguments[ i ];
- }
-
- event.delegateTarget = this;
-
- if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) {
- return;
- }
-
- handlerQueue = jQuery.event.handlers.call( this, event, handlers );
-
- i = 0;
- while ( ( matched = handlerQueue[ i++ ] ) && !event.isPropagationStopped() ) {
- event.currentTarget = matched.elem;
-
- j = 0;
- while ( ( handleObj = matched.handlers[ j++ ] ) &&
- !event.isImmediatePropagationStopped() ) {
-
- if ( !event.rnamespace || handleObj.namespace === false ||
- event.rnamespace.test( handleObj.namespace ) ) {
-
- event.handleObj = handleObj;
- event.data = handleObj.data;
-
- ret = ( ( jQuery.event.special[ handleObj.origType ] || {} ).handle ||
- handleObj.handler ).apply( matched.elem, args );
-
- if ( ret !== undefined ) {
- if ( ( event.result = ret ) === false ) {
- event.preventDefault();
- event.stopPropagation();
- }
- }
- }
- }
- }
-
- if ( special.postDispatch ) {
- special.postDispatch.call( this, event );
- }
-
- return event.result;
- },
-
- handlers: function( event, handlers ) {
- var i, handleObj, sel, matchedHandlers, matchedSelectors,
- handlerQueue = [],
- delegateCount = handlers.delegateCount,
- cur = event.target;
-
- if ( delegateCount &&
-
- cur.nodeType &&
-
- !( event.type === "click" && event.button >= 1 ) ) {
-
- for ( ; cur !== this; cur = cur.parentNode || this ) {
-
- if ( cur.nodeType === 1 && !( event.type === "click" && cur.disabled === true ) ) {
- matchedHandlers = [];
- matchedSelectors = {};
- for ( i = 0; i < delegateCount; i++ ) {
- handleObj = handlers[ i ];
-
- sel = handleObj.selector + " ";
-
- if ( matchedSelectors[ sel ] === undefined ) {
- matchedSelectors[ sel ] = handleObj.needsContext ?
- jQuery( sel, this ).index( cur ) > -1 :
- jQuery.find( sel, this, null, [ cur ] ).length;
- }
- if ( matchedSelectors[ sel ] ) {
- matchedHandlers.push( handleObj );
- }
- }
- if ( matchedHandlers.length ) {
- handlerQueue.push( { elem: cur, handlers: matchedHandlers } );
- }
- }
- }
- }
-
- cur = this;
- if ( delegateCount < handlers.length ) {
- handlerQueue.push( { elem: cur, handlers: handlers.slice( delegateCount ) } );
- }
-
- return handlerQueue;
- },
-
- addProp: function( name, hook ) {
- Object.defineProperty( jQuery.Event.prototype, name, {
- enumerable: true,
- configurable: true,
-
- get: isFunction( hook ) ?
- function() {
- if ( this.originalEvent ) {
- return hook( this.originalEvent );
- }
- } :
- function() {
- if ( this.originalEvent ) {
- return this.originalEvent[ name ];
- }
- },
-
- set: function( value ) {
- Object.defineProperty( this, name, {
- enumerable: true,
- configurable: true,
- writable: true,
- value: value
- } );
- }
- } );
- },
-
- fix: function( originalEvent ) {
- return originalEvent[ jQuery.expando ] ?
- originalEvent :
- new jQuery.Event( originalEvent );
- },
-
- special: {
- load: {
-
- noBubble: true
- },
- click: {
-
- setup: function( data ) {
-
- var el = this || data;
-
- if ( rcheckableType.test( el.type ) &&
- el.click && nodeName( el, "input" ) ) {
-
- leverageNative( el, "click", true );
- }
-
- return false;
- },
- trigger: function( data ) {
-
- var el = this || data;
-
- if ( rcheckableType.test( el.type ) &&
- el.click && nodeName( el, "input" ) ) {
-
- leverageNative( el, "click" );
- }
-
- return true;
- },
-
- _default: function( event ) {
- var target = event.target;
- return rcheckableType.test( target.type ) &&
- target.click && nodeName( target, "input" ) &&
- dataPriv.get( target, "click" ) ||
- nodeName( target, "a" );
- }
- },
-
- beforeunload: {
- postDispatch: function( event ) {
-
- if ( event.result !== undefined && event.originalEvent ) {
- event.originalEvent.returnValue = event.result;
- }
- }
- }
- }
-};
-
-function leverageNative( el, type, isSetup ) {
-
- if ( !isSetup ) {
- if ( dataPriv.get( el, type ) === undefined ) {
- jQuery.event.add( el, type, returnTrue );
- }
- return;
- }
-
- dataPriv.set( el, type, false );
- jQuery.event.add( el, type, {
- namespace: false,
- handler: function( event ) {
- var result,
- saved = dataPriv.get( this, type );
-
- if ( ( event.isTrigger & 1 ) && this[ type ] ) {
-
- if ( !saved ) {
-
- saved = slice.call( arguments );
- dataPriv.set( this, type, saved );
-
- this[ type ]();
- result = dataPriv.get( this, type );
- dataPriv.set( this, type, false );
-
- if ( saved !== result ) {
-
- event.stopImmediatePropagation();
- event.preventDefault();
-
- return result;
- }
-
- } else if ( ( jQuery.event.special[ type ] || {} ).delegateType ) {
- event.stopPropagation();
- }
-
- } else if ( saved ) {
-
- dataPriv.set( this, type, jQuery.event.trigger(
- saved[ 0 ],
- saved.slice( 1 ),
- this
- ) );
-
- event.stopPropagation();
- event.isImmediatePropagationStopped = returnTrue;
- }
- }
- } );
-}
-
-jQuery.removeEvent = function( elem, type, handle ) {
-
- if ( elem.removeEventListener ) {
- elem.removeEventListener( type, handle );
- }
-};
-
-jQuery.Event = function( src, props ) {
-
- if ( !( this instanceof jQuery.Event ) ) {
- return new jQuery.Event( src, props );
- }
-
- if ( src && src.type ) {
- this.originalEvent = src;
- this.type = src.type;
-
- this.isDefaultPrevented = src.defaultPrevented ||
- src.defaultPrevented === undefined &&
-
- src.returnValue === false ?
- returnTrue :
- returnFalse;
-
- this.target = ( src.target && src.target.nodeType === 3 ) ?
- src.target.parentNode :
- src.target;
-
- this.currentTarget = src.currentTarget;
- this.relatedTarget = src.relatedTarget;
-
- } else {
- this.type = src;
- }
-
- if ( props ) {
- jQuery.extend( this, props );
- }
-
- this.timeStamp = src && src.timeStamp || Date.now();
-
- this[ jQuery.expando ] = true;
-};
-
-jQuery.Event.prototype = {
- constructor: jQuery.Event,
- isDefaultPrevented: returnFalse,
- isPropagationStopped: returnFalse,
- isImmediatePropagationStopped: returnFalse,
- isSimulated: false,
-
- preventDefault: function() {
- var e = this.originalEvent;
-
- this.isDefaultPrevented = returnTrue;
-
- if ( e && !this.isSimulated ) {
- e.preventDefault();
- }
- },
- stopPropagation: function() {
- var e = this.originalEvent;
-
- this.isPropagationStopped = returnTrue;
-
- if ( e && !this.isSimulated ) {
- e.stopPropagation();
- }
- },
- stopImmediatePropagation: function() {
- var e = this.originalEvent;
-
- this.isImmediatePropagationStopped = returnTrue;
-
- if ( e && !this.isSimulated ) {
- e.stopImmediatePropagation();
- }
-
- this.stopPropagation();
- }
-};
-
-jQuery.each( {
- altKey: true,
- bubbles: true,
- cancelable: true,
- changedTouches: true,
- ctrlKey: true,
- detail: true,
- eventPhase: true,
- metaKey: true,
- pageX: true,
- pageY: true,
- shiftKey: true,
- view: true,
- "char": true,
- code: true,
- charCode: true,
- key: true,
- keyCode: true,
- button: true,
- buttons: true,
- clientX: true,
- clientY: true,
- offsetX: true,
- offsetY: true,
- pointerId: true,
- pointerType: true,
- screenX: true,
- screenY: true,
- targetTouches: true,
- toElement: true,
- touches: true,
- which: true
-}, jQuery.event.addProp );
-
-jQuery.each( { focus: "focusin", blur: "focusout" }, function( type, delegateType ) {
-
- function focusMappedHandler( nativeEvent ) {
- if ( document.documentMode ) {
-
- var handle = dataPriv.get( this, "handle" ),
- event = jQuery.event.fix( nativeEvent );
- event.type = nativeEvent.type === "focusin" ? "focus" : "blur";
- event.isSimulated = true;
-
- handle( nativeEvent );
-
- if ( event.target === event.currentTarget ) {
-
- handle( event );
- }
- } else {
-
- jQuery.event.simulate( delegateType, nativeEvent.target,
- jQuery.event.fix( nativeEvent ) );
- }
- }
-
- jQuery.event.special[ type ] = {
-
- setup: function() {
-
- var attaches;
-
- leverageNative( this, type, true );
-
- if ( document.documentMode ) {
-
- attaches = dataPriv.get( this, delegateType );
- if ( !attaches ) {
- this.addEventListener( delegateType, focusMappedHandler );
- }
- dataPriv.set( this, delegateType, ( attaches || 0 ) + 1 );
- } else {
-
- return false;
- }
- },
- trigger: function() {
-
- leverageNative( this, type );
-
- return true;
- },
-
- teardown: function() {
- var attaches;
-
- if ( document.documentMode ) {
- attaches = dataPriv.get( this, delegateType ) - 1;
- if ( !attaches ) {
- this.removeEventListener( delegateType, focusMappedHandler );
- dataPriv.remove( this, delegateType );
- } else {
- dataPriv.set( this, delegateType, attaches );
- }
- } else {
-
- return false;
- }
- },
-
- _default: function( event ) {
- return dataPriv.get( event.target, type );
- },
-
- delegateType: delegateType
- };
-
- jQuery.event.special[ delegateType ] = {
- setup: function() {
-
- var doc = this.ownerDocument || this.document || this,
- dataHolder = document.documentMode ? this : doc,
- attaches = dataPriv.get( dataHolder, delegateType );
-
- if ( !attaches ) {
- if ( document.documentMode ) {
- this.addEventListener( delegateType, focusMappedHandler );
- } else {
- doc.addEventListener( type, focusMappedHandler, true );
- }
- }
- dataPriv.set( dataHolder, delegateType, ( attaches || 0 ) + 1 );
- },
- teardown: function() {
- var doc = this.ownerDocument || this.document || this,
- dataHolder = document.documentMode ? this : doc,
- attaches = dataPriv.get( dataHolder, delegateType ) - 1;
-
- if ( !attaches ) {
- if ( document.documentMode ) {
- this.removeEventListener( delegateType, focusMappedHandler );
- } else {
- doc.removeEventListener( type, focusMappedHandler, true );
- }
- dataPriv.remove( dataHolder, delegateType );
- } else {
- dataPriv.set( dataHolder, delegateType, attaches );
- }
- }
- };
-} );
-
-jQuery.each( {
- mouseenter: "mouseover",
- mouseleave: "mouseout",
- pointerenter: "pointerover",
- pointerleave: "pointerout"
-}, function( orig, fix ) {
- jQuery.event.special[ orig ] = {
- delegateType: fix,
- bindType: fix,
-
- handle: function( event ) {
- var ret,
- target = this,
- related = event.relatedTarget,
- handleObj = event.handleObj;
-
- if ( !related || ( related !== target && !jQuery.contains( target, related ) ) ) {
- event.type = handleObj.origType;
- ret = handleObj.handler.apply( this, arguments );
- event.type = fix;
- }
- return ret;
- }
- };
-} );
-
-jQuery.fn.extend( {
-
- on: function( types, selector, data, fn ) {
- return on( this, types, selector, data, fn );
- },
- one: function( types, selector, data, fn ) {
- return on( this, types, selector, data, fn, 1 );
- },
- off: function( types, selector, fn ) {
- var handleObj, type;
- if ( types && types.preventDefault && types.handleObj ) {
-
- handleObj = types.handleObj;
- jQuery( types.delegateTarget ).off(
- handleObj.namespace ?
- handleObj.origType + "." + handleObj.namespace :
- handleObj.origType,
- handleObj.selector,
- handleObj.handler
- );
- return this;
- }
- if ( typeof types === "object" ) {
-
- for ( type in types ) {
- this.off( type, selector, types[ type ] );
- }
- return this;
- }
- if ( selector === false || typeof selector === "function" ) {
-
- fn = selector;
- selector = undefined;
- }
- if ( fn === false ) {
- fn = returnFalse;
- }
- return this.each( function() {
- jQuery.event.remove( this, types, fn, selector );
- } );
- }
-} );
-
-var
-
- rnoInnerhtml = /
-
-