From f72f7efe4696935c1c44f00b29148de5eafe4282 Mon Sep 17 00:00:00 2001 From: Lars Jung Date: Wed, 9 Apr 2014 18:14:32 +0200 Subject: [PATCH 001/106] Set version to 0.24.1+. --- README.md | 5 +++++ package.json | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e7329f82..0e20213a 100644 --- a/README.md +++ b/README.md @@ -54,6 +54,11 @@ It profits from these great projects: **h5ai** uses [semantic versioning](http://semver.org/). +### develop branch + +* ... + + ### v0.24.1 - *2014-04-09* * security fixes! (issues #268, #269) diff --git a/package.json b/package.json index 70451e75..378f8ec0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "h5ai", - "version": "0.24.1", + "version": "0.24.1+", "description": "a modern HTTP web server index", "url": "http://larsjung.de/h5ai/", "author": "Lars Jung", From b6735a01eadab0a338c643793a4b64bceac67d50 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?William=20Wennerstr=C3=B6m?= Date: Thu, 17 Apr 2014 16:34:12 +0200 Subject: [PATCH 002/106] Update for Swedish language Added missing translations for Swedish. --- src/_h5ai/conf/l10n/sv.json | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/_h5ai/conf/l10n/sv.json b/src/_h5ai/conf/l10n/sv.json index a39589bf..874ecce9 100644 --- a/src/_h5ai/conf/l10n/sv.json +++ b/src/_h5ai/conf/l10n/sv.json @@ -1,10 +1,17 @@ { "lang": "svenska", "details": "detaljerad", + "list": "lista", + "grid": "rutnät", "icons": "ikoner", "name": "Filnamn", "lastModified": "Senast ändrad", "size": "Filstorlek", "parentDirectory": "Till överordnad mapp", - "empty": "tom" -} \ No newline at end of file + "empty": "tom", + "folders": "kataloger", + "files": "filer", + "download": "ladda ner", + "noMatch": "ingen matchning", + "delete": "radera" +} From a75f1b61bc81f115b9dcd51a0481a622eaf06af4 Mon Sep 17 00:00:00 2001 From: Lars Jung Date: Thu, 8 May 2014 00:39:24 +0200 Subject: [PATCH 003/106] Add PHP exec wrapper. --- src/_h5ai/server/php/inc/util.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/_h5ai/server/php/inc/util.php b/src/_h5ai/server/php/inc/util.php index db5294dc..3d679a2d 100644 --- a/src/_h5ai/server/php/inc/util.php +++ b/src/_h5ai/server/php/inc/util.php @@ -72,4 +72,13 @@ function load_commented_json($file) { return json_decode($str, true); } +function exec_cmd($cmd) { + + $lines = array(); + $rc = null; + exec($cmd, $lines, $rc); + + return $lines; +} + ?> \ No newline at end of file From 364f71dd08a5155cfe4d5cfe12200b7544ee53e0 Mon Sep 17 00:00:00 2001 From: Lars Jung Date: Thu, 8 May 2014 01:31:42 +0200 Subject: [PATCH 004/106] Replace backtick operator with exec. --- src/_h5ai/conf/options.json | 2 +- src/_h5ai/server/php/inc/App.php | 19 ++++++++----------- src/_h5ai/server/php/inc/Item.php | 2 +- src/_h5ai/server/php/inc/Thumb.php | 2 +- src/_h5ai/server/php/inc/util.php | 2 +- 5 files changed, 12 insertions(+), 15 deletions(-) diff --git a/src/_h5ai/conf/options.json b/src/_h5ai/conf/options.json index eb8671c6..bf281e8a 100644 --- a/src/_h5ai/conf/options.json +++ b/src/_h5ai/conf/options.json @@ -137,7 +137,7 @@ Options Calc the size of folders. */ "foldersize": { - "enabled": false + "enabled": true }, /* diff --git a/src/_h5ai/server/php/inc/App.php b/src/_h5ai/server/php/inc/App.php index a292d71d..79faeafe 100644 --- a/src/_h5ai/server/php/inc/App.php +++ b/src/_h5ai/server/php/inc/App.php @@ -301,19 +301,16 @@ class App { } $exif = function_exists("exif_thumbnail"); $cache = @is_writable($this->get_cache_abs_path()); - if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { - $tar = @preg_match("/tar(.exe)?$/i", `which tar`) > 0; - $zip = @preg_match("/zip(.exe)?$/i", `which zip`) > 0; - $convert = @preg_match("/convert(.exe)?$/i", `which convert`) > 0; - $ffmpeg = @preg_match("/ffmpeg(.exe)?$/i", `which ffmpeg`) > 0; - $du = @preg_match("/du(.exe)?$/i", `which du`) > 0; + if (strtoupper(substr(PHP_OS, 0, 3)) === "WIN") { + $cmd = "which"; } else { - $tar = @preg_match("/tar(.exe)?$/i", `command -v tar`) > 0; - $zip = @preg_match("/zip(.exe)?$/i", `command -v zip`) > 0; - $convert = @preg_match("/convert(.exe)?$/i", `command -v convert`) > 0; - $ffmpeg = @preg_match("/ffmpeg(.exe)?$/i", `command -v ffmpeg`) > 0; - $du = @preg_match("/du(.exe)?$/i", `command -v du`) > 0; + $cmd = "command -v"; } + $tar = @preg_match("/tar(.exe)?$/i", exec_cmd($cmd . " tar")) > 0; + $zip = @preg_match("/zip(.exe)?$/i", exec_cmd($cmd . " zip")) > 0; + $convert = @preg_match("/convert(.exe)?$/i", exec_cmd($cmd . " convert")) > 0; + $ffmpeg = @preg_match("/ffmpeg(.exe)?$/i", exec_cmd($cmd . " ffmpeg")) > 0; + $du = @preg_match("/du(.exe)?$/i", exec_cmd($cmd . " du")) > 0; return array( "idx" => $this->app_abs_href . "server/php/index.php", diff --git a/src/_h5ai/server/php/inc/Item.php b/src/_h5ai/server/php/inc/Item.php index 2da5bffa..cf0adaf8 100644 --- a/src/_h5ai/server/php/inc/Item.php +++ b/src/_h5ai/server/php/inc/Item.php @@ -58,7 +58,7 @@ class Item { $options = $app->get_options(); if ($options["foldersize"]["enabled"]) { $cmd = str_replace("[DIR]", escapeshellarg($this->abs_path), Item::$FOLDER_SIZE_CMD); - $this->size = intval(preg_replace("/\s.*$/", "", `$cmd`), 10) * 1024; + $this->size = intval(preg_replace("/\s.*$/", "", exec_cmd($cmd)), 10) * 1024; } } else { $this->size = @filesize($this->abs_path); diff --git a/src/_h5ai/server/php/inc/Thumb.php b/src/_h5ai/server/php/inc/Thumb.php index c4a878f3..24efe39e 100644 --- a/src/_h5ai/server/php/inc/Thumb.php +++ b/src/_h5ai/server/php/inc/Thumb.php @@ -94,7 +94,7 @@ class Thumb { if (!file_exists($capture_abs_path) || filemtime($source_abs_path) >= filemtime($capture_abs_path)) { $cmd = str_replace("[SOURCE]", escapeshellarg($source_abs_path), $cmd); $cmd = str_replace("[TARGET]", escapeshellarg($capture_abs_path), $cmd); - `$cmd`; + exec_cmd($cmd); } return file_exists($capture_abs_path) ? $capture_abs_path : null; diff --git a/src/_h5ai/server/php/inc/util.php b/src/_h5ai/server/php/inc/util.php index 3d679a2d..19c1ca64 100644 --- a/src/_h5ai/server/php/inc/util.php +++ b/src/_h5ai/server/php/inc/util.php @@ -78,7 +78,7 @@ function exec_cmd($cmd) { $rc = null; exec($cmd, $lines, $rc); - return $lines; + return implode('\n', $lines); } ?> \ No newline at end of file From 554f79fb9421d797c8141b29e6d9aac0dcb17b67 Mon Sep 17 00:00:00 2001 From: Lars Jung Date: Thu, 8 May 2014 01:39:38 +0200 Subject: [PATCH 005/106] Update bg translation. --- src/_h5ai/conf/l10n/bg.json | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/_h5ai/conf/l10n/bg.json b/src/_h5ai/conf/l10n/bg.json index 58cf98ae..38324c12 100644 --- a/src/_h5ai/conf/l10n/bg.json +++ b/src/_h5ai/conf/l10n/bg.json @@ -1,12 +1,19 @@ { "lang": "български", "details": "детайли", + "list": "списък", + "grid": "мрежа", "icons": "икони", "name": "Име", "lastModified": "Последна промяна", "size": "Размер", "parentDirectory": "Предходна директория", - "empty": "празно", - "folders": "папки", - "files": "файлове" + "empty": "празна", + "folders": "директории", + "files": "файлове", + "download": "изтегляне", + "noMatch": "няма съвпадение", + "dateFormat": "DD-MM-YYYY HH:mm", + "filter": "филтър", + "delete": "изтрий" } \ No newline at end of file From 4919bc8a6e71bb371c6870f20476fbee59a4b2f0 Mon Sep 17 00:00:00 2001 From: Lars Jung Date: Thu, 8 May 2014 01:46:52 +0200 Subject: [PATCH 006/106] Update pt translation, add ko translation. --- src/_h5ai/conf/l10n/ko.json | 19 +++++++++++++++++++ src/_h5ai/conf/l10n/pt.json | 13 ++++++++++--- 2 files changed, 29 insertions(+), 3 deletions(-) create mode 100644 src/_h5ai/conf/l10n/ko.json diff --git a/src/_h5ai/conf/l10n/ko.json b/src/_h5ai/conf/l10n/ko.json new file mode 100644 index 00000000..bb9597cf --- /dev/null +++ b/src/_h5ai/conf/l10n/ko.json @@ -0,0 +1,19 @@ +{ + "lang": "한국어", + "details": "자세히", + "list": "목록", + "grid": "그리드", + "icons": "아이콘", + "name": "파일명", + "lastModified": "최근수정일", + "size": "크기", + "parentDirectory": "상위폴더", + "empty": "빈폴더", + "folders": "폴더", + "files": "파일", + "download": "다운로드", + "noMatch": "해당파일이 없습니다.", + "dateFormat": "YYYY-MM-DD HH:mm", + "filter": "필터", + "delete": "삭제" +} \ No newline at end of file diff --git a/src/_h5ai/conf/l10n/pt.json b/src/_h5ai/conf/l10n/pt.json index 5e238ce0..debe6b63 100644 --- a/src/_h5ai/conf/l10n/pt.json +++ b/src/_h5ai/conf/l10n/pt.json @@ -1,12 +1,19 @@ { "lang": "português", "details": "detalhes", + "list": "lista", + "grid": "grelha", "icons": "ícones", "name": "Nome", - "lastModified": "Última modificação", + "lastModified": "última modificação", "size": "Tamanho", - "parentDirectory": "Diretório superior", + "parentDirectory": "diretório acima", "empty": "vazio", "folders": "pastas", - "files": "arquivos" + "files": "arquivos", + "download": "descarregar", + "noMatch": "sem resultados", + "dateFormat": "DD-MM-YYYY HH:mm", + "filter": "filtro", + "delete": "eliminar" } \ No newline at end of file From 53c4cb645f0f26d998074b5179302ef149045e10 Mon Sep 17 00:00:00 2001 From: Lars Jung Date: Thu, 8 May 2014 01:54:05 +0200 Subject: [PATCH 007/106] Cleanup. --- src/_h5ai/conf/l10n/sv.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/_h5ai/conf/l10n/sv.json b/src/_h5ai/conf/l10n/sv.json index 874ecce9..5babca34 100644 --- a/src/_h5ai/conf/l10n/sv.json +++ b/src/_h5ai/conf/l10n/sv.json @@ -14,4 +14,4 @@ "download": "ladda ner", "noMatch": "ingen matchning", "delete": "radera" -} +} \ No newline at end of file From 365b6d80199b8010a3e7d07998cd5d2ebf50e7f1 Mon Sep 17 00:00:00 2001 From: Lars Jung Date: Thu, 8 May 2014 01:59:50 +0200 Subject: [PATCH 008/106] Update readme and license. --- LICENSE.md | 2 +- README.md | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/LICENSE.md b/LICENSE.md index 35363c3f..223593d0 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,4 +1,4 @@ -Copyright (c) 2013 Lars Jung, http://larsjung.de +Copyright (c) 2014 Lars Jung, http://larsjung.de Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in diff --git a/README.md b/README.md index 0e20213a..252a3ced 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,8 @@ It profits from these great projects: ### develop branch -* ... +* replaces PHP backtick operator with `exec` +* language updates (`bg`, `ko`, `pt`, `sv`) ### v0.24.1 - *2014-04-09* From 08e18b40b58e381b9ffdbfc188adbfaf5244bcae Mon Sep 17 00:00:00 2001 From: Lars Jung Date: Fri, 9 May 2014 21:56:12 +0200 Subject: [PATCH 009/106] Add passthru delegate and avconv support. --- src/_h5ai/index.html.jade | 4 +++- src/_h5ai/server/php/inc/App.php | 3 +++ src/_h5ai/server/php/inc/Archive.php | 2 +- src/_h5ai/server/php/inc/Thumb.php | 4 ++++ src/_h5ai/server/php/inc/util.php | 8 +++++++- 5 files changed, 18 insertions(+), 3 deletions(-) diff --git a/src/_h5ai/index.html.jade b/src/_h5ai/index.html.jade index e09cbed6..9e7f2409 100644 --- a/src/_h5ai/index.html.jade +++ b/src/_h5ai/index.html.jade @@ -48,12 +48,14 @@ html.no-js.browser( lang="en" ) span.label Use EXIF thumbs span.result ? div.info PHP EXIF extension available - li.test( data-id="ffmpeg" ) + li.test( data-id="moviethumbs" ) span.label Movie thumbs span.result ? div.info | Command line program code ffmpeg + | or + code avconv | available li.test( data-id="convert" ) span.label PDF thumbs diff --git a/src/_h5ai/server/php/inc/App.php b/src/_h5ai/server/php/inc/App.php index 79faeafe..040feda0 100644 --- a/src/_h5ai/server/php/inc/App.php +++ b/src/_h5ai/server/php/inc/App.php @@ -310,6 +310,7 @@ class App { $zip = @preg_match("/zip(.exe)?$/i", exec_cmd($cmd . " zip")) > 0; $convert = @preg_match("/convert(.exe)?$/i", exec_cmd($cmd . " convert")) > 0; $ffmpeg = @preg_match("/ffmpeg(.exe)?$/i", exec_cmd($cmd . " ffmpeg")) > 0; + $avconv = @preg_match("/avconv(.exe)?$/i", exec_cmd($cmd . " avconv")) > 0; $du = @preg_match("/du(.exe)?$/i", exec_cmd($cmd . " du")) > 0; return array( @@ -323,6 +324,8 @@ class App { "zip" => $zip, "convert" => $convert, "ffmpeg" => $ffmpeg, + "avconv" => $avconv, + "moviethumbs" => $ffmpeg || $avconv, "du" => $du ); } diff --git a/src/_h5ai/server/php/inc/Archive.php b/src/_h5ai/server/php/inc/Archive.php index 92fd30f0..34d8f9fc 100644 --- a/src/_h5ai/server/php/inc/Archive.php +++ b/src/_h5ai/server/php/inc/Archive.php @@ -48,7 +48,7 @@ class Archive { $cmd = str_replace("[DIRS]", count($this->dirs) ? implode(" ", array_map("escapeshellarg", $this->dirs)) : "", $cmd); $cmd = str_replace("[FILES]", count($this->files) ? implode(" ", array_map("escapeshellarg", $this->files)) : "", $cmd); try { - passthru($cmd); + passthru_cmd($cmd); } catch (Exeption $err) { return 500; } diff --git a/src/_h5ai/server/php/inc/Thumb.php b/src/_h5ai/server/php/inc/Thumb.php index 24efe39e..00dad454 100644 --- a/src/_h5ai/server/php/inc/Thumb.php +++ b/src/_h5ai/server/php/inc/Thumb.php @@ -3,6 +3,7 @@ class Thumb { private static $FFMPEG_CMD = "ffmpeg -ss 0:01:00 -i [SOURCE] -an -vframes 1 [TARGET]"; + private static $AVCONV_CMD = "avconv -ss 0:01:00 -i [SOURCE] -an -vframes 1 [TARGET]"; private static $CONVERT_CMD = "convert -strip [SOURCE][0] [TARGET]"; private static $THUMB_CACHE = "thumbs"; @@ -36,6 +37,9 @@ class Thumb { $capture_abs_path = $source_abs_path; } else if ($type === "mov") { $capture_abs_path = $this->capture(Thumb::$FFMPEG_CMD, $source_abs_path); + if ($capture_abs_path === null) { + $capture_abs_path = $this->capture(Thumb::$AVCONV_CMD, $source_abs_path); + } } else if ($type === "doc") { $capture_abs_path = $this->capture(Thumb::$CONVERT_CMD, $source_abs_path); } diff --git a/src/_h5ai/server/php/inc/util.php b/src/_h5ai/server/php/inc/util.php index 19c1ca64..e684bf5c 100644 --- a/src/_h5ai/server/php/inc/util.php +++ b/src/_h5ai/server/php/inc/util.php @@ -77,8 +77,14 @@ function exec_cmd($cmd) { $lines = array(); $rc = null; exec($cmd, $lines, $rc); + return implode("\n", $lines); +} - return implode('\n', $lines); +function passthru_cmd($cmd) { + + $rc = null; + passthru($cmd, $rc); + return $rc; } ?> \ No newline at end of file From 5043a5188b3fadeecda0802495fecb3ba87b970a Mon Sep 17 00:00:00 2001 From: Lars Jung Date: Fri, 9 May 2014 23:20:14 +0200 Subject: [PATCH 010/106] Add padding to rational thumbs. --- src/_h5ai/client/css/inc/view.less | 11 +++++++++-- src/_h5ai/conf/options.json | 2 +- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/_h5ai/client/css/inc/view.less b/src/_h5ai/client/css/inc/view.less index 39edaa8a..250e4400 100644 --- a/src/_h5ai/client/css/inc/view.less +++ b/src/_h5ai/client/css/inc/view.less @@ -145,9 +145,16 @@ img { vertical-align: top; + } - &.thumb { - box-shadow: 0 0 0 1px #ddd; + .thumb { + box-shadow: 0 0 0 1px rgba(0,0,0,0.12); + } + + &.rational { + .thumb { + padding: 2px; + border-radius: 3px; } } } diff --git a/src/_h5ai/conf/options.json b/src/_h5ai/conf/options.json index bf281e8a..477c219c 100644 --- a/src/_h5ai/conf/options.json +++ b/src/_h5ai/conf/options.json @@ -115,7 +115,7 @@ Options - packageName: basename of the download package, null for current foldername */ "download": { - "enabled": false, + "enabled": true, "type": "php-tar", "packageName": null }, From b67c22f33ba9a6b803350ea99b8e4e16a8dd98cc Mon Sep 17 00:00:00 2001 From: Lars Jung Date: Mon, 12 May 2014 00:39:27 +0200 Subject: [PATCH 011/106] Refactoring PHP init code. --- src/_h5ai/client/css/inc/h5ai-info.less | 4 +- src/_h5ai/client/js/inc/info.js | 4 +- src/_h5ai/index.html.jade | 20 ++--- src/_h5ai/server/php/inc/App.php | 97 ++++++++++++++++--------- src/_h5ai/server/php/index.php | 22 +----- 5 files changed, 79 insertions(+), 68 deletions(-) diff --git a/src/_h5ai/client/css/inc/h5ai-info.less b/src/_h5ai/client/css/inc/h5ai-info.less index cafce35e..d33a2f9d 100644 --- a/src/_h5ai/client/css/inc/h5ai-info.less +++ b/src/_h5ai/client/css/inc/h5ai-info.less @@ -71,7 +71,7 @@ body#h5ai-info { .test { .label { display: inline-block; - width: 350px; + width: 370px; } .result { display: inline-block; @@ -91,7 +91,7 @@ body#h5ai-info { margin: 4px 0 12px 12px; font-size: 0.7em; color: #aaa; - width: 350px; + width: 370px; line-height: 1.2em; } } diff --git a/src/_h5ai/client/js/inc/info.js b/src/_h5ai/client/js/inc/info.js index 8097b7f8..e127ff57 100644 --- a/src/_h5ai/client/js/inc/info.js +++ b/src/_h5ai/client/js/inc/info.js @@ -17,12 +17,12 @@ modulejs.define('info', ['$'], function ($) { $.getJSON('server/php/index.php', {action: 'get', checks: true}, function (json) { if (json) { - $('.idx-file .value').text(json.checks['idx']); + $('.idx-file .value').text(json.checks['path_index']); $('.test').each(function () { setCheckResult(this, json.checks[$(this).data('id')]); }); - $('.test.php .result').text(json.checks['phpversion']); + $('.test.php .result').text(json.checks['php_version']); } }); }; diff --git a/src/_h5ai/index.html.jade b/src/_h5ai/index.html.jade index 9e7f2409..8bf0e8cb 100644 --- a/src/_h5ai/index.html.jade +++ b/src/_h5ai/index.html.jade @@ -32,23 +32,23 @@ html.no-js.browser( lang="en" ) h2 Server Details ul#tests - li.test.php( data-id="php" ) + li.test.php( data-id="php_version_supported" ) span.label PHP version span.result ? - div.info PHP version >= 5.2.1 - li.test( data-id="cache" ) + div.info PHP version >= 5.3.0 + li.test( data-id="path_cache_writable" ) span.label Cache directory span.result ? div.info Web server has write access - li.test( data-id="thumbs" ) + li.test( data-id="php_jpg" ) span.label Image thumbs span.result ? div.info PHP GD extension with JPEG support available - li.test( data-id="exif" ) + li.test( data-id="php_exif" ) span.label Use EXIF thumbs span.result ? div.info PHP EXIF extension available - li.test( data-id="moviethumbs" ) + li.test( data-id="cmd_ffmpeg_or_avconv" ) span.label Movie thumbs span.result ? div.info @@ -57,28 +57,28 @@ html.no-js.browser( lang="en" ) | or code avconv | available - li.test( data-id="convert" ) + li.test( data-id="cmd_convert" ) span.label PDF thumbs span.result ? div.info | Command line program code convert | available - li.test( data-id="tar" ) + li.test( data-id="cmd_tar" ) span.label Shell tar span.result ? div.info | Command line program code tar | available - li.test( data-id="zip" ) + li.test( data-id="cmd_zip" ) span.label Shell zip span.result ? div.info | Command line program code zip | available - li.test( data-id="du" ) + li.test( data-id="cmd_du" ) span.label Folder sizes span.result ? div.info diff --git a/src/_h5ai/server/php/inc/App.php b/src/_h5ai/server/php/inc/App.php index 040feda0..63166f34 100644 --- a/src/_h5ai/server/php/inc/App.php +++ b/src/_h5ai/server/php/inc/App.php @@ -5,17 +5,48 @@ class App { public static $MAGIC_SEQUENCE = "={{pkg.name}}="; public static $FILE_PREFIX = "_{{pkg.name}}"; + private static $MIN_PHP_VERSION = "5.3.0"; private static $RE_DELIMITER = "|"; private static $CACHE_DIR = "cache"; - private $app_abs_path, $root_abs_path, + private $is_win_os, $is_supported_php, + $server_name, $server_version, + $app_abs_path, $root_abs_path, $app_abs_href, $root_abs_href, $abs_href, $abs_path, $options; - public function __construct($app_abs_path, $app_abs_href, $abs_href) { + public static function from_env() { + + $server_name = null; + $server_version = null; + if (preg_match("#^(.*?)/(.*?)(?: |$)#", strtolower(getenv("SERVER_SOFTWARE")), $matches)) { + $server_name = $matches[1]; + $server_version = $matches[2]; + } + + $script_name = getenv("SCRIPT_NAME"); + if ($server_name === "lighttpd") { + $script_name = preg_replace("#^.*//#", "/", $script_name); + } + $app_abs_href = dirname(dirname(dirname($script_name))); + + $url_parts = parse_url(getenv("REQUEST_URI")); + $abs_href = $url_parts["path"]; + + return new App($server_name, $server_version, APP_ABS_PATH, $app_abs_href, $abs_href); + } + + + public function __construct($server_name, $server_version, $app_abs_path, $app_abs_href, $abs_href) { + + $this->is_win_os = strtolower(substr(PHP_OS, 0, 3)) === "win"; + $this->is_supported_php = version_compare(PHP_VERSION, App::$MIN_PHP_VERSION) >= 0; + + $this->server_name = strtolower($server_name); + $this->server_version = strtolower($server_version); $this->app_abs_path = normalize_path($app_abs_path); $this->root_abs_path = normalize_path(dirname($this->app_abs_path)); @@ -293,41 +324,40 @@ class App { public function get_server_checks() { - $php = version_compare(PHP_VERSION, "5.2.1") >= 0; + $results = array(); + + $results["path_index"] = $this->app_abs_href . "server/php/index.php"; + $results["php_version"] = PHP_VERSION; + $results["php_version_supported"] = $this->is_supported_php; + $results["php_exif"] = function_exists("exif_thumbnail"); + $results["path_cache_writable"] = @is_writable($this->get_cache_abs_path()); + $gd = false; if (function_exists("gd_info")) { $gdinfo = gd_info(); $gd = array_key_exists("JPG Support", $gdinfo) && $gdinfo["JPG Support"] || array_key_exists("JPEG Support", $gdinfo) && $gdinfo["JPEG Support"]; } - $exif = function_exists("exif_thumbnail"); - $cache = @is_writable($this->get_cache_abs_path()); - if (strtoupper(substr(PHP_OS, 0, 3)) === "WIN") { - $cmd = "which"; - } else { - $cmd = "command -v"; - } - $tar = @preg_match("/tar(.exe)?$/i", exec_cmd($cmd . " tar")) > 0; - $zip = @preg_match("/zip(.exe)?$/i", exec_cmd($cmd . " zip")) > 0; - $convert = @preg_match("/convert(.exe)?$/i", exec_cmd($cmd . " convert")) > 0; - $ffmpeg = @preg_match("/ffmpeg(.exe)?$/i", exec_cmd($cmd . " ffmpeg")) > 0; - $avconv = @preg_match("/avconv(.exe)?$/i", exec_cmd($cmd . " avconv")) > 0; - $du = @preg_match("/du(.exe)?$/i", exec_cmd($cmd . " du")) > 0; + $results["php_jpg"] = $gd; - return array( - "idx" => $this->app_abs_href . "server/php/index.php", - "phpversion" => PHP_VERSION, - "php" => $php, - "cache" => $cache, - "thumbs" => $gd, - "exif" => $exif, - "tar" => $tar, - "zip" => $zip, - "convert" => $convert, - "ffmpeg" => $ffmpeg, - "avconv" => $avconv, - "moviethumbs" => $ffmpeg || $avconv, - "du" => $du - ); + $check_cmd = $this->is_win_os ? "which" : "command -v"; + foreach (array("tar", "zip", "convert", "ffmpeg", "avconv", "du") as $c) { + $results["cmd_" . $c] = @preg_match("#" . $c . "(.exe)?$#i", exec_cmd($check_cmd . " " . $c)) > 0; + } + $results["cmd_ffmpeg_or_avconv"] = $results["cmd_ffmpeg"] || $results["cmd_avconv"]; + + $results["is_win_os"] = $this->is_win_os; + $results["is_supported_php"] = $this->is_supported_php; + $results["server_name"] = $this->server_name; + $results["server_version"] = $this->server_version; + $results["app_abs_path"] = $this->app_abs_path; + $results["root_abs_path"] = $this->root_abs_path; + $results["app_abs_href"] = $this->app_abs_href; + $results["root_abs_href"] = $this->root_abs_href; + $results["abs_href"] = $this->abs_href; + $results["abs_path"] = $this->abs_path; + $results["options"] = $this->options; + + return $results; } @@ -335,9 +365,8 @@ class App { return array( "backend" => "php", - "api" => true, - "name" => strtolower(preg_replace("/\\/.*$/", "", getenv("SERVER_SOFTWARE"))), - "version" => strtolower(preg_replace("/^.*\\//", "", preg_replace("/\\s.*$/", "", getenv("SERVER_SOFTWARE")))) + "name" => $this->server_name, + "version" => $this->server_version ); } diff --git a/src/_h5ai/server/php/index.php b/src/_h5ai/server/php/index.php index 967d49b1..78b177c7 100644 --- a/src/_h5ai/server/php/index.php +++ b/src/_h5ai/server/php/index.php @@ -10,11 +10,6 @@ function normalize_path($path, $trailing_slash = false) { } define("APP_ABS_PATH", normalize_path(dirname(dirname(dirname(__FILE__))))); -// define("APP_ABS_HREF", normalize_path(dirname(dirname(dirname(getenv("SCRIPT_NAME")))), true)); -define("APP_ABS_HREF", normalize_path(dirname(dirname(dirname(preg_replace('#^.*//#', '/', getenv("SCRIPT_NAME"))))), true)); // fixes lighttpd issues -$url_parts = parse_url(getenv("REQUEST_URI")); -define("ABS_HREF", normalize_path($url_parts["path"]), true); - function normalized_require_once($lib) { @@ -22,25 +17,12 @@ function normalized_require_once($lib) { } -/* Fast exit on version check */ - -if (array_key_exists("version", $_REQUEST)) { - - echo json_encode(array("code" => 0, "version" => "{{pkg.version}}", "href" => APP_ABS_HREF)); - exit; -} - - -/* Load Libs */ +/* Init */ normalized_require_once("/server/php/inc/util.php"); normalized_require_once("/server/php/inc/App.php"); normalized_require_once("/server/php/inc/Item.php"); - - -/* Init */ - -$app = new App(APP_ABS_PATH, APP_ABS_HREF, ABS_HREF); +$app = App::from_env(); /* Run */ From 1899d3361b4f7f26689ad3af819d5304cab07353 Mon Sep 17 00:00:00 2001 From: Yeechan Lu Date: Mon, 12 May 2014 19:22:26 +0800 Subject: [PATCH 012/106] Update zh-cn.json --- src/_h5ai/conf/l10n/zh-cn.json | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/_h5ai/conf/l10n/zh-cn.json b/src/_h5ai/conf/l10n/zh-cn.json index 1f8e0c23..6d244fea 100644 --- a/src/_h5ai/conf/l10n/zh-cn.json +++ b/src/_h5ai/conf/l10n/zh-cn.json @@ -1,13 +1,19 @@ { "lang": "简体中文", "details": "详情", + "list": "列表", + "grid": "网格", "icons": "图标", "name": "文件名", - "lastModified": "上次修改", + "lastModified": "修改时间", "size": "大小", "parentDirectory": "上层文件夹", "empty": "空文件夹", "folders": "文件夹", "files": "文件", - "download": "下载" -} \ No newline at end of file + "download": "下载", + "noMatch": "没有匹配", + "dateFormat": "YYYY-MM-DD HH:mm", + "filter": "过滤", + "delete": "删除" +} From fc8a0589dd4ba95d4071dab5e3eb061a6eb9d35a Mon Sep 17 00:00:00 2001 From: Lars Jung Date: Wed, 14 May 2014 14:27:38 +0200 Subject: [PATCH 013/106] Refactoring. --- src/_h5ai/server/php/inc/Api.php | 23 ++++++------ src/_h5ai/server/php/inc/App.php | 13 ++++--- src/_h5ai/server/php/inc/Thumb.php | 13 +++---- src/_h5ai/server/php/inc/main.php | 57 ++++++++++++++++++++++++++++++ src/_h5ai/server/php/inc/util.php | 11 ++++++ src/_h5ai/server/php/index.php | 44 +++-------------------- 6 files changed, 99 insertions(+), 62 deletions(-) create mode 100644 src/_h5ai/server/php/inc/main.php diff --git a/src/_h5ai/server/php/inc/Api.php b/src/_h5ai/server/php/inc/Api.php index 518a4702..fe063c64 100644 --- a/src/_h5ai/server/php/inc/Api.php +++ b/src/_h5ai/server/php/inc/Api.php @@ -3,6 +3,9 @@ class Api { + private $app; + + public function __construct($app) { $this->app = $app; @@ -19,50 +22,50 @@ class Api { $response = array(); - if (array_key_exists("options", $_REQUEST)) { + if (has_request_param("options")) { use_request_params("options"); $response["options"] = $this->app->get_options(); } - if (array_key_exists("types", $_REQUEST)) { + if (has_request_param("types")) { use_request_params("types"); $response["types"] = $this->app->get_types(); } - if (array_key_exists("langs", $_REQUEST)) { + if (has_request_param("langs")) { use_request_params("langs"); $response["langs"] = $this->app->get_l10n_list(); } - if (array_key_exists("l10n", $_REQUEST)) { + if (has_request_param("l10n")) { list($iso_codes) = use_request_params("l10nCodes", "l10n"); $iso_codes = explode(":", $iso_codes); $response["l10n"] = $this->app->get_l10n($iso_codes); } - if (array_key_exists("checks", $_REQUEST)) { + if (has_request_param("checks")) { use_request_params("checks"); $response["checks"] = $this->app->get_server_checks(); } - if (array_key_exists("server", $_REQUEST)) { + if (has_request_param("server")) { use_request_params("server"); $response["server"] = $this->app->get_server_details(); } - if (array_key_exists("custom", $_REQUEST)) { + if (has_request_param("custom")) { list($abs_href) = use_optional_request_params("customHref", "custom"); $response["custom"] = $this->app->get_customizations($abs_href); } - if (array_key_exists("items", $_REQUEST)) { + if (has_request_param("items")) { list($abs_href, $what) = use_optional_request_params("itemsHref", "itemsWhat", "items"); $what = is_numeric($what) ? intval($what, 10) : 1; @@ -83,7 +86,7 @@ class Api { json_fail(1, "thumbnails disabled"); } - normalized_require_once("/server/php/inc/Thumb.php"); + normalized_require_once("Thumb.php"); if (!Thumb::is_supported()) { json_fail(2, "thumbnails not supported"); } @@ -106,7 +109,7 @@ class Api { list($as, $type, $hrefs) = use_request_params(array("as", "type", "hrefs")); - normalized_require_once("/server/php/inc/Archive.php"); + normalized_require_once("Archive.php"); $archive = new Archive($this->app); $hrefs = explode("|:|", trim($hrefs)); diff --git a/src/_h5ai/server/php/inc/App.php b/src/_h5ai/server/php/inc/App.php index 63166f34..11b69732 100644 --- a/src/_h5ai/server/php/inc/App.php +++ b/src/_h5ai/server/php/inc/App.php @@ -27,6 +27,8 @@ class App { $server_version = $matches[2]; } + $app_abs_path = normalize_path(dirname(dirname(dirname(dirname(__FILE__))))); + $script_name = getenv("SCRIPT_NAME"); if ($server_name === "lighttpd") { $script_name = preg_replace("#^.*//#", "/", $script_name); @@ -36,7 +38,7 @@ class App { $url_parts = parse_url(getenv("REQUEST_URI")); $abs_href = $url_parts["path"]; - return new App($server_name, $server_version, APP_ABS_PATH, $app_abs_href, $abs_href); + return new App($server_name, $server_version, $app_abs_path, $app_abs_href, $abs_href); } @@ -57,6 +59,10 @@ class App { $this->abs_href = normalize_path($abs_href, true); $this->abs_path = $this->get_abs_path($this->abs_href); + // echo("
");
+		// var_dump($this);
+		// exit();
+
 		$this->options = load_commented_json($this->app_abs_path . "/conf/options.json");
 	}
 
@@ -235,8 +241,6 @@ class App {
 
 	public function get_fallback() {
 
-		date_default_timezone_set("UTC");
-
 		$cache = array();
 		$folder = Item::get($this, $this->abs_path, $cache);
 		$items = $folder->get_content($cache);
@@ -327,10 +331,10 @@ class App {
 		$results = array();
 
 		$results["path_index"] = $this->app_abs_href . "server/php/index.php";
+		$results["path_cache_writable"] = @is_writable($this->get_cache_abs_path());
 		$results["php_version"] = PHP_VERSION;
 		$results["php_version_supported"] = $this->is_supported_php;
 		$results["php_exif"] = function_exists("exif_thumbnail");
-		$results["path_cache_writable"] = @is_writable($this->get_cache_abs_path());
 
 		$gd = false;
 		if (function_exists("gd_info")) {
@@ -365,6 +369,7 @@ class App {
 
 		return array(
 			"backend" => "php",
+			"api" => true,
 			"name" => $this->server_name,
 			"version" => $this->server_version
 		);
diff --git a/src/_h5ai/server/php/inc/Thumb.php b/src/_h5ai/server/php/inc/Thumb.php
index 00dad454..f982cb7f 100644
--- a/src/_h5ai/server/php/inc/Thumb.php
+++ b/src/_h5ai/server/php/inc/Thumb.php
@@ -6,19 +6,16 @@ class Thumb {
 	private static $AVCONV_CMD = "avconv -ss 0:01:00 -i [SOURCE] -an -vframes 1 [TARGET]";
 	private static $CONVERT_CMD = "convert -strip [SOURCE][0] [TARGET]";
 	private static $THUMB_CACHE = "thumbs";
+	private static $CAPTURE_CACHE = "captures";
+
 
 	public static final function is_supported() {
 
-		if (!function_exists("gd_info")) {
-			return false;
-		}
-
-		$gdinfo = gd_info();
-		return array_key_exists("JPG Support", $gdinfo) && $gdinfo["JPG Support"] || array_key_exists("JPEG Support", $gdinfo) && $gdinfo["JPEG Support"];
+		return Image::is_supported();
 	}
 
 
-	private $app;
+	private $app, $thumbs_path, $thumbs_href;
 
 
 	public function __construct($app) {
@@ -93,7 +90,7 @@ class Thumb {
 			return null;
 		}
 
-		$capture_abs_path = $this->app->get_cache_abs_path() . "/capture-" . sha1($source_abs_path) . ".jpg";
+		$capture_abs_path = $this->thumbs_path . "/capture-" . sha1($source_abs_path) . ".jpg";
 
 		if (!file_exists($capture_abs_path) || filemtime($source_abs_path) >= filemtime($capture_abs_path)) {
 			$cmd = str_replace("[SOURCE]", escapeshellarg($source_abs_path), $cmd);
diff --git a/src/_h5ai/server/php/inc/main.php b/src/_h5ai/server/php/inc/main.php
new file mode 100644
index 00000000..66badf85
--- /dev/null
+++ b/src/_h5ai/server/php/inc/main.php
@@ -0,0 +1,57 @@
+apply();
+
+} else {
+
+	header("Content-type: text/html;charset=utf-8");
+
+	global $HREF, $FALLBACK;
+	$HREF = $app->get_app_abs_href();
+	$FALLBACK = $app->get_fallback();
+	normalized_require_once("page.php");
+}
+
+?>
\ No newline at end of file
diff --git a/src/_h5ai/server/php/inc/util.php b/src/_h5ai/server/php/inc/util.php
index e684bf5c..534fcdba 100644
--- a/src/_h5ai/server/php/inc/util.php
+++ b/src/_h5ai/server/php/inc/util.php
@@ -1,5 +1,11 @@
 apply();
-
-} else {
-
-	header("Content-type: text/html;charset=utf-8");
-
-	$HREF = $app->get_app_abs_href();
-	$FALLBACK = $app->get_fallback();
-
-	normalized_require_once("/server/php/inc/page.php");
-}
+normalized_require_once("main.php");
 
 ?>
\ No newline at end of file

From c40fac67d00e5da7b0e090085a7b3d8922c75047 Mon Sep 17 00:00:00 2001
From: Lars Jung 
Date: Thu, 15 May 2014 13:04:37 +0200
Subject: [PATCH 014/106] More refactorings.

---
 src/_h5ai/server/php/inc/Api.php       |  57 +++---
 src/_h5ai/server/php/inc/App.php       | 252 ++++++++-----------------
 src/_h5ai/server/php/inc/Archive.php   |  24 +--
 src/_h5ai/server/php/inc/Item.php      |  58 +++---
 src/_h5ai/server/php/inc/Thumb.php     |  60 +++---
 src/_h5ai/server/php/inc/main.php      |  88 +++++++--
 src/_h5ai/server/php/inc/page.php.jade |  20 +-
 src/_h5ai/server/php/inc/util.php      |  42 +----
 src/_h5ai/server/php/index.php         |  10 +-
 9 files changed, 279 insertions(+), 332 deletions(-)

diff --git a/src/_h5ai/server/php/inc/Api.php b/src/_h5ai/server/php/inc/Api.php
index fe063c64..b9f855b4 100644
--- a/src/_h5ai/server/php/inc/Api.php
+++ b/src/_h5ai/server/php/inc/Api.php
@@ -16,7 +16,7 @@ class Api {
 
 		$options = $this->app->get_options();
 
-		list($action) = use_request_params(array("action"));
+		$action = use_request_param("action");
 
 		if ($action === "get") {
 
@@ -24,50 +24,54 @@ class Api {
 
 			if (has_request_param("options")) {
 
-				use_request_params("options");
+				use_request_param("options");
 				$response["options"] = $this->app->get_options();
 			}
 
 			if (has_request_param("types")) {
 
-				use_request_params("types");
+				use_request_param("types");
 				$response["types"] = $this->app->get_types();
 			}
 
 			if (has_request_param("langs")) {
 
-				use_request_params("langs");
+				use_request_param("langs");
 				$response["langs"] = $this->app->get_l10n_list();
 			}
 
 			if (has_request_param("l10n")) {
 
-				list($iso_codes) = use_request_params("l10nCodes", "l10n");
+				use_request_param("l10n");
+				$iso_codes = use_request_param("l10nCodes");
 				$iso_codes = explode(":", $iso_codes);
 				$response["l10n"] = $this->app->get_l10n($iso_codes);
 			}
 
 			if (has_request_param("checks")) {
 
-				use_request_params("checks");
+				use_request_param("checks");
 				$response["checks"] = $this->app->get_server_checks();
 			}
 
 			if (has_request_param("server")) {
 
-				use_request_params("server");
+				use_request_param("server");
 				$response["server"] = $this->app->get_server_details();
 			}
 
 			if (has_request_param("custom")) {
 
-				list($abs_href) = use_optional_request_params("customHref", "custom");
+				use_request_param("custom");
+				$abs_href = use_request_param("customHref", null);
 				$response["custom"] = $this->app->get_customizations($abs_href);
 			}
 
 			if (has_request_param("items")) {
 
-				list($abs_href, $what) = use_optional_request_params("itemsHref", "itemsWhat", "items");
+				use_request_param("items");
+				$abs_href = use_request_param("itemsHref", null);
+				$what = use_request_param("itemsWhat", null);
 				$what = is_numeric($what) ? intval($what, 10) : 1;
 				$response["items"] = $this->app->get_items($abs_href, $what);
 			}
@@ -91,15 +95,19 @@ class Api {
 				json_fail(2, "thumbnails not supported");
 			}
 
-			list($type, $src_abs_href, $mode, $width, $height) = use_request_params(array("type", "href", "mode", "width", "height"));
+			$type = use_request_param("type");
+			$src_url = use_request_param("href");
+			$mode = use_request_param("mode");
+			$width = use_request_param("width");
+			$height = use_request_param("height");
 
 			$thumb = new Thumb($this->app);
-			$thumb_href = $thumb->thumb($type, $src_abs_href, $mode, $width, $height);
-			if ($thumb_href === null) {
+			$thumb_url = $thumb->thumb($type, $src_url, $mode, $width, $height);
+			if ($thumb_url === null) {
 				json_fail(3, "thumbnail creation failed");
 			}
 
-			json_exit(array("absHref" => $thumb_href));
+			json_exit(array("absHref" => $thumb_url));
 		}
 
 
@@ -107,7 +115,9 @@ class Api {
 
 			json_fail(1, "downloads disabled", !$options["download"]["enabled"]);
 
-			list($as, $type, $hrefs) = use_request_params(array("as", "type", "hrefs"));
+			$as = use_request_param("as");
+			$type = use_request_param("type");
+			$hrefs = use_request_param("hrefs");
 
 			normalized_require_once("Archive.php");
 			$archive = new Archive($this->app);
@@ -129,7 +139,7 @@ class Api {
 
 		else if ($action === "upload") {
 
-			list($href) = use_request_params(array("href"));
+			$href = use_request_param("href");
 
 			json_fail(1, "wrong HTTP method", strtolower($_SERVER["REQUEST_METHOD"]) !== "post");
 			json_fail(2, "something went wrong", !array_key_exists("userfile", $_FILES));
@@ -139,10 +149,10 @@ class Api {
 			json_fail(3, "something went wrong [" . $userfile["error"] . "]", $userfile["error"] !== 0);
 			json_fail(4, "folders not supported", file_get_contents($userfile["tmp_name"]) === "null");
 
-			$upload_dir = $this->app->get_abs_path($href);
+			$upload_dir = $this->app->to_path($href);
 			$code = $this->app->get_http_code($href);
 
-			json_fail(5, "upload dir no h5ai folder or ignored", $code !== App::$MAGIC_SEQUENCE || $this->app->is_ignored($upload_dir));
+			json_fail(5, "upload dir no h5ai folder or ignored", $code !== MAGIC_SEQUENCE || $this->app->is_ignored($upload_dir));
 
 			$dest = $upload_dir . "/" . utf8_encode($userfile["name"]);
 
@@ -157,7 +167,7 @@ class Api {
 
 			json_fail(1, "deletion disabled", !$options["delete"]["enabled"]);
 
-			list($hrefs) = use_request_params(array("hrefs"));
+			$hrefs = use_request_param("hrefs");
 
 			$hrefs = explode("|:|", trim($hrefs));
 			$errors = array();
@@ -169,9 +179,9 @@ class Api {
 
 				$code = $this->app->get_http_code($d);
 
-				if ($code == App::$MAGIC_SEQUENCE && !$this->app->is_ignored($n)) {
+				if ($code == MAGIC_SEQUENCE && !$this->app->is_ignored($n)) {
 
-					$abs_path = $this->app->get_abs_path($href);
+					$abs_path = $this->app->to_path($href);
 
 					if (!unlink($abs_path)) {
 						$errors[] = $href;
@@ -191,16 +201,17 @@ class Api {
 
 			json_fail(1, "renaming disabled", !$options["rename"]["enabled"]);
 
-			list($href, $name) = use_request_params(array("href", "name"));
+			$href = use_request_param("href");
+			$name = use_request_param("name");
 
 			$d = normalize_path(dirname($href), true);
 			$n = basename($href);
 
 			$code = $this->app->get_http_code($d);
 
-			if ($code == App::$MAGIC_SEQUENCE && !$this->app->is_ignored($n)) {
+			if ($code == MAGIC_SEQUENCE && !$this->app->is_ignored($n)) {
 
-				$abs_path = $this->app->get_abs_path($href);
+				$abs_path = $this->app->to_path($href);
 				$folder = normalize_path(dirname($abs_path));
 
 				if (!rename($abs_path, $folder . "/" . $name)) {
diff --git a/src/_h5ai/server/php/inc/App.php b/src/_h5ai/server/php/inc/App.php
index 11b69732..b6af8c74 100644
--- a/src/_h5ai/server/php/inc/App.php
+++ b/src/_h5ai/server/php/inc/App.php
@@ -2,110 +2,28 @@
 
 class App {
 
-	public static $MAGIC_SEQUENCE = "={{pkg.name}}=";
-	public static $FILE_PREFIX = "_{{pkg.name}}";
-
-	private static $MIN_PHP_VERSION = "5.3.0";
 	private static $RE_DELIMITER = "|";
-	private static $CACHE_DIR = "cache";
 
 
-	private $is_win_os, $is_supported_php,
-			$server_name, $server_version,
-			$app_abs_path, $root_abs_path,
-			$app_abs_href, $root_abs_href,
-			$abs_href, $abs_path,
-			$options;
+	private $options;
 
 
-	public static function from_env() {
+	public function __construct() {
 
-		$server_name = null;
-		$server_version = null;
-		if (preg_match("#^(.*?)/(.*?)(?: |$)#", strtolower(getenv("SERVER_SOFTWARE")), $matches)) {
-			$server_name = $matches[1];
-			$server_version = $matches[2];
-		}
-
-		$app_abs_path = normalize_path(dirname(dirname(dirname(dirname(__FILE__)))));
-
-		$script_name = getenv("SCRIPT_NAME");
-		if ($server_name === "lighttpd") {
-			$script_name = preg_replace("#^.*//#", "/", $script_name);
-		}
-		$app_abs_href = dirname(dirname(dirname($script_name)));
-
-		$url_parts = parse_url(getenv("REQUEST_URI"));
-		$abs_href = $url_parts["path"];
-
-		return new App($server_name, $server_version, $app_abs_path, $app_abs_href, $abs_href);
+		$this->options = load_commented_json(APP_PATH . "/conf/options.json");
 	}
 
 
-	public function __construct($server_name, $server_version, $app_abs_path, $app_abs_href, $abs_href) {
+	public function get_options() {
 
-		$this->is_win_os = strtolower(substr(PHP_OS, 0, 3)) === "win";
-		$this->is_supported_php = version_compare(PHP_VERSION, App::$MIN_PHP_VERSION) >= 0;
-
-		$this->server_name = strtolower($server_name);
-		$this->server_version = strtolower($server_version);
-
-		$this->app_abs_path = normalize_path($app_abs_path);
-		$this->root_abs_path = normalize_path(dirname($this->app_abs_path));
-
-		$this->app_abs_href = normalize_path($app_abs_href, true);
-		$this->root_abs_href = normalize_path(dirname($this->app_abs_href), true);
-
-		$this->abs_href = normalize_path($abs_href, true);
-		$this->abs_path = $this->get_abs_path($this->abs_href);
-
-		// echo("
");
-		// var_dump($this);
-		// exit();
-
-		$this->options = load_commented_json($this->app_abs_path . "/conf/options.json");
+		return $this->options;
 	}
 
 
-	public function get_root_abs_path() {
+	public function to_url($path, $trailing_slash = true) {
 
-		return $this->root_abs_path;
-	}
-
-
-	public function get_root_abs_href() {
-
-		return $this->root_abs_href;
-	}
-
-
-	public function get_app_abs_href() {
-
-		return $this->app_abs_href;
-	}
-
-
-	public function get_cache_abs_path() {
-
-		return $this->app_abs_path . '/' . App::$CACHE_DIR;
-	}
-
-
-	public function get_cache_abs_href() {
-
-		return $this->app_abs_href . App::$CACHE_DIR . '/';
-	}
-
-
-	public function get_abs_href($abs_path = null, $trailing_slash = true) {
-
-		if ($abs_path === null) {
-			return $this->abs_href;
-		}
-
-		$abs_path = substr($abs_path, strlen($this->root_abs_path));
-
-		$parts = explode("/", $abs_path);
+		$rel_path = substr($path, strlen(ROOT_PATH));
+		$parts = explode("/", $rel_path);
 		$encoded_parts = array();
 		foreach ($parts as $part) {
 			if ($part) {
@@ -113,19 +31,14 @@ class App {
 			}
 		}
 
-		return normalize_path($this->root_abs_href . implode("/", $encoded_parts), $trailing_slash);
+		return normalize_path(ROOT_URL . implode("/", $encoded_parts), $trailing_slash);
 	}
 
 
-	public function get_abs_path($abs_href = null) {
+	public function to_path($url) {
 
-		if ($abs_href === null) {
-			return $this->abs_path;
-		}
-
-		$abs_href = substr($abs_href, strlen($this->root_abs_href));
-
-		return normalize_path($this->root_abs_path . "/" . rawurldecode($abs_href));
+		$rel_url = substr($url, strlen(ROOT_URL));
+		return normalize_path(ROOT_PATH . "/" . rawurldecode($rel_url));
 	}
 
 
@@ -149,71 +62,64 @@ class App {
 
 	public function read_dir($path) {
 
-		$content = array();
+		$names = array();
 		if (is_dir($path)) {
 			if ($dir = opendir($path)) {
-				while (($file = readdir($dir)) !== false) {
-					if (!$this->is_ignored($file) && !$this->is_ignored($this->get_abs_href($path) . $file)) {
-						$content[] = $file;
+				while (($name = readdir($dir)) !== false) {
+					if (!$this->is_ignored($name) && !$this->is_ignored($this->to_url($path) . $name)) {
+						$names[] = $name;
 					}
 				}
 				closedir($dir);
 			}
 		}
-		return $content;
+		return $names;
 	}
 
 
-	public function get_options() {
+	public function get_http_code($url) {
 
-		return $this->options;
-	}
+		$path = $this->to_path($url);
 
-
-	public function get_http_code($abs_href) {
-
-		$abs_path = $this->get_abs_path($abs_href);
-
-		if (!is_dir($abs_path) || strpos($abs_path, '../') || strpos($abs_path, '/..') || $abs_path == '..') {
+		if (!is_dir($path) || strpos($path, '../') || strpos($path, '/..') || $path == '..') {
 			return 500;
 		}
 
 		foreach ($this->options["view"]["indexFiles"] as $if) {
-			if (file_exists($abs_path . "/" . $if)) {
+			if (file_exists($path . "/" . $if)) {
 				return 200;
 			}
 		}
 
-		$p = $abs_path;
-		while ($p !== $this->root_abs_path) {
-			if (@is_dir($p . "/_h5ai/server")) {
+		while ($path !== ROOT_PATH) {
+			if (@is_dir($path . "/_h5ai/server")) {
 				return 200;
 			}
-			$pp = normalize_path(dirname($p));
-			if ($pp === $p) {
+			$parent_path = normalize_path(dirname($path));
+			if ($parent_path === $path) {
 				return 200;
 			}
-			$p = $pp;
+			$path = $parent_path;
 		}
-		return App::$MAGIC_SEQUENCE;
+		return MAGIC_SEQUENCE;
 	}
 
 
 	public function get_generic_json() {
 
-		return json_encode(array("items" => $this->get_items($this->abs_href, 1))) . "\n";
+		return json_encode(array("items" => $this->get_items(CURRENT_URL, 1))) . "\n";
 	}
 
 
-	public function get_items($abs_href, $what) {
+	public function get_items($url, $what) {
 
-		$code = $this->get_http_code($abs_href);
-		if ($code != App::$MAGIC_SEQUENCE) {
+		$code = $this->get_http_code($url);
+		if ($code !== MAGIC_SEQUENCE) {
 			return array();
 		}
 
 		$cache = array();
-		$folder = Item::get($this, $this->get_abs_path($abs_href), $cache);
+		$folder = Item::get($this, $this->to_path($url), $cache);
 
 		// add content of subfolders
 		if ($what >= 2 && $folder !== null) {
@@ -242,7 +148,7 @@ class App {
 	public function get_fallback() {
 
 		$cache = array();
-		$folder = Item::get($this, $this->abs_path, $cache);
+		$folder = Item::get($this, CURRENT_PATH, $cache);
 		$items = $folder->get_content($cache);
 		uasort($items, array("Item", "cmp"));
 
@@ -257,7 +163,7 @@ class App {
 
 		if ($folder->get_parent($cache)) {
 			$html .= "";
-			$html .= "folder-parent";
+			$html .= "folder-parent";
 			$html .= "Parent Directory";
 			$html .= "";
 			$html .= "";
@@ -268,8 +174,8 @@ class App {
 			$type = $item->is_folder ? "folder" : "default";
 
 			$html .= "";
-			$html .= "" . $type . "";
-			$html .= "" . basename($item->abs_path) . "";
+			$html .= "" . $type . "";
+			$html .= "" . basename($item->path) . "";
 			$html .= "" . date("Y-m-d H:i", $item->date) . "";
 			$html .= "" . ($item->size !== null ? intval($item->size / 1000) . " KB" : "" ) . "";
 			$html .= "";
@@ -283,19 +189,19 @@ class App {
 
 	public function get_types() {
 
-		return load_commented_json($this->app_abs_path . "/conf/types.json");
+		return load_commented_json(APP_PATH . "/conf/types.json");
 	}
 
 
 	public function get_l10n_list() {
 
 		$langs = array();
-		$l10nDir = $this->app_abs_path . "/conf/l10n";
-		if (is_dir($l10nDir)) {
-			if ($dir = opendir($l10nDir)) {
+		$l10n_path = APP_PATH . "/conf/l10n";
+		if (is_dir($l10n_path)) {
+			if ($dir = opendir($l10n_path)) {
 				while (($file = readdir($dir)) !== false) {
 					if (ends_with($file, ".json")) {
-						$translations = load_commented_json($l10nDir . "/" . $file);
+						$translations = load_commented_json($l10n_path . "/" . $file);
 						$langs[basename($file, ".json")] = $translations["lang"];
 					}
 				}
@@ -313,16 +219,16 @@ class App {
 			$iso_codes = func_get_args();
 		}
 
-		$result = array();
+		$results = array();
 		foreach ($iso_codes as $iso_code) {
 			if ($iso_code !== "") {
-				$file = $this->app_abs_path . "/conf/l10n/" . $iso_code . ".json";
-				$result[$iso_code] = load_commented_json($file);
-				$result[$iso_code]["isoCode"] = $iso_code;
+				$file = APP_PATH . "/conf/l10n/" . $iso_code . ".json";
+				$results[$iso_code] = load_commented_json($file);
+				$results[$iso_code]["isoCode"] = $iso_code;
 			}
 		}
 
-		return $result;
+		return $results;
 	}
 
 
@@ -330,35 +236,35 @@ class App {
 
 		$results = array();
 
-		$results["path_index"] = $this->app_abs_href . "server/php/index.php";
-		$results["path_cache_writable"] = @is_writable($this->get_cache_abs_path());
+		$results["path_index"] = INDEX_URL;
+		$results["path_cache_writable"] = @is_writable(CACHE_PATH);
 		$results["php_version"] = PHP_VERSION;
-		$results["php_version_supported"] = $this->is_supported_php;
+		$results["php_version_supported"] = IS_PHP_VERSION_SUPPORTED;
 		$results["php_exif"] = function_exists("exif_thumbnail");
 
-		$gd = false;
+		$php_jpg = false;
 		if (function_exists("gd_info")) {
-			$gdinfo = gd_info();
-			$gd = array_key_exists("JPG Support", $gdinfo) && $gdinfo["JPG Support"] || array_key_exists("JPEG Support", $gdinfo) && $gdinfo["JPEG Support"];
+			$infos = gd_info();
+			$php_jpg = array_key_exists("JPG Support", $infos) && $infos["JPG Support"] || array_key_exists("JPEG Support", $infos) && $infos["JPEG Support"];
 		}
-		$results["php_jpg"] = $gd;
+		$results["php_jpg"] = $php_jpg;
 
-		$check_cmd = $this->is_win_os ? "which" : "command -v";
-		foreach (array("tar", "zip", "convert", "ffmpeg", "avconv", "du") as $c) {
-			$results["cmd_" . $c] = @preg_match("#" . $c . "(.exe)?$#i", exec_cmd($check_cmd . " " . $c)) > 0;
+		$check_cmd = IS_WIN_OS ? "which" : "command -v";
+		foreach (array("tar", "zip", "convert", "ffmpeg", "avconv", "du") as $cmd) {
+			$results["cmd_" . $cmd] = @preg_match("#" . $cmd . "(.exe)?$#i", exec_cmd($check_cmd . " " . $cmd)) > 0;
 		}
 		$results["cmd_ffmpeg_or_avconv"] = $results["cmd_ffmpeg"] || $results["cmd_avconv"];
 
-		$results["is_win_os"] = $this->is_win_os;
-		$results["is_supported_php"] = $this->is_supported_php;
-		$results["server_name"] = $this->server_name;
-		$results["server_version"] = $this->server_version;
-		$results["app_abs_path"] = $this->app_abs_path;
-		$results["root_abs_path"] = $this->root_abs_path;
-		$results["app_abs_href"] = $this->app_abs_href;
-		$results["root_abs_href"] = $this->root_abs_href;
-		$results["abs_href"] = $this->abs_href;
-		$results["abs_path"] = $this->abs_path;
+		$results["is_win_os"] = IS_WIN_OS;
+		$results["is_supported_php"] = IS_PHP_VERSION_SUPPORTED;
+		$results["server_name"] = SERVER_NAME;
+		$results["server_version"] = SERVER_VERSION;
+		$results["app_url"] = APP_URL;
+		$results["app_path"] = APP_PATH;
+		$results["root_url"] = ROOT_URL;
+		$results["root_path"] = ROOT_PATH;
+		$results["current_url"] = CURRENT_URL;
+		$results["current_path"] = CURRENT_PATH;
 		$results["options"] = $this->options;
 
 		return $results;
@@ -370,13 +276,13 @@ class App {
 		return array(
 			"backend" => "php",
 			"api" => true,
-			"name" => $this->server_name,
-			"version" => $this->server_version
+			"name" => SERVER_NAME,
+			"version" => SERVER_VERSION
 		);
 	}
 
 
-	public function get_customizations($abs_href) {
+	public function get_customizations($url) {
 
 		if (!$this->options["custom"]["enabled"]) {
 			return array(
@@ -385,32 +291,32 @@ class App {
 			);
 		}
 
-		$abs_path = $this->get_abs_path($abs_href);
-		$file = $abs_path . "/" . App::$FILE_PREFIX . ".header.html";
+		$path = $this->to_path($url);
+
+		$file = $path . "/" . FILE_PREFIX . ".header.html";
 		$header = is_readable($file) ? file_get_contents($file) : null;
-		$file = $abs_path . "/" . App::$FILE_PREFIX . ".footer.html";
+		$file = $path . "/" . FILE_PREFIX . ".footer.html";
 		$footer = is_readable($file) ? file_get_contents($file) : null;
 
-		$p = $abs_path;
 		while ($header === null || $footer === null) {
 
 			if ($header === null) {
-				$file = $p . "/" . App::$FILE_PREFIX . ".headers.html";
+				$file = $path . "/" . FILE_PREFIX . ".headers.html";
 				$header = is_readable($file) ? file_get_contents($file) : null;
 			}
 			if ($footer === null) {
-				$file = $p . "/" . App::$FILE_PREFIX . ".footers.html";
+				$file = $path . "/" . FILE_PREFIX . ".footers.html";
 				$footer = is_readable($file) ? file_get_contents($file) : null;
 			}
 
-			if ($p === $this->root_abs_path) {
+			if ($path === ROOT_PATH) {
 				break;
 			}
-			$pp = normalize_path(dirname($p));
-			if ($pp === $p) {
+			$parent_path = normalize_path(dirname($path));
+			if ($parent_path === $path) {
 				break;
 			}
-			$p = $pp;
+			$path = $parent_path;
 		}
 
 		return array(
diff --git a/src/_h5ai/server/php/inc/Archive.php b/src/_h5ai/server/php/inc/Archive.php
index 34d8f9fc..3f15b185 100644
--- a/src/_h5ai/server/php/inc/Archive.php
+++ b/src/_h5ai/server/php/inc/Archive.php
@@ -15,12 +15,12 @@ class Archive {
 	}
 
 
-	public function output($type, $hrefs) {
+	public function output($type, $urls) {
 
 		$this->dirs = array();
 		$this->files = array();
 
-		$this->add_hrefs($hrefs);
+		$this->add_hrefs($urls);
 
 		if (count($this->dirs) === 0 && count($this->files) === 0) {
 			return 500;
@@ -44,7 +44,7 @@ class Archive {
 
 	private function shell_cmd($cmd) {
 
-		$cmd = str_replace("[ROOTDIR]", escapeshellarg($this->app->get_abs_path()), $cmd);
+		$cmd = str_replace("[ROOTDIR]", escapeshellarg(CURRENT_PATH), $cmd);
 		$cmd = str_replace("[DIRS]", count($this->dirs) ? implode(" ", array_map("escapeshellarg", $this->dirs)) : "", $cmd);
 		$cmd = str_replace("[FILES]", count($this->files) ? implode(" ", array_map("escapeshellarg", $this->files)) : "", $cmd);
 		try {
@@ -133,27 +133,27 @@ class Archive {
 		if ($fd = fopen($file, 'rb')) {
 			while (!feof($fd)) {
 				print fread($fd, Archive::$SEGMENT_SIZE);
-				ob_flush();
-				flush();
+				@ob_flush();
+				@flush();
 			}
 			fclose($fd);
 		}
 	}
 
 
-	private function add_hrefs($hrefs) {
+	private function add_hrefs($urls) {
 
-		foreach ($hrefs as $href) {
+		foreach ($urls as $href) {
 
 			$d = normalize_path(dirname($href), true);
 			$n = basename($href);
 
 			$code = $this->app->get_http_code($d);
 
-			if ($code == App::$MAGIC_SEQUENCE && !$this->app->is_ignored($n)) {
+			if ($code === MAGIC_SEQUENCE && !$this->app->is_ignored($n)) {
 
-				$real_file = $this->app->get_abs_path($href);
-				$archived_file = preg_replace("!^" . preg_quote(normalize_path($this->app->get_abs_path(), true)) . "!", "", $real_file);
+				$real_file = $this->app->to_path($href);
+				$archived_file = preg_replace("!^" . preg_quote(normalize_path(CURRENT_PATH, true)) . "!", "", $real_file);
 
 				if (is_dir($real_file)) {
 					$this->add_dir($real_file, $archived_file);
@@ -175,9 +175,9 @@ class Archive {
 
 	private function add_dir($real_dir, $archived_dir) {
 
-		$code = $this->app->get_http_code($this->app->get_abs_href($real_dir));
+		$code = $this->app->get_http_code($this->app->to_url($real_dir));
 
-		if ($code == App::$MAGIC_SEQUENCE) {
+		if ($code === MAGIC_SEQUENCE) {
 			$this->dirs[] = $archived_dir;
 
 			$files = $this->app->read_dir($real_dir);
diff --git a/src/_h5ai/server/php/inc/Item.php b/src/_h5ai/server/php/inc/Item.php
index cf0adaf8..531508c7 100644
--- a/src/_h5ai/server/php/inc/Item.php
+++ b/src/_h5ai/server/php/inc/Item.php
@@ -13,55 +13,55 @@ class Item {
 			return 1;
 		}
 
-		return strcasecmp($item1->abs_path, $item2->abs_path);
+		return strcasecmp($item1->path, $item2->path);
 	}
 
-	public static function get($app, $abs_path, &$cache) {
+	public static function get($app, $path, &$cache) {
 
-		if (!starts_with($abs_path, $app->get_root_abs_path())) {
-			error_log("ILLEGAL REQUEST: " . $abs_path . ", " . $app->get_root_abs_path());
+		if (!starts_with($path, ROOT_PATH)) {
+			error_log("ILLEGAL REQUEST: " . $path . ", " . ROOT_PATH);
 			return null;
 		}
 
-		if (is_array($cache) && array_key_exists($abs_path, $cache)) {
-			return $cache[$abs_path];
+		if (is_array($cache) && array_key_exists($path, $cache)) {
+			return $cache[$path];
 		}
 
-		$item = new Item($app, $abs_path);
+		$item = new Item($app, $path);
 
 		if (is_array($cache)) {
-			$cache[$abs_path] = $item;
+			$cache[$path] = $item;
 		}
 		return $item;
 	}
 
 
 	public $app,
-			$abs_path, $abs_href,
+			$path, $url,
 			$date, $size,
 			$is_folder,
 			$is_content_fetched;
 
 
-	private function __construct($app, $abs_path) {
+	private function __construct($app, $path) {
 
 		$this->app = $app;
 
-		$this->abs_path = normalize_path($abs_path);
-		$this->is_folder = is_dir($this->abs_path);
-		$this->abs_href = $this->app->get_abs_href($abs_path, $this->is_folder);
+		$this->path = normalize_path($path, false);
+		$this->is_folder = is_dir($this->path);
+		$this->url = $this->app->to_url($path, $this->is_folder);
 
-		$this->date = @filemtime($this->abs_path);
+		$this->date = @filemtime($this->path);
 
 		if ($this->is_folder) {
 			$this->size = null;
 			$options = $app->get_options();
 			if ($options["foldersize"]["enabled"]) {
-				$cmd = str_replace("[DIR]", escapeshellarg($this->abs_path), Item::$FOLDER_SIZE_CMD);
-				$this->size = intval(preg_replace("/\s.*$/", "", exec_cmd($cmd)), 10) * 1024;
+				$cmd = str_replace("[DIR]", escapeshellarg($this->path), Item::$FOLDER_SIZE_CMD);
+				$this->size = intval(preg_replace("#\s.*$#", "", exec_cmd($cmd)), 10) * 1024;
 			}
 		} else {
-			$this->size = @filesize($this->abs_path);
+			$this->size = @filesize($this->path);
 		}
 
 		$this->is_content_fetched = false;
@@ -71,13 +71,13 @@ class Item {
 	public function to_json_object() {
 
 		$obj = array(
-			"absHref" => $this->abs_href,
+			"absHref" => $this->url,
 			"time" => $this->date * 1000, // seconds (PHP) to milliseconds (JavaScript)
 			"size" => $this->size
 		);
 
 		if ($this->is_folder) {
-			$obj["status"] = $this->app->get_http_code($this->abs_href);
+			$obj["status"] = $this->app->get_http_code($this->url);
 			$obj["content"] = $this->is_content_fetched;
 		}
 
@@ -87,9 +87,9 @@ class Item {
 
 	public function get_parent(&$cache) {
 
-		$parent_abs_path = normalize_path(dirname($this->abs_path));
-		if ($parent_abs_path !== $this->abs_path && starts_with($parent_abs_path, $this->app->get_root_abs_path())) {
-			return Item::get($this->app, $parent_abs_path, $cache);
+		$parent_path = normalize_path(dirname($this->path), false);
+		if ($parent_path !== $this->path && starts_with($parent_path, ROOT_PATH)) {
+			return Item::get($this->app, $parent_path, $cache);
 		}
 		return null;
 	}
@@ -97,21 +97,21 @@ class Item {
 
 	public function get_content(&$cache) {
 
-		$content = array();
+		$items = array();
 
-		if ($this->app->get_http_code($this->abs_href) !== App::$MAGIC_SEQUENCE) {
-			return $content;
+		if ($this->app->get_http_code($this->url) !== MAGIC_SEQUENCE) {
+			return $items;
 		}
 
-		$files = $this->app->read_dir($this->abs_path);
+		$files = $this->app->read_dir($this->path);
 		foreach ($files as $file) {
-			$item = Item::get($this->app, $this->abs_path . "/" . $file, $cache);
-			$content[$item->abs_path] = $item;
+			$item = Item::get($this->app, $this->path . "/" . $file, $cache);
+			$items[$item->path] = $item;
 		}
 
 		$this->is_content_fetched = true;
 
-		return $content;
+		return $items;
 	}
 }
 
diff --git a/src/_h5ai/server/php/inc/Thumb.php b/src/_h5ai/server/php/inc/Thumb.php
index f982cb7f..5def0a58 100644
--- a/src/_h5ai/server/php/inc/Thumb.php
+++ b/src/_h5ai/server/php/inc/Thumb.php
@@ -21,33 +21,33 @@ class Thumb {
 	public function __construct($app) {
 
 		$this->app = $app;
-		$this->thumbs_path = $this->app->get_cache_abs_path() . "/" . Thumb::$THUMB_CACHE;
-		$this->thumbs_href = $this->app->get_cache_abs_href() . Thumb::$THUMB_CACHE;
+		$this->thumbs_path = CACHE_PATH . "/" . Thumb::$THUMB_CACHE;
+		$this->thumbs_href = CACHE_URL . Thumb::$THUMB_CACHE;
 	}
 
 
-	public function thumb($type, $source_abs_href, $mode, $width, $height) {
+	public function thumb($type, $source_url, $mode, $width, $height) {
 
-		$source_abs_path = $this->app->get_abs_path($source_abs_href);
+		$source_path = $this->app->to_path($source_url);
 
 		if ($type === "img") {
-			$capture_abs_path = $source_abs_path;
+			$capture_path = $source_path;
 		} else if ($type === "mov") {
-			$capture_abs_path = $this->capture(Thumb::$FFMPEG_CMD, $source_abs_path);
-			if ($capture_abs_path === null) {
-				$capture_abs_path = $this->capture(Thumb::$AVCONV_CMD, $source_abs_path);
+			$capture_path = $this->capture(Thumb::$FFMPEG_CMD, $source_path);
+			if ($capture_path === null) {
+				$capture_path = $this->capture(Thumb::$AVCONV_CMD, $source_path);
 			}
 		} else if ($type === "doc") {
-			$capture_abs_path = $this->capture(Thumb::$CONVERT_CMD, $source_abs_path);
+			$capture_path = $this->capture(Thumb::$CONVERT_CMD, $source_path);
 		}
 
-		return $this->thumb_href($capture_abs_path, $mode, $width, $height);
+		return $this->thumb_href($capture_path, $mode, $width, $height);
 	}
 
 
-	private function thumb_href($source_abs_path, $mode, $width, $height) {
+	private function thumb_href($source_path, $mode, $width, $height) {
 
-		if (!file_exists($source_abs_path)) {
+		if (!file_exists($source_path)) {
 			return null;
 		}
 
@@ -55,50 +55,50 @@ class Thumb {
 			@mkdir($this->thumbs_path, 0755, true);
 		}
 
-		$name = "thumb-" . sha1("$source_abs_path-$width-$height-$mode") . ".jpg";
-		$thumb_abs_path = $this->thumbs_path . "/" . $name;
-		$thumb_abs_href = $this->thumbs_href . "/" . $name;
+		$name = "thumb-" . sha1("$source_path-$width-$height-$mode") . ".jpg";
+		$thumb_path = $this->thumbs_path . "/" . $name;
+		$thumb_url = $this->thumbs_href . "/" . $name;
 
-		if (!file_exists($thumb_abs_path) || filemtime($source_abs_path) >= filemtime($thumb_abs_path)) {
+		if (!file_exists($thumb_path) || filemtime($source_path) >= filemtime($thumb_path)) {
 
 			$image = new Image();
 
 			$et = false;
 			$opts = $this->app->get_options();
 			if ($opts["thumbnails"]["exif"] === true && function_exists("exif_thumbnail")) {
-				$et = @exif_thumbnail($source_abs_path);
+				$et = @exif_thumbnail($source_path);
 			}
 			if($et !== false) {
-				file_put_contents($thumb_abs_path, $et);
-				$image->set_source($thumb_abs_path);
-				$image->normalize_exif_orientation($source_abs_path);
+				file_put_contents($thumb_path, $et);
+				$image->set_source($thumb_path);
+				$image->normalize_exif_orientation($source_path);
 			} else {
-				$image->set_source($source_abs_path);
+				$image->set_source($source_path);
 			}
 
 			$image->thumb($mode, $width, $height);
-			$image->save_dest_jpeg($thumb_abs_path, 80);
+			$image->save_dest_jpeg($thumb_path, 80);
 		}
 
-		return file_exists($thumb_abs_path) ? $thumb_abs_href : null;
+		return file_exists($thumb_path) ? $thumb_url : null;
 	}
 
 
-	private function capture($cmd, $source_abs_path) {
+	private function capture($cmd, $source_path) {
 
-		if (!file_exists($source_abs_path)) {
+		if (!file_exists($source_path)) {
 			return null;
 		}
 
-		$capture_abs_path = $this->thumbs_path . "/capture-" . sha1($source_abs_path) . ".jpg";
+		$capture_path = $this->thumbs_path . "/capture-" . sha1($source_path) . ".jpg";
 
-		if (!file_exists($capture_abs_path) || filemtime($source_abs_path) >= filemtime($capture_abs_path)) {
-			$cmd = str_replace("[SOURCE]", escapeshellarg($source_abs_path), $cmd);
-			$cmd = str_replace("[TARGET]", escapeshellarg($capture_abs_path), $cmd);
+		if (!file_exists($capture_path) || filemtime($source_path) >= filemtime($capture_path)) {
+			$cmd = str_replace("[SOURCE]", escapeshellarg($source_path), $cmd);
+			$cmd = str_replace("[TARGET]", escapeshellarg($capture_path), $cmd);
 			exec_cmd($cmd);
 		}
 
-		return file_exists($capture_abs_path) ? $capture_abs_path : null;
+		return file_exists($capture_path) ? $capture_path : null;
 	}
 }
 
diff --git a/src/_h5ai/server/php/inc/main.php b/src/_h5ai/server/php/inc/main.php
index 66badf85..bc3a8d02 100644
--- a/src/_h5ai/server/php/inc/main.php
+++ b/src/_h5ai/server/php/inc/main.php
@@ -1,14 +1,25 @@
 ";
+	$s .= $id . "  " . var_export($obj, true);
+	$s .= "
\n"; + echo($s); +} + + +function init() { + + putenv("LANG=en_US.UTF-8"); + date_default_timezone_set("UTC"); + + define("MIN_PHP_VERSION", "5.3.0"); + define("IS_PHP_VERSION_SUPPORTED", version_compare(PHP_VERSION, MIN_PHP_VERSION) >= 0); + + define("IS_WIN_OS", strtolower(substr(PHP_OS, 0, 3)) === "win"); $server_name = null; $server_version = null; @@ -17,23 +28,67 @@ function create_app() { $server_name = $matches[1]; $server_version = $matches[2]; } - - $app_abs_path = normalize_path(dirname(dirname(dirname(dirname(__FILE__))))); + define("SERVER_NAME", $server_name); + define("SERVER_VERSION", $server_version); $script_name = getenv("SCRIPT_NAME"); - if ($server_name === "lighttpd") { + if (SERVER_NAME === "lighttpd") { $script_name = preg_replace("#^.*//#", "/", $script_name); } - $app_abs_href = dirname(dirname(dirname($script_name))); + define("APP_URL", normalize_path(dirname(dirname(dirname($script_name))), true)); + define("APP_PATH", normalize_path(dirname(dirname(dirname(dirname(__FILE__)))), false)); + + define("ROOT_URL", normalize_path(dirname(APP_URL), true)); + define("ROOT_PATH", normalize_path(dirname(APP_PATH), false)); $url_parts = parse_url(getenv("REQUEST_URI")); - $abs_href = $url_parts["path"]; + $cur_url = normalize_path($url_parts["path"], true); + $rel_url = substr($cur_url, strlen(ROOT_URL)); + $cur_path = normalize_path(ROOT_PATH . "/" . rawurldecode($rel_url)); + if (!is_dir($cur_path)) { + $cur_url = normalize_path(dirname($cur_url), true); + $cur_path = normalize_path(dirname($cur_path), false); + } + define("CURRENT_URL", $cur_url); + define("CURRENT_PATH", $cur_path); - return new App($server_name, $server_version, $app_abs_path, $app_abs_href, $abs_href); + define("MAGIC_SEQUENCE", "={{pkg.name}}="); + define("FILE_PREFIX", "_{{pkg.name}}"); + + define("INDEX_URL", normalize_path(APP_URL . "server/php/index.php", false)); + define("CACHE_URL", normalize_path(APP_URL . "cache", true)); + define("CACHE_PATH", normalize_path(APP_PATH . "/cache", false)); + + // refl('DEFINED', array( + // "PHP_VERSION " => PHP_VERSION, + // "PHP_OS " => PHP_OS, + // "MIN_PHP_VERSION " => MIN_PHP_VERSION, + // "IS_PHP_VERSION_SUPPORTED " => IS_PHP_VERSION_SUPPORTED, + // "IS_WIN_OS " => IS_WIN_OS, + // "SERVER_NAME " => SERVER_NAME, + // "SERVER_VERSION " => SERVER_VERSION, + // "APP_URL " => APP_URL, + // "APP_PATH " => APP_PATH, + // "ROOT_URL " => ROOT_URL, + // "ROOT_PATH " => ROOT_PATH, + // "CURRENT_URL " => CURRENT_URL, + // "CURRENT_PATH " => CURRENT_PATH, + // "MAGIC_SEQUENCE " => MAGIC_SEQUENCE, + // "FILE_PREFIX " => FILE_PREFIX, + // "INDEX_URL " => INDEX_URL, + // "CACHE_URL " => CACHE_URL, + // "CACHE_PATH " => CACHE_PATH + // )); + // exit(); } +init(); -$app = create_app(); +normalized_require_once("util.php"); +normalized_require_once("App.php"); +normalized_require_once("Item.php"); + +$app = new App(); if (has_request_param("action")) { @@ -47,10 +102,7 @@ if (has_request_param("action")) { } else { header("Content-type: text/html;charset=utf-8"); - - global $HREF, $FALLBACK; - $HREF = $app->get_app_abs_href(); - $FALLBACK = $app->get_fallback(); + define("FALLBACK", $app->get_fallback()); normalized_require_once("page.php"); } diff --git a/src/_h5ai/server/php/inc/page.php.jade b/src/_h5ai/server/php/inc/page.php.jade index f49aec7e..c03e4f82 100644 --- a/src/_h5ai/server/php/inc/page.php.jade +++ b/src/_h5ai/server/php/inc/page.php.jade @@ -1,6 +1,6 @@ -- var href = "" -- var fallback = "" +- var url = "" +- var fallback = "" doctype 5 //if lt IE 9 @@ -15,15 +15,15 @@ html.no-js.browser( lang="en" ) title index · styled with {{pkg.name}} {{pkg.version}} ({{pkg.url}}) meta( name="description", content="index styled with {{pkg.name}} {{pkg.version}} ({{pkg.url}})" ) meta( name="viewport", content="width=device-width" ) - link( rel="shortcut icon", href!="#{href}client/images/app-16x16.ico" ) - link( rel="apple-touch-icon-precomposed", type="image/png", href!="#{href}client/images/app-48x48.png" ) - link( rel="apple-touch-icon-precomposed", sizes="57x57", type="image/png", href!="#{href}client/images/app-57x57.png" ) - link( rel="apple-touch-icon-precomposed", sizes="72x72", type="image/png", href!="#{href}client/images/app-72x72.png" ) - link( rel="apple-touch-icon-precomposed", sizes="114x114", type="image/png", href!="#{href}client/images/app-114x114.png" ) - link( rel="apple-touch-icon-precomposed", sizes="144x144", type="image/png", href!="#{href}client/images/app-144x144.png" ) + link( rel="shortcut icon", href!="#{url}client/images/app-16x16.ico" ) + link( rel="apple-touch-icon-precomposed", type="image/png", href!="#{url}client/images/app-48x48.png" ) + link( rel="apple-touch-icon-precomposed", sizes="57x57", type="image/png", href!="#{url}client/images/app-57x57.png" ) + link( rel="apple-touch-icon-precomposed", sizes="72x72", type="image/png", href!="#{url}client/images/app-72x72.png" ) + link( rel="apple-touch-icon-precomposed", sizes="114x114", type="image/png", href!="#{url}client/images/app-114x114.png" ) + link( rel="apple-touch-icon-precomposed", sizes="144x144", type="image/png", href!="#{url}client/images/app-144x144.png" ) link( rel="stylesheet", href="//fonts.googleapis.com/css?family=Ubuntu+Mono:400,700,400italic,700italic|Ubuntu:300italic,700italic,300,700" ) - link( rel="stylesheet", href!="#{href}client/css/styles.css" ) - script( src!="#{href}client/js/scripts.js" ) + link( rel="stylesheet", href!="#{url}client/css/styles.css" ) + script( src!="#{url}client/js/scripts.js" ) body diff --git a/src/_h5ai/server/php/inc/util.php b/src/_h5ai/server/php/inc/util.php index 534fcdba..a47cc5ca 100644 --- a/src/_h5ai/server/php/inc/util.php +++ b/src/_h5ai/server/php/inc/util.php @@ -1,11 +1,5 @@ Date: Sun, 18 May 2014 00:10:38 +0200 Subject: [PATCH 015/106] More refactorings. --- src/_h5ai/client/js/inc/core/server.js | 77 ++++++++-------- src/_h5ai/client/js/inc/ext/custom.js | 4 +- src/_h5ai/client/js/inc/info.js | 34 ++++--- src/_h5ai/client/js/scripts.js | 40 +++++---- src/_h5ai/index.html.jade | 24 ++--- .../server/php/inc/{Api.php => class-api.php} | 32 +++---- .../server/php/inc/{App.php => class-app.php} | 58 ++---------- .../inc/{Archive.php => class-archive.php} | 0 .../php/inc/{Item.php => class-item.php} | 6 +- .../php/inc/{Thumb.php => class-thumb.php} | 8 ++ .../server/php/inc/{main.php => setup.php} | 88 ++++++------------- src/_h5ai/server/php/inc/util.php | 51 +++++++++++ src/_h5ai/server/php/index.php | 38 +++++++- 13 files changed, 248 insertions(+), 212 deletions(-) rename src/_h5ai/server/php/inc/{Api.php => class-api.php} (87%) rename src/_h5ai/server/php/inc/{App.php => class-app.php} (79%) rename src/_h5ai/server/php/inc/{Archive.php => class-archive.php} (100%) rename src/_h5ai/server/php/inc/{Item.php => class-item.php} (90%) rename src/_h5ai/server/php/inc/{Thumb.php => class-thumb.php} (95%) rename src/_h5ai/server/php/inc/{main.php => setup.php} (51%) diff --git a/src/_h5ai/client/js/inc/core/server.js b/src/_h5ai/client/js/inc/core/server.js index 528c943c..ba215526 100644 --- a/src/_h5ai/client/js/inc/core/server.js +++ b/src/_h5ai/client/js/inc/core/server.js @@ -1,46 +1,53 @@ modulejs.define('core/server', ['$', '_', 'config', 'core/location'], function ($, _, config, location) { - var server = _.extend({}, config.server, { + var server = { - request: function (data, callback) { + backend: config.setup.BACKEND, + api: config.setup.API === true, + name: config.setup.SERVER_NAME, + version: config.setup.SERVER_VERSION, - if (server.api) { - $.ajax({ - url: location.getAbsHref(), - data: data, - type: 'POST', - dataType: 'json', - success: function (json) { + request: function (data, callback) { - callback(json); - }, - error: function () { + if (server.api) { + $.ajax({ + url: location.getAbsHref(), + data: data, + type: 'POST', + dataType: 'json', + success: function (json) { - callback(); - } - }); - } else { - callback(); + callback(json); + }, + error: function () { + + callback(); + } + }); + } else { + callback(); + } + }, + + formRequest: function (data) { + + if (server.api) { + var $form = $('
') + .attr('action', location.getAbsHref()); + + _.each(data, function (val, key) { + + $('') + .attr('name', key) + .attr('value', val) + .appendTo($form); + }); + + $form.appendTo('body').submit().remove(); + } } - }, - - formRequest: function (data) { - - var $form = $('') - .attr('action', location.getAbsHref()); - - _.each(data, function (val, key) { - - $('') - .attr('name', key) - .attr('value', val) - .appendTo($form); - }); - - $form.appendTo('body').submit().remove(); - } - }); + }; return server; }); diff --git a/src/_h5ai/client/js/inc/ext/custom.js b/src/_h5ai/client/js/inc/ext/custom.js index e2cc27a2..613165e5 100644 --- a/src/_h5ai/client/js/inc/ext/custom.js +++ b/src/_h5ai/client/js/inc/ext/custom.js @@ -5,9 +5,9 @@ modulejs.define('ext/custom', ['_', '$', 'core/settings', 'core/server', 'core/e enabled: false }, allsettings.custom), - onLocationChanged = function () { + onLocationChanged = function (item) { - server.request({action: 'get', custom: true}, function (response) { + server.request({action: 'get', custom: true, customHref: item.absHref}, function (response) { var h, f; if (response) { diff --git a/src/_h5ai/client/js/inc/info.js b/src/_h5ai/client/js/inc/info.js index e127ff57..12e1c91f 100644 --- a/src/_h5ai/client/js/inc/info.js +++ b/src/_h5ai/client/js/inc/info.js @@ -1,7 +1,22 @@ -modulejs.define('info', ['$'], function ($) { +modulejs.define('info', ['$', 'config'], function ($, config) { - var setCheckResult = function (el, result) { + var map = function (setup) { + + return { + 'php_version': setup['HAS_PHP_VERSION'], + 'cache_dir': setup['HAS_WRITABLE_CACHE'], + 'image_thumbs': setup['HAS_PHP_JPG'], + 'exif_thumbs': setup['HAS_PHP_EXIF'], + 'movie_thumbs': setup['HAS_CMD_FFMPEG'] || setup['HAS_CMD_AVCONV'], + 'pdf_thumbs': setup['HAS_CMD_CONVERT'], + 'shell_tar': setup['HAS_CMD_TAR'], + 'shell_zip': setup['HAS_CMD_ZIP'], + 'folder_sizes': setup['HAS_CMD_DU'] + }; + }, + + setValue = function (el, result) { var $result = $(el).find('.result'); @@ -14,17 +29,16 @@ modulejs.define('info', ['$'], function ($) { init = function () { - $.getJSON('server/php/index.php', {action: 'get', checks: true}, function (json) { + var setup = config.setup, + values = map(setup); - if (json) { - $('.idx-file .value').text(json.checks['path_index']); - $('.test').each(function () { + $('.test').each(function () { - setCheckResult(this, json.checks[$(this).data('id')]); - }); - $('.test.php .result').text(json.checks['php_version']); - } + setValue(this, values[$(this).data('id')]); }); + + $('.idx-file .value').text(setup['INDEX_URL']); + $('.test.php .result').text(setup['PHP_VERSION']); }; init(); diff --git a/src/_h5ai/client/js/scripts.js b/src/_h5ai/client/js/scripts.js index 7b454a7c..24fa44dd 100644 --- a/src/_h5ai/client/js/scripts.js +++ b/src/_h5ai/client/js/scripts.js @@ -31,27 +31,29 @@ // @include "inc/**/*.js" var $ = jQuery, - mode = $('script[src$="scripts.js"]').data('mode'); + mode = $('script[src$="scripts.js"]').data('mode'), + url = '.', + module = 'main'; if ($('html').hasClass('no-browser')) { - - } else if (mode === 'info') { - - $(function () { modulejs.require('info'); }); - - } else { - - $.ajax({ - url: '.', - data: {action: 'get', options: true, types: true, langs: true, server: true}, - type: 'POST', - dataType: 'json', - success: function (config) { - - modulejs.define('config', config); - $(function () { modulejs.require('main'); }); - } - }); + return; } + if (mode === 'info') { + url = 'server/php/index.php'; + module = 'info'; + } + + $.ajax({ + url: url, + data: {action: 'get', setup: true, options: true, types: true, langs: true}, + type: 'POST', + dataType: 'json', + success: function (config) { + + modulejs.define('config', config); + $(function () { modulejs.require(module); }); + } + }); + }()); diff --git a/src/_h5ai/index.html.jade b/src/_h5ai/index.html.jade index 8bf0e8cb..ee42865e 100644 --- a/src/_h5ai/index.html.jade +++ b/src/_h5ai/index.html.jade @@ -8,7 +8,7 @@ html.no-js.browser( lang="en" ) head meta( charset="utf-8" ) meta( http-equiv="X-UA-Compatible", content="IE=edge,chrome=1" ) - title {{pkg.name}} {{pkg.version}} server details + title {{pkg.name}} {{pkg.version}} Server Setup meta( name="description", content="{{pkg.name}} server details" ) meta( name="viewport", content="width=device-width" ) link( rel="shortcut icon", href="client/images/app-16x16.ico" ) @@ -27,28 +27,28 @@ html.no-js.browser( lang="en" ) a( href="http://larsjung.de/h5ai/" ) {{pkg.name}} span.build-version version {{pkg.version}} span.build-stamp {{stamp}} - span.idx-file Index File: + span.idx-file Index: code.value - h2 Server Details + h2 Server Setup ul#tests - li.test.php( data-id="php_version_supported" ) + li.test.php( data-id="php_version" ) span.label PHP version span.result ? div.info PHP version >= 5.3.0 - li.test( data-id="path_cache_writable" ) + li.test( data-id="cache_dir" ) span.label Cache directory span.result ? div.info Web server has write access - li.test( data-id="php_jpg" ) + li.test( data-id="image_thumbs" ) span.label Image thumbs span.result ? div.info PHP GD extension with JPEG support available - li.test( data-id="php_exif" ) + li.test( data-id="exif_thumbs" ) span.label Use EXIF thumbs span.result ? div.info PHP EXIF extension available - li.test( data-id="cmd_ffmpeg_or_avconv" ) + li.test( data-id="movie_thumbs" ) span.label Movie thumbs span.result ? div.info @@ -57,28 +57,28 @@ html.no-js.browser( lang="en" ) | or code avconv | available - li.test( data-id="cmd_convert" ) + li.test( data-id="pdf_thumbs" ) span.label PDF thumbs span.result ? div.info | Command line program code convert | available - li.test( data-id="cmd_tar" ) + li.test( data-id="shell_tar" ) span.label Shell tar span.result ? div.info | Command line program code tar | available - li.test( data-id="cmd_zip" ) + li.test( data-id="shell_zip" ) span.label Shell zip span.result ? div.info | Command line program code zip | available - li.test( data-id="cmd_du" ) + li.test( data-id="folder_sizes" ) span.label Folder sizes span.result ? div.info diff --git a/src/_h5ai/server/php/inc/Api.php b/src/_h5ai/server/php/inc/class-api.php similarity index 87% rename from src/_h5ai/server/php/inc/Api.php rename to src/_h5ai/server/php/inc/class-api.php index b9f855b4..914ff0a4 100644 --- a/src/_h5ai/server/php/inc/Api.php +++ b/src/_h5ai/server/php/inc/class-api.php @@ -22,6 +22,12 @@ class Api { $response = array(); + if (has_request_param("setup")) { + + use_request_param("setup"); + $response["setup"] = $this->app->get_setup(); + } + if (has_request_param("options")) { use_request_param("options"); @@ -48,32 +54,20 @@ class Api { $response["l10n"] = $this->app->get_l10n($iso_codes); } - if (has_request_param("checks")) { - - use_request_param("checks"); - $response["checks"] = $this->app->get_server_checks(); - } - - if (has_request_param("server")) { - - use_request_param("server"); - $response["server"] = $this->app->get_server_details(); - } - if (has_request_param("custom")) { use_request_param("custom"); - $abs_href = use_request_param("customHref", null); - $response["custom"] = $this->app->get_customizations($abs_href); + $url = use_request_param("customHref"); + $response["custom"] = $this->app->get_customizations($url); } if (has_request_param("items")) { use_request_param("items"); - $abs_href = use_request_param("itemsHref", null); - $what = use_request_param("itemsWhat", null); + $url = use_request_param("itemsHref"); + $what = use_request_param("itemsWhat"); $what = is_numeric($what) ? intval($what, 10) : 1; - $response["items"] = $this->app->get_items($abs_href, $what); + $response["items"] = $this->app->get_items($url, $what); } if (count($_REQUEST)) { @@ -90,7 +84,7 @@ class Api { json_fail(1, "thumbnails disabled"); } - normalized_require_once("Thumb.php"); + normalized_require_once("class-thumb"); if (!Thumb::is_supported()) { json_fail(2, "thumbnails not supported"); } @@ -119,7 +113,7 @@ class Api { $type = use_request_param("type"); $hrefs = use_request_param("hrefs"); - normalized_require_once("Archive.php"); + normalized_require_once("class-archive"); $archive = new Archive($this->app); $hrefs = explode("|:|", trim($hrefs)); diff --git a/src/_h5ai/server/php/inc/App.php b/src/_h5ai/server/php/inc/class-app.php similarity index 79% rename from src/_h5ai/server/php/inc/App.php rename to src/_h5ai/server/php/inc/class-app.php index b6af8c74..3d02ce31 100644 --- a/src/_h5ai/server/php/inc/App.php +++ b/src/_h5ai/server/php/inc/class-app.php @@ -20,6 +20,14 @@ class App { } + public function get_setup() { + + $consts = get_defined_constants(true)['user']; + $consts['PHP_VERSION'] = PHP_VERSION; + return $consts; + } + + public function to_url($path, $trailing_slash = true) { $rel_path = substr($path, strlen(ROOT_PATH)); @@ -232,56 +240,6 @@ class App { } - public function get_server_checks() { - - $results = array(); - - $results["path_index"] = INDEX_URL; - $results["path_cache_writable"] = @is_writable(CACHE_PATH); - $results["php_version"] = PHP_VERSION; - $results["php_version_supported"] = IS_PHP_VERSION_SUPPORTED; - $results["php_exif"] = function_exists("exif_thumbnail"); - - $php_jpg = false; - if (function_exists("gd_info")) { - $infos = gd_info(); - $php_jpg = array_key_exists("JPG Support", $infos) && $infos["JPG Support"] || array_key_exists("JPEG Support", $infos) && $infos["JPEG Support"]; - } - $results["php_jpg"] = $php_jpg; - - $check_cmd = IS_WIN_OS ? "which" : "command -v"; - foreach (array("tar", "zip", "convert", "ffmpeg", "avconv", "du") as $cmd) { - $results["cmd_" . $cmd] = @preg_match("#" . $cmd . "(.exe)?$#i", exec_cmd($check_cmd . " " . $cmd)) > 0; - } - $results["cmd_ffmpeg_or_avconv"] = $results["cmd_ffmpeg"] || $results["cmd_avconv"]; - - $results["is_win_os"] = IS_WIN_OS; - $results["is_supported_php"] = IS_PHP_VERSION_SUPPORTED; - $results["server_name"] = SERVER_NAME; - $results["server_version"] = SERVER_VERSION; - $results["app_url"] = APP_URL; - $results["app_path"] = APP_PATH; - $results["root_url"] = ROOT_URL; - $results["root_path"] = ROOT_PATH; - $results["current_url"] = CURRENT_URL; - $results["current_path"] = CURRENT_PATH; - $results["options"] = $this->options; - - return $results; - } - - - public function get_server_details() { - - return array( - "backend" => "php", - "api" => true, - "name" => SERVER_NAME, - "version" => SERVER_VERSION - ); - } - - public function get_customizations($url) { if (!$this->options["custom"]["enabled"]) { diff --git a/src/_h5ai/server/php/inc/Archive.php b/src/_h5ai/server/php/inc/class-archive.php similarity index 100% rename from src/_h5ai/server/php/inc/Archive.php rename to src/_h5ai/server/php/inc/class-archive.php diff --git a/src/_h5ai/server/php/inc/Item.php b/src/_h5ai/server/php/inc/class-item.php similarity index 90% rename from src/_h5ai/server/php/inc/Item.php rename to src/_h5ai/server/php/inc/class-item.php index 531508c7..94d437da 100644 --- a/src/_h5ai/server/php/inc/Item.php +++ b/src/_h5ai/server/php/inc/class-item.php @@ -2,8 +2,6 @@ class Item { - private static $FOLDER_SIZE_CMD = "du -sk [DIR]"; - public static function cmp($item1, $item2) { if ($item1->is_folder && !$item2->is_folder) { @@ -57,8 +55,8 @@ class Item { $this->size = null; $options = $app->get_options(); if ($options["foldersize"]["enabled"]) { - $cmd = str_replace("[DIR]", escapeshellarg($this->path), Item::$FOLDER_SIZE_CMD); - $this->size = intval(preg_replace("#\s.*$#", "", exec_cmd($cmd)), 10) * 1024; + $cmdv = array("du", "-sk", $this->path); + $this->size = intval(preg_replace("#\s.*$#", "", exec_cmdv($cmdv)), 10) * 1024; } } else { $this->size = @filesize($this->path); diff --git a/src/_h5ai/server/php/inc/Thumb.php b/src/_h5ai/server/php/inc/class-thumb.php similarity index 95% rename from src/_h5ai/server/php/inc/Thumb.php rename to src/_h5ai/server/php/inc/class-thumb.php index 5def0a58..2412046f 100644 --- a/src/_h5ai/server/php/inc/Thumb.php +++ b/src/_h5ai/server/php/inc/class-thumb.php @@ -93,6 +93,14 @@ class Thumb { $capture_path = $this->thumbs_path . "/capture-" . sha1($source_path) . ".jpg"; if (!file_exists($capture_path) || filemtime($source_path) >= filemtime($capture_path)) { + + // if ($type === "mov") { + // $cmdv = array("ffmpeg", "-ss", "0:01:00", "-i", $source_path, "-an", "-vframes", "1", $capture_path); + // $cmdv = array("avconv", "-ss", "0:01:00", "-i", $source_path, "-an", "-vframes", "1", $capture_path); + // } else if ($type === "doc") { + // $cmdv = array("convert", "-strip", $source_path, $capture_path); + // } + $cmd = str_replace("[SOURCE]", escapeshellarg($source_path), $cmd); $cmd = str_replace("[TARGET]", escapeshellarg($capture_path), $cmd); exec_cmd($cmd); diff --git a/src/_h5ai/server/php/inc/main.php b/src/_h5ai/server/php/inc/setup.php similarity index 51% rename from src/_h5ai/server/php/inc/main.php rename to src/_h5ai/server/php/inc/setup.php index bc3a8d02..b1107745 100644 --- a/src/_h5ai/server/php/inc/main.php +++ b/src/_h5ai/server/php/inc/setup.php @@ -1,26 +1,33 @@ "; - $s .= $id . " " . var_export($obj, true); - $s .= "
\n"; - echo($s); -} +function setup() { -function init() { - + // MISC putenv("LANG=en_US.UTF-8"); + setlocale(LC_CTYPE, "en_US.UTF-8"); date_default_timezone_set("UTC"); + define("BACKEND", "PHP"); + define("API", true); + define("MAGIC_SEQUENCE", "={{pkg.name}}="); + define("FILE_PREFIX", "_{{pkg.name}}"); + + + // PHP define("MIN_PHP_VERSION", "5.3.0"); - define("IS_PHP_VERSION_SUPPORTED", version_compare(PHP_VERSION, MIN_PHP_VERSION) >= 0); + define("HAS_PHP_VERSION", version_compare(PHP_VERSION, MIN_PHP_VERSION) >= 0); + define("HAS_PHP_EXIF", function_exists("exif_thumbnail")); + $has_php_jpg = false; + if (function_exists("gd_info")) { + $infos = gd_info(); + $has_php_jpg = array_key_exists("JPG Support", $infos) && $infos["JPG Support"] || array_key_exists("JPEG Support", $infos) && $infos["JPEG Support"]; + } + define("HAS_PHP_JPG", $has_php_jpg); - define("IS_WIN_OS", strtolower(substr(PHP_OS, 0, 3)) === "win"); + // SERVER $server_name = null; $server_version = null; $server_software = getenv("SERVER_SOFTWARE"); @@ -30,7 +37,10 @@ function init() { } define("SERVER_NAME", $server_name); define("SERVER_VERSION", $server_version); + define("HAS_WIN_OS", strtolower(substr(PHP_OS, 0, 3)) === "win"); + + // PATHS $script_name = getenv("SCRIPT_NAME"); if (SERVER_NAME === "lighttpd") { $script_name = preg_replace("#^.*//#", "/", $script_name); @@ -52,58 +62,18 @@ function init() { define("CURRENT_URL", $cur_url); define("CURRENT_PATH", $cur_path); - define("MAGIC_SEQUENCE", "={{pkg.name}}="); - define("FILE_PREFIX", "_{{pkg.name}}"); - define("INDEX_URL", normalize_path(APP_URL . "server/php/index.php", false)); + define("CACHE_URL", normalize_path(APP_URL . "cache", true)); define("CACHE_PATH", normalize_path(APP_PATH . "/cache", false)); - - // refl('DEFINED', array( - // "PHP_VERSION " => PHP_VERSION, - // "PHP_OS " => PHP_OS, - // "MIN_PHP_VERSION " => MIN_PHP_VERSION, - // "IS_PHP_VERSION_SUPPORTED " => IS_PHP_VERSION_SUPPORTED, - // "IS_WIN_OS " => IS_WIN_OS, - // "SERVER_NAME " => SERVER_NAME, - // "SERVER_VERSION " => SERVER_VERSION, - // "APP_URL " => APP_URL, - // "APP_PATH " => APP_PATH, - // "ROOT_URL " => ROOT_URL, - // "ROOT_PATH " => ROOT_PATH, - // "CURRENT_URL " => CURRENT_URL, - // "CURRENT_PATH " => CURRENT_PATH, - // "MAGIC_SEQUENCE " => MAGIC_SEQUENCE, - // "FILE_PREFIX " => FILE_PREFIX, - // "INDEX_URL " => INDEX_URL, - // "CACHE_URL " => CACHE_URL, - // "CACHE_PATH " => CACHE_PATH - // )); - // exit(); -} - -init(); - -normalized_require_once("util.php"); -normalized_require_once("App.php"); -normalized_require_once("Item.php"); - -$app = new App(); + define("HAS_WRITABLE_CACHE", @is_writable(CACHE_PATH)); -if (has_request_param("action")) { - - header("Content-type: application/json;charset=utf-8"); - - normalized_require_once("Api.php"); - $api = new Api($app); - $api->apply(); - -} else { - - header("Content-type: text/html;charset=utf-8"); - define("FALLBACK", $app->get_fallback()); - normalized_require_once("page.php"); + // EXTERNAL COMMANDS + foreach (array("tar", "zip", "convert", "ffmpeg", "avconv", "du") as $cmd) { + $cmdv = HAS_WIN_OS ? array("which", $cmd) : array("command", "-v", $cmd); + define("HAS_CMD_" . strtoupper($cmd), @preg_match("#" . $cmd . "(.exe)?$#i", exec_cmdv($cmdv)) > 0); + } } ?> \ No newline at end of file diff --git a/src/_h5ai/server/php/inc/util.php b/src/_h5ai/server/php/inc/util.php index a47cc5ca..65b6c4b2 100644 --- a/src/_h5ai/server/php/inc/util.php +++ b/src/_h5ai/server/php/inc/util.php @@ -1,5 +1,6 @@ " . $message . ": " . var_export($obj, true) . "\n"); +} + + +global $__TIMER_START, $__TIMER_LAST; +$__TIMER_START = microtime(true); +$__TIMER_LAST = $__TIMER_START; +function time_log($message) { + + global $__TIMER_START, $__TIMER_LAST; + $now = microtime(true); + if ($__TIMER_START === $__TIMER_LAST) { + error_log("-----------------------------"); + function timer_shutdown() { time_log('ex'); } + register_shutdown_function('timer_shutdown'); + } + error_log($message . " DT " . number_format($now - $__TIMER_LAST, 5) . " TT " . number_format($now - $__TIMER_START, 5)); + $__TIMER_LAST = $now; +} + ?> \ No newline at end of file diff --git a/src/_h5ai/server/php/index.php b/src/_h5ai/server/php/index.php index 3bc97245..7ead7493 100644 --- a/src/_h5ai/server/php/index.php +++ b/src/_h5ai/server/php/index.php @@ -8,9 +8,43 @@ function normalize_path($path, $trailing_slash = false) { function normalized_require_once($lib) { - require_once(normalize_path(dirname(__FILE__) . "/inc/" . $lib, false)); + require_once(normalize_path(dirname(__FILE__) . "/inc/${lib}.php", false)); } -normalized_require_once("main.php"); +normalized_require_once("util"); +normalized_require_once("setup"); +normalized_require_once("class-api"); +normalized_require_once("class-app"); +normalized_require_once("class-archive"); +normalized_require_once("class-item"); +normalized_require_once("class-thumb"); + +time_log(" 0"); +setup(); +time_log(" 1"); + +$app = new App(); + +// time_log(" 2"); +// err_log('setup', $app->get_setup()); +time_log(" 3"); + + +if (has_request_param("action")) { + + header("Content-type: application/json;charset=utf-8"); + time_log("a1"); + $api = new Api($app); + time_log("a2"); + $api->apply(); + +} else { + + header("Content-type: text/html;charset=utf-8"); + time_log("i1"); + define("FALLBACK", $app->get_fallback()); + time_log("i2"); + normalized_require_once("page"); +} ?> \ No newline at end of file From 344c0a8005b5d6dbe65ca6f313d118385ab5afd2 Mon Sep 17 00:00:00 2001 From: Lars Jung Date: Sun, 18 May 2014 22:04:06 +0200 Subject: [PATCH 016/106] More refactorings. --- src/_h5ai/conf/options.json | 8 +++- src/_h5ai/server/php/inc/class-api.php | 28 +++--------- src/_h5ai/server/php/inc/class-app.php | 8 +--- src/_h5ai/server/php/inc/class-item.php | 54 +++++++++++++++++------- src/_h5ai/server/php/inc/class-thumb.php | 20 +-------- 5 files changed, 55 insertions(+), 63 deletions(-) diff --git a/src/_h5ai/conf/options.json b/src/_h5ai/conf/options.json index 477c219c..a31a5987 100644 --- a/src/_h5ai/conf/options.json +++ b/src/_h5ai/conf/options.json @@ -135,9 +135,15 @@ Options /* Calc the size of folders. + This operation is real slow. The calculated sizes differ slightly for both + calculation types since "php" only adds the file size, while "shell-du" + also adds the sizes for the actual folder files. + + - type: "php" (sloooow) or "shell-du" (sloow) */ "foldersize": { - "enabled": true + "enabled": true, + "type": "php" }, /* diff --git a/src/_h5ai/server/php/inc/class-api.php b/src/_h5ai/server/php/inc/class-api.php index 914ff0a4..16e26611 100644 --- a/src/_h5ai/server/php/inc/class-api.php +++ b/src/_h5ai/server/php/inc/class-api.php @@ -15,7 +15,6 @@ class Api { public function apply() { $options = $this->app->get_options(); - $action = use_request_param("action"); if ($action === "get") { @@ -80,14 +79,8 @@ class Api { else if ($action === "getThumbHref") { - if (!$options["thumbnails"]["enabled"]) { - json_fail(1, "thumbnails disabled"); - } - - normalized_require_once("class-thumb"); - if (!Thumb::is_supported()) { - json_fail(2, "thumbnails not supported"); - } + json_fail(1, "thumbnails disabled", !$options["thumbnails"]["enabled"]); + json_fail(2, "thumbnails not supported", !HAS_PHP_JPG); $type = use_request_param("type"); $src_url = use_request_param("href"); @@ -97,9 +90,7 @@ class Api { $thumb = new Thumb($this->app); $thumb_url = $thumb->thumb($type, $src_url, $mode, $width, $height); - if ($thumb_url === null) { - json_fail(3, "thumbnail creation failed"); - } + json_fail(3, "thumbnail creation failed", $thumb_url === null); json_exit(array("absHref" => $thumb_url)); } @@ -113,7 +104,6 @@ class Api { $type = use_request_param("type"); $hrefs = use_request_param("hrefs"); - normalized_require_once("class-archive"); $archive = new Archive($this->app); $hrefs = explode("|:|", trim($hrefs)); @@ -124,9 +114,7 @@ class Api { header("Connection: close"); $rc = $archive->output($type, $hrefs); - if ($rc !== 0) { - json_fail("packaging failed"); - } + json_fail("packaging failed", $rc !== 0); exit; } @@ -152,7 +140,6 @@ class Api { json_fail(6, "already exists", file_exists($dest)); json_fail(7, "can't move uploaded file", !move_uploaded_file($userfile["tmp_name"], $dest)); - json_exit(); } @@ -183,11 +170,8 @@ class Api { } } - if (count($errors)) { - json_fail(2, "deletion failed for some"); - } else { - json_exit(); - } + json_fail(2, "deletion failed for some", count($errors) > 0); + json_exit(); } diff --git a/src/_h5ai/server/php/inc/class-app.php b/src/_h5ai/server/php/inc/class-app.php index 3d02ce31..74ee756e 100644 --- a/src/_h5ai/server/php/inc/class-app.php +++ b/src/_h5ai/server/php/inc/class-app.php @@ -113,12 +113,6 @@ class App { } - public function get_generic_json() { - - return json_encode(array("items" => $this->get_items(CURRENT_URL, 1))) . "\n"; - } - - public function get_items($url, $what) { $code = $this->get_http_code($url); @@ -157,7 +151,9 @@ class App { $cache = array(); $folder = Item::get($this, CURRENT_PATH, $cache); + time_log("f2"); $items = $folder->get_content($cache); + time_log("f3"); uasort($items, array("Item", "cmp")); $html = ""; diff --git a/src/_h5ai/server/php/inc/class-item.php b/src/_h5ai/server/php/inc/class-item.php index 94d437da..dd14a8a6 100644 --- a/src/_h5ai/server/php/inc/class-item.php +++ b/src/_h5ai/server/php/inc/class-item.php @@ -14,10 +14,46 @@ class Item { return strcasecmp($item1->path, $item2->path); } + + private static $size_cache = array(); + + + private static function filesize($app, $path) { + + if (array_key_exists($path, Item::$size_cache)) { + return Item::$size_cache[$path]; + } + + $size = 0; + + if (is_file($path)) { + + $size = @filesize($path); + + } else if (is_dir($path)) { + + $options = $app->get_options(); + if ($options["foldersize"]["enabled"]) { + if (HAS_CMD_DU && $options["foldersize"]["type"] === "shell-du") { + $cmdv = array("du", "-sk", $path); + $size = intval(preg_replace("#\s.*$#", "", exec_cmdv($cmdv)), 10) * 1024; + } else { + foreach ($app->read_dir($path) as $name) { + $size += Item::filesize($app, $path . "/" . $name); + } + } + } + } + + Item::$size_cache[$path] = $size; + return $size; + } + + public static function get($app, $path, &$cache) { if (!starts_with($path, ROOT_PATH)) { - error_log("ILLEGAL REQUEST: " . $path . ", " . ROOT_PATH); + err_log("ILLEGAL REQUEST: " . $path . ", " . ROOT_PATH); return null; } @@ -47,21 +83,9 @@ class Item { $this->path = normalize_path($path, false); $this->is_folder = is_dir($this->path); - $this->url = $this->app->to_url($path, $this->is_folder); - + $this->url = $app->to_url($this->path, $this->is_folder); $this->date = @filemtime($this->path); - - if ($this->is_folder) { - $this->size = null; - $options = $app->get_options(); - if ($options["foldersize"]["enabled"]) { - $cmdv = array("du", "-sk", $this->path); - $this->size = intval(preg_replace("#\s.*$#", "", exec_cmdv($cmdv)), 10) * 1024; - } - } else { - $this->size = @filesize($this->path); - } - + $this->size = Item::filesize($app, $this->path); $this->is_content_fetched = false; } diff --git a/src/_h5ai/server/php/inc/class-thumb.php b/src/_h5ai/server/php/inc/class-thumb.php index 2412046f..49cc9eaf 100644 --- a/src/_h5ai/server/php/inc/class-thumb.php +++ b/src/_h5ai/server/php/inc/class-thumb.php @@ -6,13 +6,6 @@ class Thumb { private static $AVCONV_CMD = "avconv -ss 0:01:00 -i [SOURCE] -an -vframes 1 [TARGET]"; private static $CONVERT_CMD = "convert -strip [SOURCE][0] [TARGET]"; private static $THUMB_CACHE = "thumbs"; - private static $CAPTURE_CACHE = "captures"; - - - public static final function is_supported() { - - return Image::is_supported(); - } private $app, $thumbs_path, $thumbs_href; @@ -65,7 +58,7 @@ class Thumb { $et = false; $opts = $this->app->get_options(); - if ($opts["thumbnails"]["exif"] === true && function_exists("exif_thumbnail")) { + if (HAS_PHP_EXIF && $opts["thumbnails"]["exif"] === true) { $et = @exif_thumbnail($source_path); } if($et !== false) { @@ -116,17 +109,6 @@ class Image { private $source_file, $source, $width, $height, $type, $dest; - public static final function is_supported() { - - if (!function_exists("gd_info")) { - return false; - } - - $gdinfo = gd_info(); - return array_key_exists("JPG Support", $gdinfo) && $gdinfo["JPG Support"] || array_key_exists("JPEG Support", $gdinfo) && $gdinfo["JPEG Support"]; - } - - public function __construct($filename = null) { $this->source_file = null; From 4c7912815cd8c52f5e4462c9439ef4db190518eb Mon Sep 17 00:00:00 2001 From: Lars Jung Date: Mon, 19 May 2014 22:27:19 +0200 Subject: [PATCH 017/106] More refactorings. --- src/_h5ai/server/php/inc/class-app.php | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/_h5ai/server/php/inc/class-app.php b/src/_h5ai/server/php/inc/class-app.php index 74ee756e..db5eb367 100644 --- a/src/_h5ai/server/php/inc/class-app.php +++ b/src/_h5ai/server/php/inc/class-app.php @@ -28,6 +28,12 @@ class App { } + public function get_types() { + + return load_commented_json(APP_PATH . "/conf/types.json"); + } + + public function to_url($path, $trailing_slash = true) { $rel_path = substr($path, strlen(ROOT_PATH)); @@ -191,12 +197,6 @@ class App { } - public function get_types() { - - return load_commented_json(APP_PATH . "/conf/types.json"); - } - - public function get_l10n_list() { $langs = array(); From 351c395f82e242172590fdc4d61ad829a822b582 Mon Sep 17 00:00:00 2001 From: Lars Jung Date: Mon, 19 May 2014 22:40:25 +0200 Subject: [PATCH 018/106] Remove PHP closing tags. --- src/_h5ai/server/php/inc/class-api.php | 2 -- src/_h5ai/server/php/inc/class-app.php | 2 -- src/_h5ai/server/php/inc/class-archive.php | 2 -- src/_h5ai/server/php/inc/class-item.php | 2 -- src/_h5ai/server/php/inc/class-thumb.php | 2 -- src/_h5ai/server/php/inc/setup.php | 2 -- src/_h5ai/server/php/inc/util.php | 2 -- src/_h5ai/server/php/index.php | 2 -- 8 files changed, 16 deletions(-) diff --git a/src/_h5ai/server/php/inc/class-api.php b/src/_h5ai/server/php/inc/class-api.php index 16e26611..2cba7dbe 100644 --- a/src/_h5ai/server/php/inc/class-api.php +++ b/src/_h5ai/server/php/inc/class-api.php @@ -203,5 +203,3 @@ class Api { json_fail(100, "unsupported request"); } } - -?> \ No newline at end of file diff --git a/src/_h5ai/server/php/inc/class-app.php b/src/_h5ai/server/php/inc/class-app.php index db5eb367..b46ff443 100644 --- a/src/_h5ai/server/php/inc/class-app.php +++ b/src/_h5ai/server/php/inc/class-app.php @@ -279,5 +279,3 @@ class App { ); } } - -?> \ No newline at end of file diff --git a/src/_h5ai/server/php/inc/class-archive.php b/src/_h5ai/server/php/inc/class-archive.php index 3f15b185..8adae371 100644 --- a/src/_h5ai/server/php/inc/class-archive.php +++ b/src/_h5ai/server/php/inc/class-archive.php @@ -195,5 +195,3 @@ class Archive { } } } - -?> \ No newline at end of file diff --git a/src/_h5ai/server/php/inc/class-item.php b/src/_h5ai/server/php/inc/class-item.php index dd14a8a6..16f02ae6 100644 --- a/src/_h5ai/server/php/inc/class-item.php +++ b/src/_h5ai/server/php/inc/class-item.php @@ -136,5 +136,3 @@ class Item { return $items; } } - -?> \ No newline at end of file diff --git a/src/_h5ai/server/php/inc/class-thumb.php b/src/_h5ai/server/php/inc/class-thumb.php index 49cc9eaf..33b90d8b 100644 --- a/src/_h5ai/server/php/inc/class-thumb.php +++ b/src/_h5ai/server/php/inc/class-thumb.php @@ -362,5 +362,3 @@ class Image { } } } - -?> \ No newline at end of file diff --git a/src/_h5ai/server/php/inc/setup.php b/src/_h5ai/server/php/inc/setup.php index b1107745..388e5910 100644 --- a/src/_h5ai/server/php/inc/setup.php +++ b/src/_h5ai/server/php/inc/setup.php @@ -75,5 +75,3 @@ function setup() { define("HAS_CMD_" . strtoupper($cmd), @preg_match("#" . $cmd . "(.exe)?$#i", exec_cmdv($cmdv)) > 0); } } - -?> \ No newline at end of file diff --git a/src/_h5ai/server/php/inc/util.php b/src/_h5ai/server/php/inc/util.php index 65b6c4b2..0d0397ec 100644 --- a/src/_h5ai/server/php/inc/util.php +++ b/src/_h5ai/server/php/inc/util.php @@ -122,5 +122,3 @@ function time_log($message) { error_log($message . " DT " . number_format($now - $__TIMER_LAST, 5) . " TT " . number_format($now - $__TIMER_START, 5)); $__TIMER_LAST = $now; } - -?> \ No newline at end of file diff --git a/src/_h5ai/server/php/index.php b/src/_h5ai/server/php/index.php index 7ead7493..658a1375 100644 --- a/src/_h5ai/server/php/index.php +++ b/src/_h5ai/server/php/index.php @@ -46,5 +46,3 @@ if (has_request_param("action")) { time_log("i2"); normalized_require_once("page"); } - -?> \ No newline at end of file From 0b28a9eea17555e4a057a4900138ef82dc6a588c Mon Sep 17 00:00:00 2001 From: Lars Jung Date: Wed, 21 May 2014 02:21:46 +0200 Subject: [PATCH 019/106] More refactorings. --- src/_h5ai/server/php/inc/class-api.php | 309 +++++++++++++------------ src/_h5ai/server/php/inc/class-app.php | 8 +- src/_h5ai/server/php/inc/setup.php | 2 +- 3 files changed, 163 insertions(+), 156 deletions(-) diff --git a/src/_h5ai/server/php/inc/class-api.php b/src/_h5ai/server/php/inc/class-api.php index 2cba7dbe..055f491e 100644 --- a/src/_h5ai/server/php/inc/class-api.php +++ b/src/_h5ai/server/php/inc/class-api.php @@ -3,184 +3,167 @@ class Api { - private $app; + private $actions, $app, $options; public function __construct($app) { + $this->actions = array("get", "getThumbHref", "download", "upload", "delete", "rename"); $this->app = $app; + $this->options = $app->get_options(); } public function apply() { - $options = $this->app->get_options(); $action = use_request_param("action"); - if ($action === "get") { - - $response = array(); - - if (has_request_param("setup")) { - - use_request_param("setup"); - $response["setup"] = $this->app->get_setup(); - } - - if (has_request_param("options")) { - - use_request_param("options"); - $response["options"] = $this->app->get_options(); - } - - if (has_request_param("types")) { - - use_request_param("types"); - $response["types"] = $this->app->get_types(); - } - - if (has_request_param("langs")) { - - use_request_param("langs"); - $response["langs"] = $this->app->get_l10n_list(); - } - - if (has_request_param("l10n")) { - - use_request_param("l10n"); - $iso_codes = use_request_param("l10nCodes"); - $iso_codes = explode(":", $iso_codes); - $response["l10n"] = $this->app->get_l10n($iso_codes); - } - - if (has_request_param("custom")) { - - use_request_param("custom"); - $url = use_request_param("customHref"); - $response["custom"] = $this->app->get_customizations($url); - } - - if (has_request_param("items")) { - - use_request_param("items"); - $url = use_request_param("itemsHref"); - $what = use_request_param("itemsWhat"); - $what = is_numeric($what) ? intval($what, 10) : 1; - $response["items"] = $this->app->get_items($url, $what); - } - - if (count($_REQUEST)) { - $response["unused"] = $_REQUEST; - } - - json_exit($response); + if (in_array($action, $this->actions)) { + $methodname = "on_$action"; + $this->$methodname(); } + json_fail(100, "unsupported request"); + } - else if ($action === "getThumbHref") { - json_fail(1, "thumbnails disabled", !$options["thumbnails"]["enabled"]); - json_fail(2, "thumbnails not supported", !HAS_PHP_JPG); + private function on_get() { - $type = use_request_param("type"); - $src_url = use_request_param("href"); - $mode = use_request_param("mode"); - $width = use_request_param("width"); - $height = use_request_param("height"); + $response = array(); - $thumb = new Thumb($this->app); - $thumb_url = $thumb->thumb($type, $src_url, $mode, $width, $height); - json_fail(3, "thumbnail creation failed", $thumb_url === null); + if (has_request_param("setup")) { - json_exit(array("absHref" => $thumb_url)); + use_request_param("setup"); + $response["setup"] = $this->app->get_setup(); } + if (has_request_param("options")) { - else if ($action === "download") { - - json_fail(1, "downloads disabled", !$options["download"]["enabled"]); - - $as = use_request_param("as"); - $type = use_request_param("type"); - $hrefs = use_request_param("hrefs"); - - $archive = new Archive($this->app); - - $hrefs = explode("|:|", trim($hrefs)); - - set_time_limit(0); - header("Content-Type: application/octet-stream"); - header("Content-Disposition: attachment; filename=\"$as\""); - header("Connection: close"); - $rc = $archive->output($type, $hrefs); - - json_fail("packaging failed", $rc !== 0); - exit; + use_request_param("options"); + $response["options"] = $this->app->get_options(); } + if (has_request_param("types")) { - else if ($action === "upload") { - - $href = use_request_param("href"); - - json_fail(1, "wrong HTTP method", strtolower($_SERVER["REQUEST_METHOD"]) !== "post"); - json_fail(2, "something went wrong", !array_key_exists("userfile", $_FILES)); - - $userfile = $_FILES["userfile"]; - - json_fail(3, "something went wrong [" . $userfile["error"] . "]", $userfile["error"] !== 0); - json_fail(4, "folders not supported", file_get_contents($userfile["tmp_name"]) === "null"); - - $upload_dir = $this->app->to_path($href); - $code = $this->app->get_http_code($href); - - json_fail(5, "upload dir no h5ai folder or ignored", $code !== MAGIC_SEQUENCE || $this->app->is_ignored($upload_dir)); - - $dest = $upload_dir . "/" . utf8_encode($userfile["name"]); - - json_fail(6, "already exists", file_exists($dest)); - json_fail(7, "can't move uploaded file", !move_uploaded_file($userfile["tmp_name"], $dest)); - json_exit(); + use_request_param("types"); + $response["types"] = $this->app->get_types(); } + if (has_request_param("langs")) { - else if ($action === "delete") { - - json_fail(1, "deletion disabled", !$options["delete"]["enabled"]); - - $hrefs = use_request_param("hrefs"); - - $hrefs = explode("|:|", trim($hrefs)); - $errors = array(); - - foreach ($hrefs as $href) { - - $d = normalize_path(dirname($href), true); - $n = basename($href); - - $code = $this->app->get_http_code($d); - - if ($code == MAGIC_SEQUENCE && !$this->app->is_ignored($n)) { - - $abs_path = $this->app->to_path($href); - - if (!unlink($abs_path)) { - $errors[] = $href; - } - } - } - - json_fail(2, "deletion failed for some", count($errors) > 0); - json_exit(); + use_request_param("langs"); + $response["langs"] = $this->app->get_l10n_list(); } + if (has_request_param("l10n")) { - else if ($action === "rename") { + use_request_param("l10n"); + $iso_codes = use_request_param("l10nCodes"); + $iso_codes = explode(":", $iso_codes); + $response["l10n"] = $this->app->get_l10n($iso_codes); + } - json_fail(1, "renaming disabled", !$options["rename"]["enabled"]); + if (has_request_param("custom")) { - $href = use_request_param("href"); - $name = use_request_param("name"); + use_request_param("custom"); + $url = use_request_param("customHref"); + $response["custom"] = $this->app->get_customizations($url); + } + + if (has_request_param("items")) { + + use_request_param("items"); + $url = use_request_param("itemsHref"); + $what = use_request_param("itemsWhat"); + $what = is_numeric($what) ? intval($what, 10) : 1; + $response["items"] = $this->app->get_items($url, $what); + } + + if (count($_REQUEST)) { + $response["unused"] = $_REQUEST; + } + + json_exit($response); + } + + + private function on_getThumbHref() { + + json_fail(1, "thumbnails disabled", !$this->options["thumbnails"]["enabled"]); + json_fail(2, "thumbnails not supported", !HAS_PHP_JPG); + + $type = use_request_param("type"); + $src_url = use_request_param("href"); + $mode = use_request_param("mode"); + $width = use_request_param("width"); + $height = use_request_param("height"); + + $thumb = new Thumb($this->app); + $thumb_url = $thumb->thumb($type, $src_url, $mode, $width, $height); + json_fail(3, "thumbnail creation failed", $thumb_url === null); + + json_exit(array("absHref" => $thumb_url)); + } + + + private function on_download() { + + json_fail(1, "downloads disabled", !$this->options["download"]["enabled"]); + + $as = use_request_param("as"); + $type = use_request_param("type"); + $hrefs = use_request_param("hrefs"); + + $archive = new Archive($this->app); + + $hrefs = explode("|:|", trim($hrefs)); + + set_time_limit(0); + header("Content-Type: application/octet-stream"); + header("Content-Disposition: attachment; filename=\"$as\""); + header("Connection: close"); + $rc = $archive->output($type, $hrefs); + + json_fail("packaging failed", $rc !== 0); + exit; + } + + + private function on_upload() { + + $href = use_request_param("href"); + + json_fail(1, "wrong HTTP method", strtolower($_SERVER["REQUEST_METHOD"]) !== "post"); + json_fail(2, "something went wrong", !array_key_exists("userfile", $_FILES)); + + $userfile = $_FILES["userfile"]; + + json_fail(3, "something went wrong [" . $userfile["error"] . "]", $userfile["error"] !== 0); + json_fail(4, "folders not supported", file_get_contents($userfile["tmp_name"]) === "null"); + + $upload_dir = $this->app->to_path($href); + $code = $this->app->get_http_code($href); + + json_fail(5, "upload dir no h5ai folder or ignored", $code !== MAGIC_SEQUENCE || $this->app->is_ignored($upload_dir)); + + $dest = $upload_dir . "/" . utf8_encode($userfile["name"]); + + json_fail(6, "already exists", file_exists($dest)); + json_fail(7, "can't move uploaded file", !move_uploaded_file($userfile["tmp_name"], $dest)); + json_exit(); + } + + + private function on_delete() { + + json_fail(1, "deletion disabled", !$this->options["delete"]["enabled"]); + + $hrefs = use_request_param("hrefs"); + + $hrefs = explode("|:|", trim($hrefs)); + $errors = array(); + + foreach ($hrefs as $href) { $d = normalize_path(dirname($href), true); $n = basename($href); @@ -190,16 +173,40 @@ class Api { if ($code == MAGIC_SEQUENCE && !$this->app->is_ignored($n)) { $abs_path = $this->app->to_path($href); - $folder = normalize_path(dirname($abs_path)); - if (!rename($abs_path, $folder . "/" . $name)) { - json_fail(2, "renaming failed"); + if (!unlink($abs_path)) { + $errors[] = $href; } } - - json_exit(); } - json_fail(100, "unsupported request"); + json_fail(2, "deletion failed for some", count($errors) > 0); + json_exit(); + } + + + private function on_rename() { + + json_fail(1, "renaming disabled", !$this->options["rename"]["enabled"]); + + $href = use_request_param("href"); + $name = use_request_param("name"); + + $d = normalize_path(dirname($href), true); + $n = basename($href); + + $code = $this->app->get_http_code($d); + + if ($code == MAGIC_SEQUENCE && !$this->app->is_ignored($n)) { + + $abs_path = $this->app->to_path($href); + $folder = normalize_path(dirname($abs_path)); + + if (!rename($abs_path, $folder . "/" . $name)) { + json_fail(2, "renaming failed"); + } + } + + json_exit(); } } diff --git a/src/_h5ai/server/php/inc/class-app.php b/src/_h5ai/server/php/inc/class-app.php index b46ff443..faa5fc43 100644 --- a/src/_h5ai/server/php/inc/class-app.php +++ b/src/_h5ai/server/php/inc/class-app.php @@ -22,8 +22,8 @@ class App { public function get_setup() { - $consts = get_defined_constants(true)['user']; - $consts['PHP_VERSION'] = PHP_VERSION; + $consts = get_defined_constants(true)["user"]; + $consts["PHP_VERSION"] = PHP_VERSION; return $consts; } @@ -99,8 +99,8 @@ class App { return 500; } - foreach ($this->options["view"]["indexFiles"] as $if) { - if (file_exists($path . "/" . $if)) { + foreach ($this->options["view"]["indexFiles"] as $name) { + if (file_exists($path . "/" . $name)) { return 200; } } diff --git a/src/_h5ai/server/php/inc/setup.php b/src/_h5ai/server/php/inc/setup.php index 388e5910..8802ea83 100644 --- a/src/_h5ai/server/php/inc/setup.php +++ b/src/_h5ai/server/php/inc/setup.php @@ -43,7 +43,7 @@ function setup() { // PATHS $script_name = getenv("SCRIPT_NAME"); if (SERVER_NAME === "lighttpd") { - $script_name = preg_replace("#^.*//#", "/", $script_name); + $script_name = preg_replace("#^.*?//#", "/", $script_name); } define("APP_URL", normalize_path(dirname(dirname(dirname($script_name))), true)); define("APP_PATH", normalize_path(dirname(dirname(dirname(dirname(__FILE__)))), false)); From a89db0c2598cccab52d8478343fcb54e912476a9 Mon Sep 17 00:00:00 2001 From: Lars Jung Date: Wed, 21 May 2014 16:09:02 +0200 Subject: [PATCH 020/106] More refactorings. --- src/_h5ai/server/php/inc/class-api.php | 11 +++----- src/_h5ai/server/php/inc/class-thumb.php | 33 +++++++++++------------- src/_h5ai/server/php/inc/util.php | 15 ++++------- 3 files changed, 24 insertions(+), 35 deletions(-) diff --git a/src/_h5ai/server/php/inc/class-api.php b/src/_h5ai/server/php/inc/class-api.php index 055f491e..8b0d47be 100644 --- a/src/_h5ai/server/php/inc/class-api.php +++ b/src/_h5ai/server/php/inc/class-api.php @@ -17,13 +17,10 @@ class Api { public function apply() { $action = use_request_param("action"); + json_fail(100, "unsupported request", !in_array($action, $this->actions)); - if (in_array($action, $this->actions)) { - $methodname = "on_$action"; - $this->$methodname(); - } - - json_fail(100, "unsupported request"); + $methodname = "on_$action"; + $this->$methodname(); } @@ -124,7 +121,7 @@ class Api { header("Connection: close"); $rc = $archive->output($type, $hrefs); - json_fail("packaging failed", $rc !== 0); + json_fail(2, "packaging failed", $rc !== 0); exit; } diff --git a/src/_h5ai/server/php/inc/class-thumb.php b/src/_h5ai/server/php/inc/class-thumb.php index 33b90d8b..9ca1758d 100644 --- a/src/_h5ai/server/php/inc/class-thumb.php +++ b/src/_h5ai/server/php/inc/class-thumb.php @@ -2,9 +2,9 @@ class Thumb { - private static $FFMPEG_CMD = "ffmpeg -ss 0:01:00 -i [SOURCE] -an -vframes 1 [TARGET]"; - private static $AVCONV_CMD = "avconv -ss 0:01:00 -i [SOURCE] -an -vframes 1 [TARGET]"; - private static $CONVERT_CMD = "convert -strip [SOURCE][0] [TARGET]"; + private static $FFMPEG_CMDV = array("ffmpeg", "-ss", "0:01:00", "-i", "[SRC]", "-an", "-vframes", "1", "[DEST]"); + private static $AVCONV_CMDV = array("avconv", "-ss", "0:01:00", "-i", "[SRC]", "-an", "-vframes", "1", "[DEST]"); + private static $CONVERT_CMDV = array("convert", "-strip", "[SRC][0]", "[DEST]"); private static $THUMB_CACHE = "thumbs"; @@ -26,12 +26,13 @@ class Thumb { if ($type === "img") { $capture_path = $source_path; } else if ($type === "mov") { - $capture_path = $this->capture(Thumb::$FFMPEG_CMD, $source_path); - if ($capture_path === null) { - $capture_path = $this->capture(Thumb::$AVCONV_CMD, $source_path); + if (HAS_CMD_FFMPEG) { + $capture_path = $this->capture(Thumb::$FFMPEG_CMDV, $source_path); + } else if (HAS_CMD_AVCONV) { + $capture_path = $this->capture(Thumb::$AVCONV_CMDV, $source_path); } - } else if ($type === "doc") { - $capture_path = $this->capture(Thumb::$CONVERT_CMD, $source_path); + } else if ($type === "doc" && HAS_CMD_CONVERT) { + $capture_path = $this->capture(Thumb::$CONVERT_CMDV, $source_path); } return $this->thumb_href($capture_path, $mode, $width, $height); @@ -77,7 +78,7 @@ class Thumb { } - private function capture($cmd, $source_path) { + private function capture($cmdv, $source_path) { if (!file_exists($source_path)) { return null; @@ -87,16 +88,12 @@ class Thumb { if (!file_exists($capture_path) || filemtime($source_path) >= filemtime($capture_path)) { - // if ($type === "mov") { - // $cmdv = array("ffmpeg", "-ss", "0:01:00", "-i", $source_path, "-an", "-vframes", "1", $capture_path); - // $cmdv = array("avconv", "-ss", "0:01:00", "-i", $source_path, "-an", "-vframes", "1", $capture_path); - // } else if ($type === "doc") { - // $cmdv = array("convert", "-strip", $source_path, $capture_path); - // } + foreach ($cmdv as &$arg) { + $arg = str_replace("[SRC]", $source_path, $arg); + $arg = str_replace("[DEST]", $capture_path, $arg); + } - $cmd = str_replace("[SOURCE]", escapeshellarg($source_path), $cmd); - $cmd = str_replace("[TARGET]", escapeshellarg($capture_path), $cmd); - exec_cmd($cmd); + exec_cmdv($cmdv); } return file_exists($capture_path) ? $capture_path : null; diff --git a/src/_h5ai/server/php/inc/util.php b/src/_h5ai/server/php/inc/util.php index 0d0397ec..3f90cd8e 100644 --- a/src/_h5ai/server/php/inc/util.php +++ b/src/_h5ai/server/php/inc/util.php @@ -66,15 +66,6 @@ function load_commented_json($file) { } -function exec_cmd($cmd) { - - $lines = array(); - $rc = null; - exec($cmd, $lines, $rc); - return implode("\n", $lines); -} - - function passthru_cmd($cmd) { $rc = null; @@ -89,7 +80,11 @@ function exec_cmdv($cmdv) { $cmdv = func_get_args(); } $cmd = implode(" ", array_map("escapeshellarg", $cmdv)); - return exec_cmd($cmd); + + $lines = array(); + $rc = null; + exec($cmd, $lines, $rc); + return implode("\n", $lines); } From 8eea5f56cd95249d829b07738c8713915eaa605e Mon Sep 17 00:00:00 2001 From: Lars Jung Date: Wed, 21 May 2014 19:55:53 +0200 Subject: [PATCH 021/106] More refactorings. --- src/_h5ai/server/php/inc/setup.php | 1 - src/_h5ai/server/php/inc/util.php | 38 ++++++++++++++++-------------- src/_h5ai/server/php/index.php | 4 ++-- 3 files changed, 22 insertions(+), 21 deletions(-) diff --git a/src/_h5ai/server/php/inc/setup.php b/src/_h5ai/server/php/inc/setup.php index 8802ea83..b107e758 100644 --- a/src/_h5ai/server/php/inc/setup.php +++ b/src/_h5ai/server/php/inc/setup.php @@ -3,7 +3,6 @@ function setup() { - // MISC putenv("LANG=en_US.UTF-8"); setlocale(LC_CTYPE, "en_US.UTF-8"); diff --git a/src/_h5ai/server/php/inc/util.php b/src/_h5ai/server/php/inc/util.php index 3f90cd8e..a6476715 100644 --- a/src/_h5ai/server/php/inc/util.php +++ b/src/_h5ai/server/php/inc/util.php @@ -24,12 +24,10 @@ function has_request_param($key) { } -define("NO_DEFAULT", "__NO_DEFAULT_VALUE__"); - -function use_request_param($key, $default = NO_DEFAULT) { +function use_request_param($key, $default = null) { if (!array_key_exists($key, $_REQUEST)) { - json_fail(101, "parameter '$key' is missing", $default === NO_DEFAULT); + json_fail(101, "parameter '$key' is missing", $default === null); return $default; } @@ -51,18 +49,18 @@ function ends_with($sequence, $tail) { } -function load_commented_json($file) { +function load_commented_json($path) { - if (!file_exists($file)) { + if (!file_exists($path)) { return array(); } - $str = file_get_contents($file); + $content = file_get_contents($path); // remove comments to get pure json - $str = preg_replace("/\/\*.*?\*\/|\/\/.*?(\n|$)/s", "", $str); + $content = preg_replace("/\/\*.*?\*\/|\/\/.*?(\n|$)/s", "", $content); - return json_decode($str, true); + return json_decode($content, true); } @@ -102,18 +100,22 @@ function scr_log($message, $obj = null) { } -global $__TIMER_START, $__TIMER_LAST; +global $__TIMER_START, $__TIMER_PREV; $__TIMER_START = microtime(true); -$__TIMER_LAST = $__TIMER_START; +$__TIMER_PREV = $__TIMER_START; + function time_log($message) { - global $__TIMER_START, $__TIMER_LAST; + global $__TIMER_START, $__TIMER_PREV; + $now = microtime(true); - if ($__TIMER_START === $__TIMER_LAST) { - error_log("-----------------------------"); - function timer_shutdown() { time_log('ex'); } - register_shutdown_function('timer_shutdown'); + + if ($__TIMER_START === $__TIMER_PREV) { + error_log("------------------------------"); + register_shutdown_function(function () { time_log('ex'); }); } - error_log($message . " DT " . number_format($now - $__TIMER_LAST, 5) . " TT " . number_format($now - $__TIMER_START, 5)); - $__TIMER_LAST = $now; + + error_log($message . " DT " . number_format($now - $__TIMER_PREV, 5) . " TT " . number_format($now - $__TIMER_START, 5)); + + $__TIMER_PREV = $now; } diff --git a/src/_h5ai/server/php/index.php b/src/_h5ai/server/php/index.php index 658a1375..098c04f9 100644 --- a/src/_h5ai/server/php/index.php +++ b/src/_h5ai/server/php/index.php @@ -25,9 +25,9 @@ time_log(" 1"); $app = new App(); -// time_log(" 2"); +time_log(" 2"); // err_log('setup', $app->get_setup()); -time_log(" 3"); +// time_log(" 3"); if (has_request_param("action")) { From d59379a0f17059c743431ecf912f2f08aed42775 Mon Sep 17 00:00:00 2001 From: Lars Jung Date: Wed, 21 May 2014 22:44:09 +0200 Subject: [PATCH 022/106] Add CHANGELOG.md. --- CHANGELOG.md | 419 ++++++++++++++++++++++++++++++++++++++++++++++++++ LICENSE.md | 2 +- README.md | 421 --------------------------------------------------- 3 files changed, 420 insertions(+), 422 deletions(-) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 00000000..eedba025 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,419 @@ +# Changelog +**h5ai** uses [semantic versioning](http://semver.org/). + + +## develop branch + +* replaces PHP backtick operator with `exec` +* adds PHP variant to calc folder sizes +* language updates (`bg`, `ko`, `pt`, `sv`) + + +## v0.24.1 - *2014-04-09* + +* security fixes! (issues #268, #269) +* language updates (`fi`, `fr`, `hi`, `it`, `zh-tw`) +* fixes WinOS command detection + + +## v0.24.0 - *2013-09-04* + +* updates image and text preview +* adds variable icon sizes +* adds optional natural sort of items +* adds optional checkboxes to select items +* adds text preview modes: none, fixed, markdown +* optionally hide folders in main view +* makes use of EXIF thumbnails optional +* fixes file deletion of multiple files +* fixes `setParentFolderLabels = false` +* fixes shell-arg and RegExp escape issues +* cleans code +* updates info page `/_h5ai` +* adds `aiff` to `audio` types +* adds `da` translation by Ronnie Milbo +* updates to `pl` translation by Mark + + +## v0.23.0 - *2013-07-21* + +* removes `aai` mode! +* drops support for IE7+8 (simple fallback, same as no javascript) +* uses History API if available (way faster browsing) +* faster thumbnail generation if EXIF thumbnails available +* adds optional custom headers/footers that are propageted to all subfolders +* optional hide parent folder links +* some fixes on previews +* speeds up packaged downloads +* add line wrap and line highlighting (on hover) to text preview +* new design (colors, images) +* now uses scalable images for the interface +* fixes filter (ignore parent folder, display of `no match`) +* lots of small fixes +* updates H5BP to 4.2.0 +* updates jQuery to 2.0.3 +* updates jQuery.mousewheel to 3.1.3 +* updates Moment.js to 2.1.0 +* updates markdown-js to 0.4.0-9c21acdf08 +* updates json2.js to 2013-05-26 +* adds `uk` translation by Viktor Matveenko +* updates to `pl` translation by Mark + + +## v0.22.1 - *2012-10-16* + +* bug fix concerning API requests in PHP mode +* minor changes in responsive styles + + +## v0.22 - *2012-10-14* + +* general changes h5ai directory layout and configuration +* splits configuration file (`config.json`) into files `options.json`, `types.json` and `langs.json` +* localization now in separate files +* adds auto-refresh +* adds drag'n'drop upload (PHP, experimental) +* adds file deletion (PHP, experimental) +* cleans and improves PHP code +* PHP no longer respects htaccess restrictions (so be careful) +* PHP ignore patterns might include paths now +* improves separation between aai and php mode +* improves performance in aai mode +* adds optional binary prefixes for file sizes +* improves filter: autofocus on keypress, clear on `ESC` +* download packages now packaged relative to current folder +* download package name changable +* splits type `js` into `js` and `json` +* prevents some errors with files > 2GB on 32bit OS +* adds max subfolder size in tree view +* adds ctrl-click file selection +* adds Piwik analytics extension +* temp download packages are now stored in the `cache`-folder and deleted as soon as possible +* updates translations +* adds `he` translation by [Tomer Cohen](https://github.com/tomer) +* updates 3rd party libs + + +## v0.21 - *2012-08-06* + +* fixes misaligned image previews +* adds no JavaScript fallback to PHP version +* fixes duplicate tree entries and empty main views +* adds Google Analytics support (async) +* improves filter (now ignorecase, now only checks if chars in right order) +* adds keyboard support to image preview (space, enter, backspace, left, right, up, down, f, esc) +* adds text file preview and highlighting with [SyntaxHighlighter](http://alexgorbatchev.com/SyntaxHighlighter/) (same keys as img preview) +* adds Markdown preview with [markdown-js](https://github.com/evilstreak/markdown-js) +* adds new type `markdown` +* changes language code `gr` to `el` +* adds localization for filter placeholder +* adds `hu` translation by [Rodolffo](https://github.com/Rodolffo) +* updates to [jQuery.qrcode](http://larsjung.de/qrcode/) 0.2 +* updates to [jQuery.scrollpanel](http://larsjung.de/scrollpanel/) 0.1 +* updates to [modulejs](http://larsjung.de/modulejs/) 0.2 +* updates to [Moment.js](http://momentjs.com) 1.7.0 +* updates to [Underscore.js](http://underscorejs.org) 1.3.3 + + +## v0.20 - *2012-05-11* + +* adds image preview +* adds thumbnails for video and pdf +* adds support for lighttpd, nginx and cherokee and maybe other webservers with PHP +* adds folder size in PHP version via shell `du` +* fixes some localization problems +* updates info page at `/_h5ai/` +* switches to JSHint + + +## v0.19 - *2012-04-19* + +* adds lots of config options +* changes in `config.js` and `h5ai.htaccess` +* fixes js problems in IE 7+8 +* hides broken tree view in IE < 9, adds a message to the footer +* removes hash changes since they break logical browser history +* fixes thumbnail size for portrait images in icon view +* fixes problems with file type recognition +* adds an info page at `/_h5ai/` +* sort order is preserved while browsing +* removes PHP error messages on thumbnail generation +* fixes PHP some problems with packed download +* adds support for tarred downloads +* changes crumb image for folders with an index file +* adds `index.php` to use h5ai in non-Apache environments +* switches from [Datejs](http://www.datejs.com) to [Moment.js](http://momentjs.com) +* adds [underscore.js](http://underscorejs.org) +* fixes mousewheel problems, updates [jQuery.mousewheel](https://github.com/brandonaaron/jquery-mousewheel) to 3.0.6 +* updates `lv` translation +* adds `ro` translation by [Jakob Cosoroabă](https://github.com/midday) +* adds `ja` translation by [metasta](https://github.com/metasta) +* adds `nb` translation by [Sindre Sorhus](https://github.com/sindresorhus) +* adds `sr` translation by [vBm](https://github.com/vBm) +* adds `gr` translation by [xhmikosr](https://github.com/xhmikosr) + + +## v0.18 - *2012-02-24* + +* adds optional QRCode display +* adds optional filtering for displayed files and folders +* updates design +* improves zipped download +* adds support for zipped download of htaccess restricted files +* changes h5ai.htaccess +* custom headers/footers are now optional and disabled by default +* fixes problems with folder recognition in the JS version +* fixes include problems in PHP version +* fixes path problems on servers running on Windows in PHP version +* fixes broken links in custom headers/footers while zipped download enabled +* fixes problems with thumbnails for files with single or double quotes in filename +* improves url hashes +* updates year in `LICENSE.TXT` +* updates es translation +* adds `zh-tw` translation by [Yao Wei](https://github.com/medicalwei) +* updates `zh-cn` translation + + +## v0.17 - *2011-11-28* + +* h5ai is now located in `_h5ai` to reduce collisions +* switches from HTML5 Boilerplate reset to normalization +* adds some style changes for small devices +* configuration (options, types, translations) now via `config.js` +* icons for JS version are now configured via `config.js` +* sort order configuration changed +* sorting is now done without page reload +* adds `customHeader` and `customFooter` to `config.js` +* supports restricted folders to some extent +* some style changes on tree and language menu +* fixes total file/folder count in status bar +* adds support for use with userdir (requires some manual changes) + + +## v0.16 - *2011-11-02* + +* sorts translations in `options.js` +* improves HTML head sections +* refactors JavaScript and PHP a lot +* improves/fixes file selection for zipped download +* fixes scrollbar and header/footer link issues (didn't work when zipped download enabled) +* adds support for ctrl-select +* `dateFormat` in `options.js` changed, now affecting JS and PHP version +* `dateFormat` is localizable by adding it to a translation in `options.js` +* PHP version is now configurable via `php/config.php` (set custom doc root and other PHP related things) +* image thumbs and zipped download is disabled by default now, but works fine if PHP is configured + + +## v0.15.2 - *2011-09-18* + +* adds `it` translation by [Salvo Gentile](https://github.com/SalvoGentile) and [Marco Patriarca](https://github.com/Fexys) +* switches build process from scripp to wepp + + +## v0.15.1 - *2011-09-06* + +* fixes security issues with the zipped download feature +* makes zipped download optional (but enabled by default) + + +## v0.15 - *2011-09-04* + +* adds zipped download for selected files +* cleans and refactores + + +## v0.14.1 - *2011-09-01* + +* display meta information in bottom bar (icon view) +* adds `zh-cn` translation by [Dongsheng Cai](https://github.com/dongsheng) +* adds `pl` translation by Radosław Zając +* adds `ru` translation by Богдан Илюхин + + +## v0.14 - *2011-08-16* + +* adds image thumbnails for PHP version +* new option `slideTree` to turn off auto slide in + + +## v0.13.2 - *2011-08-12* + +* changes in `/h5ai/.htaccess` ... PHP configuration ... + + +## v0.13.1 - *2011-08-12* + +* fixes initial tree display +* adds sort order option +* adds/fixes some translations +* adds `lv` translation by Sandis Veinbergs + + +## v0.13 - *2011-08-06* + +* adds PHP implementation! (should work with PHP 5.2+) +* adds new options +* changes layout of the bottom bar to display status information +* adds language selector to the bottom bar +* quotes keys in `options.js` to make it valid json +* changes value of option `lang` from `undefined` to `null` +* adds some new keys to `h5aiLangs` +* adds browser caching rules for css and js +* adds `pt` translation by [Jonnathan](https://github.com/jonnsl) +* adds `bg` translation by George Andonov + + +## v0.12.3 - *2011-07-30* + +* adds `tr` translation by [Batuhan Icoz](https://github.com/batuhanicoz) + + +## v0.12.2 - *2011-07-30* + +* adds `es` translation by Jose David Calderon Serrano + + +## v0.12.1 - *2011-07-29* + +* fixes unchecked use of console.log + + +## v0.12 - *2011-07-28* + +* improves performance + + +## v0.11 - *2011-07-27* + +* changes license to MIT license, see `LICENSE.txt` + + +## v0.10.2 - *2011-07-26* + +* improves tree scrollbar + + +## v0.10.1 - *2011-07-24* + +* fixes problems with ' in links + + +## v0.10 - *2011-07-24* + +* fixes problems with XAMPP on Windows (see `dot.htaccess` comments for instructions) +* fixes tree fade-in-fade-out effect for small displays ([issue #6](https://github.com/lrsjng/h5ai/issues/6)) +* adds custom scrollbar to tree ([issue #6](https://github.com/lrsjng/h5ai/issues/6)) +* fixes broken links caused by URI encoding/decoding ([issue #9](https://github.com/lrsjng/h5ai/issues/9)) +* adds "empty" to localization (hope Google Translate did a good job here) + + +## v0.9 - *2011-07-18* + +* links hover states between crumb, extended view and tree +* fixes size of tree view (now there's a ugly scrollbar, hopefully will be fixed) +* refactores js to improve performance and cleaned code +* adds caching for folder status codes and content +* adds `fr` translation by [Nicolas](https://github.com/Nicosmos) +* adds `nl` translation by [Stefan de Konink](https://github.com/skinkie) +* adds `sv` translation by Oscar Carlsson + + +## v0.8 - *2011-07-08* + +* removes slashes from folder labels +* optionally rename parent folder entries to real folder names, see `options.js` +* long breadcrumbs (multiple rows) no longer hide content +* error folder icons are opaque now +* refactores js a lot (again...) + + +## v0.7 - *2011-07-07* + +* removes shadows +* smarter tree side bar + + +## v0.6 - *2011-07-05* + +* refactores js +* adds localization, see `options.js` + + +## v0.5.3 - *2011-07-04* + +* refactores js +* adds basic options support via `options.js` +* adds comments to `options.js` +* adds optional tree sidebar + + +## v0.5.2 - *2011-07-02* + +* details view adjusts to window width +* links icon for *.gz and *.bz2 + + +## v0.5.1 - *2011-07-01* + +* disables tree sidebar for now, since it had unwanted side effects + + +## v0.5 - *2011-07-01* + +* adds tree sidebar +* some refactorings + + +## v0.4 - *2011-06-27* + +* adds better fallback, in case JavaScript is disabled +* rewrites js, fixed middle-button click etc. problems +* refactors css +* sorts, adds and moves icons and images +* updates dot.access + + +## v0.3.2 - *2011-06-24* + +* removes lib versions from file names +* adds 'empty' indicator for icons view + + +## v0.3.1 - *2011-06-24* + +* refactores js +* adds `folderClick` and `fileClick` callback hooks +* fixes .emtpy style + + +## v0.3 - *2011-06-23* + +* includes build stuff, files previously found in the base directory are now located in folder `target` +* styles and scripts are now minified +* adds Modernizr 2.0.4 for future use +* updates jQuery to version 1.6.1 + + +## v0.2.3 - *2011-06-17* + +* more refactoring in main.js + + +## v0.2.2 - *2011-06-16* + +* refactores a lot, adds some comments +* includes fixes from [NumEricR](https://github.com/NumEricR) +* adds top/bottom message support, only basicly styled + + +## v0.2.1 - *2011-06-16* + +* fixes croped filenames +* fixes missing .png extension in header +* adds some color to the links +* adds changelog + + +## v0.2 - *2011-06-15* + +* adds icon view diff --git a/LICENSE.md b/LICENSE.md index 223593d0..24370d2e 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -16,4 +16,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file +SOFTWARE. diff --git a/README.md b/README.md index 252a3ced..52b70113 100644 --- a/README.md +++ b/README.md @@ -48,424 +48,3 @@ It profits from these great projects: [Moment.js](http://momentjs.com) (MIT), [SyntaxHighlighter](http://alexgorbatchev.com/SyntaxHighlighter/) (MIT/GPL), [Underscore.js](http://underscorejs.org) (MIT) - - -## Changelog -**h5ai** uses [semantic versioning](http://semver.org/). - - -### develop branch - -* replaces PHP backtick operator with `exec` -* language updates (`bg`, `ko`, `pt`, `sv`) - - -### v0.24.1 - *2014-04-09* - -* security fixes! (issues #268, #269) -* language updates (`fi`, `fr`, `hi`, `it`, `zh-tw`) -* fixes WinOS command detection - - -### v0.24.0 - *2013-09-04* - -* updates image and text preview -* adds variable icon sizes -* adds optional natural sort of items -* adds optional checkboxes to select items -* adds text preview modes: none, fixed, markdown -* optionally hide folders in main view -* makes use of EXIF thumbnails optional -* fixes file deletion of multiple files -* fixes `setParentFolderLabels = false` -* fixes shell-arg and RegExp escape issues -* cleans code -* updates info page `/_h5ai` -* adds `aiff` to `audio` types -* adds `da` translation by Ronnie Milbo -* updates to `pl` translation by Mark - - -### v0.23.0 - *2013-07-21* - -* removes `aai` mode! -* drops support for IE7+8 (simple fallback, same as no javascript) -* uses History API if available (way faster browsing) -* faster thumbnail generation if EXIF thumbnails available -* adds optional custom headers/footers that are propageted to all subfolders -* optional hide parent folder links -* some fixes on previews -* speeds up packaged downloads -* add line wrap and line highlighting (on hover) to text preview -* new design (colors, images) -* now uses scalable images for the interface -* fixes filter (ignore parent folder, display of `no match`) -* lots of small fixes -* updates H5BP to 4.2.0 -* updates jQuery to 2.0.3 -* updates jQuery.mousewheel to 3.1.3 -* updates Moment.js to 2.1.0 -* updates markdown-js to 0.4.0-9c21acdf08 -* updates json2.js to 2013-05-26 -* adds `uk` translation by Viktor Matveenko -* updates to `pl` translation by Mark - - -### v0.22.1 - *2012-10-16* - -* bug fix concerning API requests in PHP mode -* minor changes in responsive styles - - -### v0.22 - *2012-10-14* - -* general changes h5ai directory layout and configuration -* splits configuration file (`config.json`) into files `options.json`, `types.json` and `langs.json` -* localization now in separate files -* adds auto-refresh -* adds drag'n'drop upload (PHP, experimental) -* adds file deletion (PHP, experimental) -* cleans and improves PHP code -* PHP no longer respects htaccess restrictions (so be careful) -* PHP ignore patterns might include paths now -* improves separation between aai and php mode -* improves performance in aai mode -* adds optional binary prefixes for file sizes -* improves filter: autofocus on keypress, clear on `ESC` -* download packages now packaged relative to current folder -* download package name changable -* splits type `js` into `js` and `json` -* prevents some errors with files > 2GB on 32bit OS -* adds max subfolder size in tree view -* adds ctrl-click file selection -* adds Piwik analytics extension -* temp download packages are now stored in the `cache`-folder and deleted as soon as possible -* updates translations -* adds `he` translation by [Tomer Cohen](https://github.com/tomer) -* updates 3rd party libs - - -### v0.21 - *2012-08-06* - -* fixes misaligned image previews -* adds no JavaScript fallback to PHP version -* fixes duplicate tree entries and empty main views -* adds Google Analytics support (async) -* improves filter (now ignorecase, now only checks if chars in right order) -* adds keyboard support to image preview (space, enter, backspace, left, right, up, down, f, esc) -* adds text file preview and highlighting with [SyntaxHighlighter](http://alexgorbatchev.com/SyntaxHighlighter/) (same keys as img preview) -* adds Markdown preview with [markdown-js](https://github.com/evilstreak/markdown-js) -* adds new type `markdown` -* changes language code `gr` to `el` -* adds localization for filter placeholder -* adds `hu` translation by [Rodolffo](https://github.com/Rodolffo) -* updates to [jQuery.qrcode](http://larsjung.de/qrcode/) 0.2 -* updates to [jQuery.scrollpanel](http://larsjung.de/scrollpanel/) 0.1 -* updates to [modulejs](http://larsjung.de/modulejs/) 0.2 -* updates to [Moment.js](http://momentjs.com) 1.7.0 -* updates to [Underscore.js](http://underscorejs.org) 1.3.3 - - -### v0.20 - *2012-05-11* - -* adds image preview -* adds thumbnails for video and pdf -* adds support for lighttpd, nginx and cherokee and maybe other webservers with PHP -* adds folder size in PHP version via shell `du` -* fixes some localization problems -* updates info page at `/_h5ai/` -* switches to JSHint - - -### v0.19 - *2012-04-19* - -* adds lots of config options -* changes in `config.js` and `h5ai.htaccess` -* fixes js problems in IE 7+8 -* hides broken tree view in IE < 9, adds a message to the footer -* removes hash changes since they break logical browser history -* fixes thumbnail size for portrait images in icon view -* fixes problems with file type recognition -* adds an info page at `/_h5ai/` -* sort order is preserved while browsing -* removes PHP error messages on thumbnail generation -* fixes PHP some problems with packed download -* adds support for tarred downloads -* changes crumb image for folders with an index file -* adds `index.php` to use h5ai in non-Apache environments -* switches from [Datejs](http://www.datejs.com) to [Moment.js](http://momentjs.com) -* adds [underscore.js](http://underscorejs.org) -* fixes mousewheel problems, updates [jQuery.mousewheel](https://github.com/brandonaaron/jquery-mousewheel) to 3.0.6 -* updates `lv` translation -* adds `ro` translation by [Jakob Cosoroabă](https://github.com/midday) -* adds `ja` translation by [metasta](https://github.com/metasta) -* adds `nb` translation by [Sindre Sorhus](https://github.com/sindresorhus) -* adds `sr` translation by [vBm](https://github.com/vBm) -* adds `gr` translation by [xhmikosr](https://github.com/xhmikosr) - - -### v0.18 - *2012-02-24* - -* adds optional QRCode display -* adds optional filtering for displayed files and folders -* updates design -* improves zipped download -* adds support for zipped download of htaccess restricted files -* changes h5ai.htaccess -* custom headers/footers are now optional and disabled by default -* fixes problems with folder recognition in the JS version -* fixes include problems in PHP version -* fixes path problems on servers running on Windows in PHP version -* fixes broken links in custom headers/footers while zipped download enabled -* fixes problems with thumbnails for files with single or double quotes in filename -* improves url hashes -* updates year in `LICENSE.TXT` -* updates es translation -* adds `zh-tw` translation by [Yao Wei](https://github.com/medicalwei) -* updates `zh-cn` translation - - -### v0.17 - *2011-11-28* - -* h5ai is now located in `_h5ai` to reduce collisions -* switches from HTML5 Boilerplate reset to normalization -* adds some style changes for small devices -* configuration (options, types, translations) now via `config.js` -* icons for JS version are now configured via `config.js` -* sort order configuration changed -* sorting is now done without page reload -* adds `customHeader` and `customFooter` to `config.js` -* supports restricted folders to some extent -* some style changes on tree and language menu -* fixes total file/folder count in status bar -* adds support for use with userdir (requires some manual changes) - - -### v0.16 - *2011-11-02* - -* sorts translations in `options.js` -* improves HTML head sections -* refactors JavaScript and PHP a lot -* improves/fixes file selection for zipped download -* fixes scrollbar and header/footer link issues (didn't work when zipped download enabled) -* adds support for ctrl-select -* `dateFormat` in `options.js` changed, now affecting JS and PHP version -* `dateFormat` is localizable by adding it to a translation in `options.js` -* PHP version is now configurable via `php/config.php` (set custom doc root and other PHP related things) -* image thumbs and zipped download is disabled by default now, but works fine if PHP is configured - - -### v0.15.2 - *2011-09-18* - -* adds `it` translation by [Salvo Gentile](https://github.com/SalvoGentile) and [Marco Patriarca](https://github.com/Fexys) -* switches build process from scripp to wepp - - -### v0.15.1 - *2011-09-06* - -* fixes security issues with the zipped download feature -* makes zipped download optional (but enabled by default) - - -### v0.15 - *2011-09-04* - -* adds zipped download for selected files -* cleans and refactores - - -### v0.14.1 - *2011-09-01* - -* display meta information in bottom bar (icon view) -* adds `zh-cn` translation by [Dongsheng Cai](https://github.com/dongsheng) -* adds `pl` translation by Radosław Zając -* adds `ru` translation by Богдан Илюхин - - -### v0.14 - *2011-08-16* - -* adds image thumbnails for PHP version -* new option `slideTree` to turn off auto slide in - - -### v0.13.2 - *2011-08-12* - -* changes in `/h5ai/.htaccess` ... PHP configuration ... - - -### v0.13.1 - *2011-08-12* - -* fixes initial tree display -* adds sort order option -* adds/fixes some translations -* adds `lv` translation by Sandis Veinbergs - - -### v0.13 - *2011-08-06* - -* adds PHP implementation! (should work with PHP 5.2+) -* adds new options -* changes layout of the bottom bar to display status information -* adds language selector to the bottom bar -* quotes keys in `options.js` to make it valid json -* changes value of option `lang` from `undefined` to `null` -* adds some new keys to `h5aiLangs` -* adds browser caching rules for css and js -* adds `pt` translation by [Jonnathan](https://github.com/jonnsl) -* adds `bg` translation by George Andonov - - -### v0.12.3 - *2011-07-30* - -* adds `tr` translation by [Batuhan Icoz](https://github.com/batuhanicoz) - - -### v0.12.2 - *2011-07-30* - -* adds `es` translation by Jose David Calderon Serrano - - -### v0.12.1 - *2011-07-29* - -* fixes unchecked use of console.log - - -### v0.12 - *2011-07-28* - -* improves performance - - -### v0.11 - *2011-07-27* - -* changes license to MIT license, see `LICENSE.txt` - - -### v0.10.2 - *2011-07-26* - -* improves tree scrollbar - - -### v0.10.1 - *2011-07-24* - -* fixes problems with ' in links - - -### v0.10 - *2011-07-24* - -* fixes problems with XAMPP on Windows (see `dot.htaccess` comments for instructions) -* fixes tree fade-in-fade-out effect for small displays ([issue #6](https://github.com/lrsjng/h5ai/issues/6)) -* adds custom scrollbar to tree ([issue #6](https://github.com/lrsjng/h5ai/issues/6)) -* fixes broken links caused by URI encoding/decoding ([issue #9](https://github.com/lrsjng/h5ai/issues/9)) -* adds "empty" to localization (hope Google Translate did a good job here) - - -### v0.9 - *2011-07-18* - -* links hover states between crumb, extended view and tree -* fixes size of tree view (now there's a ugly scrollbar, hopefully will be fixed) -* refactores js to improve performance and cleaned code -* adds caching for folder status codes and content -* adds `fr` translation by [Nicolas](https://github.com/Nicosmos) -* adds `nl` translation by [Stefan de Konink](https://github.com/skinkie) -* adds `sv` translation by Oscar Carlsson - - -### v0.8 - *2011-07-08* - -* removes slashes from folder labels -* optionally rename parent folder entries to real folder names, see `options.js` -* long breadcrumbs (multiple rows) no longer hide content -* error folder icons are opaque now -* refactores js a lot (again...) - - -### v0.7 - *2011-07-07* - -* removes shadows -* smarter tree side bar - - -### v0.6 - *2011-07-05* - -* refactores js -* adds localization, see `options.js` - - -### v0.5.3 - *2011-07-04* - -* refactores js -* adds basic options support via `options.js` -* adds comments to `options.js` -* adds optional tree sidebar - - -### v0.5.2 - *2011-07-02* - -* details view adjusts to window width -* links icon for *.gz and *.bz2 - - -### v0.5.1 - *2011-07-01* - -* disables tree sidebar for now, since it had unwanted side effects - - -### v0.5 - *2011-07-01* - -* adds tree sidebar -* some refactorings - - -### v0.4 - *2011-06-27* - -* adds better fallback, in case JavaScript is disabled -* rewrites js, fixed middle-button click etc. problems -* refactors css -* sorts, adds and moves icons and images -* updates dot.access - - -### v0.3.2 - *2011-06-24* - -* removes lib versions from file names -* adds 'empty' indicator for icons view - - -### v0.3.1 - *2011-06-24* - -* refactores js -* adds `folderClick` and `fileClick` callback hooks -* fixes .emtpy style - - -### v0.3 - *2011-06-23* - -* includes build stuff, files previously found in the base directory are now located in folder `target` -* styles and scripts are now minified -* adds Modernizr 2.0.4 for future use -* updates jQuery to version 1.6.1 - - -### v0.2.3 - *2011-06-17* - -* more refactoring in main.js - - -### v0.2.2 - *2011-06-16* - -* refactores a lot, adds some comments -* includes fixes from [NumEricR](https://github.com/NumEricR) -* adds top/bottom message support, only basicly styled - - -### v0.2.1 - *2011-06-16* - -* fixes croped filenames -* fixes missing .png extension in header -* adds some color to the links -* adds changelog - - -### v0.2 - *2011-06-15* - -* adds icon view - From e13c268e9d69875d203922f1f7e0387eac26c834 Mon Sep 17 00:00:00 2001 From: Lars Jung Date: Wed, 21 May 2014 23:42:11 +0200 Subject: [PATCH 023/106] Update JS libs. --- CHANGELOG.md | 8 +- .../lib/{jquery-2.0.3.js => jquery-2.1.1.js} | 7467 +++++++++-------- ...son2-2013-05-26.js => json2-2014-02-04.js} | 27 +- ...-0.4.0-9c21acdf08.js => markdown-0.5.0.js} | 77 +- src/_h5ai/client/js/lib/modernizr-2.6.2.js | 418 - src/_h5ai/client/js/lib/modernizr-2.8.2.js | 4 + src/_h5ai/client/js/lib/moment-2.1.0.js | 1662 ---- src/_h5ai/client/js/lib/moment-2.6.0.min.js | 6 + ...nderscore-1.5.1.js => underscore-1.6.0.js} | 339 +- 9 files changed, 4238 insertions(+), 5770 deletions(-) rename src/_h5ai/client/js/lib/{jquery-2.0.3.js => jquery-2.1.1.js} (83%) rename src/_h5ai/client/js/lib/{json2-2013-05-26.js => json2-2014-02-04.js} (98%) rename src/_h5ai/client/js/lib/{markdown-0.4.0-9c21acdf08.js => markdown-0.5.0.js} (94%) delete mode 100644 src/_h5ai/client/js/lib/modernizr-2.6.2.js create mode 100644 src/_h5ai/client/js/lib/modernizr-2.8.2.js delete mode 100644 src/_h5ai/client/js/lib/moment-2.1.0.js create mode 100644 src/_h5ai/client/js/lib/moment-2.6.0.min.js rename src/_h5ai/client/js/lib/{underscore-1.5.1.js => underscore-1.6.0.js} (82%) diff --git a/CHANGELOG.md b/CHANGELOG.md index eedba025..fe42a935 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,12 +6,18 @@ * replaces PHP backtick operator with `exec` * adds PHP variant to calc folder sizes +* updates jQuery to 2.1.1 +* updates json2.js to 2014-02-04 +* updates markdown-js to 0.5.0 +* updates Modernizr to 2.8.2 +* updates Moment.js to 2.6.0 +* updates Underscore.js to 1.6.0 * language updates (`bg`, `ko`, `pt`, `sv`) ## v0.24.1 - *2014-04-09* -* security fixes! (issues #268, #269) +* security fixes! (issues [#268], [#269]) * language updates (`fi`, `fr`, `hi`, `it`, `zh-tw`) * fixes WinOS command detection diff --git a/src/_h5ai/client/js/lib/jquery-2.0.3.js b/src/_h5ai/client/js/lib/jquery-2.1.1.js similarity index 83% rename from src/_h5ai/client/js/lib/jquery-2.0.3.js rename to src/_h5ai/client/js/lib/jquery-2.1.1.js index ebc6c187..9f7b3d38 100644 --- a/src/_h5ai/client/js/lib/jquery-2.0.3.js +++ b/src/_h5ai/client/js/lib/jquery-2.1.1.js @@ -1,81 +1,84 @@ /*! - * jQuery JavaScript Library v2.0.3 + * jQuery JavaScript Library v2.1.1 * http://jquery.com/ * * Includes Sizzle.js * http://sizzlejs.com/ * - * Copyright 2005, 2013 jQuery Foundation, Inc. and other contributors + * Copyright 2005, 2014 jQuery Foundation, Inc. and other contributors * Released under the MIT license * http://jquery.org/license * - * Date: 2013-07-03T13:30Z + * Date: 2014-05-01T17:11Z */ -(function( window, undefined ) { + +(function( global, factory ) { + + if ( typeof module === "object" && typeof module.exports === "object" ) { + // For CommonJS and CommonJS-like environments where a proper window is present, + // execute the factory and get jQuery + // For environments that do not inherently posses a window with a document + // (such as Node.js), expose a jQuery-making factory as module.exports + // This accentuates the need for the creation of a real window + // e.g. var jQuery = require("jquery")(window); + // See ticket #14549 for more info + 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 ); + } + +// Pass this if window is not defined yet +}(typeof window !== "undefined" ? window : this, function( window, noGlobal ) { // Can't do this because several apps including ASP.NET trace // the stack via arguments.caller.callee and Firefox dies if // you try to trace through "use strict" call chains. (#13335) // Support: Firefox 18+ -//"use strict"; +// + +var arr = []; + +var slice = arr.slice; + +var concat = arr.concat; + +var push = arr.push; + +var indexOf = arr.indexOf; + +var class2type = {}; + +var toString = class2type.toString; + +var hasOwn = class2type.hasOwnProperty; + +var support = {}; + + + var - // A central reference to the root jQuery(document) - rootjQuery, - - // The deferred used on DOM ready - readyList, - - // Support: IE9 - // For `typeof xmlNode.method` instead of `xmlNode.method !== undefined` - core_strundefined = typeof undefined, - // Use the correct document accordingly with window argument (sandbox) - location = window.location, document = window.document, - docElem = document.documentElement, - // Map over jQuery in case of overwrite - _jQuery = window.jQuery, - - // Map over the $ in case of overwrite - _$ = window.$, - - // [[Class]] -> type pairs - class2type = {}, - - // List of deleted data cache ids, so we can reuse them - core_deletedIds = [], - - core_version = "2.0.3", - - // Save a reference to some core methods - core_concat = core_deletedIds.concat, - core_push = core_deletedIds.push, - core_slice = core_deletedIds.slice, - core_indexOf = core_deletedIds.indexOf, - core_toString = class2type.toString, - core_hasOwn = class2type.hasOwnProperty, - core_trim = core_version.trim, + version = "2.1.1", // Define a local copy of jQuery jQuery = function( selector, context ) { // The jQuery object is actually just the init constructor 'enhanced' - return new jQuery.fn.init( selector, context, rootjQuery ); + // Need init if jQuery is called (just allow error to be thrown if not included) + return new jQuery.fn.init( selector, context ); }, - // Used for matching numbers - core_pnum = /[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source, - - // Used for splitting on whitespace - core_rnotwhite = /\S+/g, - - // A simple way to check for HTML strings - // Prioritize #id over to avoid XSS via location.hash (#9521) - // Strict HTML recognition (#11290: must start with <) - rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/, - - // Match a standalone tag - rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>|)$/, + // Support: Android<4.1 + // Make sure we trim BOM and NBSP + rtrim = /^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, // Matches dashed string for camelizing rmsPrefix = /^-ms-/, @@ -84,114 +87,13 @@ var // Used by jQuery.camelCase as callback to replace() fcamelCase = function( all, letter ) { return letter.toUpperCase(); - }, - - // The ready event handler and self cleanup method - completed = function() { - document.removeEventListener( "DOMContentLoaded", completed, false ); - window.removeEventListener( "load", completed, false ); - jQuery.ready(); }; jQuery.fn = jQuery.prototype = { // The current version of jQuery being used - jquery: core_version, + jquery: version, constructor: jQuery, - init: function( selector, context, rootjQuery ) { - var match, elem; - - // HANDLE: $(""), $(null), $(undefined), $(false) - if ( !selector ) { - return this; - } - - // Handle HTML strings - if ( typeof selector === "string" ) { - if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) { - // Assume that strings that start and end with <> are HTML and skip the regex check - match = [ null, selector, null ]; - - } else { - match = rquickExpr.exec( selector ); - } - - // Match html or make sure no context is specified for #id - if ( match && (match[1] || !context) ) { - - // HANDLE: $(html) -> $(array) - if ( match[1] ) { - context = context instanceof jQuery ? context[0] : context; - - // scripts is true for back-compat - jQuery.merge( this, jQuery.parseHTML( - match[1], - context && context.nodeType ? context.ownerDocument || context : document, - true - ) ); - - // HANDLE: $(html, props) - if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) { - for ( match in context ) { - // Properties of context are called as methods if possible - if ( jQuery.isFunction( this[ match ] ) ) { - this[ match ]( context[ match ] ); - - // ...and otherwise set as attributes - } else { - this.attr( match, context[ match ] ); - } - } - } - - return this; - - // HANDLE: $(#id) - } else { - elem = document.getElementById( match[2] ); - - // Check parentNode to catch when Blackberry 4.6 returns - // nodes that are no longer in the document #6963 - if ( elem && elem.parentNode ) { - // Inject the element directly into the jQuery object - this.length = 1; - this[0] = elem; - } - - this.context = document; - this.selector = selector; - return this; - } - - // HANDLE: $(expr, $(...)) - } else if ( !context || context.jquery ) { - return ( context || rootjQuery ).find( selector ); - - // HANDLE: $(expr, context) - // (which is just equivalent to: $(context).find(expr) - } else { - return this.constructor( context ).find( selector ); - } - - // HANDLE: $(DOMElement) - } else if ( selector.nodeType ) { - this.context = this[0] = selector; - this.length = 1; - return this; - - // HANDLE: $(function) - // Shortcut for document ready - } else if ( jQuery.isFunction( selector ) ) { - return rootjQuery.ready( selector ); - } - - if ( selector.selector !== undefined ) { - this.selector = selector.selector; - this.context = selector.context; - } - - return jQuery.makeArray( selector, this ); - }, // Start with an empty selector selector: "", @@ -200,19 +102,19 @@ jQuery.fn = jQuery.prototype = { length: 0, toArray: function() { - return core_slice.call( this ); + return slice.call( this ); }, // Get the Nth element in the matched element set OR // Get the whole matched element set as a clean array get: function( num ) { - return num == null ? + return num != null ? - // Return a 'clean' array - this.toArray() : + // Return just the one element from the set + ( num < 0 ? this[ num + this.length ] : this[ num ] ) : - // Return just the object - ( num < 0 ? this[ this.length + num ] : this[ num ] ); + // Return all the elements in a clean array + slice.call( this ); }, // Take an array of elements and push it onto the stack @@ -237,15 +139,14 @@ jQuery.fn = jQuery.prototype = { return jQuery.each( this, callback, args ); }, - ready: function( fn ) { - // Add the callback - jQuery.ready.promise().done( fn ); - - return this; + map: function( callback ) { + return this.pushStack( jQuery.map(this, function( elem, i ) { + return callback.call( elem, i, elem ); + })); }, slice: function() { - return this.pushStack( core_slice.apply( this, arguments ) ); + return this.pushStack( slice.apply( this, arguments ) ); }, first: function() { @@ -262,26 +163,17 @@ jQuery.fn = jQuery.prototype = { return this.pushStack( j >= 0 && j < len ? [ this[j] ] : [] ); }, - map: function( callback ) { - return this.pushStack( jQuery.map(this, function( elem, i ) { - return callback.call( elem, i, elem ); - })); - }, - end: function() { return this.prevObject || this.constructor(null); }, // For internal use only. // Behaves like an Array's method, not like a jQuery method. - push: core_push, - sort: [].sort, - splice: [].splice + push: push, + sort: arr.sort, + splice: arr.splice }; -// Give the init function the jQuery prototype for later instantiation -jQuery.fn.init.prototype = jQuery.fn; - jQuery.extend = jQuery.fn.extend = function() { var options, name, src, copy, copyIsArray, clone, target = arguments[0] || {}, @@ -292,9 +184,10 @@ jQuery.extend = jQuery.fn.extend = function() { // Handle a deep copy situation if ( typeof target === "boolean" ) { deep = target; - target = arguments[1] || {}; + // skip the boolean and the target - i = 2; + target = arguments[ i ] || {}; + i++; } // Handle case when target is a string or something (possible in deep copy) @@ -303,9 +196,9 @@ jQuery.extend = jQuery.fn.extend = function() { } // extend jQuery itself if only one argument is passed - if ( length === i ) { + if ( i === length ) { target = this; - --i; + i--; } for ( ; i < length; i++ ) { @@ -348,60 +241,16 @@ jQuery.extend = jQuery.fn.extend = function() { jQuery.extend({ // Unique for each copy of jQuery on the page - expando: "jQuery" + ( core_version + Math.random() ).replace( /\D/g, "" ), + expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ), - noConflict: function( deep ) { - if ( window.$ === jQuery ) { - window.$ = _$; - } + // Assume jQuery is ready without the ready module + isReady: true, - if ( deep && window.jQuery === jQuery ) { - window.jQuery = _jQuery; - } - - return jQuery; + error: function( msg ) { + throw new Error( msg ); }, - // Is the DOM ready to be used? Set to true once it occurs. - isReady: false, - - // A counter to track how many items to wait for before - // the ready event fires. See #6781 - readyWait: 1, - - // Hold (or release) the ready event - holdReady: function( hold ) { - if ( hold ) { - jQuery.readyWait++; - } else { - jQuery.ready( true ); - } - }, - - // Handle when the DOM is ready - ready: function( wait ) { - - // Abort if there are pending holds or we're already ready - if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { - return; - } - - // Remember that the DOM is ready - jQuery.isReady = true; - - // If a normal DOM Ready event fired, decrement, and wait if need be - if ( wait !== true && --jQuery.readyWait > 0 ) { - return; - } - - // If there are functions bound, to execute - readyList.resolveWith( document, [ jQuery ] ); - - // Trigger any bound ready events - if ( jQuery.fn.trigger ) { - jQuery( document ).trigger("ready").off("ready"); - } - }, + noop: function() {}, // See test/unit/core.js for details concerning isFunction. // Since version 1.3, DOM methods and functions like alert @@ -417,17 +266,10 @@ jQuery.extend({ }, isNumeric: function( obj ) { - return !isNaN( parseFloat(obj) ) && isFinite( obj ); - }, - - type: function( obj ) { - if ( obj == null ) { - return String( obj ); - } - // Support: Safari <= 5.1 (functionish RegExp) - return typeof obj === "object" || typeof obj === "function" ? - class2type[ core_toString.call(obj) ] || "object" : - typeof obj; + // parseFloat NaNs numeric-cast false positives (null|true|false|"") + // ...but misinterprets leading-number strings, particularly hex literals ("0x...") + // subtraction forces infinities to NaN + return !jQuery.isArray( obj ) && obj - parseFloat( obj ) >= 0; }, isPlainObject: function( obj ) { @@ -439,16 +281,8 @@ jQuery.extend({ return false; } - // Support: Firefox <20 - // The try/catch suppresses exceptions thrown when attempting to access - // the "constructor" property of certain host objects, ie. |window.location| - // https://bugzilla.mozilla.org/show_bug.cgi?id=814622 - try { - if ( obj.constructor && - !core_hasOwn.call( obj.constructor.prototype, "isPrototypeOf" ) ) { - return false; - } - } catch ( e ) { + if ( obj.constructor && + !hasOwn.call( obj.constructor.prototype, "isPrototypeOf" ) ) { return false; } @@ -465,69 +299,20 @@ jQuery.extend({ return true; }, - error: function( msg ) { - throw new Error( msg ); + type: function( obj ) { + if ( obj == null ) { + return obj + ""; + } + // Support: Android < 4.0, iOS < 6 (functionish RegExp) + return typeof obj === "object" || typeof obj === "function" ? + class2type[ toString.call(obj) ] || "object" : + typeof obj; }, - // data: string of html - // context (optional): If specified, the fragment will be created in this context, defaults to document - // keepScripts (optional): If true, will include scripts passed in the html string - parseHTML: function( data, context, keepScripts ) { - if ( !data || typeof data !== "string" ) { - return null; - } - if ( typeof context === "boolean" ) { - keepScripts = context; - context = false; - } - context = context || document; - - var parsed = rsingleTag.exec( data ), - scripts = !keepScripts && []; - - // Single tag - if ( parsed ) { - return [ context.createElement( parsed[1] ) ]; - } - - parsed = jQuery.buildFragment( [ data ], context, scripts ); - - if ( scripts ) { - jQuery( scripts ).remove(); - } - - return jQuery.merge( [], parsed.childNodes ); - }, - - parseJSON: JSON.parse, - - // Cross-browser xml parsing - parseXML: function( data ) { - var xml, tmp; - if ( !data || typeof data !== "string" ) { - return null; - } - - // Support: IE9 - try { - tmp = new DOMParser(); - xml = tmp.parseFromString( data , "text/xml" ); - } catch ( e ) { - xml = undefined; - } - - if ( !xml || xml.getElementsByTagName( "parsererror" ).length ) { - jQuery.error( "Invalid XML: " + data ); - } - return xml; - }, - - noop: function() {}, - // Evaluates a script in a global context globalEval: function( code ) { var script, - indirect = eval; + indirect = eval; code = jQuery.trim( code ); @@ -607,8 +392,11 @@ jQuery.extend({ return obj; }, + // Support: Android<4.1 trim: function( text ) { - return text == null ? "" : core_trim.call( text ); + return text == null ? + "" : + ( text + "" ).replace( rtrim, "" ); }, // results is for internal usage only @@ -622,7 +410,7 @@ jQuery.extend({ [ arr ] : arr ); } else { - core_push.call( ret, arr ); + push.call( ret, arr ); } } @@ -630,22 +418,16 @@ jQuery.extend({ }, inArray: function( elem, arr, i ) { - return arr == null ? -1 : core_indexOf.call( arr, elem, i ); + return arr == null ? -1 : indexOf.call( arr, elem, i ); }, merge: function( first, second ) { - var l = second.length, - i = first.length, - j = 0; + var len = +second.length, + j = 0, + i = first.length; - if ( typeof l === "number" ) { - for ( ; j < l; j++ ) { - first[ i++ ] = second[ j ]; - } - } else { - while ( second[j] !== undefined ) { - first[ i++ ] = second[ j++ ]; - } + for ( ; j < len; j++ ) { + first[ i++ ] = second[ j ]; } first.length = i; @@ -653,23 +435,23 @@ jQuery.extend({ return first; }, - grep: function( elems, callback, inv ) { - var retVal, - ret = [], + grep: function( elems, callback, invert ) { + var callbackInverse, + matches = [], i = 0, - length = elems.length; - inv = !!inv; + length = elems.length, + callbackExpect = !invert; // Go through the array, only saving the items // that pass the validator function for ( ; i < length; i++ ) { - retVal = !!callback( elems[ i ], i ); - if ( inv !== retVal ) { - ret.push( elems[ i ] ); + callbackInverse = !callback( elems[ i ], i ); + if ( callbackInverse !== callbackExpect ) { + matches.push( elems[ i ] ); } } - return ret; + return matches; }, // arg is for internal usage only @@ -680,13 +462,13 @@ jQuery.extend({ isArray = isArraylike( elems ), ret = []; - // Go through the array, translating each of the items to their + // Go through the array, translating each of the items to their new values if ( isArray ) { for ( ; i < length; i++ ) { value = callback( elems[ i ], i, arg ); if ( value != null ) { - ret[ ret.length ] = value; + ret.push( value ); } } @@ -696,13 +478,13 @@ jQuery.extend({ value = callback( elems[ i ], i, arg ); if ( value != null ) { - ret[ ret.length ] = value; + ret.push( value ); } } } // Flatten any nested arrays - return core_concat.apply( [], ret ); + return concat.apply( [], ret ); }, // A global GUID counter for objects @@ -726,9 +508,9 @@ jQuery.extend({ } // Simulated bind - args = core_slice.call( arguments, 2 ); + args = slice.call( arguments, 2 ); proxy = function() { - return fn.apply( context || this, args.concat( core_slice.call( arguments ) ) ); + return fn.apply( context || this, args.concat( slice.call( arguments ) ) ); }; // Set the guid of unique handler to the same of original handler, so it can be removed @@ -737,109 +519,13 @@ jQuery.extend({ return proxy; }, - // Multifunctional method to get and set values of a collection - // The value/s can optionally be executed if it's a function - access: function( elems, fn, key, value, chainable, emptyGet, raw ) { - var i = 0, - length = elems.length, - bulk = key == null; - - // Sets many values - if ( jQuery.type( key ) === "object" ) { - chainable = true; - for ( i in key ) { - jQuery.access( elems, fn, i, key[i], true, emptyGet, raw ); - } - - // Sets one value - } else if ( value !== undefined ) { - chainable = true; - - if ( !jQuery.isFunction( value ) ) { - raw = true; - } - - if ( bulk ) { - // Bulk operations run against the entire set - if ( raw ) { - fn.call( elems, value ); - fn = null; - - // ...except when executing function values - } else { - bulk = fn; - fn = function( elem, key, value ) { - return bulk.call( jQuery( elem ), value ); - }; - } - } - - if ( fn ) { - for ( ; i < length; i++ ) { - fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) ); - } - } - } - - return chainable ? - elems : - - // Gets - bulk ? - fn.call( elems ) : - length ? fn( elems[0], key ) : emptyGet; - }, - now: Date.now, - // A method for quickly swapping in/out CSS properties to get correct calculations. - // Note: this method belongs to the css module but it's needed here for the support module. - // If support gets modularized, this method should be moved back to the css module. - swap: function( elem, options, callback, args ) { - var ret, name, - old = {}; - - // Remember the old values, and insert the new ones - for ( name in options ) { - old[ name ] = elem.style[ name ]; - elem.style[ name ] = options[ name ]; - } - - ret = callback.apply( elem, args || [] ); - - // Revert the old values - for ( name in options ) { - elem.style[ name ] = old[ name ]; - } - - return ret; - } + // jQuery.support is not used in Core but other projects attach their + // properties to it so it needs to exist. + support: support }); -jQuery.ready.promise = function( obj ) { - if ( !readyList ) { - - readyList = jQuery.Deferred(); - - // Catch cases where $(document).ready() is called after the browser event has already occurred. - // we once tried to use readyState "interactive" here, but it caused issues like the one - // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15 - if ( document.readyState === "complete" ) { - // Handle it asynchronously to allow scripts the opportunity to delay ready - setTimeout( jQuery.ready ); - - } else { - - // Use the handy event callback - document.addEventListener( "DOMContentLoaded", completed, false ); - - // A fallback to window.onload, that will always work - window.addEventListener( "load", completed, false ); - } - } - return readyList.promise( obj ); -}; - // Populate the class2type map jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "), function(i, name) { class2type[ "[object " + name + "]" ] = name.toLowerCase(); @@ -849,7 +535,7 @@ function isArraylike( obj ) { var length = obj.length, type = jQuery.type( obj ); - if ( jQuery.isWindow( obj ) ) { + if ( type === "function" || jQuery.isWindow( obj ) ) { return false; } @@ -857,34 +543,33 @@ function isArraylike( obj ) { return true; } - return type === "array" || type !== "function" && - ( length === 0 || - typeof length === "number" && length > 0 && ( length - 1 ) in obj ); + return type === "array" || length === 0 || + typeof length === "number" && length > 0 && ( length - 1 ) in obj; } - -// All jQuery objects should point back to these -rootjQuery = jQuery(document); +var Sizzle = /*! - * Sizzle CSS Selector Engine v1.9.4-pre + * Sizzle CSS Selector Engine v1.10.19 * http://sizzlejs.com/ * * Copyright 2013 jQuery Foundation, Inc. and other contributors * Released under the MIT license * http://jquery.org/license * - * Date: 2013-06-03 + * Date: 2014-04-18 */ -(function( window, undefined ) { +(function( window ) { var i, support, - cachedruns, Expr, getText, isXML, + tokenize, compile, + select, outermostContext, sortInput, + hasDuplicate, // Local document vars setDocument, @@ -904,11 +589,9 @@ var i, classCache = createCache(), tokenCache = createCache(), compilerCache = createCache(), - hasDuplicate = false, sortOrder = function( a, b ) { if ( a === b ) { hasDuplicate = true; - return 0; } return 0; }, @@ -950,17 +633,23 @@ var i, // Proper syntax: http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier identifier = characterEncoding.replace( "w", "w#" ), - // Acceptable operators http://www.w3.org/TR/selectors/#attribute-selectors - attributes = "\\[" + whitespace + "*(" + characterEncoding + ")" + whitespace + - "*(?:([*^$|!~]?=)" + whitespace + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|)" + whitespace + "*\\]", + // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors + attributes = "\\[" + whitespace + "*(" + characterEncoding + ")(?:" + whitespace + + // Operator (capture 2) + "*([*^$|!~]?=)" + whitespace + + // "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]" + "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace + + "*\\]", - // Prefer arguments quoted, - // then not containing pseudos/brackets, - // then attribute selectors/non-parenthetical expressions, - // then anything else - // These preferences are here to reduce the number of selectors - // needing tokenize in the PSEUDO preFilter - pseudos = ":(" + characterEncoding + ")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|" + attributes.replace( 3, 8 ) + ")*)|.*)\\)|)", + pseudos = ":(" + characterEncoding + ")(?:\\((" + + // To reduce the number of selectors needing tokenize in the preFilter, prefer arguments: + // 1. quoted (capture 3; capture 4 or capture 5) + "('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|" + + // 2. simple (capture 6) + "((?:\\\\.|[^\\\\()[\\]]|" + attributes + ")*)|" + + // 3. anything else (capture 2) + ".*" + + ")\\)|)", // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), @@ -968,8 +657,7 @@ var i, rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ), - rsibling = new RegExp( whitespace + "*[+~]" ), - rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*)" + whitespace + "*\\]", "g" ), + rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*?)" + whitespace + "*\\]", "g" ), rpseudo = new RegExp( pseudos ), ridentifier = new RegExp( "^" + identifier + "$" ), @@ -990,14 +678,15 @@ var i, whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) }, + rinputs = /^(?:input|select|textarea|button)$/i, + rheader = /^h\d$/i, + rnative = /^[^{]+\{\s*\[native \w/, // Easily-parseable/retrievable ID or TAG or CLASS selectors rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, - rinputs = /^(?:input|select|textarea|button)$/i, - rheader = /^h\d$/i, - + rsibling = /[+~]/, rescape = /'|\\/g, // CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters @@ -1005,12 +694,12 @@ var i, funescape = function( _, escaped, escapedWhitespace ) { var high = "0x" + escaped - 0x10000; // NaN means non-codepoint - // Support: Firefox + // Support: Firefox<24 // Workaround erroneous numeric interpretation of +"0x" return high !== high || escapedWhitespace ? escaped : - // BMP codepoint high < 0 ? + // BMP codepoint String.fromCharCode( high + 0x10000 ) : // Supplemental Plane codepoint (surrogate pair) String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); @@ -1074,7 +763,7 @@ function Sizzle( selector, context, results, seed ) { if ( nodeType === 9 ) { elem = context.getElementById( m ); // Check parentNode to catch when Blackberry 4.6 returns - // nodes that are no longer in the document #6963 + // nodes that are no longer in the document (jQuery #6963) if ( elem && elem.parentNode ) { // Handle the case where IE, Opera, and Webkit return items // by name instead of ID @@ -1130,7 +819,7 @@ function Sizzle( selector, context, results, seed ) { while ( i-- ) { groups[i] = nid + toSelector( groups[i] ); } - newContext = rsibling.test( selector ) && context.parentNode || context; + newContext = rsibling.test( selector ) && testContext( context.parentNode ) || context; newSelector = groups.join(","); } @@ -1165,11 +854,11 @@ function createCache() { function cache( key, value ) { // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) - if ( keys.push( key += " " ) > Expr.cacheLength ) { + if ( keys.push( key + " " ) > Expr.cacheLength ) { // Only keep the most recent entries delete cache[ keys.shift() ]; } - return (cache[ key ] = value); + return (cache[ key + " " ] = value); } return cache; } @@ -1292,8 +981,21 @@ function createPositionalPseudo( fn ) { } /** - * Detect xml + * Checks a node for validity as a Sizzle context + * @param {Element|Object=} context + * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value + */ +function testContext( context ) { + return context && typeof context.getElementsByTagName !== strundefined && context; +} + +// Expose support vars for convenience +support = Sizzle.support = {}; + +/** + * Detects XML nodes * @param {Element|Object} elem An element or a document + * @returns {Boolean} True iff elem is a non-HTML XML node */ isXML = Sizzle.isXML = function( elem ) { // documentElement is verified for cases where it doesn't yet exist @@ -1302,16 +1004,14 @@ isXML = Sizzle.isXML = function( elem ) { return documentElement ? documentElement.nodeName !== "HTML" : false; }; -// Expose support vars for convenience -support = Sizzle.support = {}; - /** * Sets document-related variables once based on the current document * @param {Element|Object} [doc] An element or document object to use to set the document * @returns {Object} Returns the current document */ setDocument = Sizzle.setDocument = function( node ) { - var doc = node ? node.ownerDocument || node : preferredDoc, + var hasCompare, + doc = node ? node.ownerDocument || node : preferredDoc, parent = doc.defaultView; // If no document and documentElement is available, return @@ -1330,10 +1030,17 @@ setDocument = Sizzle.setDocument = function( node ) { // If iframe document is assigned to "document" variable and if iframe has been reloaded, // IE will throw "permission denied" error when accessing "document" variable, see jQuery #13936 // IE6-8 do not support the defaultView property so parent will be undefined - if ( parent && parent.attachEvent && parent !== parent.top ) { - parent.attachEvent( "onbeforeunload", function() { - setDocument(); - }); + if ( parent && parent !== parent.top ) { + // IE11 does not have attachEvent, so all must suffer + if ( parent.addEventListener ) { + parent.addEventListener( "unload", function() { + setDocument(); + }, false ); + } else if ( parent.attachEvent ) { + parent.attachEvent( "onunload", function() { + setDocument(); + }); + } } /* Attributes @@ -1356,7 +1063,7 @@ setDocument = Sizzle.setDocument = function( node ) { }); // Check if getElementsByClassName can be trusted - support.getElementsByClassName = assert(function( div ) { + support.getElementsByClassName = rnative.test( doc.getElementsByClassName ) && assert(function( div ) { div.innerHTML = "
"; // Support: Safari<4 @@ -1383,7 +1090,7 @@ setDocument = Sizzle.setDocument = function( node ) { var m = context.getElementById( id ); // Check parentNode to catch when Blackberry 4.6 returns // nodes that are no longer in the document #6963 - return m && m.parentNode ? [m] : []; + return m && m.parentNode ? [ m ] : []; } }; Expr.filter["ID"] = function( id ) { @@ -1463,7 +1170,15 @@ setDocument = Sizzle.setDocument = function( node ) { // setting a boolean content attribute, // since its presence should be enough // http://bugs.jquery.com/ticket/12359 - div.innerHTML = ""; + div.innerHTML = ""; + + // Support: IE8, Opera 11-12.16 + // Nothing should be selected when empty strings follow ^= or $= or *= + // The test attribute must be unknown in Opera but "safe" for WinRT + // http://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section + if ( div.querySelectorAll("[msallowclip^='']").length ) { + rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); + } // Support: IE8 // Boolean attributes and "value" are not treated correctly @@ -1480,18 +1195,16 @@ setDocument = Sizzle.setDocument = function( node ) { }); assert(function( div ) { - - // Support: Opera 10-12/IE8 - // ^= $= *= and empty values - // Should not select anything // Support: Windows 8 Native Apps - // The type attribute is restricted during .innerHTML assignment + // The type and name attributes are restricted during .innerHTML assignment var input = doc.createElement("input"); input.setAttribute( "type", "hidden" ); - div.appendChild( input ).setAttribute( "t", "" ); + div.appendChild( input ).setAttribute( "name", "D" ); - if ( div.querySelectorAll("[t^='']").length ) { - rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); + // Support: IE8 + // Enforce case-sensitivity of name attribute + if ( div.querySelectorAll("[name=d]").length ) { + rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" ); } // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) @@ -1506,7 +1219,8 @@ setDocument = Sizzle.setDocument = function( node ) { }); } - if ( (support.matchesSelector = rnative.test( (matches = docElem.webkitMatchesSelector || + if ( (support.matchesSelector = rnative.test( (matches = docElem.matches || + docElem.webkitMatchesSelector || docElem.mozMatchesSelector || docElem.oMatchesSelector || docElem.msMatchesSelector) )) ) { @@ -1528,11 +1242,12 @@ setDocument = Sizzle.setDocument = function( node ) { /* Contains ---------------------------------------------------------------------- */ + hasCompare = rnative.test( docElem.compareDocumentPosition ); // Element contains another // Purposefully does not implement inclusive descendent // As in, an element does not contain itself - contains = rnative.test( docElem.contains ) || docElem.compareDocumentPosition ? + contains = hasCompare || rnative.test( docElem.contains ) ? function( a, b ) { var adown = a.nodeType === 9 ? a.documentElement : a, bup = b && b.parentNode; @@ -1557,7 +1272,7 @@ setDocument = Sizzle.setDocument = function( node ) { ---------------------------------------------------------------------- */ // Document order sorting - sortOrder = docElem.compareDocumentPosition ? + sortOrder = hasCompare ? function( a, b ) { // Flag for duplicate removal @@ -1566,34 +1281,46 @@ setDocument = Sizzle.setDocument = function( node ) { return 0; } - var compare = b.compareDocumentPosition && a.compareDocumentPosition && a.compareDocumentPosition( b ); - + // Sort on method existence if only one input has compareDocumentPosition + var compare = !a.compareDocumentPosition - !b.compareDocumentPosition; if ( compare ) { - // Disconnected nodes - if ( compare & 1 || - (!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) { - - // Choose the first element that is related to our preferred document - if ( a === doc || contains(preferredDoc, a) ) { - return -1; - } - if ( b === doc || contains(preferredDoc, b) ) { - return 1; - } - - // Maintain original order - return sortInput ? - ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) : - 0; - } - - return compare & 4 ? -1 : 1; + return compare; } - // Not directly comparable, sort on existence of method - return a.compareDocumentPosition ? -1 : 1; + // Calculate position if both inputs belong to the same document + compare = ( a.ownerDocument || a ) === ( b.ownerDocument || b ) ? + a.compareDocumentPosition( b ) : + + // Otherwise we know they are disconnected + 1; + + // Disconnected nodes + if ( compare & 1 || + (!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) { + + // Choose the first element that is related to our preferred document + if ( a === doc || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) { + return -1; + } + if ( b === doc || b.ownerDocument === preferredDoc && contains(preferredDoc, b) ) { + return 1; + } + + // Maintain original order + return sortInput ? + ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) : + 0; + } + + return compare & 4 ? -1 : 1; } : function( a, b ) { + // Exit early if the nodes are identical + if ( a === b ) { + hasDuplicate = true; + return 0; + } + var cur, i = 0, aup = a.parentNode, @@ -1601,13 +1328,8 @@ setDocument = Sizzle.setDocument = function( node ) { ap = [ a ], bp = [ b ]; - // Exit early if the nodes are identical - if ( a === b ) { - hasDuplicate = true; - return 0; - // Parentless nodes are either documents or disconnected - } else if ( !aup || !bup ) { + if ( !aup || !bup ) { return a === doc ? -1 : b === doc ? 1 : aup ? -1 : @@ -1679,7 +1401,7 @@ Sizzle.matchesSelector = function( elem, expr ) { } catch(e) {} } - return Sizzle( expr, document, null, [elem] ).length > 0; + return Sizzle( expr, document, null, [ elem ] ).length > 0; }; Sizzle.contains = function( context, elem ) { @@ -1702,13 +1424,13 @@ Sizzle.attr = function( elem, name ) { fn( elem, name, !documentIsHTML ) : undefined; - return val === undefined ? + return val !== undefined ? + val : support.attributes || !documentIsHTML ? elem.getAttribute( name ) : (val = elem.getAttributeNode(name)) && val.specified ? val.value : - null : - val; + null; }; Sizzle.error = function( msg ) { @@ -1741,6 +1463,10 @@ Sizzle.uniqueSort = function( results ) { } } + // Clear input after sorting to release objects + // See https://github.com/jquery/sizzle/pull/225 + sortInput = null; + return results; }; @@ -1756,13 +1482,13 @@ getText = Sizzle.getText = function( elem ) { if ( !nodeType ) { // If no nodeType, this is expected to be an array - for ( ; (node = elem[i]); i++ ) { + while ( (node = elem[i++]) ) { // Do not traverse comment nodes ret += getText( node ); } } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { // Use textContent for elements - // innerText usage removed for consistency of new lines (see #11153) + // innerText usage removed for consistency of new lines (jQuery #11153) if ( typeof elem.textContent === "string" ) { return elem.textContent; } else { @@ -1804,7 +1530,7 @@ Expr = Sizzle.selectors = { match[1] = match[1].replace( runescape, funescape ); // Move the given value to match[3] whether quoted or unquoted - match[3] = ( match[4] || match[5] || "" ).replace( runescape, funescape ); + match[3] = ( match[3] || match[4] || match[5] || "" ).replace( runescape, funescape ); if ( match[2] === "~=" ) { match[3] = " " + match[3] + " "; @@ -1847,15 +1573,15 @@ Expr = Sizzle.selectors = { "PSEUDO": function( match ) { var excess, - unquoted = !match[5] && match[2]; + unquoted = !match[6] && match[2]; if ( matchExpr["CHILD"].test( match[0] ) ) { return null; } // Accept quoted arguments as-is - if ( match[3] && match[4] !== undefined ) { - match[2] = match[4]; + if ( match[3] ) { + match[2] = match[4] || match[5] || ""; // Strip excess characters from unquoted arguments } else if ( unquoted && rpseudo.test( unquoted ) && @@ -2159,12 +1885,11 @@ Expr = Sizzle.selectors = { // Contents "empty": function( elem ) { // http://www.w3.org/TR/selectors/#empty-pseudo - // :empty is only affected by element nodes and content nodes(including text(3), cdata(4)), - // not comment, processing instructions, or others - // Thanks to Diego Perini for the nodeName shortcut - // Greater than "@" means alpha characters (specifically not starting with "#" or "?") + // :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5), + // but not by others (comment: 8; processing instruction: 7; etc.) + // nodeType < 6 works because attributes (2) do not appear as children for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { - if ( elem.nodeName > "@" || elem.nodeType === 3 || elem.nodeType === 4 ) { + if ( elem.nodeType < 6 ) { return false; } } @@ -2191,11 +1916,12 @@ Expr = Sizzle.selectors = { "text": function( elem ) { var attr; - // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc) - // use getAttribute instead to test this case return elem.nodeName.toLowerCase() === "input" && elem.type === "text" && - ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === elem.type ); + + // Support: IE<8 + // New HTML5 attribute values (e.g., "search") appear with elem.type === "text" + ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === "text" ); }, // Position-in-collection @@ -2260,7 +1986,7 @@ function setFilters() {} setFilters.prototype = Expr.filters = Expr.pseudos; Expr.setFilters = new setFilters(); -function tokenize( selector, parseOnly ) { +tokenize = Sizzle.tokenize = function( selector, parseOnly ) { var matched, match, tokens, type, soFar, groups, preFilters, cached = tokenCache[ selector + " " ]; @@ -2281,7 +2007,7 @@ function tokenize( selector, parseOnly ) { // Don't consume trailing commas as valid soFar = soFar.slice( match[0].length ) || soFar; } - groups.push( tokens = [] ); + groups.push( (tokens = []) ); } matched = false; @@ -2325,7 +2051,7 @@ function tokenize( selector, parseOnly ) { Sizzle.error( selector ) : // Cache the tokens tokenCache( selector, groups ).slice( 0 ); -} +}; function toSelector( tokens ) { var i = 0, @@ -2354,8 +2080,8 @@ function addCombinator( matcher, combinator, base ) { // Check against all ancestor/preceding elements function( elem, context, xml ) { - var data, cache, outerCache, - dirkey = dirruns + " " + doneName; + var oldCache, outerCache, + newCache = [ dirruns, doneName ]; // We can't set arbitrary data on XML nodes, so they don't benefit from dir caching if ( xml ) { @@ -2370,14 +2096,17 @@ function addCombinator( matcher, combinator, base ) { while ( (elem = elem[ dir ]) ) { if ( elem.nodeType === 1 || checkNonElements ) { outerCache = elem[ expando ] || (elem[ expando ] = {}); - if ( (cache = outerCache[ dir ]) && cache[0] === dirkey ) { - if ( (data = cache[1]) === true || data === cachedruns ) { - return data === true; - } + if ( (oldCache = outerCache[ dir ]) && + oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) { + + // Assign to newCache so results back-propagate to previous elements + return (newCache[ 2 ] = oldCache[ 2 ]); } else { - cache = outerCache[ dir ] = [ dirkey ]; - cache[1] = matcher( elem, context, xml ) || cachedruns; - if ( cache[1] === true ) { + // Reuse newcache so results back-propagate to previous elements + outerCache[ dir ] = newCache; + + // A match means we're done; a fail means we have to keep checking + if ( (newCache[ 2 ] = matcher( elem, context, xml )) ) { return true; } } @@ -2401,6 +2130,15 @@ function elementMatcher( matchers ) { matchers[0]; } +function multipleContexts( selector, contexts, results ) { + var i = 0, + len = contexts.length; + for ( ; i < len; i++ ) { + Sizzle( selector, contexts[i], results ); + } + return results; +} + function condense( unmatched, map, filter, context, xml ) { var elem, newUnmatched = [], @@ -2571,31 +2309,30 @@ function matcherFromTokens( tokens ) { } function matcherFromGroupMatchers( elementMatchers, setMatchers ) { - // A counter to specify which element is currently being matched - var matcherCachedRuns = 0, - bySet = setMatchers.length > 0, + var bySet = setMatchers.length > 0, byElement = elementMatchers.length > 0, - superMatcher = function( seed, context, xml, results, expandContext ) { + superMatcher = function( seed, context, xml, results, outermost ) { var elem, j, matcher, - setMatched = [], matchedCount = 0, i = "0", unmatched = seed && [], - outermost = expandContext != null, + setMatched = [], contextBackup = outermostContext, - // We must always have either seed elements or context - elems = seed || byElement && Expr.find["TAG"]( "*", expandContext && context.parentNode || context ), + // We must always have either seed elements or outermost context + elems = seed || byElement && Expr.find["TAG"]( "*", outermost ), // Use integer dirruns iff this is the outermost matcher - dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1); + dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1), + len = elems.length; if ( outermost ) { outermostContext = context !== document && context; - cachedruns = matcherCachedRuns; } // Add elements passing elementMatchers directly to results // Keep `i` a string if there are no elements so `matchedCount` will be "00" below - for ( ; (elem = elems[i]) != null; i++ ) { + // Support: IE<9, Safari + // Tolerate NodeList properties (IE: "length"; Safari: ) matching elements by id + for ( ; i !== len && (elem = elems[i]) != null; i++ ) { if ( byElement && elem ) { j = 0; while ( (matcher = elementMatchers[j++]) ) { @@ -2606,7 +2343,6 @@ function matcherFromGroupMatchers( elementMatchers, setMatchers ) { } if ( outermost ) { dirruns = dirrunsUnique; - cachedruns = ++matcherCachedRuns; } } @@ -2671,7 +2407,7 @@ function matcherFromGroupMatchers( elementMatchers, setMatchers ) { superMatcher; } -compile = Sizzle.compile = function( selector, group /* Internal Use Only */ ) { +compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) { var i, setMatchers = [], elementMatchers = [], @@ -2679,12 +2415,12 @@ compile = Sizzle.compile = function( selector, group /* Internal Use Only */ ) { if ( !cached ) { // Generate a function of recursive functions that can be used to check each element - if ( !group ) { - group = tokenize( selector ); + if ( !match ) { + match = tokenize( selector ); } - i = group.length; + i = match.length; while ( i-- ) { - cached = matcherFromTokens( group[i] ); + cached = matcherFromTokens( match[i] ); if ( cached[ expando ] ) { setMatchers.push( cached ); } else { @@ -2694,82 +2430,91 @@ compile = Sizzle.compile = function( selector, group /* Internal Use Only */ ) { // Cache the compiled function cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) ); + + // Save selector and tokenization + cached.selector = selector; } return cached; }; -function multipleContexts( selector, contexts, results ) { - var i = 0, - len = contexts.length; - for ( ; i < len; i++ ) { - Sizzle( selector, contexts[i], results ); - } - return results; -} - -function select( selector, context, results, seed ) { +/** + * A low-level selection function that works with Sizzle's compiled + * selector functions + * @param {String|Function} selector A selector or a pre-compiled + * selector function built with Sizzle.compile + * @param {Element} context + * @param {Array} [results] + * @param {Array} [seed] A set of elements to match against + */ +select = Sizzle.select = function( selector, context, results, seed ) { var i, tokens, token, type, find, - match = tokenize( selector ); + compiled = typeof selector === "function" && selector, + match = !seed && tokenize( (selector = compiled.selector || selector) ); - if ( !seed ) { - // Try to minimize operations if there is only one group - if ( match.length === 1 ) { + results = results || []; - // Take a shortcut and set the context if the root selector is an ID - tokens = match[0] = match[0].slice( 0 ); - if ( tokens.length > 2 && (token = tokens[0]).type === "ID" && - support.getById && context.nodeType === 9 && documentIsHTML && - Expr.relative[ tokens[1].type ] ) { + // Try to minimize operations if there is no seed and only one group + if ( match.length === 1 ) { - context = ( Expr.find["ID"]( token.matches[0].replace(runescape, funescape), context ) || [] )[0]; - if ( !context ) { - return results; - } - selector = selector.slice( tokens.shift().value.length ); + // Take a shortcut and set the context if the root selector is an ID + tokens = match[0] = match[0].slice( 0 ); + if ( tokens.length > 2 && (token = tokens[0]).type === "ID" && + support.getById && 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; + + // Precompiled matchers will still verify ancestry, so step up a level + } else if ( compiled ) { + context = context.parentNode; } - // Fetch a seed set for right-to-left matching - i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length; - while ( i-- ) { - token = tokens[i]; + selector = selector.slice( tokens.shift().value.length ); + } - // Abort if we hit a combinator - if ( Expr.relative[ (type = token.type) ] ) { - break; - } - if ( (find = Expr.find[ type ]) ) { - // Search, expanding context for leading sibling combinators - if ( (seed = find( - token.matches[0].replace( runescape, funescape ), - rsibling.test( tokens[0].type ) && context.parentNode || context - )) ) { + // Fetch a seed set for right-to-left matching + i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length; + while ( i-- ) { + token = tokens[i]; - // If seed is empty or no tokens remain, we can return early - tokens.splice( i, 1 ); - selector = seed.length && toSelector( tokens ); - if ( !selector ) { - push.apply( results, seed ); - return results; - } + // Abort if we hit a combinator + if ( Expr.relative[ (type = token.type) ] ) { + break; + } + if ( (find = Expr.find[ type ]) ) { + // Search, expanding context for leading sibling combinators + if ( (seed = find( + token.matches[0].replace( runescape, funescape ), + rsibling.test( tokens[0].type ) && testContext( context.parentNode ) || context + )) ) { - break; + // If seed is empty or no tokens remain, we can return early + tokens.splice( i, 1 ); + selector = seed.length && toSelector( tokens ); + if ( !selector ) { + push.apply( results, seed ); + return results; } + + break; } } } } - // Compile and execute a filtering function + // Compile and execute a filtering function if one is not provided // Provide `match` to avoid retokenization if we modified the selector above - compile( selector, match )( + ( compiled || compile( selector, match ) )( seed, context, !documentIsHTML, results, - rsibling.test( selector ) + rsibling.test( selector ) && testContext( context.parentNode ) || context ); return results; -} +}; // One-time assignments @@ -2778,7 +2523,7 @@ support.sortStable = expando.split("").sort( sortOrder ).join("") === expando; // Support: Chrome<14 // Always assume duplicates if they aren't passed to the comparison function -support.detectDuplicates = hasDuplicate; +support.detectDuplicates = !!hasDuplicate; // Initialize against the default document setDocument(); @@ -2826,13 +2571,20 @@ if ( !assert(function( div ) { addHandle( booleans, function( elem, name, isXML ) { var val; if ( !isXML ) { - return (val = elem.getAttributeNode( name )) && val.specified ? - val.value : - elem[ name ] === true ? name.toLowerCase() : null; + return elem[ name ] === true ? name.toLowerCase() : + (val = elem.getAttributeNode( name )) && val.specified ? + val.value : + null; } }); } +return Sizzle; + +})( window ); + + + jQuery.find = Sizzle; jQuery.expr = Sizzle.selectors; jQuery.expr[":"] = jQuery.expr.pseudos; @@ -2842,14 +2594,422 @@ jQuery.isXMLDoc = Sizzle.isXML; jQuery.contains = Sizzle.contains; -})( window ); + +var rneedsContext = jQuery.expr.match.needsContext; + +var rsingleTag = (/^<(\w+)\s*\/?>(?:<\/\1>|)$/); + + + +var risSimple = /^.[^:#\[\.,]*$/; + +// Implement the identical functionality for filter and not +function winnow( elements, qualifier, not ) { + if ( jQuery.isFunction( qualifier ) ) { + return jQuery.grep( elements, function( elem, i ) { + /* jshint -W018 */ + return !!qualifier.call( elem, i, elem ) !== not; + }); + + } + + if ( qualifier.nodeType ) { + return jQuery.grep( elements, function( elem ) { + return ( elem === qualifier ) !== not; + }); + + } + + if ( typeof qualifier === "string" ) { + if ( risSimple.test( qualifier ) ) { + return jQuery.filter( qualifier, elements, not ); + } + + qualifier = jQuery.filter( qualifier, elements ); + } + + return jQuery.grep( elements, function( elem ) { + return ( indexOf.call( qualifier, elem ) >= 0 ) !== not; + }); +} + +jQuery.filter = function( expr, elems, not ) { + var elem = elems[ 0 ]; + + if ( not ) { + expr = ":not(" + expr + ")"; + } + + return elems.length === 1 && elem.nodeType === 1 ? + jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [] : + jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) { + return elem.nodeType === 1; + })); +}; + +jQuery.fn.extend({ + find: function( selector ) { + var i, + len = this.length, + ret = [], + 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; + } + } + }) ); + } + + for ( i = 0; i < len; i++ ) { + jQuery.find( selector, self[ i ], ret ); + } + + // Needed because $( selector, context ) becomes $( context ).find( selector ) + ret = this.pushStack( len > 1 ? jQuery.unique( ret ) : ret ); + ret.selector = this.selector ? this.selector + " " + selector : selector; + return 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, + + // If this is a positional/relative selector, check membership in the returned set + // so $("p:first").is("p:last") won't return true for a doc with two "p". + typeof selector === "string" && rneedsContext.test( selector ) ? + jQuery( selector ) : + selector || [], + false + ).length; + } +}); + + +// Initialize a jQuery object + + +// A central reference to the root jQuery(document) +var rootjQuery, + + // A simple way to check for HTML strings + // Prioritize #id over to avoid XSS via location.hash (#9521) + // Strict HTML recognition (#11290: must start with <) + rquickExpr = /^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/, + + init = jQuery.fn.init = function( selector, context ) { + var match, elem; + + // HANDLE: $(""), $(null), $(undefined), $(false) + if ( !selector ) { + return this; + } + + // Handle HTML strings + if ( typeof selector === "string" ) { + if ( selector[0] === "<" && selector[ selector.length - 1 ] === ">" && selector.length >= 3 ) { + // Assume that strings that start and end with <> are HTML and skip the regex check + match = [ null, selector, null ]; + + } else { + match = rquickExpr.exec( selector ); + } + + // Match html or make sure no context is specified for #id + if ( match && (match[1] || !context) ) { + + // HANDLE: $(html) -> $(array) + if ( match[1] ) { + context = context instanceof jQuery ? context[0] : context; + + // scripts is true for back-compat + // Intentionally let the error be thrown if parseHTML is not present + jQuery.merge( this, jQuery.parseHTML( + match[1], + context && context.nodeType ? context.ownerDocument || context : document, + true + ) ); + + // HANDLE: $(html, props) + if ( rsingleTag.test( match[1] ) && jQuery.isPlainObject( context ) ) { + for ( match in context ) { + // Properties of context are called as methods if possible + if ( jQuery.isFunction( this[ match ] ) ) { + this[ match ]( context[ match ] ); + + // ...and otherwise set as attributes + } else { + this.attr( match, context[ match ] ); + } + } + } + + return this; + + // HANDLE: $(#id) + } else { + elem = document.getElementById( match[2] ); + + // Check parentNode to catch when Blackberry 4.6 returns + // nodes that are no longer in the document #6963 + if ( elem && elem.parentNode ) { + // Inject the element directly into the jQuery object + this.length = 1; + this[0] = elem; + } + + this.context = document; + this.selector = selector; + return this; + } + + // HANDLE: $(expr, $(...)) + } else if ( !context || context.jquery ) { + return ( context || rootjQuery ).find( selector ); + + // HANDLE: $(expr, context) + // (which is just equivalent to: $(context).find(expr) + } else { + return this.constructor( context ).find( selector ); + } + + // HANDLE: $(DOMElement) + } else if ( selector.nodeType ) { + this.context = this[0] = selector; + this.length = 1; + return this; + + // HANDLE: $(function) + // Shortcut for document ready + } else if ( jQuery.isFunction( selector ) ) { + return typeof rootjQuery.ready !== "undefined" ? + rootjQuery.ready( selector ) : + // Execute immediately if ready is not present + selector( jQuery ); + } + + if ( selector.selector !== undefined ) { + this.selector = selector.selector; + this.context = selector.context; + } + + return jQuery.makeArray( selector, this ); + }; + +// Give the init function the jQuery prototype for later instantiation +init.prototype = jQuery.fn; + +// Initialize central reference +rootjQuery = jQuery( document ); + + +var rparentsprev = /^(?:parents|prev(?:Until|All))/, + // methods guaranteed to produce a unique set when starting from a unique set + guaranteedUnique = { + children: true, + contents: true, + next: true, + prev: true + }; + +jQuery.extend({ + 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; + }, + + sibling: function( n, elem ) { + var matched = []; + + for ( ; n; n = n.nextSibling ) { + if ( n.nodeType === 1 && n !== elem ) { + matched.push( n ); + } + } + + return matched; + } +}); + +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 = [], + pos = rneedsContext.test( selectors ) || typeof selectors !== "string" ? + jQuery( selectors, context || this.context ) : + 0; + + for ( ; i < l; i++ ) { + for ( cur = this[i]; cur && cur !== context; cur = cur.parentNode ) { + // Always skip document fragments + if ( cur.nodeType < 11 && (pos ? + pos.index(cur) > -1 : + + // Don't pass non-elements to Sizzle + cur.nodeType === 1 && + jQuery.find.matchesSelector(cur, selectors)) ) { + + matched.push( cur ); + break; + } + } + } + + return this.pushStack( matched.length > 1 ? jQuery.unique( matched ) : matched ); + }, + + // Determine the position of an element within + // the matched set of elements + index: function( elem ) { + + // No argument, return index in parent + if ( !elem ) { + return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1; + } + + // index in selector + if ( typeof elem === "string" ) { + return indexOf.call( jQuery( elem ), this[ 0 ] ); + } + + // Locate the position of the desired element + return indexOf.call( this, + + // If it receives a jQuery object, the first element is used + elem.jquery ? elem[ 0 ] : elem + ); + }, + + add: function( selector, context ) { + return this.pushStack( + jQuery.unique( + 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 jQuery.dir( elem, "parentNode" ); + }, + parentsUntil: function( elem, i, until ) { + return jQuery.dir( elem, "parentNode", until ); + }, + next: function( elem ) { + return sibling( elem, "nextSibling" ); + }, + prev: function( elem ) { + return sibling( elem, "previousSibling" ); + }, + nextAll: function( elem ) { + return jQuery.dir( elem, "nextSibling" ); + }, + prevAll: function( elem ) { + return jQuery.dir( elem, "previousSibling" ); + }, + nextUntil: function( elem, i, until ) { + return jQuery.dir( elem, "nextSibling", until ); + }, + prevUntil: function( elem, i, until ) { + return jQuery.dir( elem, "previousSibling", until ); + }, + siblings: function( elem ) { + return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem ); + }, + children: function( elem ) { + return jQuery.sibling( elem.firstChild ); + }, + contents: function( elem ) { + return elem.contentDocument || 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 ) { + // Remove duplicates + if ( !guaranteedUnique[ name ] ) { + jQuery.unique( matched ); + } + + // Reverse order for parents* and prev-derivatives + if ( rparentsprev.test( name ) ) { + matched.reverse(); + } + } + + return this.pushStack( matched ); + }; +}); +var rnotwhite = (/\S+/g); + + + // String to Object options format cache var optionsCache = {}; // Convert String-formatted options into Object-formatted ones and store in cache function createOptions( options ) { var object = optionsCache[ options ] = {}; - jQuery.each( options.match( core_rnotwhite ) || [], function( _, flag ) { + jQuery.each( options.match( rnotwhite ) || [], function( _, flag ) { object[ flag ] = true; }); return object; @@ -2966,7 +3126,7 @@ jQuery.Callbacks = function( options ) { if ( list ) { jQuery.each( arguments, function( _, arg ) { var index; - while( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { + while ( ( index = jQuery.inArray( arg, list, index ) ) > -1 ) { list.splice( index, 1 ); // Handle firing indexes if ( firing ) { @@ -3040,6 +3200,8 @@ jQuery.Callbacks = function( options ) { return self; }; + + jQuery.extend({ Deferred: function( func ) { @@ -3062,8 +3224,7 @@ jQuery.extend({ var fns = arguments; return jQuery.Deferred(function( newDefer ) { jQuery.each( tuples, function( i, tuple ) { - var action = tuple[ 0 ], - fn = jQuery.isFunction( fns[ i ] ) && fns[ i ]; + var fn = jQuery.isFunction( fns[ i ] ) && fns[ i ]; // deferred[ done | fail | progress ] for forwarding actions to newDefer deferred[ tuple[1] ](function() { var returned = fn && fn.apply( this, arguments ); @@ -3073,7 +3234,7 @@ jQuery.extend({ .fail( newDefer.reject ) .progress( newDefer.notify ); } else { - newDefer[ action + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments ); + newDefer[ tuple[ 0 ] + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments ); } }); }); @@ -3132,7 +3293,7 @@ jQuery.extend({ // Deferred helper when: function( subordinate /* , ..., subordinateN */ ) { var i = 0, - resolveValues = core_slice.call( arguments ), + resolveValues = slice.call( arguments ), length = resolveValues.length, // the count of uncompleted subordinates @@ -3145,8 +3306,8 @@ jQuery.extend({ updateFunc = function( i, contexts, values ) { return function( value ) { contexts[ i ] = this; - values[ i ] = arguments.length > 1 ? core_slice.call( arguments ) : value; - if( values === progressValues ) { + values[ i ] = arguments.length > 1 ? slice.call( arguments ) : value; + if ( values === progressValues ) { deferred.notifyWith( contexts, values ); } else if ( !( --remaining ) ) { deferred.resolveWith( contexts, values ); @@ -3181,133 +3342,169 @@ jQuery.extend({ return deferred.promise(); } }); -jQuery.support = (function( support ) { - var input = document.createElement("input"), - fragment = document.createDocumentFragment(), - div = document.createElement("div"), - select = document.createElement("select"), - opt = select.appendChild( document.createElement("option") ); - // Finish early in limited environments - if ( !input.type ) { - return support; - } - input.type = "checkbox"; +// The deferred used on DOM ready +var readyList; - // Support: Safari 5.1, iOS 5.1, Android 4.x, Android 2.3 - // Check the default checkbox/radio value ("" on old WebKit; "on" elsewhere) - support.checkOn = input.value !== ""; +jQuery.fn.ready = function( fn ) { + // Add the callback + jQuery.ready.promise().done( fn ); - // Must access the parent to make an option select properly - // Support: IE9, IE10 - support.optSelected = opt.selected; + return this; +}; - // Will be defined later - support.reliableMarginRight = true; - support.boxSizingReliable = true; - support.pixelPosition = false; +jQuery.extend({ + // Is the DOM ready to be used? Set to true once it occurs. + isReady: false, - // Make sure checked status is properly cloned - // Support: IE9, IE10 - input.checked = true; - support.noCloneChecked = input.cloneNode( true ).checked; + // A counter to track how many items to wait for before + // the ready event fires. See #6781 + readyWait: 1, - // Make sure that the options inside disabled selects aren't marked as disabled - // (WebKit marks them as disabled) - select.disabled = true; - support.optDisabled = !opt.disabled; + // Hold (or release) the ready event + holdReady: function( hold ) { + if ( hold ) { + jQuery.readyWait++; + } else { + jQuery.ready( true ); + } + }, - // Check if an input maintains its value after becoming a radio - // Support: IE9, IE10 - input = document.createElement("input"); - input.value = "t"; - input.type = "radio"; - support.radioValue = input.value === "t"; + // Handle when the DOM is ready + ready: function( wait ) { - // #11217 - WebKit loses check when the name is after the checked attribute - input.setAttribute( "checked", "t" ); - input.setAttribute( "name", "t" ); - - fragment.appendChild( input ); - - // Support: Safari 5.1, Android 4.x, Android 2.3 - // old WebKit doesn't clone checked state correctly in fragments - support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked; - - // Support: Firefox, Chrome, Safari - // Beware of CSP restrictions (https://developer.mozilla.org/en/Security/CSP) - support.focusinBubbles = "onfocusin" in window; - - div.style.backgroundClip = "content-box"; - div.cloneNode( true ).style.backgroundClip = ""; - support.clearCloneStyle = div.style.backgroundClip === "content-box"; - - // Run tests that need a body at doc ready - jQuery(function() { - var container, marginDiv, - // Support: Firefox, Android 2.3 (Prefixed box-sizing versions). - divReset = "padding:0;margin:0;border:0;display:block;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box", - body = document.getElementsByTagName("body")[ 0 ]; - - if ( !body ) { - // Return for frameset docs that don't have a body + // Abort if there are pending holds or we're already ready + if ( wait === true ? --jQuery.readyWait : jQuery.isReady ) { return; } - container = document.createElement("div"); - container.style.cssText = "border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px"; + // Remember that the DOM is ready + jQuery.isReady = true; - // Check box-sizing and margin behavior. - body.appendChild( container ).appendChild( div ); - div.innerHTML = ""; - // Support: Firefox, Android 2.3 (Prefixed box-sizing versions). - div.style.cssText = "-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%"; - - // Workaround failing boxSizing test due to offsetWidth returning wrong value - // with some non-1 values of body zoom, ticket #13543 - jQuery.swap( body, body.style.zoom != null ? { zoom: 1 } : {}, function() { - support.boxSizing = div.offsetWidth === 4; - }); - - // Use window.getComputedStyle because jsdom on node.js will break without it. - if ( window.getComputedStyle ) { - support.pixelPosition = ( window.getComputedStyle( div, null ) || {} ).top !== "1%"; - support.boxSizingReliable = ( window.getComputedStyle( div, null ) || { width: "4px" } ).width === "4px"; - - // Support: Android 2.3 - // Check if div with explicit width and no margin-right incorrectly - // gets computed margin-right based on width of container. (#3333) - // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right - marginDiv = div.appendChild( document.createElement("div") ); - marginDiv.style.cssText = div.style.cssText = divReset; - marginDiv.style.marginRight = marginDiv.style.width = "0"; - div.style.width = "1px"; - - support.reliableMarginRight = - !parseFloat( ( window.getComputedStyle( marginDiv, null ) || {} ).marginRight ); + // If a normal DOM Ready event fired, decrement, and wait if need be + if ( wait !== true && --jQuery.readyWait > 0 ) { + return; } - body.removeChild( container ); - }); + // If there are functions bound, to execute + readyList.resolveWith( document, [ jQuery ] ); - return support; -})( {} ); + // Trigger any bound ready events + if ( jQuery.fn.triggerHandler ) { + jQuery( document ).triggerHandler( "ready" ); + jQuery( document ).off( "ready" ); + } + } +}); -/* - Implementation Summary +/** + * The ready event handler and self cleanup method + */ +function completed() { + document.removeEventListener( "DOMContentLoaded", completed, false ); + window.removeEventListener( "load", completed, false ); + jQuery.ready(); +} + +jQuery.ready.promise = function( obj ) { + if ( !readyList ) { + + readyList = jQuery.Deferred(); + + // Catch cases where $(document).ready() is called after the browser event has already occurred. + // we once tried to use readyState "interactive" here, but it caused issues like the one + // discovered by ChrisS here: http://bugs.jquery.com/ticket/12282#comment:15 + if ( document.readyState === "complete" ) { + // Handle it asynchronously to allow scripts the opportunity to delay ready + setTimeout( jQuery.ready ); + + } else { + + // Use the handy event callback + document.addEventListener( "DOMContentLoaded", completed, false ); + + // A fallback to window.onload, that will always work + window.addEventListener( "load", completed, false ); + } + } + return readyList.promise( obj ); +}; + +// Kick off the DOM ready check even if the user does not +jQuery.ready.promise(); + + + + +// Multifunctional method to get and set values of a collection +// The value/s can optionally be executed if it's a function +var access = jQuery.access = function( elems, fn, key, value, chainable, emptyGet, raw ) { + var i = 0, + len = elems.length, + bulk = key == null; + + // Sets many values + if ( jQuery.type( key ) === "object" ) { + chainable = true; + for ( i in key ) { + jQuery.access( elems, fn, i, key[i], true, emptyGet, raw ); + } + + // Sets one value + } else if ( value !== undefined ) { + chainable = true; + + if ( !jQuery.isFunction( value ) ) { + raw = true; + } + + if ( bulk ) { + // Bulk operations run against the entire set + if ( raw ) { + fn.call( elems, value ); + fn = null; + + // ...except when executing function values + } 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 ) ) ); + } + } + } + + return chainable ? + elems : + + // Gets + bulk ? + fn.call( elems ) : + len ? fn( elems[0], key ) : emptyGet; +}; + + +/** + * Determines whether an object can have data + */ +jQuery.acceptData = function( owner ) { + // Accepts only: + // - Node + // - Node.ELEMENT_NODE + // - Node.DOCUMENT_NODE + // - Object + // - Any + /* jshint -W018 */ + return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType ); +}; - 1. Enforce API surface and semantic compatibility with 1.9.x branch - 2. Improve the module's maintainability by reducing the storage - paths to a single mechanism. - 3. Use the same single mechanism to support "private" and "user" data. - 4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData) - 5. Avoid exposing implementation details on user objects (eg. expando properties) - 6. Provide a clear path for implementation upgrade to WeakMap in 2014 -*/ -var data_user, data_priv, - rbrace = /(?:\{[\s\S]*\}|\[[\s\S]*\])$/, - rmultiDash = /([A-Z])/g; function Data() { // Support: Android < 4, @@ -3323,17 +3520,7 @@ function Data() { } Data.uid = 1; - -Data.accepts = function( owner ) { - // Accepts only: - // - Node - // - Node.ELEMENT_NODE - // - Node.DOCUMENT_NODE - // - Object - // - Any - return owner.nodeType ? - owner.nodeType === 1 || owner.nodeType === 9 : true; -}; +Data.accepts = jQuery.acceptData; Data.prototype = { key: function( owner ) { @@ -3470,7 +3657,7 @@ Data.prototype = { // Otherwise, create an array by matching non-whitespace name = camel; name = name in cache ? - [ name ] : ( name.match( core_rnotwhite ) || [] ); + [ name ] : ( name.match( rnotwhite ) || [] ); } } @@ -3491,15 +3678,56 @@ Data.prototype = { } } }; +var data_priv = new Data(); -// These may be used throughout the jQuery core codebase -data_user = new Data(); -data_priv = new Data(); +var data_user = new Data(); + +/* + Implementation Summary + + 1. Enforce API surface and semantic compatibility with 1.9.x branch + 2. Improve the module's maintainability by reducing the storage + paths to a single mechanism. + 3. Use the same single mechanism to support "private" and "user" data. + 4. _Never_ expose "private" data to user code (TODO: Drop _data, _removeData) + 5. Avoid exposing implementation details on user objects (eg. expando properties) + 6. Provide a clear path for implementation upgrade to WeakMap in 2014 +*/ +var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/, + rmultiDash = /([A-Z])/g; + +function dataAttr( elem, key, data ) { + var name; + + // If nothing was found internally, try to fetch any + // data from the HTML5 data-* attribute + if ( data === undefined && elem.nodeType === 1 ) { + name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase(); + data = elem.getAttribute( name ); + + if ( typeof data === "string" ) { + try { + data = data === "true" ? true : + data === "false" ? false : + data === "null" ? null : + // Only convert to a number if it doesn't change the string + +data + "" === data ? +data : + rbrace.test( data ) ? jQuery.parseJSON( data ) : + data; + } catch( e ) {} + + // Make sure we set the data so it isn't changed later + data_user.set( elem, key, data ); + } else { + data = undefined; + } + } + return data; +} + jQuery.extend({ - acceptData: Data.accepts, - hasData: function( elem ) { return data_user.hasData( elem ) || data_priv.hasData( elem ); }, @@ -3525,10 +3753,9 @@ jQuery.extend({ jQuery.fn.extend({ data: function( key, value ) { - var attrs, name, + var i, name, data, elem = this[ 0 ], - i = 0, - data = null; + attrs = elem && elem.attributes; // Gets all values if ( key === undefined ) { @@ -3536,13 +3763,17 @@ jQuery.fn.extend({ data = data_user.get( elem ); if ( elem.nodeType === 1 && !data_priv.get( elem, "hasDataAttrs" ) ) { - attrs = elem.attributes; - for ( ; i < attrs.length; i++ ) { - name = attrs[ i ].name; + i = attrs.length; + while ( i-- ) { - if ( name.indexOf( "data-" ) === 0 ) { - name = jQuery.camelCase( name.slice(5) ); - dataAttr( elem, name, data[ name ] ); + // Support: IE11+ + // The attrs elements can be null (#14894) + if ( attrs[ i ] ) { + name = attrs[ i ].name; + if ( name.indexOf( "data-" ) === 0 ) { + name = jQuery.camelCase( name.slice(5) ); + dataAttr( elem, name, data[ name ] ); + } } } data_priv.set( elem, "hasDataAttrs", true ); @@ -3559,7 +3790,7 @@ jQuery.fn.extend({ }); } - return jQuery.access( this, function( value ) { + return access( this, function( value ) { var data, camelKey = jQuery.camelCase( key ); @@ -3622,34 +3853,7 @@ jQuery.fn.extend({ } }); -function dataAttr( elem, key, data ) { - var name; - // If nothing was found internally, try to fetch any - // data from the HTML5 data-* attribute - if ( data === undefined && elem.nodeType === 1 ) { - name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase(); - data = elem.getAttribute( name ); - - if ( typeof data === "string" ) { - try { - data = data === "true" ? true : - data === "false" ? false : - data === "null" ? null : - // Only convert to a number if it doesn't change the string - +data + "" === data ? +data : - rbrace.test( data ) ? JSON.parse( data ) : - data; - } catch( e ) {} - - // Make sure we set the data so it isn't changed later - data_user.set( elem, key, data ); - } else { - data = undefined; - } - } - return data; -} jQuery.extend({ queue: function( elem, type, data ) { var queue; @@ -3748,19 +3952,6 @@ jQuery.fn.extend({ jQuery.dequeue( this, type ); }); }, - // Based off of the plugin by Clint Helfers, with permission. - // http://blindsignals.com/index.php/2009/07/jquery-delay/ - delay: function( time, type ) { - time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time; - type = type || "fx"; - - return this.queue( type, function( next, hooks ) { - var timeout = setTimeout( next, time ); - hooks.stop = function() { - clearTimeout( timeout ); - }; - }); - }, clearQueue: function( type ) { return this.queue( type || "fx", [] ); }, @@ -3784,7 +3975,7 @@ jQuery.fn.extend({ } type = type || "fx"; - while( i-- ) { + while ( i-- ) { tmp = data_priv.get( elements[ i ], type + "queueHooks" ); if ( tmp && tmp.empty ) { count++; @@ -3795,510 +3986,54 @@ jQuery.fn.extend({ return defer.promise( obj ); } }); -var nodeHook, boolHook, - rclass = /[\t\r\n\f]/g, - rreturn = /\r/g, - rfocusable = /^(?:input|select|textarea|button)$/i; +var pnum = (/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/).source; -jQuery.fn.extend({ - attr: function( name, value ) { - return jQuery.access( this, jQuery.attr, name, value, arguments.length > 1 ); - }, +var cssExpand = [ "Top", "Right", "Bottom", "Left" ]; - removeAttr: function( name ) { - return this.each(function() { - jQuery.removeAttr( this, name ); - }); - }, - - prop: function( name, value ) { - return jQuery.access( this, jQuery.prop, name, value, arguments.length > 1 ); - }, - - removeProp: function( name ) { - return this.each(function() { - delete this[ jQuery.propFix[ name ] || name ]; - }); - }, - - addClass: function( value ) { - var classes, elem, cur, clazz, j, - i = 0, - len = this.length, - proceed = typeof value === "string" && value; - - if ( jQuery.isFunction( value ) ) { - return this.each(function( j ) { - jQuery( this ).addClass( value.call( this, j, this.className ) ); - }); - } - - if ( proceed ) { - // The disjunction here is for better compressibility (see removeClass) - classes = ( value || "" ).match( core_rnotwhite ) || []; - - for ( ; i < len; i++ ) { - elem = this[ i ]; - cur = elem.nodeType === 1 && ( elem.className ? - ( " " + elem.className + " " ).replace( rclass, " " ) : - " " - ); - - if ( cur ) { - j = 0; - while ( (clazz = classes[j++]) ) { - if ( cur.indexOf( " " + clazz + " " ) < 0 ) { - cur += clazz + " "; - } - } - elem.className = jQuery.trim( cur ); - - } - } - } - - return this; - }, - - removeClass: function( value ) { - var classes, elem, cur, clazz, j, - i = 0, - len = this.length, - proceed = arguments.length === 0 || typeof value === "string" && value; - - if ( jQuery.isFunction( value ) ) { - return this.each(function( j ) { - jQuery( this ).removeClass( value.call( this, j, this.className ) ); - }); - } - if ( proceed ) { - classes = ( value || "" ).match( core_rnotwhite ) || []; - - for ( ; i < len; i++ ) { - elem = this[ i ]; - // This expression is here for better compressibility (see addClass) - cur = elem.nodeType === 1 && ( elem.className ? - ( " " + elem.className + " " ).replace( rclass, " " ) : - "" - ); - - if ( cur ) { - j = 0; - while ( (clazz = classes[j++]) ) { - // Remove *all* instances - while ( cur.indexOf( " " + clazz + " " ) >= 0 ) { - cur = cur.replace( " " + clazz + " ", " " ); - } - } - elem.className = value ? jQuery.trim( cur ) : ""; - } - } - } - - return this; - }, - - toggleClass: function( value, stateVal ) { - var type = typeof value; - - if ( typeof stateVal === "boolean" && type === "string" ) { - return stateVal ? this.addClass( value ) : this.removeClass( value ); - } - - if ( jQuery.isFunction( value ) ) { - return this.each(function( i ) { - jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal ); - }); - } - - return this.each(function() { - if ( type === "string" ) { - // toggle individual class names - var className, - i = 0, - self = jQuery( this ), - classNames = value.match( core_rnotwhite ) || []; - - while ( (className = classNames[ i++ ]) ) { - // check each className given, space separated list - if ( self.hasClass( className ) ) { - self.removeClass( className ); - } else { - self.addClass( className ); - } - } - - // Toggle whole class name - } else if ( type === core_strundefined || type === "boolean" ) { - if ( this.className ) { - // store className if set - data_priv.set( this, "__className__", this.className ); - } - - // If the element has a class name or if we're passed "false", - // then remove the whole classname (if there was one, the above saved it). - // Otherwise bring back whatever was previously saved (if anything), - // falling back to the empty string if nothing was stored. - this.className = this.className || value === false ? "" : data_priv.get( this, "__className__" ) || ""; - } - }); - }, - - hasClass: function( selector ) { - var className = " " + selector + " ", - i = 0, - l = this.length; - for ( ; i < l; i++ ) { - if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) >= 0 ) { - return true; - } - } - - return false; - }, - - val: function( value ) { - var hooks, ret, isFunction, - elem = this[0]; - - if ( !arguments.length ) { - if ( elem ) { - hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ]; - - if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) { - return ret; - } - - ret = elem.value; - - return typeof ret === "string" ? - // handle most common string cases - ret.replace(rreturn, "") : - // handle cases where value is null/undef or number - ret == null ? "" : ret; - } - - return; - } - - isFunction = jQuery.isFunction( value ); - - return this.each(function( i ) { - var val; - - if ( this.nodeType !== 1 ) { - return; - } - - if ( isFunction ) { - val = value.call( this, i, jQuery( this ).val() ); - } else { - val = value; - } - - // Treat null/undefined as ""; convert numbers to string - if ( val == null ) { - val = ""; - } else if ( typeof val === "number" ) { - val += ""; - } else if ( jQuery.isArray( val ) ) { - val = jQuery.map(val, function ( value ) { - return value == null ? "" : value + ""; - }); - } - - hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ]; - - // If set returns undefined, fall back to normal setting - if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) { - this.value = val; - } - }); - } -}); - -jQuery.extend({ - valHooks: { - option: { - get: function( elem ) { - // attributes.value is undefined in Blackberry 4.7 but - // uses .value. See #6932 - var val = elem.attributes.value; - return !val || val.specified ? elem.value : elem.text; - } - }, - select: { - get: function( elem ) { - var value, option, - options = elem.options, - index = elem.selectedIndex, - one = elem.type === "select-one" || index < 0, - values = one ? null : [], - max = one ? index + 1 : options.length, - i = index < 0 ? - max : - one ? index : 0; - - // Loop through all the selected options - for ( ; i < max; i++ ) { - option = options[ i ]; - - // IE6-9 doesn't update selected after form reset (#2551) - if ( ( option.selected || i === index ) && - // Don't return options that are disabled or in a disabled optgroup - ( jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null ) && - ( !option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" ) ) ) { - - // Get the specific value for the option - value = jQuery( option ).val(); - - // We don't need an array for one selects - if ( one ) { - return value; - } - - // Multi-Selects return an array - values.push( value ); - } - } - - return values; - }, - - set: function( elem, value ) { - var optionSet, option, - options = elem.options, - values = jQuery.makeArray( value ), - i = options.length; - - while ( i-- ) { - option = options[ i ]; - if ( (option.selected = jQuery.inArray( jQuery(option).val(), values ) >= 0) ) { - optionSet = true; - } - } - - // force browsers to behave consistently when non-matching value is set - if ( !optionSet ) { - elem.selectedIndex = -1; - } - return values; - } - } - }, - - attr: function( elem, name, value ) { - var hooks, ret, - nType = elem.nodeType; - - // don't get/set attributes on text, comment and attribute nodes - if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { - return; - } - - // Fallback to prop when attributes are not supported - if ( typeof elem.getAttribute === core_strundefined ) { - return jQuery.prop( elem, name, value ); - } - - // All attributes are lowercase - // Grab necessary hook if one is defined - if ( nType !== 1 || !jQuery.isXMLDoc( elem ) ) { - name = name.toLowerCase(); - hooks = jQuery.attrHooks[ name ] || - ( jQuery.expr.match.bool.test( name ) ? boolHook : nodeHook ); - } - - if ( value !== undefined ) { - - if ( value === null ) { - jQuery.removeAttr( elem, name ); - - } else if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) { - return ret; - - } else { - elem.setAttribute( name, value + "" ); - return value; - } - - } else if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) { - return ret; - - } else { - ret = jQuery.find.attr( elem, name ); - - // Non-existent attributes return null, we normalize to undefined - return ret == null ? - undefined : - ret; - } - }, - - removeAttr: function( elem, value ) { - var name, propName, - i = 0, - attrNames = value && value.match( core_rnotwhite ); - - if ( attrNames && elem.nodeType === 1 ) { - while ( (name = attrNames[i++]) ) { - propName = jQuery.propFix[ name ] || name; - - // Boolean attributes get special treatment (#10870) - if ( jQuery.expr.match.bool.test( name ) ) { - // Set corresponding property to false - elem[ propName ] = false; - } - - elem.removeAttribute( name ); - } - } - }, - - attrHooks: { - type: { - set: function( elem, value ) { - if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) { - // Setting the type on a radio button after the value resets the value in IE6-9 - // Reset value to default in case type is set after value during creation - var val = elem.value; - elem.setAttribute( "type", value ); - if ( val ) { - elem.value = val; - } - return value; - } - } - } - }, - - propFix: { - "for": "htmlFor", - "class": "className" - }, - - prop: function( elem, name, value ) { - var ret, hooks, notxml, - nType = elem.nodeType; - - // don't get/set properties on text, comment and attribute nodes - if ( !elem || nType === 3 || nType === 8 || nType === 2 ) { - return; - } - - notxml = nType !== 1 || !jQuery.isXMLDoc( elem ); - - if ( notxml ) { - // Fix name and attach hooks - name = jQuery.propFix[ name ] || name; - hooks = jQuery.propHooks[ name ]; - } - - if ( value !== undefined ) { - return hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ? - ret : - ( elem[ name ] = value ); - - } else { - return hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ? - ret : - elem[ name ]; - } - }, - - propHooks: { - tabIndex: { - get: function( elem ) { - return elem.hasAttribute( "tabindex" ) || rfocusable.test( elem.nodeName ) || elem.href ? - elem.tabIndex : - -1; - } - } - } -}); - -// Hooks for boolean attributes -boolHook = { - set: function( elem, value, name ) { - if ( value === false ) { - // Remove boolean attributes when set to false - jQuery.removeAttr( elem, name ); - } else { - elem.setAttribute( name, name ); - } - return name; - } -}; -jQuery.each( jQuery.expr.match.bool.source.match( /\w+/g ), function( i, name ) { - var getter = jQuery.expr.attrHandle[ name ] || jQuery.find.attr; - - jQuery.expr.attrHandle[ name ] = function( elem, name, isXML ) { - var fn = jQuery.expr.attrHandle[ name ], - ret = isXML ? - undefined : - /* jshint eqeqeq: false */ - // Temporarily disable this handler to check existence - (jQuery.expr.attrHandle[ name ] = undefined) != - getter( elem, name, isXML ) ? - - name.toLowerCase() : - null; - - // Restore handler - jQuery.expr.attrHandle[ name ] = fn; - - return ret; +var isHidden = function( elem, el ) { + // isHidden might be called from jQuery#filter function; + // in that case, element will be second argument + elem = el || elem; + return jQuery.css( elem, "display" ) === "none" || !jQuery.contains( elem.ownerDocument, elem ); }; -}); -// Support: IE9+ -// Selectedness for an option in an optgroup can be inaccurate -if ( !jQuery.support.optSelected ) { - jQuery.propHooks.selected = { - get: function( elem ) { - var parent = elem.parentNode; - if ( parent && parent.parentNode ) { - parent.parentNode.selectedIndex; - } - return null; - } - }; -} +var rcheckableType = (/^(?:checkbox|radio)$/i); -jQuery.each([ - "tabIndex", - "readOnly", - "maxLength", - "cellSpacing", - "cellPadding", - "rowSpan", - "colSpan", - "useMap", - "frameBorder", - "contentEditable" -], function() { - jQuery.propFix[ this.toLowerCase() ] = this; -}); -// Radios and checkboxes getter/setter -jQuery.each([ "radio", "checkbox" ], function() { - jQuery.valHooks[ this ] = { - set: function( elem, value ) { - if ( jQuery.isArray( value ) ) { - return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 ); - } - } - }; - if ( !jQuery.support.checkOn ) { - jQuery.valHooks[ this ].get = function( elem ) { - // Support: Webkit - // "" is returned instead of "on" if a value isn't specified - return elem.getAttribute("value") === null ? "on" : elem.value; - }; - } -}); -var rkeyEvent = /^key/, - rmouseEvent = /^(?:mouse|contextmenu)|click/, + +(function() { + var fragment = document.createDocumentFragment(), + div = fragment.appendChild( document.createElement( "div" ) ), + input = document.createElement( "input" ); + + // #11217 - WebKit loses check when the name is after the checked attribute + // Support: Windows Web Apps (WWA) + // `name` and `type` need .setAttribute for WWA + input.setAttribute( "type", "radio" ); + input.setAttribute( "checked", "checked" ); + input.setAttribute( "name", "t" ); + + div.appendChild( input ); + + // Support: Safari 5.1, iOS 5.1, Android 4.x, Android 2.3 + // old WebKit doesn't clone checked state correctly in fragments + support.checkClone = div.cloneNode( true ).cloneNode( true ).lastChild.checked; + + // Make sure textarea (and checkbox) defaultValue is properly cloned + // Support: IE9-IE11+ + div.innerHTML = ""; + support.noCloneChecked = !!div.cloneNode( true ).lastChild.defaultValue; +})(); +var strundefined = typeof undefined; + + + +support.focusinBubbles = "onfocusin" in window; + + +var + rkeyEvent = /^key/, + rmouseEvent = /^(?:mouse|pointer|contextmenu)|click/, rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, rtypenamespace = /^([^.]*)(?:\.(.+)|)$/; @@ -4356,16 +4091,13 @@ jQuery.event = { eventHandle = elemData.handle = function( e ) { // Discard the second event of a jQuery.event.trigger() and // when an event is called after a page has unloaded - return typeof jQuery !== core_strundefined && (!e || jQuery.event.triggered !== e.type) ? - jQuery.event.dispatch.apply( eventHandle.elem, arguments ) : - undefined; + return typeof jQuery !== strundefined && jQuery.event.triggered !== e.type ? + jQuery.event.dispatch.apply( elem, arguments ) : undefined; }; - // Add elem as a property of the handle fn to prevent a memory leak with IE non-native events - eventHandle.elem = elem; } // Handle multiple events separated by a space - types = ( types || "" ).match( core_rnotwhite ) || [""]; + types = ( types || "" ).match( rnotwhite ) || [ "" ]; t = types.length; while ( t-- ) { tmp = rtypenamespace.exec( types[t] ) || []; @@ -4430,8 +4162,6 @@ jQuery.event = { jQuery.event.global[ type ] = true; } - // Nullify elem to prevent memory leaks in IE - elem = null; }, // Detach an event or set of events from an element @@ -4447,7 +4177,7 @@ jQuery.event = { } // Once for each type.namespace in types; type may be omitted - types = ( types || "" ).match( core_rnotwhite ) || [""]; + types = ( types || "" ).match( rnotwhite ) || [ "" ]; t = types.length; while ( t-- ) { tmp = rtypenamespace.exec( types[t] ) || []; @@ -4509,8 +4239,8 @@ jQuery.event = { var i, cur, tmp, bubbleType, ontype, handle, special, eventPath = [ elem || document ], - type = core_hasOwn.call( event, "type" ) ? event.type : event, - namespaces = core_hasOwn.call( event, "namespace" ) ? event.namespace.split(".") : []; + type = hasOwn.call( event, "type" ) ? event.type : event, + namespaces = hasOwn.call( event, "namespace" ) ? event.namespace.split(".") : []; cur = tmp = elem = elem || document; @@ -4596,8 +4326,11 @@ jQuery.event = { // Native handler handle = ontype && cur[ ontype ]; - if ( handle && jQuery.acceptData( cur ) && handle.apply && handle.apply( cur, data ) === false ) { - event.preventDefault(); + if ( handle && handle.apply && jQuery.acceptData( cur ) ) { + event.result = handle.apply( cur, data ); + if ( event.result === false ) { + event.preventDefault(); + } } } event.type = type; @@ -4641,7 +4374,7 @@ jQuery.event = { var i, j, ret, matched, handleObj, handlerQueue = [], - args = core_slice.call( arguments ), + args = slice.call( arguments ), handlers = ( data_priv.get( this, "events" ) || {} )[ event.type ] || [], special = jQuery.event.special[ event.type ] || {}; @@ -4822,7 +4555,7 @@ jQuery.event = { event.target = event.target.parentNode; } - return fixHook.filter? fixHook.filter( event, originalEvent ) : event; + return fixHook.filter ? fixHook.filter( event, originalEvent ) : event; }, special: { @@ -4869,7 +4602,7 @@ jQuery.event = { // Support: Firefox 20+ // Firefox doesn't alert if the returnValue field is not set. - if ( event.result !== undefined ) { + if ( event.result !== undefined && event.originalEvent ) { event.originalEvent.returnValue = event.result; } } @@ -4919,8 +4652,12 @@ jQuery.Event = function( src, props ) { // Events bubbling up the document may have been marked as prevented // by a handler lower down the tree; reflect the correct value. - this.isDefaultPrevented = ( src.defaultPrevented || - src.getPreventDefault && src.getPreventDefault() ) ? returnTrue : returnFalse; + this.isDefaultPrevented = src.defaultPrevented || + src.defaultPrevented === undefined && + // Support: Android < 4.0 + src.returnValue === false ? + returnTrue : + returnFalse; // Event type } else { @@ -4965,7 +4702,14 @@ jQuery.Event.prototype = { } }, stopImmediatePropagation: function() { + var e = this.originalEvent; + this.isImmediatePropagationStopped = returnTrue; + + if ( e && e.stopImmediatePropagation ) { + e.stopImmediatePropagation(); + } + this.stopPropagation(); } }; @@ -4974,7 +4718,9 @@ jQuery.Event.prototype = { // Support: Chrome 15+ jQuery.each({ mouseenter: "mouseover", - mouseleave: "mouseout" + mouseleave: "mouseout", + pointerenter: "pointerover", + pointerleave: "pointerout" }, function( orig, fix ) { jQuery.event.special[ orig ] = { delegateType: fix, @@ -5000,24 +4746,34 @@ jQuery.each({ // Create "bubbling" focus and blur events // Support: Firefox, Chrome, Safari -if ( !jQuery.support.focusinBubbles ) { +if ( !support.focusinBubbles ) { jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) { - // Attach a single capturing handler while someone wants focusin/focusout - var attaches = 0, - handler = function( event ) { + // Attach a single capturing handler on the document while someone wants focusin/focusout + var handler = function( event ) { jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true ); }; jQuery.event.special[ fix ] = { setup: function() { - if ( attaches++ === 0 ) { - document.addEventListener( orig, handler, true ); + var doc = this.ownerDocument || this, + attaches = data_priv.access( doc, fix ); + + if ( !attaches ) { + doc.addEventListener( orig, handler, true ); } + data_priv.access( doc, fix, ( attaches || 0 ) + 1 ); }, teardown: function() { - if ( --attaches === 0 ) { - document.removeEventListener( orig, handler, true ); + var doc = this.ownerDocument || this, + attaches = data_priv.access( doc, fix ) - 1; + + if ( !attaches ) { + doc.removeEventListener( orig, handler, true ); + data_priv.remove( doc, fix ); + + } else { + data_priv.access( doc, fix, attaches ); } } }; @@ -5126,294 +4882,13 @@ jQuery.fn.extend({ } } }); -var isSimple = /^.[^:#\[\.,]*$/, - rparentsprev = /^(?:parents|prev(?:Until|All))/, - rneedsContext = jQuery.expr.match.needsContext, - // methods guaranteed to produce a unique set when starting from a unique set - guaranteedUnique = { - children: true, - contents: true, - next: true, - prev: true - }; -jQuery.fn.extend({ - find: function( selector ) { - var i, - ret = [], - self = this, - len = self.length; - 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; - } - } - }) ); - } - - for ( i = 0; i < len; i++ ) { - jQuery.find( selector, self[ i ], ret ); - } - - // Needed because $( selector, context ) becomes $( context ).find( selector ) - ret = this.pushStack( len > 1 ? jQuery.unique( ret ) : ret ); - ret.selector = this.selector ? this.selector + " " + selector : selector; - return ret; - }, - - 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; - } - } - }); - }, - - not: function( selector ) { - return this.pushStack( winnow(this, selector || [], true) ); - }, - - filter: function( selector ) { - return this.pushStack( winnow(this, selector || [], false) ); - }, - - is: function( selector ) { - return !!winnow( - this, - - // If this is a positional/relative selector, check membership in the returned set - // so $("p:first").is("p:last") won't return true for a doc with two "p". - typeof selector === "string" && rneedsContext.test( selector ) ? - jQuery( selector ) : - selector || [], - false - ).length; - }, - - closest: function( selectors, context ) { - var cur, - i = 0, - l = this.length, - matched = [], - pos = ( rneedsContext.test( selectors ) || typeof selectors !== "string" ) ? - jQuery( selectors, context || this.context ) : - 0; - - for ( ; i < l; i++ ) { - for ( cur = this[i]; cur && cur !== context; cur = cur.parentNode ) { - // Always skip document fragments - if ( cur.nodeType < 11 && (pos ? - pos.index(cur) > -1 : - - // Don't pass non-elements to Sizzle - cur.nodeType === 1 && - jQuery.find.matchesSelector(cur, selectors)) ) { - - cur = matched.push( cur ); - break; - } - } - } - - return this.pushStack( matched.length > 1 ? jQuery.unique( matched ) : matched ); - }, - - // Determine the position of an element within - // the matched set of elements - index: function( elem ) { - - // No argument, return index in parent - if ( !elem ) { - return ( this[ 0 ] && this[ 0 ].parentNode ) ? this.first().prevAll().length : -1; - } - - // index in selector - if ( typeof elem === "string" ) { - return core_indexOf.call( jQuery( elem ), this[ 0 ] ); - } - - // Locate the position of the desired element - return core_indexOf.call( this, - - // If it receives a jQuery object, the first element is used - elem.jquery ? elem[ 0 ] : elem - ); - }, - - add: function( selector, context ) { - var set = typeof selector === "string" ? - jQuery( selector, context ) : - jQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ), - all = jQuery.merge( this.get(), set ); - - return this.pushStack( jQuery.unique(all) ); - }, - - 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 jQuery.dir( elem, "parentNode" ); - }, - parentsUntil: function( elem, i, until ) { - return jQuery.dir( elem, "parentNode", until ); - }, - next: function( elem ) { - return sibling( elem, "nextSibling" ); - }, - prev: function( elem ) { - return sibling( elem, "previousSibling" ); - }, - nextAll: function( elem ) { - return jQuery.dir( elem, "nextSibling" ); - }, - prevAll: function( elem ) { - return jQuery.dir( elem, "previousSibling" ); - }, - nextUntil: function( elem, i, until ) { - return jQuery.dir( elem, "nextSibling", until ); - }, - prevUntil: function( elem, i, until ) { - return jQuery.dir( elem, "previousSibling", until ); - }, - siblings: function( elem ) { - return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem ); - }, - children: function( elem ) { - return jQuery.sibling( elem.firstChild ); - }, - contents: function( elem ) { - return elem.contentDocument || 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 ) { - // Remove duplicates - if ( !guaranteedUnique[ name ] ) { - jQuery.unique( matched ); - } - - // Reverse order for parents* and prev-derivatives - if ( rparentsprev.test( name ) ) { - matched.reverse(); - } - } - - return this.pushStack( matched ); - }; -}); - -jQuery.extend({ - filter: function( expr, elems, not ) { - var elem = elems[ 0 ]; - - if ( not ) { - expr = ":not(" + expr + ")"; - } - - return elems.length === 1 && elem.nodeType === 1 ? - jQuery.find.matchesSelector( elem, expr ) ? [ elem ] : [] : - jQuery.find.matches( expr, jQuery.grep( elems, function( elem ) { - return elem.nodeType === 1; - })); - }, - - 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; - }, - - sibling: function( n, elem ) { - var matched = []; - - for ( ; n; n = n.nextSibling ) { - if ( n.nodeType === 1 && n !== elem ) { - matched.push( n ); - } - } - - return matched; - } -}); - -// Implement the identical functionality for filter and not -function winnow( elements, qualifier, not ) { - if ( jQuery.isFunction( qualifier ) ) { - return jQuery.grep( elements, function( elem, i ) { - /* jshint -W018 */ - return !!qualifier.call( elem, i, elem ) !== not; - }); - - } - - if ( qualifier.nodeType ) { - return jQuery.grep( elements, function( elem ) { - return ( elem === qualifier ) !== not; - }); - - } - - if ( typeof qualifier === "string" ) { - if ( isSimple.test( qualifier ) ) { - return jQuery.filter( qualifier, elements, not ); - } - - qualifier = jQuery.filter( qualifier, elements ); - } - - return jQuery.grep( elements, function( elem ) { - return ( core_indexOf.call( qualifier, elem ) >= 0 ) !== not; - }); -} -var rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi, +var + rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi, rtagName = /<([\w:]+)/, rhtml = /<|&#?\w+;/, rnoInnerhtml = /<(?:script|style|link)/i, - manipulation_rcheckableType = /^(?:checkbox|radio)$/i, // checked="checked" or checked rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i, rscriptType = /^$|\/(?:java|ecma)script/i, @@ -5440,278 +4915,103 @@ wrapMap.optgroup = wrapMap.option; wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead; wrapMap.th = wrapMap.td; -jQuery.fn.extend({ - text: function( value ) { - return jQuery.access( this, function( value ) { - return value === undefined ? - jQuery.text( this ) : - this.empty().append( ( this[ 0 ] && this[ 0 ].ownerDocument || document ).createTextNode( value ) ); - }, null, value, arguments.length ); - }, +// Support: 1.x compatibility +// Manipulating tables requires a tbody +function manipulationTarget( elem, content ) { + return jQuery.nodeName( elem, "table" ) && + jQuery.nodeName( content.nodeType !== 11 ? content : content.firstChild, "tr" ) ? - append: function() { - return this.domManip( arguments, function( elem ) { - if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { - var target = manipulationTarget( this, elem ); - target.appendChild( elem ); - } - }); - }, + elem.getElementsByTagName("tbody")[0] || + elem.appendChild( elem.ownerDocument.createElement("tbody") ) : + elem; +} - prepend: function() { - return this.domManip( arguments, function( elem ) { - if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { - var target = manipulationTarget( this, elem ); - target.insertBefore( elem, target.firstChild ); - } - }); - }, +// Replace/restore the type attribute of script elements for safe DOM manipulation +function disableScript( elem ) { + elem.type = (elem.getAttribute("type") !== null) + "/" + elem.type; + return elem; +} +function restoreScript( elem ) { + var match = rscriptTypeMasked.exec( elem.type ); - before: function() { - return this.domManip( arguments, function( elem ) { - if ( this.parentNode ) { - this.parentNode.insertBefore( elem, this ); - } - }); - }, - - after: function() { - return this.domManip( arguments, function( elem ) { - if ( this.parentNode ) { - this.parentNode.insertBefore( elem, this.nextSibling ); - } - }); - }, - - // keepData is for internal use only--do not document - remove: function( selector, keepData ) { - var elem, - elems = selector ? jQuery.filter( selector, this ) : this, - i = 0; - - for ( ; (elem = elems[i]) != null; i++ ) { - if ( !keepData && elem.nodeType === 1 ) { - jQuery.cleanData( getAll( elem ) ); - } - - if ( elem.parentNode ) { - if ( keepData && jQuery.contains( elem.ownerDocument, elem ) ) { - setGlobalEval( getAll( elem, "script" ) ); - } - elem.parentNode.removeChild( elem ); - } - } - - return this; - }, - - empty: function() { - var elem, - i = 0; - - for ( ; (elem = this[i]) != null; i++ ) { - if ( elem.nodeType === 1 ) { - - // Prevent memory leaks - jQuery.cleanData( getAll( elem, false ) ); - - // Remove any remaining nodes - elem.textContent = ""; - } - } - - return this; - }, - - clone: function( dataAndEvents, deepDataAndEvents ) { - dataAndEvents = dataAndEvents == null ? false : dataAndEvents; - deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; - - return this.map( function () { - return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); - }); - }, - - html: function( value ) { - return jQuery.access( this, function( value ) { - var elem = this[ 0 ] || {}, - i = 0, - l = this.length; - - if ( value === undefined && elem.nodeType === 1 ) { - return elem.innerHTML; - } - - // See if we can take a shortcut and just use innerHTML - if ( typeof value === "string" && !rnoInnerhtml.test( value ) && - !wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) { - - value = value.replace( rxhtmlTag, "<$1>" ); - - try { - for ( ; i < l; i++ ) { - elem = this[ i ] || {}; - - // Remove element nodes and prevent memory leaks - if ( elem.nodeType === 1 ) { - jQuery.cleanData( getAll( elem, false ) ); - elem.innerHTML = value; - } - } - - elem = 0; - - // If using innerHTML throws an exception, use the fallback method - } catch( e ) {} - } - - if ( elem ) { - this.empty().append( value ); - } - }, null, value, arguments.length ); - }, - - replaceWith: function() { - var - // Snapshot the DOM in case .domManip sweeps something relevant into its fragment - args = jQuery.map( this, function( elem ) { - return [ elem.nextSibling, elem.parentNode ]; - }), - i = 0; - - // Make the changes, replacing each context element with the new content - this.domManip( arguments, function( elem ) { - var next = args[ i++ ], - parent = args[ i++ ]; - - if ( parent ) { - // Don't use the snapshot next if it has moved (#13810) - if ( next && next.parentNode !== parent ) { - next = this.nextSibling; - } - jQuery( this ).remove(); - parent.insertBefore( elem, next ); - } - // Allow new content to include elements from the context set - }, true ); - - // Force removal if there was no new content (e.g., from empty arguments) - return i ? this : this.remove(); - }, - - detach: function( selector ) { - return this.remove( selector, true ); - }, - - domManip: function( args, callback, allowIntersection ) { - - // Flatten any nested arrays - args = core_concat.apply( [], args ); - - var fragment, first, scripts, hasScripts, node, doc, - i = 0, - l = this.length, - set = this, - iNoClone = l - 1, - value = args[ 0 ], - isFunction = jQuery.isFunction( value ); - - // We can't cloneNode fragments that contain checked, in WebKit - if ( isFunction || !( l <= 1 || typeof value !== "string" || jQuery.support.checkClone || !rchecked.test( value ) ) ) { - return this.each(function( index ) { - var self = set.eq( index ); - if ( isFunction ) { - args[ 0 ] = value.call( this, index, self.html() ); - } - self.domManip( args, callback, allowIntersection ); - }); - } - - if ( l ) { - fragment = jQuery.buildFragment( args, this[ 0 ].ownerDocument, false, !allowIntersection && this ); - first = fragment.firstChild; - - if ( fragment.childNodes.length === 1 ) { - fragment = first; - } - - if ( first ) { - scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); - hasScripts = scripts.length; - - // Use the original fragment for the last item instead of the first because it can end up - // being emptied incorrectly in certain situations (#8070). - for ( ; i < l; i++ ) { - node = fragment; - - if ( i !== iNoClone ) { - node = jQuery.clone( node, true, true ); - - // Keep references to cloned scripts for later restoration - if ( hasScripts ) { - // Support: QtWebKit - // jQuery.merge because core_push.apply(_, arraylike) throws - jQuery.merge( scripts, getAll( node, "script" ) ); - } - } - - callback.call( this[ i ], node, i ); - } - - if ( hasScripts ) { - doc = scripts[ scripts.length - 1 ].ownerDocument; - - // Reenable scripts - jQuery.map( scripts, restoreScript ); - - // Evaluate executable scripts on first document insertion - for ( i = 0; i < hasScripts; i++ ) { - node = scripts[ i ]; - if ( rscriptType.test( node.type || "" ) && - !data_priv.access( node, "globalEval" ) && jQuery.contains( doc, node ) ) { - - if ( node.src ) { - // Hope ajax is available... - jQuery._evalUrl( node.src ); - } else { - jQuery.globalEval( node.textContent.replace( rcleanScript, "" ) ); - } - } - } - } - } - } - - return this; + if ( match ) { + elem.type = match[ 1 ]; + } else { + elem.removeAttribute("type"); } -}); -jQuery.each({ - appendTo: "append", - prependTo: "prepend", - insertBefore: "before", - insertAfter: "after", - replaceAll: "replaceWith" -}, function( name, original ) { - jQuery.fn[ name ] = function( selector ) { - var elems, - ret = [], - insert = jQuery( selector ), - last = insert.length - 1, - i = 0; + return elem; +} - for ( ; i <= last; i++ ) { - elems = i === last ? this : this.clone( true ); - jQuery( insert[ i ] )[ original ]( elems ); +// Mark scripts as having already been evaluated +function setGlobalEval( elems, refElements ) { + var i = 0, + l = elems.length; - // Support: QtWebKit - // .get() because core_push.apply(_, arraylike) throws - core_push.apply( ret, elems.get() ); + for ( ; i < l; i++ ) { + data_priv.set( + elems[ i ], "globalEval", !refElements || data_priv.get( refElements[ i ], "globalEval" ) + ); + } +} + +function cloneCopyEvent( src, dest ) { + var i, l, type, pdataOld, pdataCur, udataOld, udataCur, events; + + if ( dest.nodeType !== 1 ) { + return; + } + + // 1. Copy private data: events, handlers, etc. + if ( data_priv.hasData( src ) ) { + pdataOld = data_priv.access( src ); + pdataCur = data_priv.set( dest, pdataOld ); + events = pdataOld.events; + + if ( events ) { + delete pdataCur.handle; + pdataCur.events = {}; + + for ( type in events ) { + for ( i = 0, l = events[ type ].length; i < l; i++ ) { + jQuery.event.add( dest, type, events[ type ][ i ] ); + } + } } + } - return this.pushStack( ret ); - }; -}); + // 2. Copy user data + if ( data_user.hasData( src ) ) { + udataOld = data_user.access( src ); + udataCur = jQuery.extend( {}, udataOld ); + + data_user.set( dest, udataCur ); + } +} + +function getAll( context, tag ) { + var ret = context.getElementsByTagName ? context.getElementsByTagName( tag || "*" ) : + context.querySelectorAll ? context.querySelectorAll( tag || "*" ) : + []; + + return tag === undefined || tag && jQuery.nodeName( context, tag ) ? + jQuery.merge( [ context ], ret ) : + ret; +} + +// Support: IE >= 9 +function fixInput( src, dest ) { + var nodeName = dest.nodeName.toLowerCase(); + + // Fails to persist the checked state of a cloned checkbox or radio button. + if ( nodeName === "input" && rcheckableType.test( src.type ) ) { + dest.checked = src.checked; + + // Fails to return the selected option to the default selected state when cloning options + } else if ( nodeName === "input" || nodeName === "textarea" ) { + dest.defaultValue = src.defaultValue; + } +} jQuery.extend({ clone: function( elem, dataAndEvents, deepDataAndEvents ) { @@ -5721,7 +5021,8 @@ jQuery.extend({ // Support: IE >= 9 // Fix Cloning issues - if ( !jQuery.support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) && !jQuery.isXMLDoc( elem ) ) { + if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) && + !jQuery.isXMLDoc( elem ) ) { // We eschew Sizzle here for performance reasons: http://jsperf.com/getall-vs-sizzle/2 destElements = getAll( clone ); @@ -5758,10 +5059,10 @@ jQuery.extend({ buildFragment: function( elems, context, scripts, selection ) { var elem, tmp, tag, wrap, contains, j, - i = 0, - l = elems.length, fragment = context.createDocumentFragment(), - nodes = []; + nodes = [], + i = 0, + l = elems.length; for ( ; i < l; i++ ) { elem = elems[ i ]; @@ -5771,7 +5072,7 @@ jQuery.extend({ // Add nodes directly if ( jQuery.type( elem ) === "object" ) { // Support: QtWebKit - // jQuery.merge because core_push.apply(_, arraylike) throws + // jQuery.merge because push.apply(_, arraylike) throws jQuery.merge( nodes, elem.nodeType ? [ elem ] : elem ); // Convert non-html into a text node @@ -5783,7 +5084,7 @@ jQuery.extend({ tmp = tmp || fragment.appendChild( context.createElement("div") ); // Deserialize a standard representation - tag = ( rtagName.exec( elem ) || ["", ""] )[ 1 ].toLowerCase(); + tag = ( rtagName.exec( elem ) || [ "", "" ] )[ 1 ].toLowerCase(); wrap = wrapMap[ tag ] || wrapMap._default; tmp.innerHTML = wrap[ 1 ] + elem.replace( rxhtmlTag, "<$1>" ) + wrap[ 2 ]; @@ -5794,7 +5095,7 @@ jQuery.extend({ } // Support: QtWebKit - // jQuery.merge because core_push.apply(_, arraylike) throws + // jQuery.merge because push.apply(_, arraylike) throws jQuery.merge( nodes, tmp.childNodes ); // Remember the top-level container @@ -5844,18 +5145,17 @@ jQuery.extend({ }, cleanData: function( elems ) { - var data, elem, events, type, key, j, + var data, elem, type, key, special = jQuery.event.special, i = 0; for ( ; (elem = elems[ i ]) !== undefined; i++ ) { - if ( Data.accepts( elem ) ) { + if ( jQuery.acceptData( elem ) ) { key = elem[ data_priv.expando ]; if ( key && (data = data_priv.cache[ key ]) ) { - events = Object.keys( data.events || {} ); - if ( events.length ) { - for ( j = 0; (type = events[j]) !== undefined; j++ ) { + if ( data.events ) { + for ( type in data.events ) { if ( special[ type ] ) { jQuery.event.remove( elem, type ); @@ -5874,490 +5174,372 @@ jQuery.extend({ // Discard any remaining `user` data delete data_user.cache[ elem[ data_user.expando ] ]; } - }, - - _evalUrl: function( url ) { - return jQuery.ajax({ - url: url, - type: "GET", - dataType: "script", - async: false, - global: false, - "throws": true - }); } }); -// Support: 1.x compatibility -// Manipulating tables requires a tbody -function manipulationTarget( elem, content ) { - return jQuery.nodeName( elem, "table" ) && - jQuery.nodeName( content.nodeType === 1 ? content : content.firstChild, "tr" ) ? - - elem.getElementsByTagName("tbody")[0] || - elem.appendChild( elem.ownerDocument.createElement("tbody") ) : - elem; -} - -// Replace/restore the type attribute of script elements for safe DOM manipulation -function disableScript( elem ) { - elem.type = (elem.getAttribute("type") !== null) + "/" + elem.type; - return elem; -} -function restoreScript( elem ) { - var match = rscriptTypeMasked.exec( elem.type ); - - if ( match ) { - elem.type = match[ 1 ]; - } else { - elem.removeAttribute("type"); - } - - return elem; -} - -// Mark scripts as having already been evaluated -function setGlobalEval( elems, refElements ) { - var l = elems.length, - i = 0; - - for ( ; i < l; i++ ) { - data_priv.set( - elems[ i ], "globalEval", !refElements || data_priv.get( refElements[ i ], "globalEval" ) - ); - } -} - -function cloneCopyEvent( src, dest ) { - var i, l, type, pdataOld, pdataCur, udataOld, udataCur, events; - - if ( dest.nodeType !== 1 ) { - return; - } - - // 1. Copy private data: events, handlers, etc. - if ( data_priv.hasData( src ) ) { - pdataOld = data_priv.access( src ); - pdataCur = data_priv.set( dest, pdataOld ); - events = pdataOld.events; - - if ( events ) { - delete pdataCur.handle; - pdataCur.events = {}; - - for ( type in events ) { - for ( i = 0, l = events[ type ].length; i < l; i++ ) { - jQuery.event.add( dest, type, events[ type ][ i ] ); - } - } - } - } - - // 2. Copy user data - if ( data_user.hasData( src ) ) { - udataOld = data_user.access( src ); - udataCur = jQuery.extend( {}, udataOld ); - - data_user.set( dest, udataCur ); - } -} - - -function getAll( context, tag ) { - var ret = context.getElementsByTagName ? context.getElementsByTagName( tag || "*" ) : - context.querySelectorAll ? context.querySelectorAll( tag || "*" ) : - []; - - return tag === undefined || tag && jQuery.nodeName( context, tag ) ? - jQuery.merge( [ context ], ret ) : - ret; -} - -// Support: IE >= 9 -function fixInput( src, dest ) { - var nodeName = dest.nodeName.toLowerCase(); - - // Fails to persist the checked state of a cloned checkbox or radio button. - if ( nodeName === "input" && manipulation_rcheckableType.test( src.type ) ) { - dest.checked = src.checked; - - // Fails to return the selected option to the default selected state when cloning options - } else if ( nodeName === "input" || nodeName === "textarea" ) { - dest.defaultValue = src.defaultValue; - } -} jQuery.fn.extend({ - wrapAll: function( html ) { - var wrap; + text: function( value ) { + return access( this, function( value ) { + return value === undefined ? + jQuery.text( this ) : + this.empty().each(function() { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + this.textContent = value; + } + }); + }, null, value, arguments.length ); + }, - if ( jQuery.isFunction( html ) ) { - return this.each(function( i ) { - jQuery( this ).wrapAll( html.call(this, i) ); - }); - } + append: function() { + return this.domManip( arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.appendChild( elem ); + } + }); + }, - if ( this[ 0 ] ) { + prepend: function() { + return this.domManip( arguments, function( elem ) { + if ( this.nodeType === 1 || this.nodeType === 11 || this.nodeType === 9 ) { + var target = manipulationTarget( this, elem ); + target.insertBefore( elem, target.firstChild ); + } + }); + }, - // The elements to wrap the target around - wrap = jQuery( html, this[ 0 ].ownerDocument ).eq( 0 ).clone( true ); + before: function() { + return this.domManip( arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this ); + } + }); + }, - if ( this[ 0 ].parentNode ) { - wrap.insertBefore( this[ 0 ] ); + after: function() { + return this.domManip( arguments, function( elem ) { + if ( this.parentNode ) { + this.parentNode.insertBefore( elem, this.nextSibling ); + } + }); + }, + + remove: function( selector, keepData /* Internal Use Only */ ) { + var elem, + elems = selector ? jQuery.filter( selector, this ) : this, + i = 0; + + for ( ; (elem = elems[i]) != null; i++ ) { + if ( !keepData && elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem ) ); } - wrap.map(function() { - var elem = this; - - while ( elem.firstElementChild ) { - elem = elem.firstElementChild; + if ( elem.parentNode ) { + if ( keepData && jQuery.contains( elem.ownerDocument, elem ) ) { + setGlobalEval( getAll( elem, "script" ) ); } - - return elem; - }).append( this ); + elem.parentNode.removeChild( elem ); + } } return this; }, - wrapInner: function( html ) { - if ( jQuery.isFunction( html ) ) { - return this.each(function( i ) { - jQuery( this ).wrapInner( html.call(this, i) ); + empty: function() { + var elem, + i = 0; + + for ( ; (elem = this[i]) != null; i++ ) { + if ( elem.nodeType === 1 ) { + + // Prevent memory leaks + jQuery.cleanData( getAll( elem, false ) ); + + // Remove any remaining nodes + elem.textContent = ""; + } + } + + return this; + }, + + clone: function( dataAndEvents, deepDataAndEvents ) { + dataAndEvents = dataAndEvents == null ? false : dataAndEvents; + deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents; + + return this.map(function() { + return jQuery.clone( this, dataAndEvents, deepDataAndEvents ); + }); + }, + + html: function( value ) { + return access( this, function( value ) { + var elem = this[ 0 ] || {}, + i = 0, + l = this.length; + + if ( value === undefined && elem.nodeType === 1 ) { + return elem.innerHTML; + } + + // See if we can take a shortcut and just use innerHTML + if ( typeof value === "string" && !rnoInnerhtml.test( value ) && + !wrapMap[ ( rtagName.exec( value ) || [ "", "" ] )[ 1 ].toLowerCase() ] ) { + + value = value.replace( rxhtmlTag, "<$1>" ); + + try { + for ( ; i < l; i++ ) { + elem = this[ i ] || {}; + + // Remove element nodes and prevent memory leaks + if ( elem.nodeType === 1 ) { + jQuery.cleanData( getAll( elem, false ) ); + elem.innerHTML = value; + } + } + + elem = 0; + + // If using innerHTML throws an exception, use the fallback method + } catch( e ) {} + } + + if ( elem ) { + this.empty().append( value ); + } + }, null, value, arguments.length ); + }, + + replaceWith: function() { + var arg = arguments[ 0 ]; + + // Make the changes, replacing each context element with the new content + this.domManip( arguments, function( elem ) { + arg = this.parentNode; + + jQuery.cleanData( getAll( this ) ); + + if ( arg ) { + arg.replaceChild( elem, this ); + } + }); + + // Force removal if there was no new content (e.g., from empty arguments) + return arg && (arg.length || arg.nodeType) ? this : this.remove(); + }, + + detach: function( selector ) { + return this.remove( selector, true ); + }, + + domManip: function( args, callback ) { + + // Flatten any nested arrays + args = concat.apply( [], args ); + + var fragment, first, scripts, hasScripts, node, doc, + i = 0, + l = this.length, + set = this, + iNoClone = l - 1, + value = args[ 0 ], + isFunction = jQuery.isFunction( value ); + + // We can't cloneNode fragments that contain checked, in WebKit + if ( isFunction || + ( l > 1 && typeof value === "string" && + !support.checkClone && rchecked.test( value ) ) ) { + return this.each(function( index ) { + var self = set.eq( index ); + if ( isFunction ) { + args[ 0 ] = value.call( this, index, self.html() ); + } + self.domManip( args, callback ); }); } - return this.each(function() { - var self = jQuery( this ), - contents = self.contents(); + if ( l ) { + fragment = jQuery.buildFragment( args, this[ 0 ].ownerDocument, false, this ); + first = fragment.firstChild; - if ( contents.length ) { - contents.wrapAll( html ); - - } else { - self.append( html ); - } - }); - }, - - wrap: function( html ) { - var isFunction = jQuery.isFunction( html ); - - return this.each(function( i ) { - jQuery( this ).wrapAll( isFunction ? html.call(this, i) : html ); - }); - }, - - unwrap: function() { - return this.parent().each(function() { - if ( !jQuery.nodeName( this, "body" ) ) { - jQuery( this ).replaceWith( this.childNodes ); - } - }).end(); - } -}); -var curCSS, iframe, - // swappable if display is none or starts with table except "table", "table-cell", or "table-caption" - // see here for display values: https://developer.mozilla.org/en-US/docs/CSS/display - rdisplayswap = /^(none|table(?!-c[ea]).+)/, - rmargin = /^margin/, - rnumsplit = new RegExp( "^(" + core_pnum + ")(.*)$", "i" ), - rnumnonpx = new RegExp( "^(" + core_pnum + ")(?!px)[a-z%]+$", "i" ), - rrelNum = new RegExp( "^([+-])=(" + core_pnum + ")", "i" ), - elemdisplay = { BODY: "block" }, - - cssShow = { position: "absolute", visibility: "hidden", display: "block" }, - cssNormalTransform = { - letterSpacing: 0, - fontWeight: 400 - }, - - cssExpand = [ "Top", "Right", "Bottom", "Left" ], - cssPrefixes = [ "Webkit", "O", "Moz", "ms" ]; - -// return a css property mapped to a potentially vendor prefixed property -function vendorPropName( style, name ) { - - // shortcut for names that are not vendor prefixed - if ( name in style ) { - return name; - } - - // check for vendor prefixed names - var capName = name.charAt(0).toUpperCase() + name.slice(1), - origName = name, - i = cssPrefixes.length; - - while ( i-- ) { - name = cssPrefixes[ i ] + capName; - if ( name in style ) { - return name; - } - } - - return origName; -} - -function isHidden( elem, el ) { - // isHidden might be called from jQuery#filter function; - // in that case, element will be second argument - elem = el || elem; - return jQuery.css( elem, "display" ) === "none" || !jQuery.contains( elem.ownerDocument, elem ); -} - -// NOTE: we've included the "window" in window.getComputedStyle -// because jsdom on node.js will break without it. -function getStyles( elem ) { - return window.getComputedStyle( elem, null ); -} - -function showHide( elements, show ) { - var display, elem, hidden, - values = [], - index = 0, - length = elements.length; - - for ( ; index < length; index++ ) { - elem = elements[ index ]; - if ( !elem.style ) { - continue; - } - - values[ index ] = data_priv.get( elem, "olddisplay" ); - display = elem.style.display; - if ( show ) { - // Reset the inline display of this element to learn if it is - // being hidden by cascaded rules or not - if ( !values[ index ] && display === "none" ) { - elem.style.display = ""; + if ( fragment.childNodes.length === 1 ) { + fragment = first; } - // Set elements which have been overridden with display: none - // in a stylesheet to whatever the default browser style is - // for such an element - if ( elem.style.display === "" && isHidden( elem ) ) { - values[ index ] = data_priv.access( elem, "olddisplay", css_defaultDisplay(elem.nodeName) ); - } - } else { + if ( first ) { + scripts = jQuery.map( getAll( fragment, "script" ), disableScript ); + hasScripts = scripts.length; - if ( !values[ index ] ) { - hidden = isHidden( elem ); + // Use the original fragment for the last item instead of the first because it can end up + // being emptied incorrectly in certain situations (#8070). + for ( ; i < l; i++ ) { + node = fragment; - if ( display && display !== "none" || !hidden ) { - data_priv.set( elem, "olddisplay", hidden ? display : jQuery.css(elem, "display") ); + if ( i !== iNoClone ) { + node = jQuery.clone( node, true, true ); + + // Keep references to cloned scripts for later restoration + if ( hasScripts ) { + // Support: QtWebKit + // jQuery.merge because push.apply(_, arraylike) throws + jQuery.merge( scripts, getAll( node, "script" ) ); + } + } + + callback.call( this[ i ], node, i ); + } + + if ( hasScripts ) { + doc = scripts[ scripts.length - 1 ].ownerDocument; + + // Reenable scripts + jQuery.map( scripts, restoreScript ); + + // Evaluate executable scripts on first document insertion + for ( i = 0; i < hasScripts; i++ ) { + node = scripts[ i ]; + if ( rscriptType.test( node.type || "" ) && + !data_priv.access( node, "globalEval" ) && jQuery.contains( doc, node ) ) { + + if ( node.src ) { + // Optional AJAX dependency, but won't run scripts if not present + if ( jQuery._evalUrl ) { + jQuery._evalUrl( node.src ); + } + } else { + jQuery.globalEval( node.textContent.replace( rcleanScript, "" ) ); + } + } + } } } } - } - // Set the display of most of the elements in a second loop - // to avoid the constant reflow - for ( index = 0; index < length; index++ ) { - elem = elements[ index ]; - if ( !elem.style ) { - continue; - } - if ( !show || elem.style.display === "none" || elem.style.display === "" ) { - elem.style.display = show ? values[ index ] || "" : "none"; - } + return this; } +}); - return elements; +jQuery.each({ + appendTo: "append", + prependTo: "prepend", + insertBefore: "before", + insertAfter: "after", + replaceAll: "replaceWith" +}, function( name, original ) { + jQuery.fn[ name ] = function( selector ) { + var elems, + ret = [], + insert = jQuery( selector ), + last = insert.length - 1, + i = 0; + + for ( ; i <= last; i++ ) { + elems = i === last ? this : this.clone( true ); + jQuery( insert[ i ] )[ original ]( elems ); + + // Support: QtWebKit + // .get() because push.apply(_, arraylike) throws + push.apply( ret, elems.get() ); + } + + return this.pushStack( ret ); + }; +}); + + +var iframe, + elemdisplay = {}; + +/** + * Retrieve the actual display of a element + * @param {String} name nodeName of the element + * @param {Object} doc Document object + */ +// Called only from within defaultDisplay +function actualDisplay( name, doc ) { + var style, + elem = jQuery( doc.createElement( name ) ).appendTo( doc.body ), + + // getDefaultComputedStyle might be reliably used only on attached element + display = window.getDefaultComputedStyle && ( style = window.getDefaultComputedStyle( elem[ 0 ] ) ) ? + + // Use of this method is a temporary fix (more like optmization) until something better comes along, + // since it was removed from specification and supported only in FF + style.display : jQuery.css( elem[ 0 ], "display" ); + + // We don't have any data stored on the element, + // so use "detach" method as fast way to get rid of the element + elem.detach(); + + return display; } -jQuery.fn.extend({ - css: function( name, value ) { - return jQuery.access( this, function( elem, name, value ) { - var styles, len, - map = {}, - i = 0; +/** + * Try to determine the default display value of an element + * @param {String} nodeName + */ +function defaultDisplay( nodeName ) { + var doc = document, + display = elemdisplay[ nodeName ]; - if ( jQuery.isArray( name ) ) { - styles = getStyles( elem ); - len = name.length; + if ( !display ) { + display = actualDisplay( nodeName, doc ); - for ( ; i < len; i++ ) { - map[ name[ i ] ] = jQuery.css( elem, name[ i ], false, styles ); - } + // If the simple way fails, read from inside an iframe + if ( display === "none" || !display ) { - return map; - } + // Use the already-created iframe if possible + iframe = (iframe || jQuery( "