From f2d3cba2316a39e3d27e2e93e52562e72e7bd99d Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Fri, 12 Feb 2021 21:20:04 +0300 Subject: add HTTP_ACCEPT_LANGUAGE handling for php8 --- include/functions.php | 78 ++++++++++++++++++++++++++++++++++++--------------- include/sessions.php | 1 - 2 files changed, 55 insertions(+), 24 deletions(-) (limited to 'include') diff --git a/include/functions.php b/include/functions.php index f870f3382..6362adbbe 100644 --- a/include/functions.php +++ b/include/functions.php @@ -91,14 +91,8 @@ define('SUBSTRING_FOR_DATE', 'SUBSTRING'); } - /** - * Return available translations names. - * - * @access public - * @return array A array of available translations. - */ function get_translations() { - $tr = array( + $t = array( "auto" => __("Detect automatically"), "ar_SA" => "العربيّة (Arabic)", "bg_BG" => "Bulgarian", @@ -129,38 +123,76 @@ "fi_FI" => "Suomi", "tr_TR" => "Türkçe"); - return $tr; + return $t; } - require_once "lib/accept-to-gettext.php"; require_once "lib/gettext/gettext.inc.php"; function startup_gettext() { - # Get locale from Accept-Language header - if (version_compare(PHP_VERSION, '8.0.0', '<')) { - $lang = al2gt(array_keys(get_translations()), "text/html"); - } else { - $lang = ""; // FIXME: do something with accept-to-gettext.php - } + $selected_locale = ""; + + // https://www.codingwithjesse.com/blog/use-accept-language-header/ + if (!empty($_SERVER['HTTP_ACCEPT_LANGUAGE'])) { + $valid_langs = []; + $translations = array_keys(get_translations()); + + array_shift($translations); // remove "auto" + + // full locale first + foreach ($translations as $t) { + $lang = strtolower(str_replace("_", "-", (string)$t)); + $valid_langs[$lang] = $t; + + $lang = substr($lang, 0, 2); + if (!isset($valid_langs[$lang])) + $valid_langs[$lang] = $t; + } - if (defined('_TRANSLATION_OVERRIDE_DEFAULT')) { - $lang = _TRANSLATION_OVERRIDE_DEFAULT; + // break up string into pieces (languages and q factors) + preg_match_all('/([a-z]{1,8}(-[a-z]{1,8})?)\s*(;\s*q\s*=\s*(1|0\.[0-9]+))?/i', + $_SERVER['HTTP_ACCEPT_LANGUAGE'], $lang_parse); + + if (count($lang_parse[1])) { + // create a list like "en" => 0.8 + $langs = array_combine($lang_parse[1], $lang_parse[4]); + + if (is_array($langs)) { + // set default to 1 for any without q factor + foreach ($langs as $lang => $val) { + if ($val === '') $langs[$lang] = 1; + } + + // sort list based on value + arsort($langs, SORT_NUMERIC); + + foreach (array_keys($langs) as $lang) { + $lang = strtolower($lang); + + foreach ($valid_langs as $vlang => $vlocale) { + if ($vlang == $lang) { + $selected_locale = $vlocale; + break 2; + } + } + } + } + } } if (!empty($_SESSION["uid"]) && get_schema_version() >= 120) { - $pref_lang = get_pref("USER_LANGUAGE", $_SESSION["uid"]); + $pref_locale = get_pref("USER_LANGUAGE", $_SESSION["uid"]); - if ($pref_lang && $pref_lang != 'auto') { - $lang = $pref_lang; + if (!empty($pref_locale) && $pref_locale != 'auto') { + $selected_locale = $pref_locale; } } - if ($lang) { + if ($selected_locale) { if (defined('LC_MESSAGES')) { - _setlocale(LC_MESSAGES, $lang); + _setlocale(LC_MESSAGES, $selected_locale); } else if (defined('LC_ALL')) { - _setlocale(LC_ALL, $lang); + _setlocale(LC_ALL, $selected_locale); } _bindtextdomain("messages", "locale"); diff --git a/include/sessions.php b/include/sessions.php index d7dde782e..3119a4e07 100644 --- a/include/sessions.php +++ b/include/sessions.php @@ -5,7 +5,6 @@ require_once "classes/db.php"; require_once "autoload.php"; require_once "errorhandler.php"; - require_once "lib/accept-to-gettext.php"; require_once "lib/gettext/gettext.inc.php"; $session_expire = min(2147483647 - time() - 1, max(SESSION_COOKIE_LIFETIME, 86400)); -- cgit v1.2.3-54-g00ecf From 119a4226d812918733a815a896cfed8380188c15 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Fri, 12 Feb 2021 21:21:23 +0300 Subject: validate_csrf: remove warning --- include/functions.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/functions.php b/include/functions.php index 6362adbbe..eaf7d8243 100644 --- a/include/functions.php +++ b/include/functions.php @@ -310,7 +310,7 @@ } function validate_csrf($csrf_token) { - return isset($csrf_token) && hash_equals($_SESSION['csrf_token'], $csrf_token); + return isset($csrf_token) && hash_equals($_SESSION['csrf_token'] ?? "", $csrf_token); } function truncate_string($str, $max_len, $suffix = '…') { -- cgit v1.2.3-54-g00ecf From 6af83e3881b3f38104027275913f7fc55251d020 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Fri, 12 Feb 2021 21:43:38 +0300 Subject: drop ENABLE_GZIP_OUTPUT; system prefs: load php info only if needed --- api/index.php | 8 +------- backend.php | 4 ---- classes/pref/system.php | 20 +++++++++++++------- config.php-dist | 7 ------- include/sanity_config.php | 4 ++-- js/PrefHelpers.js | 7 +++++++ public.php | 4 ---- 7 files changed, 23 insertions(+), 31 deletions(-) (limited to 'include') diff --git a/api/index.php b/api/index.php index 77552af46..9e998df84 100644 --- a/api/index.php +++ b/api/index.php @@ -22,13 +22,7 @@ ini_set('session.use_cookies', 0); ini_set("session.gc_maxlifetime", 86400); - if (defined('ENABLE_GZIP_OUTPUT') && ENABLE_GZIP_OUTPUT && - function_exists("ob_gzhandler")) { - - ob_start("ob_gzhandler"); - } else { - ob_start(); - } + ob_start(); $input = file_get_contents("php://input"); diff --git a/backend.php b/backend.php index 2ea396987..030676dcb 100644 --- a/backend.php +++ b/backend.php @@ -38,10 +38,6 @@ header("Content-Type: text/json; charset=utf-8"); - if (ENABLE_GZIP_OUTPUT && function_exists("ob_gzhandler")) { - ob_start("ob_gzhandler"); - } - if (SINGLE_USER_MODE) { UserHelper::authenticate( "admin", null); } diff --git a/classes/pref/system.php b/classes/pref/system.php index d91339698..a7512915a 100644 --- a/classes/pref/system.php +++ b/classes/pref/system.php @@ -25,6 +25,15 @@ class Pref_System extends Handler_Protected { $this->pdo->query("DELETE FROM ttrss_error_log"); } + function getphpinfo() { + ob_start(); + phpinfo(); + $info = ob_get_contents(); + ob_end_clean(); + + print preg_replace( '%^.*(.*).*$%ms','$1', $info); + } + private function log_viewer(int $page, int $severity) { $errno_values = []; @@ -167,14 +176,11 @@ class Pref_System extends Handler_Protected { print "
"; - ob_start(); - phpinfo(); - $info = ob_get_contents(); - ob_end_clean(); + print ""; - print "
"; - print preg_replace( '%^.*(.*).*$%ms','$1', $info); - print "
"; + print "
" . __("Loading, please wait...") . "
"; print "
"; # accordion pane diff --git a/config.php-dist b/config.php-dist index cd0ee0078..2ee1c719d 100644 --- a/config.php-dist +++ b/config.php-dist @@ -122,13 +122,6 @@ define('CHECK_FOR_UPDATES', true); // Check for updates automatically if running Git version - define('ENABLE_GZIP_OUTPUT', false); - // Selectively gzip output to improve wire performance. This requires - // PHP Zlib extension on the server. - // Enabling this can break tt-rss in several httpd/php configurations, - // if you experience weird errors and tt-rss failing to start, blank pages - // after login, or content encoding errors, disable it. - define('PLUGINS', 'auth_internal, note'); // Comma-separated list of plugins to load automatically for all users. // System plugins have to be specified here. Please enable at least one diff --git a/include/sanity_config.php b/include/sanity_config.php index 7aa4f6b0f..5059ee83b 100644 --- a/include/sanity_config.php +++ b/include/sanity_config.php @@ -1,3 +1,3 @@ - +$required_defines = array( 'DB_TYPE', 'DB_HOST', 'DB_USER', 'DB_NAME', 'DB_PASS', 'MYSQL_CHARSET', 'SELF_URL_PATH', 'SINGLE_USER_MODE', 'SIMPLE_UPDATE_MODE', 'PHP_EXECUTABLE', 'LOCK_DIRECTORY', 'CACHE_DIR', 'ICONS_DIR', 'ICONS_URL', 'AUTH_AUTO_CREATE', 'AUTH_AUTO_LOGIN', 'FORCE_ARTICLE_PURGE', 'SESSION_COOKIE_LIFETIME', 'SMTP_FROM_NAME', 'SMTP_FROM_ADDRESS', 'DIGEST_SUBJECT', 'CHECK_FOR_UPDATES', 'PLUGINS', 'LOG_DESTINATION', 'CONFIG_VERSION'); ?> diff --git a/js/PrefHelpers.js b/js/PrefHelpers.js index 5bb76d179..3b9e985a6 100644 --- a/js/PrefHelpers.js +++ b/js/PrefHelpers.js @@ -51,6 +51,13 @@ const Helpers = { return false; }, + System: { + getPHPInfo: function(widget) { + xhrPost("backend.php", {op: 'pref-system', method: 'getphpinfo'}, (transport) => { + widget.attr('content', transport.responseText); + }); + } + }, EventLog: { log_page: 0, refresh: function() { diff --git a/public.php b/public.php index 36308e25e..3e4a9e023 100644 --- a/public.php +++ b/public.php @@ -16,10 +16,6 @@ if (!init_plugins()) return; - if (ENABLE_GZIP_OUTPUT && function_exists("ob_gzhandler")) { - ob_start("ob_gzhandler"); - } - $method = $_REQUEST["op"]; $override = PluginHost::getInstance()->lookup_handler("public", $method); -- cgit v1.2.3-54-g00ecf From 103d30ad3f92ed03156fee400801d9a38f946b34 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Sat, 13 Feb 2021 22:16:17 +0300 Subject: batch subscribe: use client dialog --- classes/pref/feeds.php | 50 +++------------------------ include/controls.php | 26 +++++++++----- js/PrefFeedTree.js | 94 ++++++++++++++++++++++++++++++++++++-------------- 3 files changed, 90 insertions(+), 80 deletions(-) (limited to 'include') diff --git a/classes/pref/feeds.php b/classes/pref/feeds.php index f337d7f4e..2649d58a1 100755 --- a/classes/pref/feeds.php +++ b/classes/pref/feeds.php @@ -1557,52 +1557,10 @@ class Pref_Feeds extends Handler_Protected { } function batchSubscribe() { - print "
"; - - print_hidden("op", "pref-feeds"); - print_hidden("method", "batchaddfeeds"); - - print "
".__("One valid feed per line (no detection is done)")."
"; - print "
"; - - print ""; - - if (get_pref('ENABLE_FEED_CATS')) { - print "
"; - print " "; - print_feed_cat_select("cat", false, 'dojoType="fox.form.Select"'); - print "
"; - } - - print "
"; - - print ""; - - print ""; - print ""; - - print "
- "; - print "
"; - - print "
- - -
"; - - print "
"; + print json_encode([ + "enable_cats" => (int)get_pref('ENABLE_FEED_CATS'), + "cat_select" => format_feed_cat_select("cat", false, 'dojoType="fox.form.Select"') + ]); } function batchAddFeeds() { diff --git a/include/controls.php b/include/controls.php index 8f49e99c5..e6678db9a 100755 --- a/include/controls.php +++ b/include/controls.php @@ -181,11 +181,19 @@ function print_feed_multi_select($id, $default_ids = [], } } -function print_feed_cat_select($id, $default_id, - $attributes, $include_all_cats = true, $root_id = null, $nest_level = 0) { +function print_feed_cat_select($id, $default_id, $attributes, $include_all_cats = true, + $root_id = null, $nest_level = 0) { + + print format_feed_cat_select($id, $default_id, $attributes, $include_all_cats, $root_id, $nest_level); +} + +function format_feed_cat_select($id, $default_id, $attributes, $include_all_cats = true, + $root_id = null, $nest_level = 0) { + + $ret = ""; if (!$root_id) { - print ""; } $pdo = Db::pdo(); @@ -215,18 +223,18 @@ function print_feed_cat_select($id, $default_id, $line["title"] = " " . $line["title"]; if ($line["title"]) - printf("", + $ret .= sprintf("", $line["id"], htmlspecialchars($line["title"])); if ($line["num_children"] > 0) - print_feed_cat_select($id, $default_id, $attributes, + $ret .= format_feed_cat_select($id, $default_id, $attributes, $include_all_cats, $line["id"], $nest_level+1); } if (!$root_id) { if ($include_all_cats) { if ($found > 0) { - print ""; + $ret .= ""; } if ($default_id == 0) { @@ -235,10 +243,12 @@ function print_feed_cat_select($id, $default_id, $is_selected = ""; } - print ""; + $ret .= ""; } - print ""; + $ret .= ""; } + + return $ret; } function stylesheet_tag($filename, $id = false) { diff --git a/js/PrefFeedTree.js b/js/PrefFeedTree.js index 3e3584a9a..c3dda4187 100644 --- a/js/PrefFeedTree.js +++ b/js/PrefFeedTree.js @@ -378,35 +378,77 @@ define(["dojo/_base/declare", "dojo/dom-construct", "lib/CheckBoxTree", "dojo/_b } }, batchSubscribe: function() { - const dialog = new fox.SingleUseDialog({ - id: "batchSubDlg", - title: __("Batch subscribe"), - execute: function () { - if (this.validate()) { - Notify.progress(__("Subscribing to feeds..."), true); - - xhrPost("backend.php", this.attr('value'), () => { - Notify.close(); - - const tree = dijit.byId("feedTree"); - if (tree) tree.reload(); - - dialog.hide(); - }); - } - }, - content: __("Loading, please wait...") - }); + xhrJson("backend.php", {op: 'pref-feeds', method: 'batchSubscribe'}, (reply) => { + const dialog = new fox.SingleUseDialog({ + id: "batchSubDlg", + title: __("Batch subscribe"), + execute: function () { + if (this.validate()) { + Notify.progress(__("Subscribing to feeds..."), true); - const tmph = dojo.connect(dialog, 'onShow', function () { - dojo.disconnect(tmph); + xhrPost("backend.php", this.attr('value'), () => { + Notify.close(); - xhrPost("backend.php", {op: 'pref-feeds', method: 'batchSubscribe'}, (transport) => { - dialog.attr('content', transport.responseText); - }) - }); + const tree = dijit.byId("feedTree"); + if (tree) tree.reload(); + + dialog.hide(); + }); + } + }, + content: ` +
+ ${App.FormFields.hidden("op", "pref-feeds")} + ${App.FormFields.hidden("method", "batchaddfeeds")} + +
+ ${__("One valid feed per line (no detection is done)")} +
+ +
+ + + ${reply.enable_cats ? + `
+ + ${reply.cat_select} +
+ ` : '' + } +
+ + + + + +
+ +
+ +
+ + +
+
+ ` + }); - dialog.show(); + dialog.show(); + + }); }, showInactiveFeeds: function() { xhrJson("backend.php", {op: 'pref-feeds', method: 'inactivefeeds'}, function (reply) { -- cgit v1.2.3-54-g00ecf From 15fd23c374eb32c50733de1906065f552afbfbc4 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Sun, 14 Feb 2021 09:15:51 +0300 Subject: use shortcut echo syntax for php templates --- classes/feeds.php | 10 +++--- classes/handler/public.php | 44 +++++++++++------------ classes/pref/feeds.php | 74 +++++++++++++++++++------------------- classes/pref/filters.php | 20 +++++------ classes/pref/labels.php | 12 +++---- classes/pref/system.php | 42 +++++++++++----------- include/login_form.php | 34 +++++++++--------- index.php | 78 ++++++++++++++++++++--------------------- plugins/af_redditimgur/init.php | 4 +-- plugins/auth_internal/init.php | 18 +++++----- plugins/toggle_sidebar/init.php | 2 +- prefs.php | 28 +++++++-------- 12 files changed, 183 insertions(+), 183 deletions(-) (limited to 'include') diff --git a/classes/feeds.php b/classes/feeds.php index 031a671ae..e6bd1459d 100755 --- a/classes/feeds.php +++ b/classes/feeds.php @@ -838,14 +838,14 @@ class Feeds extends Handler_Protected {
-

Feed Debugger: getFeedTitle($feed_id) ?>

+

Feed Debugger: getFeedTitle($feed_id) ?>

- + - +
- +
- +
diff --git a/classes/handler/public.php b/classes/handler/public.php index db8a924ad..481145606 100755 --- a/classes/handler/public.php +++ b/classes/handler/public.php @@ -519,7 +519,7 @@ class Handler_Public extends Handler { - <?php echo __("Share with Tiny Tiny RSS") ?> + <?= __("Share with Tiny Tiny RSS") ?>
- - + +
- - + +
- +
- +
- - - + + +
@@ -635,24 +635,24 @@ class Handler_Public extends Handler { -
+
- + " /> + required="1" value="" />
- + "/> + value=""/>

@@ -660,7 +660,7 @@ class Handler_Public extends Handler {
- +
@@ -781,7 +781,7 @@ class Handler_Public extends Handler { };
-

+

- +
- + Database Updater - +
-

+

diff --git a/classes/pref/feeds.php b/classes/pref/feeds.php index 72a8344ad..6b5df0289 100755 --- a/classes/pref/feeds.php +++ b/classes/pref/feeds.php @@ -1223,52 +1223,52 @@ class Pref_Feeds extends Handler_Protected {
+ value=""> +
- +
+ dojoType="dijit.MenuItem">
+ dojoType="dijit.MenuItem">
- +
+ dojoType="dijit.MenuItem">
+ dojoType="dijit.MenuItem">
+ dojoType="dijit.MenuItem">
+ dojoType="dijit.MenuItem">
-
+
- +
+ dojoType="dijit.MenuItem">
+ dojoType="dijit.MenuItem">
+ dojoType="dijit.MenuItem">
- - + +
" + autoExpand="" persist="true" model="feedModel" openOnClick="false"> @@ -1311,19 +1311,19 @@ class Pref_Feeds extends Handler_Protected { private function index_opml() { ?> -

+

-
+ + + + + diff --git a/classes/pref/labels.php b/classes/pref/labels.php index 22a2dddea..d182a0995 100644 --- a/classes/pref/labels.php +++ b/classes/pref/labels.php @@ -195,23 +195,23 @@ class Pref_Labels extends Handler_Protected {
- +
+ dojoType='dijit.MenuItem'>
+ dojoType='dijit.MenuItem'>
+ + + diff --git a/classes/pref/system.php b/classes/pref/system.php index 72e15e4f3..1adddf116 100644 --- a/classes/pref/system.php +++ b/classes/pref/system.php @@ -76,30 +76,30 @@ class Pref_System extends Handler_Protected {
- -
- + - - - - - + + + + + - + - - - + + + - + @@ -162,7 +162,7 @@ class Pref_System extends Handler_Protected { $page = (int) ($_REQUEST["page"] ?? 0); ?>
-
'> +
'> log_viewer($page, $severity); @@ -172,11 +172,11 @@ class Pref_System extends Handler_Protected { ?>
-
'> +
'> -
+
run_hooks(PluginHost::HOOK_PREFS_TAB, "prefSystem") ?> diff --git a/include/login_form.php b/include/login_form.php index aec305b13..aa6a72260 100755 --- a/include/login_form.php +++ b/include/login_form.php @@ -92,29 +92,29 @@
-

+

-
- +
- + " /> + required="1" value="" />
- + "/> + value=""/>
- +
- +
@@ -143,11 +143,11 @@ +
- +
@@ -155,11 +155,11 @@ +
- +
0) { ?> @@ -167,7 +167,7 @@ @@ -177,7 +177,7 @@
- +
@@ -185,7 +185,7 @@
diff --git a/index.php b/index.php index fa23570ff..8bfca1af2 100644 --- a/index.php +++ b/index.php @@ -47,7 +47,7 @@ } ?> @@ -68,7 +68,7 @@ @@ -50,7 +50,7 @@
-

Feed Debugger: getFeedTitle($feed_id) ?>

+

Feed Debugger: _get_title($feed_id) ?>

@@ -731,7 +731,7 @@ class Feeds extends Handler_Protected { } - static function catchup_feed($feed, $cat_view, $owner_uid = false, $mode = 'all', $search = false) { + static function _catchup($feed, $cat_view, $owner_uid = false, $mode = 'all', $search = false) { if (!$owner_uid) $owner_uid = $_SESSION['uid']; @@ -751,7 +751,7 @@ class Feeds extends Handler_Protected { // fall back in case of no plugins if (empty($search_qpart)) { - list($search_qpart, $search_words) = self::search_to_sql($search[0], $search[1], $owner_uid); + list($search_qpart, $search_words) = self::_search_to_sql($search[0], $search[1], $owner_uid); } } else { $search_qpart = "true"; @@ -791,7 +791,7 @@ class Feeds extends Handler_Protected { if ($feed >= 0) { if ($feed > 0) { - $children = self::getChildCategories($feed, $owner_uid); + $children = self::_get_child_cats($feed, $owner_uid); array_push($children, $feed); $children = array_map("intval", $children); @@ -902,7 +902,7 @@ class Feeds extends Handler_Protected { } } - static function getFeedArticles($feed, $is_cat = false, $unread_only = false, + static function _get_counters($feed, $is_cat = false, $unread_only = false, $owner_uid = false) { $n_feed = (int) $feed; @@ -921,7 +921,7 @@ class Feeds extends Handler_Protected { $match_part = ""; if ($is_cat) { - return self::getCategoryUnread($n_feed, $owner_uid); + return self::_get_cat_unread($n_feed, $owner_uid); } else if ($n_feed == -6) { return 0; } else if ($feed != "0" && $n_feed == 0) { @@ -967,7 +967,7 @@ class Feeds extends Handler_Protected { $label_id = Labels::feed_to_label_id($feed); - return self::getLabelUnread($label_id, $owner_uid); + return self::_get_label_unread($label_id, $owner_uid); } if ($match_part) { @@ -1009,7 +1009,7 @@ class Feeds extends Handler_Protected { $login = $need_auth ? clean($_REQUEST['login']) : ''; $pass = $need_auth ? clean($_REQUEST['pass']) : ''; - $rc = Feeds::subscribe_to_feed($feed, $cat, $login, $pass); + $rc = Feeds::_subscribe($feed, $cat, $login, $pass); print json_encode(array("result" => $rc)); } @@ -1027,7 +1027,7 @@ class Feeds extends Handler_Protected { * 5 - Couldn't download the URL content. * 6 - Content is an invalid XML. */ - static function subscribe_to_feed($url, $cat_id = 0, + static function _subscribe($url, $cat_id = 0, $auth_login = '', $auth_pass = '') { global $fetch_last_error; @@ -1056,8 +1056,8 @@ class Feeds extends Handler_Protected { return array("code" => 5, "message" => $fetch_last_error); } - if (mb_strpos($fetch_last_content_type, "html") !== false && self::is_html($contents)) { - $feedUrls = self::get_feeds_from_html($url, $contents); + if (mb_strpos($fetch_last_content_type, "html") !== false && self::_is_html($contents)) { + $feedUrls = self::_get_feeds_from_html($url, $contents); if (count($feedUrls) == 0) { return array("code" => 3); @@ -1100,15 +1100,15 @@ class Feeds extends Handler_Protected { } } - static function getIconFile($feed_id) { + static function _get_icon_file($feed_id) { return ICONS_DIR . "/$feed_id.ico"; } - static function feedHasIcon($id) { + static function _has_icon($id) { return is_file(ICONS_DIR . "/$id.ico") && filesize(ICONS_DIR . "/$id.ico") > 0; } - static function getFeedIcon($id) { + static function _get_icon($id) { switch ($id) { case 0: return "archive"; @@ -1132,7 +1132,7 @@ class Feeds extends Handler_Protected { if ($id < LABEL_BASE_INDEX) { return "label"; } else { - $icon = self::getIconFile($id); + $icon = self::_get_icon_file($id); if ($icon && file_exists($icon)) { return ICONS_URL . "/" . basename($icon) . "?" . filemtime($icon); @@ -1144,11 +1144,11 @@ class Feeds extends Handler_Protected { return false; } - static function getFeedTitle($id, $cat = false) { + static function _get_title($id, $cat = false) { $pdo = Db::pdo(); if ($cat) { - return self::getCategoryTitle($id); + return self::_get_cat_title($id); } else if ($id == -1) { return __("Starred articles"); } else if ($id == -2) { @@ -1191,7 +1191,7 @@ class Feeds extends Handler_Protected { } // only real cats - static function getCategoryMarked($cat, $owner_uid = false) { + static function _get_cat_marked($cat, $owner_uid = false) { if (!$owner_uid) $owner_uid = $_SESSION["uid"]; @@ -1214,7 +1214,7 @@ class Feeds extends Handler_Protected { } } - static function getCategoryUnread($cat, $owner_uid = false) { + static function _get_cat_unread($cat, $owner_uid = false) { if (!$owner_uid) $owner_uid = $_SESSION["uid"]; @@ -1248,7 +1248,7 @@ class Feeds extends Handler_Protected { } // only accepts real cats (>= 0) - static function getCategoryChildrenUnread($cat, $owner_uid = false) { + static function _get_cat_children_unread($cat, $owner_uid = false) { if (!$owner_uid) $owner_uid = $_SESSION["uid"]; $pdo = Db::pdo(); @@ -1260,14 +1260,14 @@ class Feeds extends Handler_Protected { $unread = 0; while ($line = $sth->fetch()) { - $unread += self::getCategoryUnread($line["id"], $owner_uid); - $unread += self::getCategoryChildrenUnread($line["id"], $owner_uid); + $unread += self::_get_cat_unread($line["id"], $owner_uid); + $unread += self::_get_cat_children_unread($line["id"], $owner_uid); } return $unread; } - static function getGlobalUnread($user_id = false) { + static function _get_global_unread($user_id = false) { if (!$user_id) $user_id = $_SESSION["uid"]; @@ -1283,7 +1283,7 @@ class Feeds extends Handler_Protected { return $row["count"]; } - static function getCategoryTitle($cat_id) { + static function _get_cat_title($cat_id) { if ($cat_id == -1) { return __("Special"); @@ -1305,7 +1305,7 @@ class Feeds extends Handler_Protected { } } - static function getLabelUnread($label_id, $owner_uid = false) { + private static function _get_label_unread($label_id, $owner_uid = false) { if (!$owner_uid) $owner_uid = $_SESSION["uid"]; $pdo = Db::pdo(); @@ -1322,7 +1322,7 @@ class Feeds extends Handler_Protected { } } - static function queryFeedHeadlines($params) { + static function _get_headlines($params) { $pdo = Db::pdo(); @@ -1368,7 +1368,7 @@ class Feeds extends Handler_Protected { // fall back in case of no plugins if (!$search_query_part) { - list($search_query_part, $search_words) = self::search_to_sql($search, $search_language, $owner_uid); + list($search_query_part, $search_words) = self::_search_to_sql($search, $search_language, $owner_uid); } if (DB_TYPE == "pgsql") { @@ -1406,7 +1406,7 @@ class Feeds extends Handler_Protected { $unread = getFeedUnread($feed, $cat_view); if ($cat_view && $feed > 0 && $include_children) - $unread += self::getCategoryChildrenUnread($feed); + $unread += self::_get_cat_children_unread($feed); if ($unread > 0) { $view_query_part = " unread = true AND "; @@ -1450,7 +1450,7 @@ class Feeds extends Handler_Protected { if ($feed > 0) { if ($include_children) { # sub-cats - $subcats = self::getChildCategories($feed, $owner_uid); + $subcats = self::_get_child_cats($feed, $owner_uid); array_push($subcats, $feed); $subcats = array_map("intval", $subcats); @@ -1574,7 +1574,7 @@ class Feeds extends Handler_Protected { $feed_title = T_sprintf("Search results: %s", $search); } else { if ($cat_view) { - $feed_title = self::getCategoryTitle($feed); + $feed_title = self::_get_cat_title($feed); } else { if (is_numeric($feed) && $feed > 0) { $ssth = $pdo->prepare("SELECT title,site_url,last_error,last_updated @@ -1587,7 +1587,7 @@ class Feeds extends Handler_Protected { $last_error = $row["last_error"]; $last_updated = $row["last_updated"]; } else { - $feed_title = self::getFeedTitle($feed); + $feed_title = self::_get_title($feed); } } } @@ -1802,7 +1802,7 @@ class Feeds extends Handler_Protected { } - static function getParentCategories($cat, $owner_uid) { + static function _get_parent_cats($cat, $owner_uid) { $rv = array(); $pdo = Db::pdo(); @@ -1813,13 +1813,13 @@ class Feeds extends Handler_Protected { while ($line = $sth->fetch()) { array_push($rv, $line["parent_cat"]); - $rv = array_merge($rv, self::getParentCategories($line["parent_cat"], $owner_uid)); + $rv = array_merge($rv, self::_get_parent_cats($line["parent_cat"], $owner_uid)); } return $rv; } - static function getChildCategories($cat, $owner_uid) { + static function _get_child_cats($cat, $owner_uid) { $rv = array(); $pdo = Db::pdo(); @@ -1830,13 +1830,13 @@ class Feeds extends Handler_Protected { while ($line = $sth->fetch()) { array_push($rv, $line["id"]); - $rv = array_merge($rv, self::getChildCategories($line["id"], $owner_uid)); + $rv = array_merge($rv, self::_get_child_cats($line["id"], $owner_uid)); } return $rv; } - static function getFeedCategory($feed) { + static function _cat_of_feed($feed) { $pdo = Db::pdo(); $sth = $pdo->prepare("SELECT cat_id FROM ttrss_feeds @@ -1851,7 +1851,7 @@ class Feeds extends Handler_Protected { } - function color_of($name) { + private function _color_of($name) { $colormap = [ "#1cd7d7","#d91111","#1212d7","#8e16e5","#7b7b7b", "#39f110","#0bbea6","#ec0e0e","#1534f2","#b9e416", "#479af2","#f36b14","#10c7e9","#1e8fe7","#e22727" ]; @@ -1867,7 +1867,7 @@ class Feeds extends Handler_Protected { return $colormap[$sum]; } - static function get_feeds_from_html($url, $content) { + private static function _get_feeds_from_html($url, $content) { $url = UrlHelper::validate($url); $baseUrl = substr($url, 0, strrpos($url, '/') + 1); @@ -1895,11 +1895,11 @@ class Feeds extends Handler_Protected { return $feedUrls; } - static function is_html($content) { + static function _is_html($content) { return preg_match("/

".__('OPML Utility')."

"; - Feeds::add_feed_category("Imported feeds"); + Feeds::_add_cat("Imported feeds"); $this->opml_notice(__("Importing OPML...")); @@ -205,7 +205,7 @@ class OPML extends Handler_Protected { if (!$tmp_line["match_on"]) { if ($cat_filter && $tmp_line["cat_id"] || $tmp_line["feed_id"]) { - $tmp_line["feed"] = Feeds::getFeedTitle( + $tmp_line["feed"] = Feeds::_get_title( $cat_filter ? $tmp_line["cat_id"] : $tmp_line["feed_id"], $cat_filter); } else { @@ -218,13 +218,13 @@ class OPML extends Handler_Protected { if (strpos($feed_id, "CAT:") === 0) { $feed_id = (int)substr($feed_id, 4); if ($feed_id) { - array_push($match, [Feeds::getCategoryTitle($feed_id), true, false]); + array_push($match, [Feeds::_get_cat_title($feed_id), true, false]); } else { array_push($match, [0, true, true]); } } else { if ($feed_id) { - array_push($match, [Feeds::getFeedTitle((int)$feed_id), false, false]); + array_push($match, [Feeds::_get_title((int)$feed_id), false, false]); } else { array_push($match, [0, false, true]); } @@ -523,7 +523,7 @@ class OPML extends Handler_Protected { $order_id = (int) $root_node->attributes->getNamedItem('ttrssSortOrder')->nodeValue; if (!$order_id) $order_id = 0; - Feeds::add_feed_category($cat_title, $parent_id, $order_id); + Feeds::_add_cat($cat_title, $parent_id, $order_id); $cat_id = $this->get_feed_category($cat_title, $parent_id); } @@ -638,7 +638,7 @@ class OPML extends Handler_Protected { $url_path = get_self_url_prefix(); $url_path .= "/opml.php?op=publish&key=" . - Feeds::get_feed_access_key('OPML:Publish', false, $_SESSION["uid"]); + Feeds::_get_access_key('OPML:Publish', false, $_SESSION["uid"]); return $url_path; } diff --git a/classes/pref/feeds.php b/classes/pref/feeds.php index af3877159..edba71c5c 100755 --- a/classes/pref/feeds.php +++ b/classes/pref/feeds.php @@ -98,7 +98,7 @@ class Pref_Feeds extends Handler_Protected { $feed['checkbox'] = false; $feed['unread'] = -1; $feed['error'] = $feed_line['last_error']; - $feed['icon'] = Feeds::getFeedIcon($feed_line['id']); + $feed['icon'] = Feeds::_get_icon($feed_line['id']); $feed['param'] = TimeHelper::make_local_datetime( $feed_line['last_updated'], true); $feed['updates_disabled'] = (int)($feed_line['update_interval'] < 0); @@ -266,7 +266,7 @@ class Pref_Feeds extends Handler_Protected { $feed['name'] = $feed_line['title']; $feed['checkbox'] = false; $feed['error'] = $feed_line['last_error']; - $feed['icon'] = Feeds::getFeedIcon($feed_line['id']); + $feed['icon'] = Feeds::_get_icon($feed_line['id']); $feed['param'] = TimeHelper::make_local_datetime( $feed_line['last_updated'], true); $feed['unread'] = -1; @@ -301,7 +301,7 @@ class Pref_Feeds extends Handler_Protected { $feed['name'] = $feed_line['title']; $feed['checkbox'] = false; $feed['error'] = $feed_line['last_error']; - $feed['icon'] = Feeds::getFeedIcon($feed_line['id']); + $feed['icon'] = Feeds::_get_icon($feed_line['id']); $feed['param'] = TimeHelper::make_local_datetime( $feed_line['last_updated'], true); $feed['unread'] = -1; @@ -777,7 +777,7 @@ class Pref_Feeds extends Handler_Protected { /* Icon */ - print ""; + print ""; print " @@ -1189,7 +1189,7 @@ class Pref_Feeds extends Handler_Protected { function addCat() { $feed_cat = clean($_REQUEST["cat"]); - Feeds::add_feed_category($feed_cat); + Feeds::_add_cat($feed_cat); } function importOpml() { @@ -1420,9 +1420,9 @@ class Pref_Feeds extends Handler_Protected { $obj['id'] = 'CAT:' . $cat_id; $obj['items'] = array(); - $obj['name'] = Feeds::getCategoryTitle($cat_id); + $obj['name'] = Feeds::_get_cat_title($cat_id); $obj['type'] = 'category'; - $obj['unread'] = -1; //(int) Feeds::getCategoryUnread($cat_id); + $obj['unread'] = -1; //(int) Feeds::_get_cat_unread($cat_id); $obj['bare_id'] = $cat_id; return $obj; @@ -1433,7 +1433,7 @@ class Pref_Feeds extends Handler_Protected { $feed_id = (int) $feed_id; if (!$title) - $title = Feeds::getFeedTitle($feed_id, false); + $title = Feeds::_get_title($feed_id, false); if ($unread === false) $unread = getFeedUnread($feed_id, false); @@ -1444,7 +1444,7 @@ class Pref_Feeds extends Handler_Protected { $obj['type'] = 'feed'; $obj['error'] = $error; $obj['updated'] = $updated; - $obj['icon'] = Feeds::getFeedIcon($feed_id); + $obj['icon'] = Feeds::_get_icon($feed_id); $obj['bare_id'] = $feed_id; $obj['auxcounter'] = 0; @@ -1611,11 +1611,11 @@ class Pref_Feeds extends Handler_Protected { 'id' => $feed_id, 'is_cat' => (int)$is_cat, 'q' => $search, - 'key' => Feeds::get_feed_access_key($feed_id, $is_cat, $_SESSION["uid"]) + 'key' => Feeds::_get_access_key($feed_id, $is_cat, $_SESSION["uid"]) ]); print json_encode([ - "title" => Feeds::getFeedTitle($feed_id, $is_cat), + "title" => Feeds::_get_title($feed_id, $is_cat), "link" => $link ]); } @@ -1627,7 +1627,7 @@ class Pref_Feeds extends Handler_Protected { WHERE feed_id = ? AND is_cat = ? AND owner_uid = ?"); $sth->execute([$feed_id, bool_to_sql_bool($is_cat), $owner_uid]); - return Feeds::get_feed_access_key($feed_id, $is_cat, $owner_uid); + return Feeds::_get_access_key($feed_id, $is_cat, $owner_uid); } // Silent diff --git a/classes/pref/filters.php b/classes/pref/filters.php index 1c264f642..62bcb8f59 100755 --- a/classes/pref/filters.php +++ b/classes/pref/filters.php @@ -189,10 +189,10 @@ class Pref_Filters extends Handler_Protected { if (strpos($feed_id, "CAT:") === 0) { $feed_id = (int)substr($feed_id, 4); - array_push($feeds_fmt, Feeds::getCategoryTitle($feed_id)); + array_push($feeds_fmt, Feeds::_get_cat_title($feed_id)); } else { if ($feed_id) - array_push($feeds_fmt, Feeds::getFeedTitle((int)$feed_id)); + array_push($feeds_fmt, Feeds::_get_title((int)$feed_id)); else array_push($feeds_fmt, __("All feeds")); } @@ -203,9 +203,9 @@ class Pref_Filters extends Handler_Protected { } else { $where = $line["cat_filter"] ? - Feeds::getCategoryTitle($line["cat_id"]) : + Feeds::_get_cat_title($line["cat_id"]) : ($line["feed_id"] ? - Feeds::getFeedTitle($line["feed_id"]) : __("All feeds")); + Feeds::_get_title($line["feed_id"]) : __("All feeds")); } # $where = $line["cat_id"] . "/" . $line["feed_id"]; @@ -494,10 +494,10 @@ class Pref_Filters extends Handler_Protected { if (strpos($feed_id, "CAT:") === 0) { $feed_id = (int)substr($feed_id, 4); - array_push($feeds_fmt, Feeds::getCategoryTitle($feed_id)); + array_push($feeds_fmt, Feeds::_get_cat_title($feed_id)); } else { if ($feed_id) - array_push($feeds_fmt, Feeds::getFeedTitle((int)$feed_id)); + array_push($feeds_fmt, Feeds::_get_title((int)$feed_id)); else array_push($feeds_fmt, __("All feeds")); } diff --git a/classes/rpc.php b/classes/rpc.php index 2bbaf4135..206a93d60 100755 --- a/classes/rpc.php +++ b/classes/rpc.php @@ -219,7 +219,7 @@ class RPC extends Handler_Protected { $search_query = clean($_REQUEST['search_query']); $search_lang = clean($_REQUEST['search_lang']); - Feeds::catchup_feed($feed_id, $is_cat, false, $mode, [$search_query, $search_lang]); + Feeds::_catchup($feed_id, $is_cat, false, $mode, [$search_query, $search_lang]); // return counters here synchronously so that frontend can figure out next unread feed properly print json_encode(['counters' => Counters::getAllCounters()]); diff --git a/classes/rssutils.php b/classes/rssutils.php index 9dd7c4ab1..0e90d8fe4 100755 --- a/classes/rssutils.php +++ b/classes/rssutils.php @@ -1239,7 +1239,7 @@ class RSSUtils { Debug::log("purging feed...", Debug::$LOG_VERBOSE); - Feeds::purge_feed($feed, 0); + Feeds::_purge($feed, 0); $sth = $pdo->prepare("UPDATE ttrss_feeds SET last_updated = NOW(), @@ -1706,7 +1706,7 @@ class RSSUtils { $filters = array(); $feed_id = (int) $feed_id; - $cat_id = (int)Feeds::getFeedCategory($feed_id); + $cat_id = (int)Feeds::_cat_of_feed($feed_id); if ($cat_id == 0) $null_cat_qpart = "cat_id IS NULL OR"; @@ -1720,7 +1720,7 @@ class RSSUtils { $sth->execute([$owner_uid]); $check_cats = array_merge( - Feeds::getParentCategories($cat_id, $owner_uid), + Feeds::_get_parent_cats($cat_id, $owner_uid), [$cat_id]); $check_cats_str = join(",", $check_cats); diff --git a/include/functions.php b/include/functions.php index eaf7d8243..4557c0411 100644 --- a/include/functions.php +++ b/include/functions.php @@ -217,7 +217,7 @@ // @deprecated function getFeedUnread($feed, $is_cat = false) { - return Feeds::getFeedArticles($feed, $is_cat, true, $_SESSION["uid"]); + return Feeds::_get_counters($feed, $is_cat, true, $_SESSION["uid"]); } // @deprecated diff --git a/js/Feeds.js b/js/Feeds.js index 986936285..73f1bc338 100644 --- a/js/Feeds.js +++ b/js/Feeds.js @@ -501,7 +501,7 @@ const Feeds = { const tree = dijit.byId("feedTree"); if (tree && tree.model) - return tree.getFeedCategory(feed); + return tree._cat_of_feed(feed); } catch (e) { // diff --git a/plugins/af_comics/filters/af_comics_gocomics.php b/plugins/af_comics/filters/af_comics_gocomics.php index 949b7a235..6c5c7c0d3 100644 --- a/plugins/af_comics/filters/af_comics_gocomics.php +++ b/plugins/af_comics/filters/af_comics_gocomics.php @@ -11,7 +11,7 @@ class Af_Comics_Gocomics extends Af_ComicFilter { public function on_subscribe($url) { if (preg_match('#^https?://www\.gocomics\.com/([-a-z0-9]+)$#i', $url)) - return ''; // Get is_html() to return false. + return ''; // Get _is_html() to return false. else return false; } diff --git a/plugins/af_comics/filters/af_comics_gocomics_farside.php b/plugins/af_comics/filters/af_comics_gocomics_farside.php index 9663da8f9..89322209d 100644 --- a/plugins/af_comics/filters/af_comics_gocomics_farside.php +++ b/plugins/af_comics/filters/af_comics_gocomics_farside.php @@ -11,7 +11,7 @@ class Af_Comics_Gocomics_FarSide extends Af_ComicFilter { public function on_subscribe($url) { if (preg_match("#^https?://www\.thefarside\.com#", $url)) - return ''; // Get is_html() to return false. + return ''; // Get _is_html() to return false. else return false; } diff --git a/plugins/af_psql_trgm/init.php b/plugins/af_psql_trgm/init.php index 163b0ec38..47ff98fc2 100644 --- a/plugins/af_psql_trgm/init.php +++ b/plugins/af_psql_trgm/init.php @@ -209,7 +209,7 @@ class Af_Psql_Trgm extends Plugin { print "
  • " . "rss_feed " . - Feeds::getFeedTitle($f) . "
  • "; + Feeds::_get_title($f) . ""; } print ""; } diff --git a/plugins/af_readability/init.php b/plugins/af_readability/init.php index a76c98380..4d21a831c 100755 --- a/plugins/af_readability/init.php +++ b/plugins/af_readability/init.php @@ -122,7 +122,7 @@ class Af_Readability extends Plugin { print "
  • rss_feed ". - Feeds::getFeedTitle($f) . " " . ($is_append ? __("(append)") : "") . "
  • "; + Feeds::_get_title($f) . " " . ($is_append ? __("(append)") : "") . ""; } print ""; } diff --git a/plugins/vf_shared/init.php b/plugins/vf_shared/init.php index 8c38cbf32..1112f6f2f 100644 --- a/plugins/vf_shared/init.php +++ b/plugins/vf_shared/init.php @@ -60,7 +60,7 @@ class VF_Shared extends Plugin { "override_vfeed" => "ttrss_feeds.title AS feed_title," ); - $qfh_ret = Feeds::queryFeedHeadlines($params); + $qfh_ret = Feeds::_get_headlines($params); $qfh_ret[1] = __("Shared articles"); return $qfh_ret; -- cgit v1.2.3-54-g00ecf From cfad740c9916132dced9f46826313da6f7b7dd1d Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Mon, 15 Feb 2021 16:38:18 +0300 Subject: drop legacy db_ functions wrapper --- include/db.php | 38 -------------------------------------- 1 file changed, 38 deletions(-) delete mode 100644 include/db.php (limited to 'include') diff --git a/include/db.php b/include/db.php deleted file mode 100644 index c02e90ef2..000000000 --- a/include/db.php +++ /dev/null @@ -1,38 +0,0 @@ -escape_string($s, $strip_tags); -} - -function db_query($query, $die_on_error = true) { - return Db::get()->query($query, $die_on_error); -} - -function db_fetch_assoc($result) { - return Db::get()->fetch_assoc($result); -} - - -function db_num_rows($result) { - return Db::get()->num_rows($result); -} - -function db_fetch_result($result, $row, $param) { - return Db::get()->fetch_result($result, $row, $param); -} - -function db_affected_rows($result) { - return Db::get()->affected_rows($result); -} - -function db_last_error() { - return Db::get()->last_error(); -} - -function db_last_query_error() { - return Db::get()->last_query_error(); -} - -function db_quote($str){ - return Db::get()->quote($str); -} -- cgit v1.2.3-54-g00ecf From 9f55454f63b11ad8d2b2e0a8264a0f0dae919f6b Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Mon, 15 Feb 2021 16:51:35 +0300 Subject: remove the rest of db.php; rename some leftover methods in feeds --- api/index.php | 1 - backend.php | 1 - classes/feeds.php | 12 ++++++------ include/db-prefs.php | 2 -- include/sessions.php | 1 - js/App.js | 3 +-- js/FeedTree.js | 2 +- opml.php | 1 - public.php | 1 - update.php | 1 - update_daemon2.php | 1 - 11 files changed, 8 insertions(+), 18 deletions(-) (limited to 'include') diff --git a/api/index.php b/api/index.php index 9e998df84..664e92abe 100644 --- a/api/index.php +++ b/api/index.php @@ -14,7 +14,6 @@ define('NO_SESSION_AUTOSTART', true); require_once "autoload.php"; - require_once "db.php"; require_once "db-prefs.php"; require_once "functions.php"; require_once "sessions.php"; diff --git a/backend.php b/backend.php index e72d97ca4..89b06b7eb 100644 --- a/backend.php +++ b/backend.php @@ -27,7 +27,6 @@ require_once "sessions.php"; require_once "functions.php"; require_once "config.php"; - require_once "db.php"; require_once "db-prefs.php"; $op = (string)clean($op); diff --git a/classes/feeds.php b/classes/feeds.php index 07e3aa455..2a3efdb92 100755 --- a/classes/feeds.php +++ b/classes/feeds.php @@ -438,7 +438,7 @@ class Feeds extends Handler_Protected { * when there's nothing to load - e.g. no stuff in fresh feed */ if ($feed == -5) { - print json_encode($this->generate_dashboard_feed()); + print json_encode($this->_generate_dashboard_feed()); return; } @@ -466,7 +466,7 @@ class Feeds extends Handler_Protected { } if ($sth && !$sth->fetch()) { - print json_encode($this->generate_error_feed(__("Feed not found."))); + print json_encode($this->_generate_error_feed(__("Feed not found."))); return; } @@ -523,7 +523,7 @@ class Feeds extends Handler_Protected { } - private function generate_dashboard_feed() { + private function _generate_dashboard_feed() { $reply = array(); $reply['headlines']['id'] = -5; @@ -565,7 +565,7 @@ class Feeds extends Handler_Protected { return $reply; } - private function generate_error_feed($error) { + private function _generate_error_feed($error) { $reply = array(); $reply['headlines']['id'] = -7; @@ -630,7 +630,7 @@ class Feeds extends Handler_Protected { print ""; } - function update_debugger() { + function updatedebugger() { header("Content-type: text/html"); $xdebug = isset($_REQUEST["xdebug"]) ? (int)$_REQUEST["xdebug"] : 1; @@ -690,7 +690,7 @@ class Feeds extends Handler_Protected {
    - + diff --git a/include/db-prefs.php b/include/db-prefs.php index 91235b479..ce5753638 100644 --- a/include/db-prefs.php +++ b/include/db-prefs.php @@ -1,6 +1,4 @@ read($pref_name, $user_id, $die_on_error); } diff --git a/include/sessions.php b/include/sessions.php index 3119a4e07..4de894c95 100644 --- a/include/sessions.php +++ b/include/sessions.php @@ -2,7 +2,6 @@ // Original from http://www.daniweb.com/code/snippet43.html require_once "config.php"; - require_once "classes/db.php"; require_once "autoload.php"; require_once "errorhandler.php"; require_once "lib/gettext/gettext.inc.php"; diff --git a/js/App.js b/js/App.js index 5c2d7726c..4b2adc388 100644 --- a/js/App.js +++ b/js/App.js @@ -998,10 +998,9 @@ const App = { }; this.hotkey_actions["feed_debug_update"] = () => { if (!Feeds.activeIsCat() && parseInt(Feeds.getActive()) > 0) { - //window.open("backend.php?op=feeds&method=update_debugger&feed_id=" + Feeds.getActive()); /* global __csrf_token */ - App.postOpenWindow("backend.php", {op: "feeds", method: "update_debugger", + App.postOpenWindow("backend.php", {op: "feeds", method: "updatedebugger", feed_id: Feeds.getActive(), csrf_token: __csrf_token}); } else { diff --git a/js/FeedTree.js b/js/FeedTree.js index 26c1c916c..694cf8332 100755 --- a/js/FeedTree.js +++ b/js/FeedTree.js @@ -102,7 +102,7 @@ define(["dojo/_base/declare", "dojo/dom-construct", "dojo/_base/array", "dojo/co label: __("Debug feed"), onClick: function() { /* global __csrf_token */ - App.postOpenWindow("backend.php", {op: "feeds", method: "update_debugger", + App.postOpenWindow("backend.php", {op: "feeds", method: "updatedebugger", feed_id: this.getParent().row_id, csrf_token: __csrf_token}); }})); } diff --git a/opml.php b/opml.php index 9b7809e0e..6f13a6f3c 100644 --- a/opml.php +++ b/opml.php @@ -7,7 +7,6 @@ require_once "sessions.php"; require_once "sanity_check.php"; require_once "config.php"; - require_once "db.php"; require_once "db-prefs.php"; if (!init_plugins()) return; diff --git a/public.php b/public.php index dcfc4056e..59b5a499c 100644 --- a/public.php +++ b/public.php @@ -7,7 +7,6 @@ require_once "functions.php"; require_once "sanity_check.php"; require_once "config.php"; - require_once "db.php"; require_once "db-prefs.php"; startup_gettext(); diff --git a/update.php b/update.php index cb927e49a..1f79dccf0 100755 --- a/update.php +++ b/update.php @@ -11,7 +11,6 @@ require_once "functions.php"; require_once "config.php"; require_once "sanity_check.php"; - require_once "db.php"; require_once "db-prefs.php"; function make_stampfile($filename) { diff --git a/update_daemon2.php b/update_daemon2.php index 61cc85617..2a016df48 100755 --- a/update_daemon2.php +++ b/update_daemon2.php @@ -19,7 +19,6 @@ define_default('SPAWN_INTERVAL', DAEMON_SLEEP_INTERVAL); // seconds require_once "sanity_check.php"; - require_once "db.php"; require_once "db-prefs.php"; if (!function_exists('pcntl_fork')) { -- cgit v1.2.3-54-g00ecf From 5d42ce553faa3a3eb2a12d66c9b0870ad778c163 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Mon, 15 Feb 2021 16:55:55 +0300 Subject: drop legacy DB interface and related sanity checks --- classes/db.php | 47 ------------------------- classes/db/mysqli.php | 85 -------------------------------------------- classes/db/pgsql.php | 91 ------------------------------------------------ classes/idb.php | 13 ------- include/sanity_check.php | 8 ----- 5 files changed, 244 deletions(-) delete mode 100644 classes/db/mysqli.php delete mode 100644 classes/db/pgsql.php delete mode 100644 classes/idb.php (limited to 'include') diff --git a/classes/db.php b/classes/db.php index 6199c82bb..490cecd57 100755 --- a/classes/db.php +++ b/classes/db.php @@ -1,13 +1,9 @@ adapter = new Db_Mysqli(); - break; - case "pgsql": - $this->adapter = new Db_Pgsql(); - break; - default: - die("Unknown DB_TYPE: " . DB_TYPE); - } - - if (!$this->adapter) { - print("Error initializing database adapter for " . DB_TYPE); - exit(100); - } - - $this->link = $this->adapter->connect(DB_HOST, DB_USER, DB_PASS, DB_NAME, defined('DB_PORT') ? DB_PORT : ""); - - if (!$this->link) { - print("Error connecting through adapter: " . $this->adapter->last_error()); - exit(101); - } - - error_reporting($er); - } - // this really shouldn't be used unless a separate PDO connection is needed // normal usage is Db::pdo()->prepare(...) etc public function pdo_connect() { @@ -92,17 +56,6 @@ class Db return self::$instance; } - public static function get() : Db { - if (self::$instance == null) - self::$instance = new self(); - - if (!self::$instance->adapter) { - self::$instance->legacy_connect(); - } - - return self::$instance->adapter; - } - public static function pdo() : PDO { if (self::$instance == null) self::$instance = new self(); diff --git a/classes/db/mysqli.php b/classes/db/mysqli.php deleted file mode 100644 index a05b121fc..000000000 --- a/classes/db/mysqli.php +++ /dev/null @@ -1,85 +0,0 @@ -link = mysqli_connect($host, $user, $pass, $db, $port); - else - $this->link = mysqli_connect($host, $user, $pass, $db); - - if ($this->link) { - $this->init(); - - return $this->link; - } else { - print("Unable to connect to database (as $user to $host, database $db): " . mysqli_connect_error()); - exit(102); - } - } - - function escape_string($s, $strip_tags = true) { - if ($strip_tags) $s = strip_tags($s); - - return mysqli_real_escape_string($this->link, $s); - } - - function query($query, $die_on_error = true) { - $result = @mysqli_query($this->link, $query); - if (!$result) { - $this->last_error = @mysqli_error($this->link); - - @mysqli_query($this->link, "ROLLBACK"); - user_error("Query $query failed: " . ($this->link ? $this->last_error : "No connection"), - $die_on_error ? E_USER_ERROR : E_USER_WARNING); - } - - return $result; - } - - function fetch_assoc($result) { - return mysqli_fetch_assoc($result); - } - - - function num_rows($result) { - return mysqli_num_rows($result); - } - - function fetch_result($result, $row, $param) { - if (mysqli_data_seek($result, $row)) { - $line = mysqli_fetch_assoc($result); - return $line[$param]; - } else { - return false; - } - } - - function close() { - return mysqli_close($this->link); - } - - function affected_rows($result) { - return mysqli_affected_rows($this->link); - } - - function last_error() { - return mysqli_error($this->link); - } - - function last_query_error() { - return $this->last_error; - } - - function init() { - $this->query("SET time_zone = '+0:0'"); - - if (defined('MYSQL_CHARSET') && MYSQL_CHARSET) { - mysqli_set_charset($this->link, MYSQL_CHARSET); - } - - return true; - } - -} diff --git a/classes/db/pgsql.php b/classes/db/pgsql.php deleted file mode 100644 index 98fab6bea..000000000 --- a/classes/db/pgsql.php +++ /dev/null @@ -1,91 +0,0 @@ - 0) { - $string = "$string port=" . $port; - } - - $this->link = pg_connect($string); - - if (!$this->link) { - print("Unable to connect to database (as $user to $host, database $db):" . pg_last_error()); - exit(102); - } - - $this->init(); - - return $this->link; - } - - function escape_string($s, $strip_tags = true) { - if ($strip_tags) $s = strip_tags($s); - - return pg_escape_string($s); - } - - function query($query, $die_on_error = true) { - $result = @pg_query($this->link, $query); - - if (!$result) { - $this->last_error = @pg_last_error($this->link); - - @pg_query($this->link, "ROLLBACK"); - $query = htmlspecialchars($query); // just in case - user_error("Query $query failed: " . ($this->link ? $this->last_error : "No connection"), - $die_on_error ? E_USER_ERROR : E_USER_WARNING); - } - return $result; - } - - function fetch_assoc($result) { - return pg_fetch_assoc($result); - } - - - function num_rows($result) { - return pg_num_rows($result); - } - - function fetch_result($result, $row, $param) { - return pg_fetch_result($result, $row, $param); - } - - function close() { - return pg_close($this->link); - } - - function affected_rows($result) { - return pg_affected_rows($result); - } - - function last_error() { - return pg_last_error($this->link); - } - - function last_query_error() { - return $this->last_error; - } - - function init() { - $this->query("set client_encoding = 'UTF-8'"); - pg_set_client_encoding("UNICODE"); - $this->query("set datestyle = 'ISO, european'"); - $this->query("set TIME ZONE 0"); - $this->query("set cpu_tuple_cost = 0.5"); - - return true; - } -} \ No newline at end of file diff --git a/classes/idb.php b/classes/idb.php deleted file mode 100644 index 37fd69906..000000000 --- a/classes/idb.php +++ /dev/null @@ -1,13 +0,0 @@ - Date: Tue, 16 Feb 2021 14:23:00 +0300 Subject: add namespaced controls with unified naming; deprecated old-style control shortcuts --- classes/feeds.php | 22 ++- classes/handler/public.php | 4 +- classes/pref/feeds.php | 27 ++-- classes/pref/filters.php | 21 +-- classes/pref/prefs.php | 41 +++-- classes/pref/system.php | 4 +- include/controls.php | 383 +++++++++++--------------------------------- include/controls_compat.php | 312 ++++++++++++++++++++++++++++++++++++ include/functions.php | 1 + include/login_form.php | 2 +- plugins/mail/init.php | 2 +- 11 files changed, 470 insertions(+), 349 deletions(-) create mode 100644 include/controls_compat.php (limited to 'include') diff --git a/classes/feeds.php b/classes/feeds.php index f3ce1e48b..aa1f889e0 100755 --- a/classes/feeds.php +++ b/classes/feeds.php @@ -583,7 +583,7 @@ class Feeds extends Handler_Protected { function subscribeToFeed() { print json_encode([ - "cat_select" => format_feed_cat_select("cat", false, 'dojoType="fox.form.Select"') + "cat_select" => \Controls\select_feeds_cats("cat") ]); } @@ -607,8 +607,8 @@ class Feeds extends Handler_Protected { if (DB_TYPE == "pgsql") { print "
    "; print ""; - print_select("search_language", get_pref('DEFAULT_SEARCH_LANGUAGE'), Pref_Feeds::get_ts_languages(), - "dojoType='fox.form.Select' title=\"".__('Used for word stemming')."\""); + print \Controls\select_tag("search_language", get_pref('DEFAULT_SEARCH_LANGUAGE'), Pref_Feeds::get_ts_languages(), + "title=\"".__('Used for word stemming')."\""); print "
    "; } @@ -668,6 +668,15 @@ class Feeds extends Handler_Protected { display : none; } + - - + +
    diff --git a/classes/pref/system.php b/classes/pref/system.php index f0a5f7b7f..d3b733364 100644 --- a/classes/pref/system.php +++ b/classes/pref/system.php @@ -90,12 +90,12 @@ class Pref_System extends Handler_Administrative {
    - __("Errors"), E_USER_WARNING => __("Warnings"), E_USER_NOTICE => __("Everything") - ], 'dojoType="fox.form.Select" onchange="Helpers.EventLog.refresh()"') ?> + ], 'onchange="Helpers.EventLog.refresh()"', "severity") ?>
    diff --git a/include/controls.php b/include/controls.php index f706931db..34a4af1f2 100755 --- a/include/controls.php +++ b/include/controls.php @@ -1,326 +1,133 @@ "; - foreach ($values as $v) { - if ($v == $default) - $sel = "selected=\"1\""; - else - $sel = ""; + $rv = ""; -} + $rv .= ""; + } -function print_select_hash($id, $default, $values, $attributes = "", $name = "") { - if (!$name) $name = $id; + $rv .= ""; - print ""; -} + $values = []; -function format_hidden($name, $value) { - return ""; -} + while ($row = $sth->fetch()) { + array_push($values, $row["caption"]); + } -function print_hidden($name, $value) { - print format_hidden($name, $value); -} + return select_tag($name, $value, $values, $attributes, $id); + } -function format_checkbox($id, $checked, $value = "", $attributes = "") { - $checked_str = $checked ? "checked" : ""; - $value_str = $value ? "value=\"$value\"" : ""; + function select_hash(string $name, $value, array $values, string $attributes = "", string $id = "") { + $dojo_type = strpos($attributes, "dojoType") === false ? "dojoType='fox.form.Select'" : ""; - return ""; -} + $rv = ""; - if ($v == $default) - $sel = "checked"; - else - $sel = ""; + return $rv; + } - if ($v == $true_is) { - $sel .= " value=\"1\""; - } else { - $sel .= " value=\"0\""; - } + function hidden_tag(string $name, string $value) { + return ""; + } - print " $v "; + function checkbox_tag(string $name, bool $checked, string $value = "", string $attributes = "", string $id = "") { + $is_checked = $checked ? "checked" : ""; + $value_str = $value ? "value=\"".htmlspecialchars($value)."\"" : ""; - } -} + return ""; + } -function print_feed_multi_select($id, $default_ids = [], - $attributes = "", $include_all_feeds = true, - $root_id = null, $nest_level = 0) { + function select_feeds_cats(string $name, int $default_id = null, string $attributes = "", + bool $include_all_cats = true, string $root_id = null, int $nest_level = 0, string $id = "") { - $pdo = Db::pdo(); + $ret = ""; - print_r(in_array("CAT:6",$default_ids)); + if (!$root_id) { + $ret .= ""; - if ($include_all_feeds) { - $is_selected = (in_array("0", $default_ids)) ? "selected=\"1\"" : ""; - print ""; - } - } + $pdo = \Db::pdo(); - if (get_pref('ENABLE_FEED_CATS')) { + if (!$root_id) $root_id = null; - if (!$root_id) $root_id = null; + $sth = $pdo->prepare("SELECT id,title, + (SELECT COUNT(id) FROM ttrss_feed_categories AS c2 WHERE + c2.parent_cat = ttrss_feed_categories.id) AS num_children + FROM ttrss_feed_categories + WHERE owner_uid = :uid AND + (parent_cat = :root_id OR (:root_id IS NULL AND parent_cat IS NULL)) ORDER BY title"); + $sth->execute([":uid" => $_SESSION['uid'], ":root_id" => $root_id]); - $sth = $pdo->prepare("SELECT id,title, - (SELECT COUNT(id) FROM ttrss_feed_categories AS c2 WHERE - c2.parent_cat = ttrss_feed_categories.id) AS num_children - FROM ttrss_feed_categories - WHERE owner_uid = :uid AND - (parent_cat = :root_id OR (:root_id IS NULL AND parent_cat IS NULL)) ORDER BY title"); + $found = 0; - $sth->execute([":uid" => $_SESSION['uid'], ":root_id" => $root_id]); + while ($line = $sth->fetch()) { + ++$found; - while ($line = $sth->fetch()) { + if ($line["id"] == $default_id) { + $is_selected = "selected=\"1\""; + } else { + $is_selected = ""; + } - for ($i = 0; $i < $nest_level; $i++) - $line["title"] = " " . $line["title"]; + for ($i = 0; $i < $nest_level; $i++) + $line["title"] = " " . $line["title"]; - $is_selected = in_array("CAT:".$line["id"], $default_ids) ? "selected=\"1\"" : ""; + if ($line["title"]) + $ret .= sprintf("", + $line["id"], htmlspecialchars($line["title"])); - printf("", - $line["id"], htmlspecialchars($line["title"])); + if ($line["num_children"] > 0) + $ret .= select_feeds_cats($id, $default_id, $attributes, + $include_all_cats, $line["id"], $nest_level+1, $id); + } - if ($line["num_children"] > 0) - print_feed_multi_select($id, $default_ids, $attributes, - $include_all_feeds, $line["id"], $nest_level+1); + if (!$root_id) { + if ($include_all_cats) { + if ($found > 0) { + $ret .= ""; + } - $f_sth = $pdo->prepare("SELECT id,title FROM ttrss_feeds - WHERE cat_id = ? AND owner_uid = ? ORDER BY title"); + if ($default_id == 0) { + $is_selected = "selected=\"1\""; + } else { + $is_selected = ""; + } - $f_sth->execute([$line['id'], $_SESSION['uid']]); + $ret .= ""; + } + $ret .= ""; + } - while ($fline = $f_sth->fetch()) { - $is_selected = (in_array($fline["id"], $default_ids)) ? "selected=\"1\"" : ""; + return $ret; + } - $fline["title"] = " " . $fline["title"]; - - for ($i = 0; $i < $nest_level; $i++) - $fline["title"] = " " . $fline["title"]; - - printf("", - $fline["id"], htmlspecialchars($fline["title"])); - } - } - - if (!$root_id) { - $is_selected = in_array("CAT:0", $default_ids) ? "selected=\"1\"" : ""; - - printf("", - __("Uncategorized")); - - $f_sth = $pdo->prepare("SELECT id,title FROM ttrss_feeds - WHERE cat_id IS NULL AND owner_uid = ? ORDER BY title"); - $f_sth->execute([$_SESSION['uid']]); - - while ($fline = $f_sth->fetch()) { - $is_selected = in_array($fline["id"], $default_ids) ? "selected=\"1\"" : ""; - - $fline["title"] = " " . $fline["title"]; - - for ($i = 0; $i < $nest_level; $i++) - $fline["title"] = " " . $fline["title"]; - - printf("", - $fline["id"], htmlspecialchars($fline["title"])); - } - } - - } else { - $sth = $pdo->prepare("SELECT id,title FROM ttrss_feeds - WHERE owner_uid = ? ORDER BY title"); - $sth->execute([$_SESSION['uid']]); - - while ($line = $sth->fetch()) { - - $is_selected = (in_array($line["id"], $default_ids)) ? "selected=\"1\"" : ""; - - printf("", - $line["id"], htmlspecialchars($line["title"])); - } - } - - if (!$root_id) { - print ""; - } -} - -function print_feed_cat_select($id, $default_id, $attributes, $include_all_cats = true, - $root_id = null, $nest_level = 0) { - - print format_feed_cat_select($id, $default_id, $attributes, $include_all_cats, $root_id, $nest_level); -} - -function format_feed_cat_select($id, $default_id, $attributes, $include_all_cats = true, - $root_id = null, $nest_level = 0) { - - $ret = ""; - - if (!$root_id) { - $ret .= ""; - } - - return $ret; -} - -function stylesheet_tag($filename, $id = false) { - $timestamp = filemtime($filename); - - $id_part = $id ? "id=\"$id\"" : ""; - - return "\n"; -} - -function javascript_tag($filename) { - $query = ""; - - if (!(strpos($filename, "?") === false)) { - $query = substr($filename, strpos($filename, "?")+1); - $filename = substr($filename, 0, strpos($filename, "?")); - } - - $timestamp = filemtime($filename); - - if ($query) $timestamp .= "&$query"; - - return "\n"; -} - -function format_warning($msg, $id = "") { - return "
    $msg
    "; -} - -function format_notice($msg, $id = "") { - return "
    $msg
    "; -} - -function format_error($msg, $id = "") { - return "
    $msg
    "; -} - -function print_notice($msg) { - return print format_notice($msg); -} - -function print_warning($msg) { - return print format_warning($msg); -} - -function print_error($msg) { - return print format_error($msg); -} - -function print_label_select($name, $value, $attributes = "") { - - $pdo = Db::pdo(); - - $sth = $pdo->prepare("SELECT caption FROM ttrss_labels2 - WHERE owner_uid = ? ORDER BY caption"); - $sth->execute([$_SESSION['uid']]); - - print ""; - - -} diff --git a/include/controls_compat.php b/include/controls_compat.php new file mode 100644 index 000000000..3d2779b08 --- /dev/null +++ b/include/controls_compat.php @@ -0,0 +1,312 @@ +"; + foreach ($values as $v) { + if ($v == $default) + $sel = "selected=\"1\""; + else + $sel = ""; + + $v = trim($v); + + print ""; + } + print ""; +} + +function print_select_hash($id, $default, $values, $attributes = "", $name = "") { + if (!$name) $name = $id; + + print ""; +} + +function format_hidden($name, $value) { + return ""; +} + +function print_hidden($name, $value) { + print format_hidden($name, $value); +} + +function format_checkbox($id, $checked, $value = "", $attributes = "") { + $checked_str = $checked ? "checked" : ""; + $value_str = $value ? "value=\"$value\"" : ""; + + return ""; +} + +function print_checkbox($id, $checked, $value = "", $attributes = "") { + print format_checkbox($id, $checked, $value, $attributes); +} + +function format_button($type, $value, $attributes = "") { + return ""; +} + +function print_button($type, $value, $attributes = "") { + print format_button($type, $value, $attributes); +} + +function print_feed_multi_select($id, $default_ids = [], + $attributes = "", $include_all_feeds = true, + $root_id = null, $nest_level = 0) { + + $pdo = Db::pdo(); + + print_r(in_array("CAT:6",$default_ids)); + + if (!$root_id) { + print ""; + } +} + +function print_feed_cat_select($id, $default_id, $attributes, $include_all_cats = true, + $root_id = null, $nest_level = 0) { + + print format_feed_cat_select($id, $default_id, $attributes, $include_all_cats, $root_id, $nest_level); +} + +function format_feed_cat_select($id, $default_id, $attributes, $include_all_cats = true, + $root_id = null, $nest_level = 0) { + + $ret = ""; + + if (!$root_id) { + $ret .= ""; + } + + return $ret; +} + +function stylesheet_tag($filename, $id = false) { + $timestamp = filemtime($filename); + + $id_part = $id ? "id=\"$id\"" : ""; + + return "\n"; +} + +function javascript_tag($filename) { + $query = ""; + + if (!(strpos($filename, "?") === false)) { + $query = substr($filename, strpos($filename, "?")+1); + $filename = substr($filename, 0, strpos($filename, "?")); + } + + $timestamp = filemtime($filename); + + if ($query) $timestamp .= "&$query"; + + return "\n"; +} + +function format_warning($msg, $id = "") { + return "
    $msg
    "; +} + +function format_notice($msg, $id = "") { + return "
    $msg
    "; +} + +function format_error($msg, $id = "") { + return "
    $msg
    "; +} + +function print_notice($msg) { + return print format_notice($msg); +} + +function print_warning($msg) { + return print format_warning($msg); +} + +function print_error($msg) { + return print format_error($msg); +} + +function print_label_select($name, $value, $attributes = "") { + + $pdo = Db::pdo(); + + $sth = $pdo->prepare("SELECT caption FROM ttrss_labels2 + WHERE owner_uid = ? ORDER BY caption"); + $sth->execute([$_SESSION['uid']]); + + print ""; + + +} diff --git a/include/functions.php b/include/functions.php index 4557c0411..174ef39f0 100644 --- a/include/functions.php +++ b/include/functions.php @@ -203,6 +203,7 @@ require_once 'db-prefs.php'; require_once 'controls.php'; + require_once 'controls_compat.php'; define('SELF_USER_AGENT', 'Tiny Tiny RSS/' . get_version() . ' (http://tt-rss.org/)'); ini_set('user_agent', SELF_USER_AGENT); diff --git a/include/login_form.php b/include/login_form.php index aa6a72260..798efa624 100755 --- a/include/login_form.php +++ b/include/login_form.php @@ -97,7 +97,7 @@ - + diff --git a/plugins/mail/init.php b/plugins/mail/init.php index 829620ebc..b1263ece5 100644 --- a/plugins/mail/init.php +++ b/plugins/mail/init.php @@ -160,7 +160,7 @@ class Mail extends Plugin { style=\"width : 30em;\" name=\"destination\" id=\"emailArticleDlg_destination\">"; */ - print_select("destination", "", $addresslist, 'style="width: 30em" dojoType="dijit.form.ComboBox"'); + print \Controls\select_tag("destination", "", $addresslist, 'style="width: 30em" dojoType="dijit.form.ComboBox"'); /* print "
    "; */ -- cgit v1.2.3-54-g00ecf From d7127cead362ba00b0defd93b2091ce15aeae2f3 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Tue, 16 Feb 2021 14:42:27 +0300 Subject: feed debugger: use hidden helpers; add button helpers --- classes/feeds.php | 14 +++++++------- include/controls.php | 8 ++++++++ 2 files changed, 15 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/classes/feeds.php b/classes/feeds.php index aa1f889e0..400c302d6 100755 --- a/classes/feeds.php +++ b/classes/feeds.php @@ -697,12 +697,12 @@ class Feeds extends Handler_Protected {

    Feed Debugger: _get_title($feed_id) ?>

    - - - - - - + + + + + +
    - +
    diff --git a/include/controls.php b/include/controls.php index 34a4af1f2..78f02233f 100755 --- a/include/controls.php +++ b/include/controls.php @@ -1,6 +1,14 @@ ".htmlspecialchars($value).""; + } + + function submit_tag(string $value, $attributes = "") { + return button_tag($value, "submit", "class=\"alt-primary\" $attributes"); + } + function select_tag(string $name, $value, array $values, string $attributes = "", string $id = "") { $dojo_type = strpos($attributes, "dojoType") === false ? "dojoType='fox.form.Select'" : ""; -- cgit v1.2.3-54-g00ecf From af4b3e7df06e50e956e9fda281935a7483f5673a Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Tue, 16 Feb 2021 15:05:32 +0300 Subject: login form: use control helpers --- include/controls.php | 4 ++-- include/login_form.php | 15 ++++++++------- 2 files changed, 10 insertions(+), 9 deletions(-) (limited to 'include') diff --git a/include/controls.php b/include/controls.php index 78f02233f..b351a9449 100755 --- a/include/controls.php +++ b/include/controls.php @@ -64,12 +64,12 @@ name=\"".htmlspecialchars($name)."\" value=\"".htmlspecialchars($value)."\">"; } - function checkbox_tag(string $name, bool $checked, string $value = "", string $attributes = "", string $id = "") { + function checkbox_tag(string $name, bool $checked = false, string $value = "", string $attributes = "", string $id = "") { $is_checked = $checked ? "checked" : ""; $value_str = $value ? "value=\"".htmlspecialchars($value)."\"" : ""; return ""; + $value_str $is_checked $attributes id=\"".htmlspecialchars($id)."\">"; } function select_feeds_cats(string $name, int $default_id = null, string $attributes = "", diff --git a/include/login_form.php b/include/login_form.php index 798efa624..e4eb52a51 100755 --- a/include/login_form.php +++ b/include/login_form.php @@ -141,8 +141,8 @@
    -
    @@ -153,9 +153,10 @@
    - +
    @@ -166,7 +167,7 @@
    @@ -177,7 +178,7 @@
    - +
    -- cgit v1.2.3-54-g00ecf From 4f4e57bb26620ba6f4adcc413ff60b86fdeb158f Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Tue, 16 Feb 2021 15:27:22 +0300 Subject: hidden_tag: temporarily prevent htmlspecialchars() to stop embedded JSON from breaking --- include/controls.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include') diff --git a/include/controls.php b/include/controls.php index b351a9449..6d3cfc417 100755 --- a/include/controls.php +++ b/include/controls.php @@ -61,7 +61,7 @@ function hidden_tag(string $name, string $value) { return ""; + name=\"".htmlspecialchars($name)."\" value=\"$value\">"; } function checkbox_tag(string $name, bool $checked = false, string $value = "", string $attributes = "", string $id = "") { -- cgit v1.2.3-54-g00ecf From 627af2c236bf4a370a5d31641757b69d679513f6 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Tue, 16 Feb 2021 15:36:40 +0300 Subject: amend previous to fix actual underlying problem (double escaping) --- classes/pref/filters.php | 8 ++------ include/controls.php | 2 +- 2 files changed, 3 insertions(+), 7 deletions(-) (limited to 'include') diff --git a/classes/pref/filters.php b/classes/pref/filters.php index 36357234b..caefb1ea7 100755 --- a/classes/pref/filters.php +++ b/classes/pref/filters.php @@ -388,11 +388,9 @@ class Pref_Filters extends Handler_Protected { if (!$line["inverse"]) unset($line["inverse"]); unset($line["match_on"]); - $data = htmlspecialchars((string)json_encode($line)); - print "
  • ".$this->_get_rule_name($line)."". - \Controls\hidden_tag("rule[]", $data)."
  • "; + \Controls\hidden_tag("rule[]", (string)json_encode($line)).""; } } @@ -430,11 +428,9 @@ class Pref_Filters extends Handler_Protected { unset($line["filter_id"]); unset($line["id"]); - $data = htmlspecialchars((string)json_encode($line)); - print "
  • ".$this->_get_action_name($line)."". - \Controls\hidden_tag("action[]", $data)."
  • "; + \Controls\hidden_tag("action[]", (string)json_encode($line)).""; } } diff --git a/include/controls.php b/include/controls.php index 6d3cfc417..b351a9449 100755 --- a/include/controls.php +++ b/include/controls.php @@ -61,7 +61,7 @@ function hidden_tag(string $name, string $value) { return ""; + name=\"".htmlspecialchars($name)."\" value=\"".htmlspecialchars($value)."\">"; } function checkbox_tag(string $name, bool $checked = false, string $value = "", string $attributes = "", string $id = "") { -- cgit v1.2.3-54-g00ecf From bdbbdbb0eddd125bec167da5f42bbd95c770151f Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Tue, 16 Feb 2021 16:59:21 +0300 Subject: rework controls to accept parameters as array --- classes/feeds.php | 2 +- classes/pref/feeds.php | 15 +++++++-------- classes/pref/filters.php | 8 ++++---- classes/pref/prefs.php | 4 ++-- classes/pref/system.php | 2 +- include/controls.php | 47 ++++++++++++++++++++++++++++++----------------- include/login_form.php | 3 ++- plugins/mail/init.php | 3 ++- 8 files changed, 49 insertions(+), 35 deletions(-) (limited to 'include') diff --git a/classes/feeds.php b/classes/feeds.php index 4e6fdbaf8..43c8bf584 100755 --- a/classes/feeds.php +++ b/classes/feeds.php @@ -608,7 +608,7 @@ class Feeds extends Handler_Protected { print "
    "; print ""; print \Controls\select_tag("search_language", get_pref('DEFAULT_SEARCH_LANGUAGE'), Pref_Feeds::get_ts_languages(), - "title=\"".__('Used for word stemming')."\""); + ["title" => __('Used for word stemming')]); print "
    "; } diff --git a/classes/pref/feeds.php b/classes/pref/feeds.php index 636f2d69b..ce2d321bd 100755 --- a/classes/pref/feeds.php +++ b/classes/pref/feeds.php @@ -644,8 +644,10 @@ class Pref_Feeds extends Handler_Protected { $local_purge_intervals = [ T_nsprintf('%d day', '%d days', $purge_interval, $purge_interval) ]; } - print \Controls\select_hash("purge_interval", $purge_interval, $local_purge_intervals, - ((FORCE_ARTICLE_PURGE == 0) ? "" : 'disabled="1"')); + print \Controls\select_hash("purge_interval", + $purge_interval, + $local_purge_intervals, + (FORCE_ARTICLE_PURGE == 0) ? [] : ["disabled" => 1]); print ""; @@ -815,8 +817,6 @@ class Pref_Feeds extends Handler_Protected { print_notice("Enable the options you wish to apply using checkboxes on the right:"); - print "

    "; - print \Controls\hidden_tag("ids", "$feed_ids"); print \Controls\hidden_tag("op", "pref-feeds"); print \Controls\hidden_tag("method", "batchEditSave"); @@ -846,8 +846,7 @@ class Pref_Feeds extends Handler_Protected { print "

    "; print " "; - print \Controls\select_tag("feed_language", "", $this::get_ts_languages(), - 'disabled="1"'); + print \Controls\select_tag("feed_language", "", $this::get_ts_languages(), ["disabled"=> 1]); $this->batch_edit_cbox("feed_language"); @@ -868,7 +867,7 @@ class Pref_Feeds extends Handler_Protected { $local_update_intervals = $update_intervals; $local_update_intervals[0] .= sprintf(" (%s)", $update_intervals[get_pref("DEFAULT_UPDATE_INTERVAL")]); - print \Controls\select_hash("update_interval", "", $local_update_intervals, 'disabled="1"'); + print \Controls\select_hash("update_interval", "", $local_update_intervals, ["disabled" => 1]); $this->batch_edit_cbox("update_interval"); @@ -890,7 +889,7 @@ class Pref_Feeds extends Handler_Protected { else $local_purge_intervals[0] .= " " . sprintf("(%s)", __("Disabled")); - print \Controls\select_hash("purge_interval", "", $local_purge_intervals, 'disabled="1"'); + print \Controls\select_hash("purge_interval", "", $local_purge_intervals, ["disabled" => 1]); $this->batch_edit_cbox("purge_interval"); diff --git a/classes/pref/filters.php b/classes/pref/filters.php index caefb1ea7..e9c58cc3e 100755 --- a/classes/pref/filters.php +++ b/classes/pref/filters.php @@ -894,7 +894,7 @@ class Pref_Filters extends Handler_Protected { dojoType='fox.form.Select'");*/ print \Controls\select_labels("action_param_label", $action_param, - "style=\"$label_param_hidden\"", + ["style" => $label_param_hidden], "filterDlg_actionParamLabel"); $filter_actions = PluginHost::getInstance()->get_filter_actions(); @@ -909,16 +909,16 @@ class Pref_Filters extends Handler_Protected { } if (count($filter_action_hash) == 0) { - $filter_plugin_disabled = "disabled"; + $filter_plugin_disabled = ["disabled" => "1"]; $filter_action_hash["no-data"] = __("No actions available"); } else { - $filter_plugin_disabled = ""; + $filter_plugin_disabled = []; } print \Controls\select_hash("action_param_plugin", $action_param, $filter_action_hash, - "style=\"$plugin_param_hidden\" $filter_plugin_disabled", + array_merge(["style" => $plugin_param_hidden], $filter_plugin_disabled), "filterDlg_actionParamPlugin"); print ""; diff --git a/classes/pref/prefs.php b/classes/pref/prefs.php index d8491995d..3bc8a9a9c 100644 --- a/classes/pref/prefs.php +++ b/classes/pref/prefs.php @@ -664,13 +664,13 @@ class Pref_Prefs extends Handler_Protected { if ($pref_name == "USER_LANGUAGE") { print \Controls\select_hash($pref_name, $value, get_translations(), - "style='width : 220px; margin : 0px'"); + ["style" => 'width : 220px; margin : 0px']); } else if ($pref_name == "USER_TIMEZONE") { $timezones = explode("\n", file_get_contents("lib/timezones.txt")); - print \Controls\select_tag($pref_name, $value, $timezones, 'dojoType="dijit.form.FilteringSelect"'); + print \Controls\select_tag($pref_name, $value, $timezones, ["dojoType" => "dijit.form.FilteringSelect"]); } else if ($pref_name == "BLACKLISTED_TAGS") { # TODO: other possible "; - + name='note'> + "; - print " "; - print ""; - print ""; - + ?> +
    + + +
    + 0) { - $article["content"] = "
    -
    "; + $article["content"] = "
    ". + \Controls\button_tag(__("Not work safe (click to toggle)"), '', ['onclick' => 'Plugins.NSFW.toggle(this)']). + " +
    "; } return $article; } function hook_render_article_cdm($article) { - $tags = array_map("trim", explode(",", $this->host->get($this, "tags"))); - $a_tags = array_map("trim", explode(",", $article["tag_cache"])); - - if (count(array_intersect($tags, $a_tags)) > 0) { - $article["content"] = "
    -
    "; - } - - return $article; + return $this->hook_render_article($article); } function hook_prefs_tab($args) { if ($args != "prefPrefs") return; - print "
    extension ".__("NSFW Plugin")."\">"; - - print "
    "; - $tags = $this->host->get($this, "tags"); - print "
    "; - - print ""; - - print \Controls\hidden_tag("op", "pluginhandler"); - print \Controls\hidden_tag("method", "save"); - print \Controls\hidden_tag("plugin", "nsfw"); - - print ""; - - print ""; - print ""; + - print "
    ".__("Tags to consider NSFW (comma-separated)")."
    "; +
    - print "

    "; +

    + +
    - print "
    "; +
    - print "
    "; #pane + + +
    + host->set($this, "tags", $tags); diff --git a/plugins/share/init.php b/plugins/share/init.php index 42923ed8a..846e1f39c 100644 --- a/plugins/share/init.php +++ b/plugins/share/init.php @@ -41,14 +41,14 @@ class Share extends Plugin { function hook_prefs_tab_section($id) { if ($id == "prefFeedsPublishedGenerated") { + ?> +
    - print "
    "; - - print "

    " . __("You can disable all articles shared by unique URLs here.") . "

    "; - - print " "; +

    + + execute([$uuid, $param, $_SESSION['uid']]); } - print "
    " . __("You can share this article by the following unique URL:") . "
    "; + $url_path = htmlspecialchars(get_self_url_prefix() . "/public.php?op=share&key=$uuid"); - $url_path = get_self_url_prefix(); - $url_path .= "/public.php?op=share&key=$uuid"; + ?> - print "
    -
    - $url_path -
    -
    "; +
    - /* if (!label_find_id(__('Shared'), $_SESSION["uid"])) - label_create(__('Shared'), $_SESSION["uid"]); - label_add_article($ref_id, __('Shared'), $_SESSION['uid']); */ +
    +
    + +
    +
    + "; - - print ""; - - print ""; - - print ""; - - print ""; + ?> +
    + 'alt-danger', 'onclick' => "App.dialogOf(this).unshare()"]) ?> + "App.dialogOf(this).newurl()"]) ?> + +
    + Date: Wed, 17 Feb 2021 21:44:21 +0300 Subject: * add (disabled) shortcut syntax for plugin methods * add controls shortcut for pluginhandler tags * add similar shortcut for frontend * allow plugins to selectively exclude their methods from CSRF checking --- backend.php | 11 +++++++++++ classes/plugin.php | 4 ++++ classes/pluginhandler.php | 2 +- classes/pluginhost.php | 13 ++++++++++++- include/controls.php | 12 +++++++++++- js/App.js | 3 +++ plugins/af_proxy_http/init.php | 4 +--- plugins/af_psql_trgm/init.php | 4 +--- plugins/af_readability/init.js | 2 +- plugins/af_readability/init.php | 6 ++---- plugins/af_redditimgur/init.php | 9 +++++---- plugins/mail/init.php | 12 ++++-------- plugins/mail/mail.js | 2 +- plugins/mailto/init.js | 2 +- plugins/note/init.php | 4 +--- plugins/note/note.js | 2 +- plugins/nsfw/init.php | 4 +--- plugins/share/share.js | 8 +++----- plugins/share/share_prefs.js | 2 +- 19 files changed, 65 insertions(+), 41 deletions(-) (limited to 'include') diff --git a/backend.php b/backend.php index 9ecc22914..e64c6561f 100644 --- a/backend.php +++ b/backend.php @@ -88,6 +88,17 @@ 5 => __("Power User"), 10 => __("Administrator")); + // shortcut syntax for plugin methods (?op=plugin--pmethod&...params) + /* if (strpos($op, PluginHost::PUBLIC_METHOD_DELIMITER) !== false) { + list ($plugin, $pmethod) = explode(PluginHost::PUBLIC_METHOD_DELIMITER, $op, 2); + + // TODO: better implementation that won't modify $_REQUEST + $_REQUEST["plugin"] = $plugin; + $method = $pmethod; + $op = "pluginhandler"; + } */ + + // TODO: figure out if is this still needed $op = str_replace("-", "_", $op); $override = PluginHost::getInstance()->lookup_handler($op, $method); diff --git a/classes/plugin.php b/classes/plugin.php index 2416418cd..6c572467a 100644 --- a/classes/plugin.php +++ b/classes/plugin.php @@ -54,4 +54,8 @@ abstract class Plugin { return vsprintf($this->__($msgid), $args); } + + function csrf_ignore($method) { + return false; + } } diff --git a/classes/pluginhandler.php b/classes/pluginhandler.php index a0e60b4e6..608f80dcb 100644 --- a/classes/pluginhandler.php +++ b/classes/pluginhandler.php @@ -11,7 +11,7 @@ class PluginHandler extends Handler_Protected { if ($plugin) { if (method_exists($plugin, $method)) { - if (validate_csrf($csrf_token)) { + if (validate_csrf($csrf_token) || $plugin->csrf_ignore($method)) { $plugin->$method(); } else { user_error("Rejected ${plugin_name}->${method}(): invalid CSRF token.", E_USER_WARNING); diff --git a/classes/pluginhost.php b/classes/pluginhost.php index 097bf987c..065fa99c4 100755 --- a/classes/pluginhost.php +++ b/classes/pluginhost.php @@ -611,6 +611,17 @@ class PluginHost { $params)); } + // shortcut syntax (disabled for now) + /* function get_method_url(Plugin $sender, string $method, $params) { + return get_self_url_prefix() . "/backend.php?" . + http_build_query( + array_merge( + [ + "op" => strtolower(get_class($sender) . self::PUBLIC_METHOD_DELIMITER . $method), + ], + $params)); + } */ + // WARNING: endpoint in public.php, exposed to unauthenticated users function get_public_method_url(Plugin $sender, string $method, $params) { if ($sender->is_public_method($method)) { @@ -618,7 +629,7 @@ class PluginHost { http_build_query( array_merge( [ - "op" => strtolower(get_class($sender) . PluginHost::PUBLIC_METHOD_DELIMITER . $method), + "op" => strtolower(get_class($sender) . self::PUBLIC_METHOD_DELIMITER . $method), ], $params)); } else { diff --git a/include/controls.php b/include/controls.php index 4c60d94f3..d8506877b 100755 --- a/include/controls.php +++ b/include/controls.php @@ -11,6 +11,17 @@ return $rv; } + // shortcut syntax (disabled) + /* function pluginhandler_tags(\Plugin $plugin, string $method) { + return hidden_tag("op", strtolower(get_class($plugin) . \PluginHost::PUBLIC_METHOD_DELIMITER . $method)); + } */ + + function pluginhandler_tags(\Plugin $plugin, string $method) { + return hidden_tag("op", "pluginhandler") . + hidden_tag("plugin", strtolower(get_class($plugin))) . + hidden_tag("method", $method); + } + function button_tag(string $value, string $type, array $attributes = []) { return ""; } @@ -155,4 +166,3 @@ return $ret; } - diff --git a/js/App.js b/js/App.js index 9d8f6c275..aeca688b7 100644 --- a/js/App.js +++ b/js/App.js @@ -101,6 +101,9 @@ const App = { return dijit.getEnclosingWidget(elem.closest('.dijitDialog')); }, + getPhArgs(plugin, method, args = {}) { + return {...{op: "pluginhandler", plugin: plugin, method: method}, ...args}; + }, label_to_feed_id: function(label) { return this.LABEL_BASE_INDEX - 1 - Math.abs(label); }, diff --git a/plugins/af_proxy_http/init.php b/plugins/af_proxy_http/init.php index 5804e450f..d6cee5fcd 100644 --- a/plugins/af_proxy_http/init.php +++ b/plugins/af_proxy_http/init.php @@ -229,9 +229,7 @@ class Af_Proxy_Http extends Plugin { } "; - print \Controls\hidden_tag("op", "pluginhandler"); - print \Controls\hidden_tag("method", "save"); - print \Controls\hidden_tag("plugin", "af_proxy_http"); + print \Controls\pluginhandler_tags($this, "save"); $proxy_all = sql_bool_to_bool($this->host->get($this, "proxy_all")); print \Controls\checkbox_tag("proxy_all", $proxy_all); diff --git a/plugins/af_psql_trgm/init.php b/plugins/af_psql_trgm/init.php index 1d83ce5e0..bfbbdf49c 100644 --- a/plugins/af_psql_trgm/init.php +++ b/plugins/af_psql_trgm/init.php @@ -157,9 +157,7 @@ class Af_Psql_Trgm extends Plugin { } "; - print \Controls\hidden_tag("op", "pluginhandler"); - print \Controls\hidden_tag("method", "save"); - print \Controls\hidden_tag("plugin", "af_psql_trgm"); + print \Controls\pluginhandler_tags($this, "save"); print "

    " . __("Global settings") . "

    "; diff --git a/plugins/af_readability/init.js b/plugins/af_readability/init.js index 3155475cc..ff2d94e8b 100644 --- a/plugins/af_readability/init.js +++ b/plugins/af_readability/init.js @@ -16,7 +16,7 @@ Plugins.Af_Readability = { Notify.progress("Loading, please wait..."); - xhrJson("backend.php",{ op: "pluginhandler", plugin: "af_readability", method: "embed", param: id }, (reply) => { + xhrJson("backend.php", App.getPhArgs("af_readability", "embed", {id: id}), (reply) => { if (content && reply.content) { content.setAttribute(self.orig_attr_name, content.innerHTML); diff --git a/plugins/af_readability/init.php b/plugins/af_readability/init.php index aeef8cddc..43d064fc7 100755 --- a/plugins/af_readability/init.php +++ b/plugins/af_readability/init.php @@ -67,9 +67,7 @@ class Af_Readability extends Plugin {
    - - - + <?= __("Share with Tiny Tiny RSS") ?> Tiny Tiny RSS @@ -946,7 +943,6 @@ class Handler_Public extends Handler { diff --git a/include/login_form.php b/include/login_form.php index c3660bced..d1af23459 100755 --- a/include/login_form.php +++ b/include/login_form.php @@ -6,10 +6,8 @@ { + reply.forEach((p) => { profile .attr("disabled", false) .addOption(p); diff --git a/index.php b/index.php index 4a4d336ca..4494082dd 100644 --- a/index.php +++ b/index.php @@ -76,8 +76,7 @@ -1, - Gecko: ua.indexOf('Gecko') > -1 && ua.indexOf('KHTML') === -1, - MobileSafari: /Apple.*Mobile/.test(ua) - } - })(), - - BrowserFeatures: { - XPath: !!document.evaluate, - - SelectorsAPI: !!document.querySelector, - - ElementExtensions: (function() { - var constructor = window.Element || window.HTMLElement; - return !!(constructor && constructor.prototype); - })(), - SpecificElementExtensions: (function() { - if (typeof window.HTMLDivElement !== 'undefined') - return true; - - var div = document.createElement('div'), - form = document.createElement('form'), - isSupported = false; - - if (div['__proto__'] && (div['__proto__'] !== form['__proto__'])) { - isSupported = true; - } - - div = form = null; - - return isSupported; - })() - }, - - ScriptFragment: ']*>([\\S\\s]*?)<\/script\\s*>', - JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/, - - emptyFunction: function() { }, - - K: function(x) { return x } -}; - -if (Prototype.Browser.MobileSafari) - Prototype.BrowserFeatures.SpecificElementExtensions = false; -/* Based on Alex Arnell's inheritance implementation. */ - -var Class = (function() { - - var IS_DONTENUM_BUGGY = (function(){ - for (var p in { toString: 1 }) { - if (p === 'toString') return false; - } - return true; - })(); - - function subclass() {}; - function create() { - var parent = null, properties = $A(arguments); - if (Object.isFunction(properties[0])) - parent = properties.shift(); - - function klass() { - this.initialize.apply(this, arguments); - } - - Object.extend(klass, Class.Methods); - klass.superclass = parent; - klass.subclasses = []; - - if (parent) { - subclass.prototype = parent.prototype; - klass.prototype = new subclass; - parent.subclasses.push(klass); - } - - for (var i = 0, length = properties.length; i < length; i++) - klass.addMethods(properties[i]); - - if (!klass.prototype.initialize) - klass.prototype.initialize = Prototype.emptyFunction; - - klass.prototype.constructor = klass; - return klass; - } - - function addMethods(source) { - var ancestor = this.superclass && this.superclass.prototype, - properties = Object.keys(source); - - if (IS_DONTENUM_BUGGY) { - if (source.toString != Object.prototype.toString) - properties.push("toString"); - if (source.valueOf != Object.prototype.valueOf) - properties.push("valueOf"); - } - - for (var i = 0, length = properties.length; i < length; i++) { - var property = properties[i], value = source[property]; - if (ancestor && Object.isFunction(value) && - value.argumentNames()[0] == "$super") { - var method = value; - value = (function(m) { - return function() { return ancestor[m].apply(this, arguments); }; - })(property).wrap(method); - - value.valueOf = (function(method) { - return function() { return method.valueOf.call(method); }; - })(method); - - value.toString = (function(method) { - return function() { return method.toString.call(method); }; - })(method); - } - this.prototype[property] = value; - } - - return this; - } - - return { - create: create, - Methods: { - addMethods: addMethods - } - }; -})(); -(function() { - - var _toString = Object.prototype.toString, - _hasOwnProperty = Object.prototype.hasOwnProperty, - NULL_TYPE = 'Null', - UNDEFINED_TYPE = 'Undefined', - BOOLEAN_TYPE = 'Boolean', - NUMBER_TYPE = 'Number', - STRING_TYPE = 'String', - OBJECT_TYPE = 'Object', - FUNCTION_CLASS = '[object Function]', - BOOLEAN_CLASS = '[object Boolean]', - NUMBER_CLASS = '[object Number]', - STRING_CLASS = '[object String]', - ARRAY_CLASS = '[object Array]', - DATE_CLASS = '[object Date]', - NATIVE_JSON_STRINGIFY_SUPPORT = window.JSON && - typeof JSON.stringify === 'function' && - JSON.stringify(0) === '0' && - typeof JSON.stringify(Prototype.K) === 'undefined'; - - - - var DONT_ENUMS = ['toString', 'toLocaleString', 'valueOf', - 'hasOwnProperty', 'isPrototypeOf', 'propertyIsEnumerable', 'constructor']; - - var IS_DONTENUM_BUGGY = (function(){ - for (var p in { toString: 1 }) { - if (p === 'toString') return false; - } - return true; - })(); - - function Type(o) { - switch(o) { - case null: return NULL_TYPE; - case (void 0): return UNDEFINED_TYPE; - } - var type = typeof o; - switch(type) { - case 'boolean': return BOOLEAN_TYPE; - case 'number': return NUMBER_TYPE; - case 'string': return STRING_TYPE; - } - return OBJECT_TYPE; - } - - function extend(destination, source) { - for (var property in source) - destination[property] = source[property]; - return destination; - } - - function inspect(object) { - try { - if (isUndefined(object)) return 'undefined'; - if (object === null) return 'null'; - return object.inspect ? object.inspect() : String(object); - } catch (e) { - if (e instanceof RangeError) return '...'; - throw e; - } - } - - function toJSON(value) { - return Str('', { '': value }, []); - } - - function Str(key, holder, stack) { - var value = holder[key]; - if (Type(value) === OBJECT_TYPE && typeof value.toJSON === 'function') { - value = value.toJSON(key); - } - - var _class = _toString.call(value); - - switch (_class) { - case NUMBER_CLASS: - case BOOLEAN_CLASS: - case STRING_CLASS: - value = value.valueOf(); - } - - switch (value) { - case null: return 'null'; - case true: return 'true'; - case false: return 'false'; - } - - var type = typeof value; - switch (type) { - case 'string': - return value.inspect(true); - case 'number': - return isFinite(value) ? String(value) : 'null'; - case 'object': - - for (var i = 0, length = stack.length; i < length; i++) { - if (stack[i] === value) { - throw new TypeError("Cyclic reference to '" + value + "' in object"); - } - } - stack.push(value); - - var partial = []; - if (_class === ARRAY_CLASS) { - for (var i = 0, length = value.length; i < length; i++) { - var str = Str(i, value, stack); - partial.push(typeof str === 'undefined' ? 'null' : str); - } - partial = '[' + partial.join(',') + ']'; - } else { - var keys = Object.keys(value); - for (var i = 0, length = keys.length; i < length; i++) { - var key = keys[i], str = Str(key, value, stack); - if (typeof str !== "undefined") { - partial.push(key.inspect(true)+ ':' + str); - } - } - partial = '{' + partial.join(',') + '}'; - } - stack.pop(); - return partial; - } - } - - function stringify(object) { - return JSON.stringify(object); - } - - function toQueryString(object) { - return $H(object).toQueryString(); - } - - function toHTML(object) { - return object && object.toHTML ? object.toHTML() : String.interpret(object); - } - - function keys(object) { - if (Type(object) !== OBJECT_TYPE) { throw new TypeError(); } - var results = []; - for (var property in object) { - if (_hasOwnProperty.call(object, property)) - results.push(property); - } - - if (IS_DONTENUM_BUGGY) { - for (var i = 0; property = DONT_ENUMS[i]; i++) { - if (_hasOwnProperty.call(object, property)) - results.push(property); - } - } - - return results; - } - - function values(object) { - var results = []; - for (var property in object) - results.push(object[property]); - return results; - } - - function clone(object) { - return extend({ }, object); - } - - function isElement(object) { - return !!(object && object.nodeType == 1); - } - - function isArray(object) { - return _toString.call(object) === ARRAY_CLASS; - } - - var hasNativeIsArray = (typeof Array.isArray == 'function') - && Array.isArray([]) && !Array.isArray({}); - - if (hasNativeIsArray) { - isArray = Array.isArray; - } - - function isHash(object) { - return object instanceof Hash; - } - - function isFunction(object) { - return _toString.call(object) === FUNCTION_CLASS; - } - - function isString(object) { - return _toString.call(object) === STRING_CLASS; - } - - function isNumber(object) { - return _toString.call(object) === NUMBER_CLASS; - } - - function isDate(object) { - return _toString.call(object) === DATE_CLASS; - } - - function isUndefined(object) { - return typeof object === "undefined"; - } - - extend(Object, { - extend: extend, - inspect: inspect, - toJSON: NATIVE_JSON_STRINGIFY_SUPPORT ? stringify : toJSON, - toQueryString: toQueryString, - toHTML: toHTML, - keys: Object.keys || keys, - values: values, - clone: clone, - isElement: isElement, - isArray: isArray, - isHash: isHash, - isFunction: isFunction, - isString: isString, - isNumber: isNumber, - isDate: isDate, - isUndefined: isUndefined - }); -})(); -Object.extend(Function.prototype, (function() { - var slice = Array.prototype.slice; - - function update(array, args) { - var arrayLength = array.length, length = args.length; - while (length--) array[arrayLength + length] = args[length]; - return array; - } - - function merge(array, args) { - array = slice.call(array, 0); - return update(array, args); - } - - function argumentNames() { - var names = this.toString().match(/^[\s\(]*function[^(]*\(([^)]*)\)/)[1] - .replace(/\/\/.*?[\r\n]|\/\*(?:.|[\r\n])*?\*\//g, '') - .replace(/\s+/g, '').split(','); - return names.length == 1 && !names[0] ? [] : names; - } - - - function bind(context) { - if (arguments.length < 2 && Object.isUndefined(arguments[0])) - return this; - - if (!Object.isFunction(this)) - throw new TypeError("The object is not callable."); - - var nop = function() {}; - var __method = this, args = slice.call(arguments, 1); - - var bound = function() { - var a = merge(args, arguments); - var c = this instanceof bound ? this : context; - return __method.apply(c, a); - }; - - nop.prototype = this.prototype; - bound.prototype = new nop(); - - return bound; - } - - function bindAsEventListener(context) { - var __method = this, args = slice.call(arguments, 1); - return function(event) { - var a = update([event || window.event], args); - return __method.apply(context, a); - } - } - - function curry() { - if (!arguments.length) return this; - var __method = this, args = slice.call(arguments, 0); - return function() { - var a = merge(args, arguments); - return __method.apply(this, a); - } - } - - function delay(timeout) { - var __method = this, args = slice.call(arguments, 1); - timeout = timeout * 1000; - return window.setTimeout(function() { - return __method.apply(__method, args); - }, timeout); - } - - function defer() { - var args = update([0.01], arguments); - return this.delay.apply(this, args); - } - - function wrap(wrapper) { - var __method = this; - return function() { - var a = update([__method.bind(this)], arguments); - return wrapper.apply(this, a); - } - } - - function methodize() { - if (this._methodized) return this._methodized; - var __method = this; - return this._methodized = function() { - var a = update([this], arguments); - return __method.apply(null, a); - }; - } - - var extensions = { - argumentNames: argumentNames, - bindAsEventListener: bindAsEventListener, - curry: curry, - delay: delay, - defer: defer, - wrap: wrap, - methodize: methodize - }; - - if (!Function.prototype.bind) - extensions.bind = bind; - - return extensions; -})()); - - - -(function(proto) { - - - function toISOString() { - return this.getUTCFullYear() + '-' + - (this.getUTCMonth() + 1).toPaddedString(2) + '-' + - this.getUTCDate().toPaddedString(2) + 'T' + - this.getUTCHours().toPaddedString(2) + ':' + - this.getUTCMinutes().toPaddedString(2) + ':' + - this.getUTCSeconds().toPaddedString(2) + 'Z'; - } - - - function toJSON() { - return this.toISOString(); - } - - if (!proto.toISOString) proto.toISOString = toISOString; - if (!proto.toJSON) proto.toJSON = toJSON; - -})(Date.prototype); - - -RegExp.prototype.match = RegExp.prototype.test; - -RegExp.escape = function(str) { - return String(str).replace(/([.*+?^=!:${}()|[\]\/\\])/g, '\\$1'); -}; -var PeriodicalExecuter = Class.create({ - initialize: function(callback, frequency) { - this.callback = callback; - this.frequency = frequency; - this.currentlyExecuting = false; - - this.registerCallback(); - }, - - registerCallback: function() { - this.timer = setInterval(this.onTimerEvent.bind(this), this.frequency * 1000); - }, - - execute: function() { - this.callback(this); - }, - - stop: function() { - if (!this.timer) return; - clearInterval(this.timer); - this.timer = null; - }, - - onTimerEvent: function() { - if (!this.currentlyExecuting) { - try { - this.currentlyExecuting = true; - this.execute(); - this.currentlyExecuting = false; - } catch(e) { - this.currentlyExecuting = false; - throw e; - } - } - } -}); -Object.extend(String, { - interpret: function(value) { - return value == null ? '' : String(value); - }, - specialChar: { - '\b': '\\b', - '\t': '\\t', - '\n': '\\n', - '\f': '\\f', - '\r': '\\r', - '\\': '\\\\' - } -}); - -Object.extend(String.prototype, (function() { - var NATIVE_JSON_PARSE_SUPPORT = window.JSON && - typeof JSON.parse === 'function' && - JSON.parse('{"test": true}').test; - - function prepareReplacement(replacement) { - if (Object.isFunction(replacement)) return replacement; - var template = new Template(replacement); - return function(match) { return template.evaluate(match) }; - } - - function isNonEmptyRegExp(regexp) { - return regexp.source && regexp.source !== '(?:)'; - } - - - function gsub(pattern, replacement) { - var result = '', source = this, match; - replacement = prepareReplacement(replacement); - - if (Object.isString(pattern)) - pattern = RegExp.escape(pattern); - - if (!(pattern.length || isNonEmptyRegExp(pattern))) { - replacement = replacement(''); - return replacement + source.split('').join(replacement) + replacement; - } - - while (source.length > 0) { - match = source.match(pattern) - if (match && match[0].length > 0) { - result += source.slice(0, match.index); - result += String.interpret(replacement(match)); - source = source.slice(match.index + match[0].length); - } else { - result += source, source = ''; - } - } - return result; - } - - function sub(pattern, replacement, count) { - replacement = prepareReplacement(replacement); - count = Object.isUndefined(count) ? 1 : count; - - return this.gsub(pattern, function(match) { - if (--count < 0) return match[0]; - return replacement(match); - }); - } - - function scan(pattern, iterator) { - this.gsub(pattern, iterator); - return String(this); - } - - function truncate(length, truncation) { - length = length || 30; - truncation = Object.isUndefined(truncation) ? '...' : truncation; - return this.length > length ? - this.slice(0, length - truncation.length) + truncation : String(this); - } - - function strip() { - return this.replace(/^\s+/, '').replace(/\s+$/, ''); - } - - function stripTags() { - return this.replace(/<\w+(\s+("[^"]*"|'[^']*'|[^>])+)?(\/)?>|<\/\w+>/gi, ''); - } - - function stripScripts() { - return this.replace(new RegExp(Prototype.ScriptFragment, 'img'), ''); - } - - function extractScripts() { - var matchAll = new RegExp(Prototype.ScriptFragment, 'img'), - matchOne = new RegExp(Prototype.ScriptFragment, 'im'); - return (this.match(matchAll) || []).map(function(scriptTag) { - return (scriptTag.match(matchOne) || ['', ''])[1]; - }); - } - - function evalScripts() { - return this.extractScripts().map(function(script) { return eval(script); }); - } - - function escapeHTML() { - return this.replace(/&/g,'&').replace(//g,'>'); - } - - function unescapeHTML() { - return this.stripTags().replace(/</g,'<').replace(/>/g,'>').replace(/&/g,'&'); - } - - - function toQueryParams(separator) { - var match = this.strip().match(/([^?#]*)(#.*)?$/); - if (!match) return { }; - - return match[1].split(separator || '&').inject({ }, function(hash, pair) { - if ((pair = pair.split('='))[0]) { - var key = decodeURIComponent(pair.shift()), - value = pair.length > 1 ? pair.join('=') : pair[0]; - - if (value != undefined) { - value = value.gsub('+', ' '); - value = decodeURIComponent(value); - } - - if (key in hash) { - if (!Object.isArray(hash[key])) hash[key] = [hash[key]]; - hash[key].push(value); - } - else hash[key] = value; - } - return hash; - }); - } - - function toArray() { - return this.split(''); - } - - function succ() { - return this.slice(0, this.length - 1) + - String.fromCharCode(this.charCodeAt(this.length - 1) + 1); - } - - function times(count) { - return count < 1 ? '' : new Array(count + 1).join(this); - } - - function camelize() { - return this.replace(/-+(.)?/g, function(match, chr) { - return chr ? chr.toUpperCase() : ''; - }); - } - - function capitalize() { - return this.charAt(0).toUpperCase() + this.substring(1).toLowerCase(); - } - - function underscore() { - return this.replace(/::/g, '/') - .replace(/([A-Z]+)([A-Z][a-z])/g, '$1_$2') - .replace(/([a-z\d])([A-Z])/g, '$1_$2') - .replace(/-/g, '_') - .toLowerCase(); - } - - function dasherize() { - return this.replace(/_/g, '-'); - } - - function inspect(useDoubleQuotes) { - var escapedString = this.replace(/[\x00-\x1f\\]/g, function(character) { - if (character in String.specialChar) { - return String.specialChar[character]; - } - return '\\u00' + character.charCodeAt().toPaddedString(2, 16); - }); - if (useDoubleQuotes) return '"' + escapedString.replace(/"/g, '\\"') + '"'; - return "'" + escapedString.replace(/'/g, '\\\'') + "'"; - } - - function unfilterJSON(filter) { - return this.replace(filter || Prototype.JSONFilter, '$1'); - } - - function isJSON() { - var str = this; - if (str.blank()) return false; - str = str.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@'); - str = str.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']'); - str = str.replace(/(?:^|:|,)(?:\s*\[)+/g, ''); - return (/^[\],:{}\s]*$/).test(str); - } - - function evalJSON(sanitize) { - var json = this.unfilterJSON(), - cx = /[\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff\u0000]/g; - if (cx.test(json)) { - json = json.replace(cx, function (a) { - return '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4); - }); - } - try { - if (!sanitize || json.isJSON()) return eval('(' + json + ')'); - } catch (e) { } - throw new SyntaxError('Badly formed JSON string: ' + this.inspect()); - } - - function parseJSON() { - var json = this.unfilterJSON(); - return JSON.parse(json); - } - - function include(pattern) { - return this.indexOf(pattern) > -1; - } - - function startsWith(pattern, position) { - position = Object.isNumber(position) ? position : 0; - return this.lastIndexOf(pattern, position) === position; - } - - function endsWith(pattern, position) { - pattern = String(pattern); - position = Object.isNumber(position) ? position : this.length; - if (position < 0) position = 0; - if (position > this.length) position = this.length; - var d = position - pattern.length; - return d >= 0 && this.indexOf(pattern, d) === d; - } - - function empty() { - return this == ''; - } - - function blank() { - return /^\s*$/.test(this); - } - - function interpolate(object, pattern) { - return new Template(this, pattern).evaluate(object); - } - - return { - gsub: gsub, - sub: sub, - scan: scan, - truncate: truncate, - strip: String.prototype.trim || strip, - stripTags: stripTags, - stripScripts: stripScripts, - extractScripts: extractScripts, - evalScripts: evalScripts, - escapeHTML: escapeHTML, - unescapeHTML: unescapeHTML, - toQueryParams: toQueryParams, - parseQuery: toQueryParams, - toArray: toArray, - succ: succ, - times: times, - camelize: camelize, - capitalize: capitalize, - underscore: underscore, - dasherize: dasherize, - inspect: inspect, - unfilterJSON: unfilterJSON, - isJSON: isJSON, - evalJSON: NATIVE_JSON_PARSE_SUPPORT ? parseJSON : evalJSON, - include: include, - startsWith: String.prototype.startsWith || startsWith, - endsWith: String.prototype.endsWith || endsWith, - empty: empty, - blank: blank, - interpolate: interpolate - }; -})()); - -var Template = Class.create({ - initialize: function(template, pattern) { - this.template = template.toString(); - this.pattern = pattern || Template.Pattern; - }, - - evaluate: function(object) { - if (object && Object.isFunction(object.toTemplateReplacements)) - object = object.toTemplateReplacements(); - - return this.template.gsub(this.pattern, function(match) { - if (object == null) return (match[1] + ''); - - var before = match[1] || ''; - if (before == '\\') return match[2]; - - var ctx = object, expr = match[3], - pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/; - - match = pattern.exec(expr); - if (match == null) return before; - - while (match != null) { - var comp = match[1].startsWith('[') ? match[2].replace(/\\\\]/g, ']') : match[1]; - ctx = ctx[comp]; - if (null == ctx || '' == match[3]) break; - expr = expr.substring('[' == match[3] ? match[1].length : match[0].length); - match = pattern.exec(expr); - } - - return before + String.interpret(ctx); - }); - } -}); -Template.Pattern = /(^|.|\r|\n)(#\{(.*?)\})/; - -var $break = { }; - -var Enumerable = (function() { - function each(iterator, context) { - try { - this._each(iterator, context); - } catch (e) { - if (e != $break) throw e; - } - return this; - } - - function eachSlice(number, iterator, context) { - var index = -number, slices = [], array = this.toArray(); - if (number < 1) return array; - while ((index += number) < array.length) - slices.push(array.slice(index, index+number)); - return slices.collect(iterator, context); - } - - function all(iterator, context) { - iterator = iterator || Prototype.K; - var result = true; - this.each(function(value, index) { - result = result && !!iterator.call(context, value, index, this); - if (!result) throw $break; - }, this); - return result; - } - - function any(iterator, context) { - iterator = iterator || Prototype.K; - var result = false; - this.each(function(value, index) { - if (result = !!iterator.call(context, value, index, this)) - throw $break; - }, this); - return result; - } - - function collect(iterator, context) { - iterator = iterator || Prototype.K; - var results = []; - this.each(function(value, index) { - results.push(iterator.call(context, value, index, this)); - }, this); - return results; - } - - function detect(iterator, context) { - var result; - this.each(function(value, index) { - if (iterator.call(context, value, index, this)) { - result = value; - throw $break; - } - }, this); - return result; - } - - function findAll(iterator, context) { - var results = []; - this.each(function(value, index) { - if (iterator.call(context, value, index, this)) - results.push(value); - }, this); - return results; - } - - function grep(filter, iterator, context) { - iterator = iterator || Prototype.K; - var results = []; - - if (Object.isString(filter)) - filter = new RegExp(RegExp.escape(filter)); - - this.each(function(value, index) { - if (filter.match(value)) - results.push(iterator.call(context, value, index, this)); - }, this); - return results; - } - - function include(object) { - if (Object.isFunction(this.indexOf) && this.indexOf(object) != -1) - return true; - - var found = false; - this.each(function(value) { - if (value == object) { - found = true; - throw $break; - } - }); - return found; - } - - function inGroupsOf(number, fillWith) { - fillWith = Object.isUndefined(fillWith) ? null : fillWith; - return this.eachSlice(number, function(slice) { - while(slice.length < number) slice.push(fillWith); - return slice; - }); - } - - function inject(memo, iterator, context) { - this.each(function(value, index) { - memo = iterator.call(context, memo, value, index, this); - }, this); - return memo; - } - - function invoke(method) { - var args = $A(arguments).slice(1); - return this.map(function(value) { - return value[method].apply(value, args); - }); - } - - function max(iterator, context) { - iterator = iterator || Prototype.K; - var result; - this.each(function(value, index) { - value = iterator.call(context, value, index, this); - if (result == null || value >= result) - result = value; - }, this); - return result; - } - - function min(iterator, context) { - iterator = iterator || Prototype.K; - var result; - this.each(function(value, index) { - value = iterator.call(context, value, index, this); - if (result == null || value < result) - result = value; - }, this); - return result; - } - - function partition(iterator, context) { - iterator = iterator || Prototype.K; - var trues = [], falses = []; - this.each(function(value, index) { - (iterator.call(context, value, index, this) ? - trues : falses).push(value); - }, this); - return [trues, falses]; - } - - function pluck(property) { - var results = []; - this.each(function(value) { - results.push(value[property]); - }); - return results; - } - - function reject(iterator, context) { - var results = []; - this.each(function(value, index) { - if (!iterator.call(context, value, index, this)) - results.push(value); - }, this); - return results; - } - - function sortBy(iterator, context) { - return this.map(function(value, index) { - return { - value: value, - criteria: iterator.call(context, value, index, this) - }; - }, this).sort(function(left, right) { - var a = left.criteria, b = right.criteria; - return a < b ? -1 : a > b ? 1 : 0; - }).pluck('value'); - } - - function toArray() { - return this.map(); - } - - function zip() { - var iterator = Prototype.K, args = $A(arguments); - if (Object.isFunction(args.last())) - iterator = args.pop(); - - var collections = [this].concat(args).map($A); - return this.map(function(value, index) { - return iterator(collections.pluck(index)); - }); - } - - function size() { - return this.toArray().length; - } - - function inspect() { - return '#'; - } - - - - - - - - - - return { - each: each, - eachSlice: eachSlice, - all: all, - every: all, - any: any, - some: any, - collect: collect, - map: collect, - detect: detect, - findAll: findAll, - select: findAll, - filter: findAll, - grep: grep, - include: include, - member: include, - inGroupsOf: inGroupsOf, - inject: inject, - invoke: invoke, - max: max, - min: min, - partition: partition, - pluck: pluck, - reject: reject, - sortBy: sortBy, - toArray: toArray, - entries: toArray, - zip: zip, - size: size, - inspect: inspect, - find: detect - }; -})(); - -function $A(iterable) { - if (!iterable) return []; - if ('toArray' in Object(iterable)) return iterable.toArray(); - var length = iterable.length || 0, results = new Array(length); - while (length--) results[length] = iterable[length]; - return results; -} - - -function $w(string) { - if (!Object.isString(string)) return []; - string = string.strip(); - return string ? string.split(/\s+/) : []; -} - -Array.from = $A; - - -(function() { - var arrayProto = Array.prototype, - slice = arrayProto.slice, - _each = arrayProto.forEach; // use native browser JS 1.6 implementation if available - - function each(iterator, context) { - for (var i = 0, length = this.length >>> 0; i < length; i++) { - if (i in this) iterator.call(context, this[i], i, this); - } - } - if (!_each) _each = each; - - function clear() { - this.length = 0; - return this; - } - - function first() { - return this[0]; - } - - function last() { - return this[this.length - 1]; - } - - function compact() { - return this.select(function(value) { - return value != null; - }); - } - - function flatten() { - return this.inject([], function(array, value) { - if (Object.isArray(value)) - return array.concat(value.flatten()); - array.push(value); - return array; - }); - } - - function without() { - var values = slice.call(arguments, 0); - return this.select(function(value) { - return !values.include(value); - }); - } - - function reverse(inline) { - return (inline === false ? this.toArray() : this)._reverse(); - } - - function uniq(sorted) { - return this.inject([], function(array, value, index) { - if (0 == index || (sorted ? array.last() != value : !array.include(value))) - array.push(value); - return array; - }); - } - - function intersect(array) { - return this.uniq().findAll(function(item) { - return array.indexOf(item) !== -1; - }); - } - - - function clone() { - return slice.call(this, 0); - } - - function size() { - return this.length; - } - - function inspect() { - return '[' + this.map(Object.inspect).join(', ') + ']'; - } - - function indexOf(item, i) { - if (this == null) throw new TypeError(); - - var array = Object(this), length = array.length >>> 0; - if (length === 0) return -1; - - i = Number(i); - if (isNaN(i)) { - i = 0; - } else if (i !== 0 && isFinite(i)) { - i = (i > 0 ? 1 : -1) * Math.floor(Math.abs(i)); - } - - if (i > length) return -1; - - var k = i >= 0 ? i : Math.max(length - Math.abs(i), 0); - for (; k < length; k++) - if (k in array && array[k] === item) return k; - return -1; - } - - - function lastIndexOf(item, i) { - if (this == null) throw new TypeError(); - - var array = Object(this), length = array.length >>> 0; - if (length === 0) return -1; - - if (!Object.isUndefined(i)) { - i = Number(i); - if (isNaN(i)) { - i = 0; - } else if (i !== 0 && isFinite(i)) { - i = (i > 0 ? 1 : -1) * Math.floor(Math.abs(i)); - } - } else { - i = length; - } - - var k = i >= 0 ? Math.min(i, length - 1) : - length - Math.abs(i); - - for (; k >= 0; k--) - if (k in array && array[k] === item) return k; - return -1; - } - - function concat(_) { - var array = [], items = slice.call(arguments, 0), item, n = 0; - items.unshift(this); - for (var i = 0, length = items.length; i < length; i++) { - item = items[i]; - if (Object.isArray(item) && !('callee' in item)) { - for (var j = 0, arrayLength = item.length; j < arrayLength; j++) { - if (j in item) array[n] = item[j]; - n++; - } - } else { - array[n++] = item; - } - } - array.length = n; - return array; - } - - - function wrapNative(method) { - return function() { - if (arguments.length === 0) { - return method.call(this, Prototype.K); - } else if (arguments[0] === undefined) { - var args = slice.call(arguments, 1); - args.unshift(Prototype.K); - return method.apply(this, args); - } else { - return method.apply(this, arguments); - } - }; - } - - - function map(iterator) { - if (this == null) throw new TypeError(); - iterator = iterator || Prototype.K; - - var object = Object(this); - var results = [], context = arguments[1], n = 0; - - for (var i = 0, length = object.length >>> 0; i < length; i++) { - if (i in object) { - results[n] = iterator.call(context, object[i], i, object); - } - n++; - } - results.length = n; - return results; - } - - if (arrayProto.map) { - map = wrapNative(Array.prototype.map); - } - - function filter(iterator) { - if (this == null || !Object.isFunction(iterator)) - throw new TypeError(); - - var object = Object(this); - var results = [], context = arguments[1], value; - - for (var i = 0, length = object.length >>> 0; i < length; i++) { - if (i in object) { - value = object[i]; - if (iterator.call(context, value, i, object)) { - results.push(value); - } - } - } - return results; - } - - if (arrayProto.filter) { - filter = Array.prototype.filter; - } - - function some(iterator) { - if (this == null) throw new TypeError(); - iterator = iterator || Prototype.K; - var context = arguments[1]; - - var object = Object(this); - for (var i = 0, length = object.length >>> 0; i < length; i++) { - if (i in object && iterator.call(context, object[i], i, object)) { - return true; - } - } - - return false; - } - - if (arrayProto.some) { - some = wrapNative(Array.prototype.some); - } - - function every(iterator) { - if (this == null) throw new TypeError(); - iterator = iterator || Prototype.K; - var context = arguments[1]; - - var object = Object(this); - for (var i = 0, length = object.length >>> 0; i < length; i++) { - if (i in object && !iterator.call(context, object[i], i, object)) { - return false; - } - } - - return true; - } - - if (arrayProto.every) { - every = wrapNative(Array.prototype.every); - } - - - Object.extend(arrayProto, Enumerable); - - if (arrayProto.entries === Enumerable.entries) { - delete arrayProto.entries; - } - - if (!arrayProto._reverse) - arrayProto._reverse = arrayProto.reverse; - - Object.extend(arrayProto, { - _each: _each, - - map: map, - collect: map, - select: filter, - filter: filter, - findAll: filter, - some: some, - any: some, - every: every, - all: every, - - clear: clear, - first: first, - last: last, - compact: compact, - flatten: flatten, - without: without, - reverse: reverse, - uniq: uniq, - intersect: intersect, - clone: clone, - toArray: clone, - size: size, - inspect: inspect - }); - - var CONCAT_ARGUMENTS_BUGGY = (function() { - return [].concat(arguments)[0][0] !== 1; - })(1,2); - - if (CONCAT_ARGUMENTS_BUGGY) arrayProto.concat = concat; - - if (!arrayProto.indexOf) arrayProto.indexOf = indexOf; - if (!arrayProto.lastIndexOf) arrayProto.lastIndexOf = lastIndexOf; -})(); -function $H(object) { - return new Hash(object); -}; - -var Hash = Class.create(Enumerable, (function() { - function initialize(object) { - this._object = Object.isHash(object) ? object.toObject() : Object.clone(object); - } - - - function _each(iterator, context) { - var i = 0; - for (var key in this._object) { - var value = this._object[key], pair = [key, value]; - pair.key = key; - pair.value = value; - iterator.call(context, pair, i); - i++; - } - } - - function set(key, value) { - return this._object[key] = value; - } - - function get(key) { - if (this._object[key] !== Object.prototype[key]) - return this._object[key]; - } - - function unset(key) { - var value = this._object[key]; - delete this._object[key]; - return value; - } - - function toObject() { - return Object.clone(this._object); - } - - - - function keys() { - return this.pluck('key'); - } - - function values() { - return this.pluck('value'); - } - - function index(value) { - var match = this.detect(function(pair) { - return pair.value === value; - }); - return match && match.key; - } - - function merge(object) { - return this.clone().update(object); - } - - function update(object) { - return new Hash(object).inject(this, function(result, pair) { - result.set(pair.key, pair.value); - return result; - }); - } - - function toQueryPair(key, value) { - if (Object.isUndefined(value)) return key; - - value = String.interpret(value); - - value = value.gsub(/(\r)?\n/, '\r\n'); - value = encodeURIComponent(value); - value = value.gsub(/%20/, '+'); - return key + '=' + value; - } - - function toQueryString() { - return this.inject([], function(results, pair) { - var key = encodeURIComponent(pair.key), values = pair.value; - - if (values && typeof values == 'object') { - if (Object.isArray(values)) { - var queryValues = []; - for (var i = 0, len = values.length, value; i < len; i++) { - value = values[i]; - queryValues.push(toQueryPair(key, value)); - } - return results.concat(queryValues); - } - } else results.push(toQueryPair(key, values)); - return results; - }).join('&'); - } - - function inspect() { - return '#'; - } - - function clone() { - return new Hash(this); - } - - return { - initialize: initialize, - _each: _each, - set: set, - get: get, - unset: unset, - toObject: toObject, - toTemplateReplacements: toObject, - keys: keys, - values: values, - index: index, - merge: merge, - update: update, - toQueryString: toQueryString, - inspect: inspect, - toJSON: toObject, - clone: clone - }; -})()); - -Hash.from = $H; -Object.extend(Number.prototype, (function() { - function toColorPart() { - return this.toPaddedString(2, 16); - } - - function succ() { - return this + 1; - } - - function times(iterator, context) { - $R(0, this, true).each(iterator, context); - return this; - } - - function toPaddedString(length, radix) { - var string = this.toString(radix || 10); - return '0'.times(length - string.length) + string; - } - - function abs() { - return Math.abs(this); - } - - function round() { - return Math.round(this); - } - - function ceil() { - return Math.ceil(this); - } - - function floor() { - return Math.floor(this); - } - - return { - toColorPart: toColorPart, - succ: succ, - times: times, - toPaddedString: toPaddedString, - abs: abs, - round: round, - ceil: ceil, - floor: floor - }; -})()); - -function $R(start, end, exclusive) { - return new ObjectRange(start, end, exclusive); -} - -var ObjectRange = Class.create(Enumerable, (function() { - function initialize(start, end, exclusive) { - this.start = start; - this.end = end; - this.exclusive = exclusive; - } - - function _each(iterator, context) { - var value = this.start, i; - for (i = 0; this.include(value); i++) { - iterator.call(context, value, i); - value = value.succ(); - } - } - - function include(value) { - if (value < this.start) - return false; - if (this.exclusive) - return value < this.end; - return value <= this.end; - } - - return { - initialize: initialize, - _each: _each, - include: include - }; -})()); - - - -var Abstract = { }; - - -var Try = { - these: function() { - var returnValue; - - for (var i = 0, length = arguments.length; i < length; i++) { - var lambda = arguments[i]; - try { - returnValue = lambda(); - break; - } catch (e) { } - } - - return returnValue; - } -}; - -var Ajax = { - getTransport: function() { - return Try.these( - function() {return new XMLHttpRequest()}, - function() {return new ActiveXObject('Msxml2.XMLHTTP')}, - function() {return new ActiveXObject('Microsoft.XMLHTTP')} - ) || false; - }, - - activeRequestCount: 0 -}; - -Ajax.Responders = { - responders: [], - - _each: function(iterator, context) { - this.responders._each(iterator, context); - }, - - register: function(responder) { - if (!this.include(responder)) - this.responders.push(responder); - }, - - unregister: function(responder) { - this.responders = this.responders.without(responder); - }, - - dispatch: function(callback, request, transport, json) { - this.each(function(responder) { - if (Object.isFunction(responder[callback])) { - try { - responder[callback].apply(responder, [request, transport, json]); - } catch (e) { } - } - }); - } -}; - -Object.extend(Ajax.Responders, Enumerable); - -Ajax.Responders.register({ - onCreate: function() { Ajax.activeRequestCount++ }, - onComplete: function() { Ajax.activeRequestCount-- } -}); -Ajax.Base = Class.create({ - initialize: function(options) { - this.options = { - method: 'post', - asynchronous: true, - contentType: 'application/x-www-form-urlencoded', - encoding: 'UTF-8', - parameters: '', - evalJSON: true, - evalJS: true - }; - Object.extend(this.options, options || { }); - - this.options.method = this.options.method.toLowerCase(); - - if (Object.isHash(this.options.parameters)) - this.options.parameters = this.options.parameters.toObject(); - } -}); -Ajax.Request = Class.create(Ajax.Base, { - _complete: false, - - initialize: function($super, url, options) { - $super(options); - this.transport = Ajax.getTransport(); - this.request(url); - }, - - request: function(url) { - this.url = url; - this.method = this.options.method; - var params = Object.isString(this.options.parameters) ? - this.options.parameters : - Object.toQueryString(this.options.parameters); - - if (!['get', 'post'].include(this.method)) { - params += (params ? '&' : '') + "_method=" + this.method; - this.method = 'post'; - } - - if (params && this.method === 'get') { - this.url += (this.url.include('?') ? '&' : '?') + params; - } - - this.parameters = params.toQueryParams(); - - try { - var response = new Ajax.Response(this); - if (this.options.onCreate) this.options.onCreate(response); - Ajax.Responders.dispatch('onCreate', this, response); - - this.transport.open(this.method.toUpperCase(), this.url, - this.options.asynchronous); - - if (this.options.asynchronous) this.respondToReadyState.bind(this).defer(1); - - this.transport.onreadystatechange = this.onStateChange.bind(this); - this.setRequestHeaders(); - - this.body = this.method == 'post' ? (this.options.postBody || params) : null; - this.transport.send(this.body); - - /* Force Firefox to handle ready state 4 for synchronous requests */ - if (!this.options.asynchronous && this.transport.overrideMimeType) - this.onStateChange(); - - } - catch (e) { - this.dispatchException(e); - } - }, - - onStateChange: function() { - var readyState = this.transport.readyState; - if (readyState > 1 && !((readyState == 4) && this._complete)) - this.respondToReadyState(this.transport.readyState); - }, - - setRequestHeaders: function() { - var headers = { - 'X-Requested-With': 'XMLHttpRequest', - 'X-Prototype-Version': Prototype.Version, - 'Accept': 'text/javascript, text/html, application/xml, text/xml, */*' - }; - - if (this.method == 'post') { - headers['Content-type'] = this.options.contentType + - (this.options.encoding ? '; charset=' + this.options.encoding : ''); - - /* Force "Connection: close" for older Mozilla browsers to work - * around a bug where XMLHttpRequest sends an incorrect - * Content-length header. See Mozilla Bugzilla #246651. - */ - if (this.transport.overrideMimeType && - (navigator.userAgent.match(/Gecko\/(\d{4})/) || [0,2005])[1] < 2005) - headers['Connection'] = 'close'; - } - - if (typeof this.options.requestHeaders == 'object') { - var extras = this.options.requestHeaders; - - if (Object.isFunction(extras.push)) - for (var i = 0, length = extras.length; i < length; i += 2) - headers[extras[i]] = extras[i+1]; - else - $H(extras).each(function(pair) { headers[pair.key] = pair.value }); - } - - for (var name in headers) - if (headers[name] != null) - this.transport.setRequestHeader(name, headers[name]); - }, - - success: function() { - var status = this.getStatus(); - return !status || (status >= 200 && status < 300) || status == 304; - }, - - getStatus: function() { - try { - if (this.transport.status === 1223) return 204; - return this.transport.status || 0; - } catch (e) { return 0 } - }, - - respondToReadyState: function(readyState) { - var state = Ajax.Request.Events[readyState], response = new Ajax.Response(this); - - if (state == 'Complete') { - try { - this._complete = true; - (this.options['on' + response.status] - || this.options['on' + (this.success() ? 'Success' : 'Failure')] - || Prototype.emptyFunction)(response, response.headerJSON); - } catch (e) { - this.dispatchException(e); - } - - var contentType = response.getHeader('Content-type'); - if (this.options.evalJS == 'force' - || (this.options.evalJS && this.isSameOrigin() && contentType - && contentType.match(/^\s*(text|application)\/(x-)?(java|ecma)script(;.*)?\s*$/i))) - this.evalResponse(); - } - - try { - (this.options['on' + state] || Prototype.emptyFunction)(response, response.headerJSON); - Ajax.Responders.dispatch('on' + state, this, response, response.headerJSON); - } catch (e) { - this.dispatchException(e); - } - - if (state == 'Complete') { - this.transport.onreadystatechange = Prototype.emptyFunction; - } - }, - - isSameOrigin: function() { - var m = this.url.match(/^\s*https?:\/\/[^\/]*/); - return !m || (m[0] == '#{protocol}//#{domain}#{port}'.interpolate({ - protocol: location.protocol, - domain: document.domain, - port: location.port ? ':' + location.port : '' - })); - }, - - getHeader: function(name) { - try { - return this.transport.getResponseHeader(name) || null; - } catch (e) { return null; } - }, - - evalResponse: function() { - try { - return eval((this.transport.responseText || '').unfilterJSON()); - } catch (e) { - this.dispatchException(e); - } - }, - - dispatchException: function(exception) { - (this.options.onException || Prototype.emptyFunction)(this, exception); - Ajax.Responders.dispatch('onException', this, exception); - } -}); - -Ajax.Request.Events = - ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete']; - - - - - - - - -Ajax.Response = Class.create({ - initialize: function(request){ - this.request = request; - var transport = this.transport = request.transport, - readyState = this.readyState = transport.readyState; - - if ((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) { - this.status = this.getStatus(); - this.statusText = this.getStatusText(); - this.responseText = String.interpret(transport.responseText); - this.headerJSON = this._getHeaderJSON(); - } - - if (readyState == 4) { - var xml = transport.responseXML; - this.responseXML = Object.isUndefined(xml) ? null : xml; - this.responseJSON = this._getResponseJSON(); - } - }, - - status: 0, - - statusText: '', - - getStatus: Ajax.Request.prototype.getStatus, - - getStatusText: function() { - try { - return this.transport.statusText || ''; - } catch (e) { return '' } - }, - - getHeader: Ajax.Request.prototype.getHeader, - - getAllHeaders: function() { - try { - return this.getAllResponseHeaders(); - } catch (e) { return null } - }, - - getResponseHeader: function(name) { - return this.transport.getResponseHeader(name); - }, - - getAllResponseHeaders: function() { - return this.transport.getAllResponseHeaders(); - }, - - _getHeaderJSON: function() { - var json = this.getHeader('X-JSON'); - if (!json) return null; - - try { - json = decodeURIComponent(escape(json)); - } catch(e) { - } - - try { - return json.evalJSON(this.request.options.sanitizeJSON || - !this.request.isSameOrigin()); - } catch (e) { - this.request.dispatchException(e); - } - }, - - _getResponseJSON: function() { - var options = this.request.options; - if (!options.evalJSON || (options.evalJSON != 'force' && - !(this.getHeader('Content-type') || '').include('application/json')) || - this.responseText.blank()) - return null; - try { - return this.responseText.evalJSON(options.sanitizeJSON || - !this.request.isSameOrigin()); - } catch (e) { - this.request.dispatchException(e); - } - } -}); - -Ajax.Updater = Class.create(Ajax.Request, { - initialize: function($super, container, url, options) { - this.container = { - success: (container.success || container), - failure: (container.failure || (container.success ? null : container)) - }; - - options = Object.clone(options); - var onComplete = options.onComplete; - options.onComplete = (function(response, json) { - this.updateContent(response.responseText); - if (Object.isFunction(onComplete)) onComplete(response, json); - }).bind(this); - - $super(url, options); - }, - - updateContent: function(responseText) { - var receiver = this.container[this.success() ? 'success' : 'failure'], - options = this.options; - - if (!options.evalScripts) responseText = responseText.stripScripts(); - - if (receiver = $(receiver)) { - if (options.insertion) { - if (Object.isString(options.insertion)) { - var insertion = { }; insertion[options.insertion] = responseText; - receiver.insert(insertion); - } - else options.insertion(receiver, responseText); - } - else receiver.update(responseText); - } - } -}); - -Ajax.PeriodicalUpdater = Class.create(Ajax.Base, { - initialize: function($super, container, url, options) { - $super(options); - this.onComplete = this.options.onComplete; - - this.frequency = (this.options.frequency || 2); - this.decay = (this.options.decay || 1); - - this.updater = { }; - this.container = container; - this.url = url; - - this.start(); - }, - - start: function() { - this.options.onComplete = this.updateComplete.bind(this); - this.onTimerEvent(); - }, - - stop: function() { - this.updater.options.onComplete = undefined; - clearTimeout(this.timer); - (this.onComplete || Prototype.emptyFunction).apply(this, arguments); - }, - - updateComplete: function(response) { - if (this.options.decay) { - this.decay = (response.responseText == this.lastText ? - this.decay * this.options.decay : 1); - - this.lastText = response.responseText; - } - this.timer = this.onTimerEvent.bind(this).delay(this.decay * this.frequency); - }, - - onTimerEvent: function() { - this.updater = new Ajax.Updater(this.container, this.url, this.options); - } -}); - -(function(GLOBAL) { - - var UNDEFINED; - var SLICE = Array.prototype.slice; - - var DIV = document.createElement('div'); - - - function $(element) { - if (arguments.length > 1) { - for (var i = 0, elements = [], length = arguments.length; i < length; i++) - elements.push($(arguments[i])); - return elements; - } - - if (Object.isString(element)) - element = document.getElementById(element); - return Element.extend(element); - } - - GLOBAL.$ = $; - - - if (!GLOBAL.Node) GLOBAL.Node = {}; - - if (!GLOBAL.Node.ELEMENT_NODE) { - Object.extend(GLOBAL.Node, { - ELEMENT_NODE: 1, - ATTRIBUTE_NODE: 2, - TEXT_NODE: 3, - CDATA_SECTION_NODE: 4, - ENTITY_REFERENCE_NODE: 5, - ENTITY_NODE: 6, - PROCESSING_INSTRUCTION_NODE: 7, - COMMENT_NODE: 8, - DOCUMENT_NODE: 9, - DOCUMENT_TYPE_NODE: 10, - DOCUMENT_FRAGMENT_NODE: 11, - NOTATION_NODE: 12 - }); - } - - var ELEMENT_CACHE = {}; - - function shouldUseCreationCache(tagName, attributes) { - if (tagName === 'select') return false; - if ('type' in attributes) return false; - return true; - } - - var HAS_EXTENDED_CREATE_ELEMENT_SYNTAX = (function(){ - try { - var el = document.createElement(''); - return el.tagName.toLowerCase() === 'input' && el.name === 'x'; - } - catch(err) { - return false; - } - })(); - - - var oldElement = GLOBAL.Element; - function Element(tagName, attributes) { - attributes = attributes || {}; - tagName = tagName.toLowerCase(); - - if (HAS_EXTENDED_CREATE_ELEMENT_SYNTAX && attributes.name) { - tagName = '<' + tagName + ' name="' + attributes.name + '">'; - delete attributes.name; - return Element.writeAttribute(document.createElement(tagName), attributes); - } - - if (!ELEMENT_CACHE[tagName]) - ELEMENT_CACHE[tagName] = Element.extend(document.createElement(tagName)); - - var node = shouldUseCreationCache(tagName, attributes) ? - ELEMENT_CACHE[tagName].cloneNode(false) : document.createElement(tagName); - - return Element.writeAttribute(node, attributes); - } - - GLOBAL.Element = Element; - - Object.extend(GLOBAL.Element, oldElement || {}); - if (oldElement) GLOBAL.Element.prototype = oldElement.prototype; - - Element.Methods = { ByTag: {}, Simulated: {} }; - - var methods = {}; - - var INSPECT_ATTRIBUTES = { id: 'id', className: 'class' }; - function inspect(element) { - element = $(element); - var result = '<' + element.tagName.toLowerCase(); - - var attribute, value; - for (var property in INSPECT_ATTRIBUTES) { - attribute = INSPECT_ATTRIBUTES[property]; - value = (element[property] || '').toString(); - if (value) result += ' ' + attribute + '=' + value.inspect(true); - } - - return result + '>'; - } - - methods.inspect = inspect; - - - function visible(element) { - return $(element).getStyle('display') !== 'none'; - } - - function toggle(element, bool) { - element = $(element); - if (typeof bool !== 'boolean') - bool = !Element.visible(element); - Element[bool ? 'show' : 'hide'](element); - - return element; - } - - function hide(element) { - element = $(element); - element.style.display = 'none'; - return element; - } - - function show(element) { - element = $(element); - element.style.display = ''; - return element; - } - - - Object.extend(methods, { - visible: visible, - toggle: toggle, - hide: hide, - show: show - }); - - - function remove(element) { - element = $(element); - element.parentNode.removeChild(element); - return element; - } - - var SELECT_ELEMENT_INNERHTML_BUGGY = (function(){ - var el = document.createElement("select"), - isBuggy = true; - el.innerHTML = ""; - if (el.options && el.options[0]) { - isBuggy = el.options[0].nodeName.toUpperCase() !== "OPTION"; - } - el = null; - return isBuggy; - })(); - - var TABLE_ELEMENT_INNERHTML_BUGGY = (function(){ - try { - var el = document.createElement("table"); - if (el && el.tBodies) { - el.innerHTML = "test"; - var isBuggy = typeof el.tBodies[0] == "undefined"; - el = null; - return isBuggy; - } - } catch (e) { - return true; - } - })(); - - var LINK_ELEMENT_INNERHTML_BUGGY = (function() { - try { - var el = document.createElement('div'); - el.innerHTML = ""; - var isBuggy = (el.childNodes.length === 0); - el = null; - return isBuggy; - } catch(e) { - return true; - } - })(); - - var ANY_INNERHTML_BUGGY = SELECT_ELEMENT_INNERHTML_BUGGY || - TABLE_ELEMENT_INNERHTML_BUGGY || LINK_ELEMENT_INNERHTML_BUGGY; - - var SCRIPT_ELEMENT_REJECTS_TEXTNODE_APPENDING = (function () { - var s = document.createElement("script"), - isBuggy = false; - try { - s.appendChild(document.createTextNode("")); - isBuggy = !s.firstChild || - s.firstChild && s.firstChild.nodeType !== 3; - } catch (e) { - isBuggy = true; - } - s = null; - return isBuggy; - })(); - - function update(element, content) { - element = $(element); - - var descendants = element.getElementsByTagName('*'), - i = descendants.length; - while (i--) purgeElement(descendants[i]); - - if (content && content.toElement) - content = content.toElement(); - - if (Object.isElement(content)) - return element.update().insert(content); - - - content = Object.toHTML(content); - var tagName = element.tagName.toUpperCase(); - - if (tagName === 'SCRIPT' && SCRIPT_ELEMENT_REJECTS_TEXTNODE_APPENDING) { - element.text = content; - return element; - } - - if (ANY_INNERHTML_BUGGY) { - if (tagName in INSERTION_TRANSLATIONS.tags) { - while (element.firstChild) - element.removeChild(element.firstChild); - - var nodes = getContentFromAnonymousElement(tagName, content.stripScripts()); - for (var i = 0, node; node = nodes[i]; i++) - element.appendChild(node); - - } else if (LINK_ELEMENT_INNERHTML_BUGGY && Object.isString(content) && content.indexOf(' -1) { - while (element.firstChild) - element.removeChild(element.firstChild); - - var nodes = getContentFromAnonymousElement(tagName, - content.stripScripts(), true); - - for (var i = 0, node; node = nodes[i]; i++) - element.appendChild(node); - } else { - element.innerHTML = content.stripScripts(); - } - } else { - element.innerHTML = content.stripScripts(); - } - - content.evalScripts.bind(content).defer(); - return element; - } - - function replace(element, content) { - element = $(element); - - if (content && content.toElement) { - content = content.toElement(); - } else if (!Object.isElement(content)) { - content = Object.toHTML(content); - var range = element.ownerDocument.createRange(); - range.selectNode(element); - content.evalScripts.bind(content).defer(); - content = range.createContextualFragment(content.stripScripts()); - } - - element.parentNode.replaceChild(content, element); - return element; - } - - var INSERTION_TRANSLATIONS = { - before: function(element, node) { - element.parentNode.insertBefore(node, element); - }, - top: function(element, node) { - element.insertBefore(node, element.firstChild); - }, - bottom: function(element, node) { - element.appendChild(node); - }, - after: function(element, node) { - element.parentNode.insertBefore(node, element.nextSibling); - }, - - tags: { - TABLE: ['', '
    ', 1], - TBODY: ['', '
    ', 2], - TR: ['', '
    ', 3], - TD: ['
    ', '
    ', 4], - SELECT: ['', 1] - } - }; - - var tags = INSERTION_TRANSLATIONS.tags; - - Object.extend(tags, { - THEAD: tags.TBODY, - TFOOT: tags.TBODY, - TH: tags.TD - }); - - function replace_IE(element, content) { - element = $(element); - if (content && content.toElement) - content = content.toElement(); - if (Object.isElement(content)) { - element.parentNode.replaceChild(content, element); - return element; - } - - content = Object.toHTML(content); - var parent = element.parentNode, tagName = parent.tagName.toUpperCase(); - - if (tagName in INSERTION_TRANSLATIONS.tags) { - var nextSibling = Element.next(element); - var fragments = getContentFromAnonymousElement( - tagName, content.stripScripts()); - - parent.removeChild(element); - - var iterator; - if (nextSibling) - iterator = function(node) { parent.insertBefore(node, nextSibling) }; - else - iterator = function(node) { parent.appendChild(node); } - - fragments.each(iterator); - } else { - element.outerHTML = content.stripScripts(); - } - - content.evalScripts.bind(content).defer(); - return element; - } - - if ('outerHTML' in document.documentElement) - replace = replace_IE; - - function isContent(content) { - if (Object.isUndefined(content) || content === null) return false; - - if (Object.isString(content) || Object.isNumber(content)) return true; - if (Object.isElement(content)) return true; - if (content.toElement || content.toHTML) return true; - - return false; - } - - function insertContentAt(element, content, position) { - position = position.toLowerCase(); - var method = INSERTION_TRANSLATIONS[position]; - - if (content && content.toElement) content = content.toElement(); - if (Object.isElement(content)) { - method(element, content); - return element; - } - - content = Object.toHTML(content); - var tagName = ((position === 'before' || position === 'after') ? - element.parentNode : element).tagName.toUpperCase(); - - var childNodes = getContentFromAnonymousElement(tagName, content.stripScripts()); - - if (position === 'top' || position === 'after') childNodes.reverse(); - - for (var i = 0, node; node = childNodes[i]; i++) - method(element, node); - - content.evalScripts.bind(content).defer(); - } - - function insert(element, insertions) { - element = $(element); - - if (isContent(insertions)) - insertions = { bottom: insertions }; - - for (var position in insertions) - insertContentAt(element, insertions[position], position); - - return element; - } - - function wrap(element, wrapper, attributes) { - element = $(element); - - if (Object.isElement(wrapper)) { - $(wrapper).writeAttribute(attributes || {}); - } else if (Object.isString(wrapper)) { - wrapper = new Element(wrapper, attributes); - } else { - wrapper = new Element('div', wrapper); - } - - if (element.parentNode) - element.parentNode.replaceChild(wrapper, element); - - wrapper.appendChild(element); - - return wrapper; - } - - function cleanWhitespace(element) { - element = $(element); - var node = element.firstChild; - - while (node) { - var nextNode = node.nextSibling; - if (node.nodeType === Node.TEXT_NODE && !/\S/.test(node.nodeValue)) - element.removeChild(node); - node = nextNode; - } - return element; - } - - function empty(element) { - return $(element).innerHTML.blank(); - } - - function getContentFromAnonymousElement(tagName, html, force) { - var t = INSERTION_TRANSLATIONS.tags[tagName], div = DIV; - - var workaround = !!t; - if (!workaround && force) { - workaround = true; - t = ['', '', 0]; - } - - if (workaround) { - div.innerHTML = ' ' + t[0] + html + t[1]; - div.removeChild(div.firstChild); - for (var i = t[2]; i--; ) - div = div.firstChild; - } else { - div.innerHTML = html; - } - - return $A(div.childNodes); - } - - function clone(element, deep) { - if (!(element = $(element))) return; - var clone = element.cloneNode(deep); - if (!HAS_UNIQUE_ID_PROPERTY) { - clone._prototypeUID = UNDEFINED; - if (deep) { - var descendants = Element.select(clone, '*'), - i = descendants.length; - while (i--) - descendants[i]._prototypeUID = UNDEFINED; - } - } - return Element.extend(clone); - } - - function purgeElement(element) { - var uid = getUniqueElementID(element); - if (uid) { - Element.stopObserving(element); - if (!HAS_UNIQUE_ID_PROPERTY) - element._prototypeUID = UNDEFINED; - delete Element.Storage[uid]; - } - } - - function purgeCollection(elements) { - var i = elements.length; - while (i--) - purgeElement(elements[i]); - } - - function purgeCollection_IE(elements) { - var i = elements.length, element, uid; - while (i--) { - element = elements[i]; - uid = getUniqueElementID(element); - delete Element.Storage[uid]; - delete Event.cache[uid]; - } - } - - if (HAS_UNIQUE_ID_PROPERTY) { - purgeCollection = purgeCollection_IE; - } - - - function purge(element) { - if (!(element = $(element))) return; - purgeElement(element); - - var descendants = element.getElementsByTagName('*'), - i = descendants.length; - - while (i--) purgeElement(descendants[i]); - - return null; - } - - Object.extend(methods, { - remove: remove, - update: update, - replace: replace, - insert: insert, - wrap: wrap, - cleanWhitespace: cleanWhitespace, - empty: empty, - clone: clone, - purge: purge - }); - - - - function recursivelyCollect(element, property, maximumLength) { - element = $(element); - maximumLength = maximumLength || -1; - var elements = []; - - while (element = element[property]) { - if (element.nodeType === Node.ELEMENT_NODE) - elements.push(Element.extend(element)); - - if (elements.length === maximumLength) break; - } - - return elements; - } - - - function ancestors(element) { - return recursivelyCollect(element, 'parentNode'); - } - - function descendants(element) { - return Element.select(element, '*'); - } - - function firstDescendant(element) { - element = $(element).firstChild; - while (element && element.nodeType !== Node.ELEMENT_NODE) - element = element.nextSibling; - - return $(element); - } - - function immediateDescendants(element) { - var results = [], child = $(element).firstChild; - - while (child) { - if (child.nodeType === Node.ELEMENT_NODE) - results.push(Element.extend(child)); - - child = child.nextSibling; - } - - return results; - } - - function previousSiblings(element) { - return recursivelyCollect(element, 'previousSibling'); - } - - function nextSiblings(element) { - return recursivelyCollect(element, 'nextSibling'); - } - - function siblings(element) { - element = $(element); - var previous = previousSiblings(element), - next = nextSiblings(element); - return previous.reverse().concat(next); - } - - function match(element, selector) { - element = $(element); - - if (Object.isString(selector)) - return Prototype.Selector.match(element, selector); - - return selector.match(element); - } - - - function _recursivelyFind(element, property, expression, index) { - element = $(element), expression = expression || 0, index = index || 0; - if (Object.isNumber(expression)) { - index = expression, expression = null; - } - - while (element = element[property]) { - if (element.nodeType !== 1) continue; - if (expression && !Prototype.Selector.match(element, expression)) - continue; - if (--index >= 0) continue; - - return Element.extend(element); - } - } - - - function up(element, expression, index) { - element = $(element); - - if (arguments.length === 1) return $(element.parentNode); - return _recursivelyFind(element, 'parentNode', expression, index); - } - - function down(element, expression, index) { - if (arguments.length === 1) return firstDescendant(element); - element = $(element), expression = expression || 0, index = index || 0; - - if (Object.isNumber(expression)) - index = expression, expression = '*'; - - var node = Prototype.Selector.select(expression, element)[index]; - return Element.extend(node); - } - - function previous(element, expression, index) { - return _recursivelyFind(element, 'previousSibling', expression, index); - } - - function next(element, expression, index) { - return _recursivelyFind(element, 'nextSibling', expression, index); - } - - function select(element) { - element = $(element); - var expressions = SLICE.call(arguments, 1).join(', '); - return Prototype.Selector.select(expressions, element); - } - - function adjacent(element) { - element = $(element); - var expressions = SLICE.call(arguments, 1).join(', '); - var siblings = Element.siblings(element), results = []; - for (var i = 0, sibling; sibling = siblings[i]; i++) { - if (Prototype.Selector.match(sibling, expressions)) - results.push(sibling); - } - - return results; - } - - function descendantOf_DOM(element, ancestor) { - element = $(element), ancestor = $(ancestor); - if (!element || !ancestor) return false; - while (element = element.parentNode) - if (element === ancestor) return true; - return false; - } - - function descendantOf_contains(element, ancestor) { - element = $(element), ancestor = $(ancestor); - if (!element || !ancestor) return false; - if (!ancestor.contains) return descendantOf_DOM(element, ancestor); - return ancestor.contains(element) && ancestor !== element; - } - - function descendantOf_compareDocumentPosition(element, ancestor) { - element = $(element), ancestor = $(ancestor); - if (!element || !ancestor) return false; - return (element.compareDocumentPosition(ancestor) & 8) === 8; - } - - var descendantOf; - if (DIV.compareDocumentPosition) { - descendantOf = descendantOf_compareDocumentPosition; - } else if (DIV.contains) { - descendantOf = descendantOf_contains; - } else { - descendantOf = descendantOf_DOM; - } - - - Object.extend(methods, { - recursivelyCollect: recursivelyCollect, - ancestors: ancestors, - descendants: descendants, - firstDescendant: firstDescendant, - immediateDescendants: immediateDescendants, - previousSiblings: previousSiblings, - nextSiblings: nextSiblings, - siblings: siblings, - match: match, - up: up, - down: down, - previous: previous, - next: next, - select: select, - adjacent: adjacent, - descendantOf: descendantOf, - - getElementsBySelector: select, - - childElements: immediateDescendants - }); - - - var idCounter = 1; - function identify(element) { - element = $(element); - var id = Element.readAttribute(element, 'id'); - if (id) return id; - - do { id = 'anonymous_element_' + idCounter++ } while ($(id)); - - Element.writeAttribute(element, 'id', id); - return id; - } - - - function readAttribute(element, name) { - return $(element).getAttribute(name); - } - - function readAttribute_IE(element, name) { - element = $(element); - - var table = ATTRIBUTE_TRANSLATIONS.read; - if (table.values[name]) - return table.values[name](element, name); - - if (table.names[name]) name = table.names[name]; - - if (name.include(':')) { - if (!element.attributes || !element.attributes[name]) return null; - return element.attributes[name].value; - } - - return element.getAttribute(name); - } - - function readAttribute_Opera(element, name) { - if (name === 'title') return element.title; - return element.getAttribute(name); - } - - var PROBLEMATIC_ATTRIBUTE_READING = (function() { - DIV.setAttribute('onclick', []); - var value = DIV.getAttribute('onclick'); - var isFunction = Object.isArray(value); - DIV.removeAttribute('onclick'); - return isFunction; - })(); - - if (PROBLEMATIC_ATTRIBUTE_READING) { - readAttribute = readAttribute_IE; - } else if (Prototype.Browser.Opera) { - readAttribute = readAttribute_Opera; - } - - - function writeAttribute(element, name, value) { - element = $(element); - var attributes = {}, table = ATTRIBUTE_TRANSLATIONS.write; - - if (typeof name === 'object') { - attributes = name; - } else { - attributes[name] = Object.isUndefined(value) ? true : value; - } - - for (var attr in attributes) { - name = table.names[attr] || attr; - value = attributes[attr]; - if (table.values[attr]) { - value = table.values[attr](element, value); - if (Object.isUndefined(value)) continue; - } - if (value === false || value === null) - element.removeAttribute(name); - else if (value === true) - element.setAttribute(name, name); - else element.setAttribute(name, value); - } - - return element; - } - - var PROBLEMATIC_HAS_ATTRIBUTE_WITH_CHECKBOXES = (function () { - if (!HAS_EXTENDED_CREATE_ELEMENT_SYNTAX) { - return false; - } - var checkbox = document.createElement(''); - checkbox.checked = true; - var node = checkbox.getAttributeNode('checked'); - return !node || !node.specified; - })(); - - function hasAttribute(element, attribute) { - attribute = ATTRIBUTE_TRANSLATIONS.has[attribute] || attribute; - var node = $(element).getAttributeNode(attribute); - return !!(node && node.specified); - } - - function hasAttribute_IE(element, attribute) { - if (attribute === 'checked') { - return element.checked; - } - return hasAttribute(element, attribute); - } - - GLOBAL.Element.Methods.Simulated.hasAttribute = - PROBLEMATIC_HAS_ATTRIBUTE_WITH_CHECKBOXES ? - hasAttribute_IE : hasAttribute; - - function classNames(element) { - return new Element.ClassNames(element); - } - - var regExpCache = {}; - function getRegExpForClassName(className) { - if (regExpCache[className]) return regExpCache[className]; - - var re = new RegExp("(^|\\s+)" + className + "(\\s+|$)"); - regExpCache[className] = re; - return re; - } - - function hasClassName(element, className) { - if (!(element = $(element))) return; - - var elementClassName = element.className; - - if (elementClassName.length === 0) return false; - if (elementClassName === className) return true; - - return getRegExpForClassName(className).test(elementClassName); - } - - function addClassName(element, className) { - if (!(element = $(element))) return; - - if (!hasClassName(element, className)) - element.className += (element.className ? ' ' : '') + className; - - return element; - } - - function removeClassName(element, className) { - if (!(element = $(element))) return; - - element.className = element.className.replace( - getRegExpForClassName(className), ' ').strip(); - - return element; - } - - function toggleClassName(element, className, bool) { - if (!(element = $(element))) return; - - if (Object.isUndefined(bool)) - bool = !hasClassName(element, className); - - var method = Element[bool ? 'addClassName' : 'removeClassName']; - return method(element, className); - } - - var ATTRIBUTE_TRANSLATIONS = {}; - - var classProp = 'className', forProp = 'for'; - - DIV.setAttribute(classProp, 'x'); - if (DIV.className !== 'x') { - DIV.setAttribute('class', 'x'); - if (DIV.className === 'x') - classProp = 'class'; - } - - var LABEL = document.createElement('label'); - LABEL.setAttribute(forProp, 'x'); - if (LABEL.htmlFor !== 'x') { - LABEL.setAttribute('htmlFor', 'x'); - if (LABEL.htmlFor === 'x') - forProp = 'htmlFor'; - } - LABEL = null; - - function _getAttr(element, attribute) { - return element.getAttribute(attribute); - } - - function _getAttr2(element, attribute) { - return element.getAttribute(attribute, 2); - } - - function _getAttrNode(element, attribute) { - var node = element.getAttributeNode(attribute); - return node ? node.value : ''; - } - - function _getFlag(element, attribute) { - return $(element).hasAttribute(attribute) ? attribute : null; - } - - DIV.onclick = Prototype.emptyFunction; - var onclickValue = DIV.getAttribute('onclick'); - - var _getEv; - - if (String(onclickValue).indexOf('{') > -1) { - _getEv = function(element, attribute) { - var value = element.getAttribute(attribute); - if (!value) return null; - value = value.toString(); - value = value.split('{')[1]; - value = value.split('}')[0]; - return value.strip(); - }; - } - else if (onclickValue === '') { - _getEv = function(element, attribute) { - var value = element.getAttribute(attribute); - if (!value) return null; - return value.strip(); - }; - } - - ATTRIBUTE_TRANSLATIONS.read = { - names: { - 'class': classProp, - 'className': classProp, - 'for': forProp, - 'htmlFor': forProp - }, - - values: { - style: function(element) { - return element.style.cssText.toLowerCase(); - }, - title: function(element) { - return element.title; - } - } - }; - - ATTRIBUTE_TRANSLATIONS.write = { - names: { - className: 'class', - htmlFor: 'for', - cellpadding: 'cellPadding', - cellspacing: 'cellSpacing' - }, - - values: { - checked: function(element, value) { - value = !!value; - element.checked = value; - return value ? 'checked' : null; - }, - - style: function(element, value) { - element.style.cssText = value ? value : ''; - } - } - }; - - ATTRIBUTE_TRANSLATIONS.has = { names: {} }; - - Object.extend(ATTRIBUTE_TRANSLATIONS.write.names, - ATTRIBUTE_TRANSLATIONS.read.names); - - var CAMEL_CASED_ATTRIBUTE_NAMES = $w('colSpan rowSpan vAlign dateTime ' + - 'accessKey tabIndex encType maxLength readOnly longDesc frameBorder'); - - for (var i = 0, attr; attr = CAMEL_CASED_ATTRIBUTE_NAMES[i]; i++) { - ATTRIBUTE_TRANSLATIONS.write.names[attr.toLowerCase()] = attr; - ATTRIBUTE_TRANSLATIONS.has.names[attr.toLowerCase()] = attr; - } - - Object.extend(ATTRIBUTE_TRANSLATIONS.read.values, { - href: _getAttr2, - src: _getAttr2, - type: _getAttr, - action: _getAttrNode, - disabled: _getFlag, - checked: _getFlag, - readonly: _getFlag, - multiple: _getFlag, - onload: _getEv, - onunload: _getEv, - onclick: _getEv, - ondblclick: _getEv, - onmousedown: _getEv, - onmouseup: _getEv, - onmouseover: _getEv, - onmousemove: _getEv, - onmouseout: _getEv, - onfocus: _getEv, - onblur: _getEv, - onkeypress: _getEv, - onkeydown: _getEv, - onkeyup: _getEv, - onsubmit: _getEv, - onreset: _getEv, - onselect: _getEv, - onchange: _getEv - }); - - - Object.extend(methods, { - identify: identify, - readAttribute: readAttribute, - writeAttribute: writeAttribute, - classNames: classNames, - hasClassName: hasClassName, - addClassName: addClassName, - removeClassName: removeClassName, - toggleClassName: toggleClassName - }); - - - function normalizeStyleName(style) { - if (style === 'float' || style === 'styleFloat') - return 'cssFloat'; - return style.camelize(); - } - - function normalizeStyleName_IE(style) { - if (style === 'float' || style === 'cssFloat') - return 'styleFloat'; - return style.camelize(); - } - - function setStyle(element, styles) { - element = $(element); - var elementStyle = element.style, match; - - if (Object.isString(styles)) { - elementStyle.cssText += ';' + styles; - if (styles.include('opacity')) { - var opacity = styles.match(/opacity:\s*(\d?\.?\d*)/)[1]; - Element.setOpacity(element, opacity); - } - return element; - } - - for (var property in styles) { - if (property === 'opacity') { - Element.setOpacity(element, styles[property]); - } else { - var value = styles[property]; - if (property === 'float' || property === 'cssFloat') { - property = Object.isUndefined(elementStyle.styleFloat) ? - 'cssFloat' : 'styleFloat'; - } - elementStyle[property] = value; - } - } - - return element; - } - - - function getStyle(element, style) { - element = $(element); - style = normalizeStyleName(style); - - var value = element.style[style]; - if (!value || value === 'auto') { - var css = document.defaultView.getComputedStyle(element, null); - value = css ? css[style] : null; - } - - if (style === 'opacity') return value ? parseFloat(value) : 1.0; - return value === 'auto' ? null : value; - } - - function getStyle_Opera(element, style) { - switch (style) { - case 'height': case 'width': - if (!Element.visible(element)) return null; - - var dim = parseInt(getStyle(element, style), 10); - - if (dim !== element['offset' + style.capitalize()]) - return dim + 'px'; - - return Element.measure(element, style); - - default: return getStyle(element, style); - } - } - - function getStyle_IE(element, style) { - element = $(element); - style = normalizeStyleName_IE(style); - - var value = element.style[style]; - if (!value && element.currentStyle) { - value = element.currentStyle[style]; - } - - if (style === 'opacity') { - if (!STANDARD_CSS_OPACITY_SUPPORTED) - return getOpacity_IE(element); - else return value ? parseFloat(value) : 1.0; - } - - if (value === 'auto') { - if ((style === 'width' || style === 'height') && Element.visible(element)) - return Element.measure(element, style) + 'px'; - return null; - } - - return value; - } - - function stripAlphaFromFilter_IE(filter) { - return (filter || '').replace(/alpha\([^\)]*\)/gi, ''); - } - - function hasLayout_IE(element) { - if (!element.currentStyle || !element.currentStyle.hasLayout) - element.style.zoom = 1; - return element; - } - - var STANDARD_CSS_OPACITY_SUPPORTED = (function() { - DIV.style.cssText = "opacity:.55"; - return /^0.55/.test(DIV.style.opacity); - })(); - - function setOpacity(element, value) { - element = $(element); - if (value == 1 || value === '') value = ''; - else if (value < 0.00001) value = 0; - element.style.opacity = value; - return element; - } - - function setOpacity_IE(element, value) { - if (STANDARD_CSS_OPACITY_SUPPORTED) - return setOpacity(element, value); - - element = hasLayout_IE($(element)); - var filter = Element.getStyle(element, 'filter'), - style = element.style; - - if (value == 1 || value === '') { - filter = stripAlphaFromFilter_IE(filter); - if (filter) style.filter = filter; - else style.removeAttribute('filter'); - return element; - } - - if (value < 0.00001) value = 0; - - style.filter = stripAlphaFromFilter_IE(filter) + - ' alpha(opacity=' + (value * 100) + ')'; - - return element; - } - - - function getOpacity(element) { - return Element.getStyle(element, 'opacity'); - } - - function getOpacity_IE(element) { - if (STANDARD_CSS_OPACITY_SUPPORTED) - return getOpacity(element); - - var filter = Element.getStyle(element, 'filter'); - if (filter.length === 0) return 1.0; - var match = (filter || '').match(/alpha\(opacity=(.*)\)/i); - if (match && match[1]) return parseFloat(match[1]) / 100; - return 1.0; - } - - - Object.extend(methods, { - setStyle: setStyle, - getStyle: getStyle, - setOpacity: setOpacity, - getOpacity: getOpacity - }); - - if ('styleFloat' in DIV.style) { - methods.getStyle = getStyle_IE; - methods.setOpacity = setOpacity_IE; - methods.getOpacity = getOpacity_IE; - } - - var UID = 0; - - GLOBAL.Element.Storage = { UID: 1 }; - - function getUniqueElementID(element) { - if (element === window) return 0; - - if (typeof element._prototypeUID === 'undefined') - element._prototypeUID = Element.Storage.UID++; - return element._prototypeUID; - } - - function getUniqueElementID_IE(element) { - if (element === window) return 0; - if (element == document) return 1; - return element.uniqueID; - } - - var HAS_UNIQUE_ID_PROPERTY = ('uniqueID' in DIV); - if (HAS_UNIQUE_ID_PROPERTY) - getUniqueElementID = getUniqueElementID_IE; - - function getStorage(element) { - if (!(element = $(element))) return; - - var uid = getUniqueElementID(element); - - if (!Element.Storage[uid]) - Element.Storage[uid] = $H(); - - return Element.Storage[uid]; - } - - function store(element, key, value) { - if (!(element = $(element))) return; - var storage = getStorage(element); - if (arguments.length === 2) { - storage.update(key); - } else { - storage.set(key, value); - } - return element; - } - - function retrieve(element, key, defaultValue) { - if (!(element = $(element))) return; - var storage = getStorage(element), value = storage.get(key); - - if (Object.isUndefined(value)) { - storage.set(key, defaultValue); - value = defaultValue; - } - - return value; - } - - - Object.extend(methods, { - getStorage: getStorage, - store: store, - retrieve: retrieve - }); - - - var Methods = {}, ByTag = Element.Methods.ByTag, - F = Prototype.BrowserFeatures; - - if (!F.ElementExtensions && ('__proto__' in DIV)) { - GLOBAL.HTMLElement = {}; - GLOBAL.HTMLElement.prototype = DIV['__proto__']; - F.ElementExtensions = true; - } - - function checkElementPrototypeDeficiency(tagName) { - if (typeof window.Element === 'undefined') return false; - if (!HAS_EXTENDED_CREATE_ELEMENT_SYNTAX) return false; - var proto = window.Element.prototype; - if (proto) { - var id = '_' + (Math.random() + '').slice(2), - el = document.createElement(tagName); - proto[id] = 'x'; - var isBuggy = (el[id] !== 'x'); - delete proto[id]; - el = null; - return isBuggy; - } - - return false; - } - - var HTMLOBJECTELEMENT_PROTOTYPE_BUGGY = - checkElementPrototypeDeficiency('object'); - - function extendElementWith(element, methods) { - for (var property in methods) { - var value = methods[property]; - if (Object.isFunction(value) && !(property in element)) - element[property] = value.methodize(); - } - } - - var EXTENDED = {}; - function elementIsExtended(element) { - var uid = getUniqueElementID(element); - return (uid in EXTENDED); - } - - function extend(element) { - if (!element || elementIsExtended(element)) return element; - if (element.nodeType !== Node.ELEMENT_NODE || element == window) - return element; - - var methods = Object.clone(Methods), - tagName = element.tagName.toUpperCase(); - - if (ByTag[tagName]) Object.extend(methods, ByTag[tagName]); - - extendElementWith(element, methods); - EXTENDED[getUniqueElementID(element)] = true; - return element; - } - - function extend_IE8(element) { - if (!element || elementIsExtended(element)) return element; - - var t = element.tagName; - if (t && (/^(?:object|applet|embed)$/i.test(t))) { - extendElementWith(element, Element.Methods); - extendElementWith(element, Element.Methods.Simulated); - extendElementWith(element, Element.Methods.ByTag[t.toUpperCase()]); - } - - return element; - } - - if (F.SpecificElementExtensions) { - extend = HTMLOBJECTELEMENT_PROTOTYPE_BUGGY ? extend_IE8 : Prototype.K; - } - - function addMethodsToTagName(tagName, methods) { - tagName = tagName.toUpperCase(); - if (!ByTag[tagName]) ByTag[tagName] = {}; - Object.extend(ByTag[tagName], methods); - } - - function mergeMethods(destination, methods, onlyIfAbsent) { - if (Object.isUndefined(onlyIfAbsent)) onlyIfAbsent = false; - for (var property in methods) { - var value = methods[property]; - if (!Object.isFunction(value)) continue; - if (!onlyIfAbsent || !(property in destination)) - destination[property] = value.methodize(); - } - } - - function findDOMClass(tagName) { - var klass; - var trans = { - "OPTGROUP": "OptGroup", "TEXTAREA": "TextArea", "P": "Paragraph", - "FIELDSET": "FieldSet", "UL": "UList", "OL": "OList", "DL": "DList", - "DIR": "Directory", "H1": "Heading", "H2": "Heading", "H3": "Heading", - "H4": "Heading", "H5": "Heading", "H6": "Heading", "Q": "Quote", - "INS": "Mod", "DEL": "Mod", "A": "Anchor", "IMG": "Image", "CAPTION": - "TableCaption", "COL": "TableCol", "COLGROUP": "TableCol", "THEAD": - "TableSection", "TFOOT": "TableSection", "TBODY": "TableSection", "TR": - "TableRow", "TH": "TableCell", "TD": "TableCell", "FRAMESET": - "FrameSet", "IFRAME": "IFrame" - }; - if (trans[tagName]) klass = 'HTML' + trans[tagName] + 'Element'; - if (window[klass]) return window[klass]; - klass = 'HTML' + tagName + 'Element'; - if (window[klass]) return window[klass]; - klass = 'HTML' + tagName.capitalize() + 'Element'; - if (window[klass]) return window[klass]; - - var element = document.createElement(tagName), - proto = element['__proto__'] || element.constructor.prototype; - - element = null; - return proto; - } - - function addMethods(methods) { - if (arguments.length === 0) addFormMethods(); - - if (arguments.length === 2) { - var tagName = methods; - methods = arguments[1]; - } - - if (!tagName) { - Object.extend(Element.Methods, methods || {}); - } else { - if (Object.isArray(tagName)) { - for (var i = 0, tag; tag = tagName[i]; i++) - addMethodsToTagName(tag, methods); - } else { - addMethodsToTagName(tagName, methods); - } - } - - var ELEMENT_PROTOTYPE = window.HTMLElement ? HTMLElement.prototype : - Element.prototype; - - if (F.ElementExtensions) { - mergeMethods(ELEMENT_PROTOTYPE, Element.Methods); - mergeMethods(ELEMENT_PROTOTYPE, Element.Methods.Simulated, true); - } - - if (F.SpecificElementExtensions) { - for (var tag in Element.Methods.ByTag) { - var klass = findDOMClass(tag); - if (Object.isUndefined(klass)) continue; - mergeMethods(klass.prototype, ByTag[tag]); - } - } - - Object.extend(Element, Element.Methods); - Object.extend(Element, Element.Methods.Simulated); - delete Element.ByTag; - delete Element.Simulated; - - Element.extend.refresh(); - - ELEMENT_CACHE = {}; - } - - Object.extend(GLOBAL.Element, { - extend: extend, - addMethods: addMethods - }); - - if (extend === Prototype.K) { - GLOBAL.Element.extend.refresh = Prototype.emptyFunction; - } else { - GLOBAL.Element.extend.refresh = function() { - if (Prototype.BrowserFeatures.ElementExtensions) return; - Object.extend(Methods, Element.Methods); - Object.extend(Methods, Element.Methods.Simulated); - - EXTENDED = {}; - }; - } - - function addFormMethods() { - Object.extend(Form, Form.Methods); - Object.extend(Form.Element, Form.Element.Methods); - Object.extend(Element.Methods.ByTag, { - "FORM": Object.clone(Form.Methods), - "INPUT": Object.clone(Form.Element.Methods), - "SELECT": Object.clone(Form.Element.Methods), - "TEXTAREA": Object.clone(Form.Element.Methods), - "BUTTON": Object.clone(Form.Element.Methods) - }); - } - - Element.addMethods(methods); - - function destroyCache_IE() { - DIV = null; - ELEMENT_CACHE = null; - } - - if (window.attachEvent) - window.attachEvent('onunload', destroyCache_IE); - -})(this); -(function() { - - function toDecimal(pctString) { - var match = pctString.match(/^(\d+)%?$/i); - if (!match) return null; - return (Number(match[1]) / 100); - } - - function getRawStyle(element, style) { - element = $(element); - - var value = element.style[style]; - if (!value || value === 'auto') { - var css = document.defaultView.getComputedStyle(element, null); - value = css ? css[style] : null; - } - - if (style === 'opacity') return value ? parseFloat(value) : 1.0; - return value === 'auto' ? null : value; - } - - function getRawStyle_IE(element, style) { - var value = element.style[style]; - if (!value && element.currentStyle) { - value = element.currentStyle[style]; - } - return value; - } - - function getContentWidth(element, context) { - var boxWidth = element.offsetWidth; - - var bl = getPixelValue(element, 'borderLeftWidth', context) || 0; - var br = getPixelValue(element, 'borderRightWidth', context) || 0; - var pl = getPixelValue(element, 'paddingLeft', context) || 0; - var pr = getPixelValue(element, 'paddingRight', context) || 0; - - return boxWidth - bl - br - pl - pr; - } - - if (!Object.isUndefined(document.documentElement.currentStyle) && !Prototype.Browser.Opera) { - getRawStyle = getRawStyle_IE; - } - - - function getPixelValue(value, property, context) { - var element = null; - if (Object.isElement(value)) { - element = value; - value = getRawStyle(element, property); - } - - if (value === null || Object.isUndefined(value)) { - return null; - } - - if ((/^(?:-)?\d+(\.\d+)?(px)?$/i).test(value)) { - return window.parseFloat(value); - } - - var isPercentage = value.include('%'), isViewport = (context === document.viewport); - - if (/\d/.test(value) && element && element.runtimeStyle && !(isPercentage && isViewport)) { - var style = element.style.left, rStyle = element.runtimeStyle.left; - element.runtimeStyle.left = element.currentStyle.left; - element.style.left = value || 0; - value = element.style.pixelLeft; - element.style.left = style; - element.runtimeStyle.left = rStyle; - - return value; - } - - if (element && isPercentage) { - context = context || element.parentNode; - var decimal = toDecimal(value), whole = null; - - var isHorizontal = property.include('left') || property.include('right') || - property.include('width'); - - var isVertical = property.include('top') || property.include('bottom') || - property.include('height'); - - if (context === document.viewport) { - if (isHorizontal) { - whole = document.viewport.getWidth(); - } else if (isVertical) { - whole = document.viewport.getHeight(); - } - } else { - if (isHorizontal) { - whole = $(context).measure('width'); - } else if (isVertical) { - whole = $(context).measure('height'); - } - } - - return (whole === null) ? 0 : whole * decimal; - } - - return 0; - } - - function toCSSPixels(number) { - if (Object.isString(number) && number.endsWith('px')) - return number; - return number + 'px'; - } - - function isDisplayed(element) { - while (element && element.parentNode) { - var display = element.getStyle('display'); - if (display === 'none') { - return false; - } - element = $(element.parentNode); - } - return true; - } - - var hasLayout = Prototype.K; - if ('currentStyle' in document.documentElement) { - hasLayout = function(element) { - if (!element.currentStyle.hasLayout) { - element.style.zoom = 1; - } - return element; - }; - } - - function cssNameFor(key) { - if (key.include('border')) key = key + '-width'; - return key.camelize(); - } - - Element.Layout = Class.create(Hash, { - initialize: function($super, element, preCompute) { - $super(); - this.element = $(element); - - Element.Layout.PROPERTIES.each( function(property) { - this._set(property, null); - }, this); - - if (preCompute) { - this._preComputing = true; - this._begin(); - Element.Layout.PROPERTIES.each( this._compute, this ); - this._end(); - this._preComputing = false; - } - }, - - _set: function(property, value) { - return Hash.prototype.set.call(this, property, value); - }, - - set: function(property, value) { - throw "Properties of Element.Layout are read-only."; - }, - - get: function($super, property) { - var value = $super(property); - return value === null ? this._compute(property) : value; - }, - - _begin: function() { - if (this._isPrepared()) return; - - var element = this.element; - if (isDisplayed(element)) { - this._setPrepared(true); - return; - } - - - var originalStyles = { - position: element.style.position || '', - width: element.style.width || '', - visibility: element.style.visibility || '', - display: element.style.display || '' - }; - - element.store('prototype_original_styles', originalStyles); - - var position = getRawStyle(element, 'position'), width = element.offsetWidth; - - if (width === 0 || width === null) { - element.style.display = 'block'; - width = element.offsetWidth; - } - - var context = (position === 'fixed') ? document.viewport : - element.parentNode; - - var tempStyles = { - visibility: 'hidden', - display: 'block' - }; - - if (position !== 'fixed') tempStyles.position = 'absolute'; - - element.setStyle(tempStyles); - - var positionedWidth = element.offsetWidth, newWidth; - if (width && (positionedWidth === width)) { - newWidth = getContentWidth(element, context); - } else if (position === 'absolute' || position === 'fixed') { - newWidth = getContentWidth(element, context); - } else { - var parent = element.parentNode, pLayout = $(parent).getLayout(); - - newWidth = pLayout.get('width') - - this.get('margin-left') - - this.get('border-left') - - this.get('padding-left') - - this.get('padding-right') - - this.get('border-right') - - this.get('margin-right'); - } - - element.setStyle({ width: newWidth + 'px' }); - - this._setPrepared(true); - }, - - _end: function() { - var element = this.element; - var originalStyles = element.retrieve('prototype_original_styles'); - element.store('prototype_original_styles', null); - element.setStyle(originalStyles); - this._setPrepared(false); - }, - - _compute: function(property) { - var COMPUTATIONS = Element.Layout.COMPUTATIONS; - if (!(property in COMPUTATIONS)) { - throw "Property not found."; - } - - return this._set(property, COMPUTATIONS[property].call(this, this.element)); - }, - - _isPrepared: function() { - return this.element.retrieve('prototype_element_layout_prepared', false); - }, - - _setPrepared: function(bool) { - return this.element.store('prototype_element_layout_prepared', bool); - }, - - toObject: function() { - var args = $A(arguments); - var keys = (args.length === 0) ? Element.Layout.PROPERTIES : - args.join(' ').split(' '); - var obj = {}; - keys.each( function(key) { - if (!Element.Layout.PROPERTIES.include(key)) return; - var value = this.get(key); - if (value != null) obj[key] = value; - }, this); - return obj; - }, - - toHash: function() { - var obj = this.toObject.apply(this, arguments); - return new Hash(obj); - }, - - toCSS: function() { - var args = $A(arguments); - var keys = (args.length === 0) ? Element.Layout.PROPERTIES : - args.join(' ').split(' '); - var css = {}; - - keys.each( function(key) { - if (!Element.Layout.PROPERTIES.include(key)) return; - if (Element.Layout.COMPOSITE_PROPERTIES.include(key)) return; - - var value = this.get(key); - if (value != null) css[cssNameFor(key)] = value + 'px'; - }, this); - return css; - }, - - inspect: function() { - return "#"; - } - }); - - Object.extend(Element.Layout, { - PROPERTIES: $w('height width top left right bottom border-left border-right border-top border-bottom padding-left padding-right padding-top padding-bottom margin-top margin-bottom margin-left margin-right padding-box-width padding-box-height border-box-width border-box-height margin-box-width margin-box-height'), - - COMPOSITE_PROPERTIES: $w('padding-box-width padding-box-height margin-box-width margin-box-height border-box-width border-box-height'), - - COMPUTATIONS: { - 'height': function(element) { - if (!this._preComputing) this._begin(); - - var bHeight = this.get('border-box-height'); - if (bHeight <= 0) { - if (!this._preComputing) this._end(); - return 0; - } - - var bTop = this.get('border-top'), - bBottom = this.get('border-bottom'); - - var pTop = this.get('padding-top'), - pBottom = this.get('padding-bottom'); - - if (!this._preComputing) this._end(); - - return bHeight - bTop - bBottom - pTop - pBottom; - }, - - 'width': function(element) { - if (!this._preComputing) this._begin(); - - var bWidth = this.get('border-box-width'); - if (bWidth <= 0) { - if (!this._preComputing) this._end(); - return 0; - } - - var bLeft = this.get('border-left'), - bRight = this.get('border-right'); - - var pLeft = this.get('padding-left'), - pRight = this.get('padding-right'); - - if (!this._preComputing) this._end(); - return bWidth - bLeft - bRight - pLeft - pRight; - }, - - 'padding-box-height': function(element) { - var height = this.get('height'), - pTop = this.get('padding-top'), - pBottom = this.get('padding-bottom'); - - return height + pTop + pBottom; - }, - - 'padding-box-width': function(element) { - var width = this.get('width'), - pLeft = this.get('padding-left'), - pRight = this.get('padding-right'); - - return width + pLeft + pRight; - }, - - 'border-box-height': function(element) { - if (!this._preComputing) this._begin(); - var height = element.offsetHeight; - if (!this._preComputing) this._end(); - return height; - }, - - 'border-box-width': function(element) { - if (!this._preComputing) this._begin(); - var width = element.offsetWidth; - if (!this._preComputing) this._end(); - return width; - }, - - 'margin-box-height': function(element) { - var bHeight = this.get('border-box-height'), - mTop = this.get('margin-top'), - mBottom = this.get('margin-bottom'); - - if (bHeight <= 0) return 0; - - return bHeight + mTop + mBottom; - }, - - 'margin-box-width': function(element) { - var bWidth = this.get('border-box-width'), - mLeft = this.get('margin-left'), - mRight = this.get('margin-right'); - - if (bWidth <= 0) return 0; - - return bWidth + mLeft + mRight; - }, - - 'top': function(element) { - var offset = element.positionedOffset(); - return offset.top; - }, - - 'bottom': function(element) { - var offset = element.positionedOffset(), - parent = element.getOffsetParent(), - pHeight = parent.measure('height'); - - var mHeight = this.get('border-box-height'); - - return pHeight - mHeight - offset.top; - }, - - 'left': function(element) { - var offset = element.positionedOffset(); - return offset.left; - }, - - 'right': function(element) { - var offset = element.positionedOffset(), - parent = element.getOffsetParent(), - pWidth = parent.measure('width'); - - var mWidth = this.get('border-box-width'); - - return pWidth - mWidth - offset.left; - }, - - 'padding-top': function(element) { - return getPixelValue(element, 'paddingTop'); - }, - - 'padding-bottom': function(element) { - return getPixelValue(element, 'paddingBottom'); - }, - - 'padding-left': function(element) { - return getPixelValue(element, 'paddingLeft'); - }, - - 'padding-right': function(element) { - return getPixelValue(element, 'paddingRight'); - }, - - 'border-top': function(element) { - return getPixelValue(element, 'borderTopWidth'); - }, - - 'border-bottom': function(element) { - return getPixelValue(element, 'borderBottomWidth'); - }, - - 'border-left': function(element) { - return getPixelValue(element, 'borderLeftWidth'); - }, - - 'border-right': function(element) { - return getPixelValue(element, 'borderRightWidth'); - }, - - 'margin-top': function(element) { - return getPixelValue(element, 'marginTop'); - }, - - 'margin-bottom': function(element) { - return getPixelValue(element, 'marginBottom'); - }, - - 'margin-left': function(element) { - return getPixelValue(element, 'marginLeft'); - }, - - 'margin-right': function(element) { - return getPixelValue(element, 'marginRight'); - } - } - }); - - if ('getBoundingClientRect' in document.documentElement) { - Object.extend(Element.Layout.COMPUTATIONS, { - 'right': function(element) { - var parent = hasLayout(element.getOffsetParent()); - var rect = element.getBoundingClientRect(), - pRect = parent.getBoundingClientRect(); - - return (pRect.right - rect.right).round(); - }, - - 'bottom': function(element) { - var parent = hasLayout(element.getOffsetParent()); - var rect = element.getBoundingClientRect(), - pRect = parent.getBoundingClientRect(); - - return (pRect.bottom - rect.bottom).round(); - } - }); - } - - Element.Offset = Class.create({ - initialize: function(left, top) { - this.left = left.round(); - this.top = top.round(); - - this[0] = this.left; - this[1] = this.top; - }, - - relativeTo: function(offset) { - return new Element.Offset( - this.left - offset.left, - this.top - offset.top - ); - }, - - inspect: function() { - return "#".interpolate(this); - }, - - toString: function() { - return "[#{left}, #{top}]".interpolate(this); - }, - - toArray: function() { - return [this.left, this.top]; - } - }); - - function getLayout(element, preCompute) { - return new Element.Layout(element, preCompute); - } - - function measure(element, property) { - return $(element).getLayout().get(property); - } - - function getHeight(element) { - return Element.getDimensions(element).height; - } - - function getWidth(element) { - return Element.getDimensions(element).width; - } - - function getDimensions(element) { - element = $(element); - var display = Element.getStyle(element, 'display'); - - if (display && display !== 'none') { - return { width: element.offsetWidth, height: element.offsetHeight }; - } - - var style = element.style; - var originalStyles = { - visibility: style.visibility, - position: style.position, - display: style.display - }; - - var newStyles = { - visibility: 'hidden', - display: 'block' - }; - - if (originalStyles.position !== 'fixed') - newStyles.position = 'absolute'; - - Element.setStyle(element, newStyles); - - var dimensions = { - width: element.offsetWidth, - height: element.offsetHeight - }; - - Element.setStyle(element, originalStyles); - - return dimensions; - } - - function getOffsetParent(element) { - element = $(element); - - function selfOrBody(element) { - return isHtml(element) ? $(document.body) : $(element); - } - - if (isDocument(element) || isDetached(element) || isBody(element) || isHtml(element)) - return $(document.body); - - var isInline = (Element.getStyle(element, 'display') === 'inline'); - if (!isInline && element.offsetParent) return selfOrBody(element.offsetParent); - - while ((element = element.parentNode) && element !== document.body) { - if (Element.getStyle(element, 'position') !== 'static') { - return selfOrBody(element); - } - } - - return $(document.body); - } - - - function cumulativeOffset(element) { - element = $(element); - var valueT = 0, valueL = 0; - if (element.parentNode) { - do { - valueT += element.offsetTop || 0; - valueL += element.offsetLeft || 0; - element = element.offsetParent; - } while (element); - } - return new Element.Offset(valueL, valueT); - } - - function positionedOffset(element) { - element = $(element); - - var layout = element.getLayout(); - - var valueT = 0, valueL = 0; - do { - valueT += element.offsetTop || 0; - valueL += element.offsetLeft || 0; - element = element.offsetParent; - if (element) { - if (isBody(element)) break; - var p = Element.getStyle(element, 'position'); - if (p !== 'static') break; - } - } while (element); - - valueL -= layout.get('margin-left'); - valueT -= layout.get('margin-top'); - - return new Element.Offset(valueL, valueT); - } - - function cumulativeScrollOffset(element) { - var valueT = 0, valueL = 0; - do { - if (element === document.body) { - var bodyScrollNode = document.documentElement || document.body.parentNode || document.body; - valueT += !Object.isUndefined(window.pageYOffset) ? window.pageYOffset : bodyScrollNode.scrollTop || 0; - valueL += !Object.isUndefined(window.pageXOffset) ? window.pageXOffset : bodyScrollNode.scrollLeft || 0; - break; - } else { - valueT += element.scrollTop || 0; - valueL += element.scrollLeft || 0; - element = element.parentNode; - } - } while (element); - return new Element.Offset(valueL, valueT); - } - - function viewportOffset(forElement) { - var valueT = 0, valueL = 0, docBody = document.body; - - forElement = $(forElement); - var element = forElement; - do { - valueT += element.offsetTop || 0; - valueL += element.offsetLeft || 0; - if (element.offsetParent == docBody && - Element.getStyle(element, 'position') == 'absolute') break; - } while (element = element.offsetParent); - - element = forElement; - do { - if (element != docBody) { - valueT -= element.scrollTop || 0; - valueL -= element.scrollLeft || 0; - } - } while (element = element.parentNode); - return new Element.Offset(valueL, valueT); - } - - function absolutize(element) { - element = $(element); - - if (Element.getStyle(element, 'position') === 'absolute') { - return element; - } - - var offsetParent = getOffsetParent(element); - var eOffset = element.viewportOffset(), - pOffset = offsetParent.viewportOffset(); - - var offset = eOffset.relativeTo(pOffset); - var layout = element.getLayout(); - - element.store('prototype_absolutize_original_styles', { - position: element.getStyle('position'), - left: element.getStyle('left'), - top: element.getStyle('top'), - width: element.getStyle('width'), - height: element.getStyle('height') - }); - - element.setStyle({ - position: 'absolute', - top: offset.top + 'px', - left: offset.left + 'px', - width: layout.get('width') + 'px', - height: layout.get('height') + 'px' - }); - - return element; - } - - function relativize(element) { - element = $(element); - if (Element.getStyle(element, 'position') === 'relative') { - return element; - } - - var originalStyles = - element.retrieve('prototype_absolutize_original_styles'); - - if (originalStyles) element.setStyle(originalStyles); - return element; - } - - - function scrollTo(element) { - element = $(element); - var pos = Element.cumulativeOffset(element); - window.scrollTo(pos.left, pos.top); - return element; - } - - - function makePositioned(element) { - element = $(element); - var position = Element.getStyle(element, 'position'), styles = {}; - if (position === 'static' || !position) { - styles.position = 'relative'; - if (Prototype.Browser.Opera) { - styles.top = 0; - styles.left = 0; - } - Element.setStyle(element, styles); - Element.store(element, 'prototype_made_positioned', true); - } - return element; - } - - function undoPositioned(element) { - element = $(element); - var storage = Element.getStorage(element), - madePositioned = storage.get('prototype_made_positioned'); - - if (madePositioned) { - storage.unset('prototype_made_positioned'); - Element.setStyle(element, { - position: '', - top: '', - bottom: '', - left: '', - right: '' - }); - } - return element; - } - - function makeClipping(element) { - element = $(element); - - var storage = Element.getStorage(element), - madeClipping = storage.get('prototype_made_clipping'); - - if (Object.isUndefined(madeClipping)) { - var overflow = Element.getStyle(element, 'overflow'); - storage.set('prototype_made_clipping', overflow); - if (overflow !== 'hidden') - element.style.overflow = 'hidden'; - } - - return element; - } - - function undoClipping(element) { - element = $(element); - var storage = Element.getStorage(element), - overflow = storage.get('prototype_made_clipping'); - - if (!Object.isUndefined(overflow)) { - storage.unset('prototype_made_clipping'); - element.style.overflow = overflow || ''; - } - - return element; - } - - function clonePosition(element, source, options) { - options = Object.extend({ - setLeft: true, - setTop: true, - setWidth: true, - setHeight: true, - offsetTop: 0, - offsetLeft: 0 - }, options || {}); - - var docEl = document.documentElement; - - source = $(source); - element = $(element); - var p, delta, layout, styles = {}; - - if (options.setLeft || options.setTop) { - p = Element.viewportOffset(source); - delta = [0, 0]; - if (Element.getStyle(element, 'position') === 'absolute') { - var parent = Element.getOffsetParent(element); - if (parent !== document.body) delta = Element.viewportOffset(parent); - } - } - - function pageScrollXY() { - var x = 0, y = 0; - if (Object.isNumber(window.pageXOffset)) { - x = window.pageXOffset; - y = window.pageYOffset; - } else if (document.body && (document.body.scrollLeft || document.body.scrollTop)) { - x = document.body.scrollLeft; - y = document.body.scrollTop; - } else if (docEl && (docEl.scrollLeft || docEl.scrollTop)) { - x = docEl.scrollLeft; - y = docEl.scrollTop; - } - return { x: x, y: y }; - } - - var pageXY = pageScrollXY(); - - - if (options.setWidth || options.setHeight) { - layout = Element.getLayout(source); - } - - if (options.setLeft) - styles.left = (p[0] + pageXY.x - delta[0] + options.offsetLeft) + 'px'; - if (options.setTop) - styles.top = (p[1] + pageXY.y - delta[1] + options.offsetTop) + 'px'; - - var currentLayout = element.getLayout(); - - if (options.setWidth) { - styles.width = layout.get('width') + 'px'; - } - if (options.setHeight) { - styles.height = layout.get('height') + 'px'; - } - - return Element.setStyle(element, styles); - } - - - if (Prototype.Browser.IE) { - getOffsetParent = getOffsetParent.wrap( - function(proceed, element) { - element = $(element); - - if (isDocument(element) || isDetached(element) || isBody(element) || isHtml(element)) - return $(document.body); - - var position = element.getStyle('position'); - if (position !== 'static') return proceed(element); - - element.setStyle({ position: 'relative' }); - var value = proceed(element); - element.setStyle({ position: position }); - return value; - } - ); - - positionedOffset = positionedOffset.wrap(function(proceed, element) { - element = $(element); - if (!element.parentNode) return new Element.Offset(0, 0); - var position = element.getStyle('position'); - if (position !== 'static') return proceed(element); - - var offsetParent = element.getOffsetParent(); - if (offsetParent && offsetParent.getStyle('position') === 'fixed') - hasLayout(offsetParent); - - element.setStyle({ position: 'relative' }); - var value = proceed(element); - element.setStyle({ position: position }); - return value; - }); - } else if (Prototype.Browser.Webkit) { - cumulativeOffset = function(element) { - element = $(element); - var valueT = 0, valueL = 0; - do { - valueT += element.offsetTop || 0; - valueL += element.offsetLeft || 0; - if (element.offsetParent == document.body) { - if (Element.getStyle(element, 'position') == 'absolute') break; - } - - element = element.offsetParent; - } while (element); - - return new Element.Offset(valueL, valueT); - }; - } - - - Element.addMethods({ - getLayout: getLayout, - measure: measure, - getWidth: getWidth, - getHeight: getHeight, - getDimensions: getDimensions, - getOffsetParent: getOffsetParent, - cumulativeOffset: cumulativeOffset, - positionedOffset: positionedOffset, - cumulativeScrollOffset: cumulativeScrollOffset, - viewportOffset: viewportOffset, - absolutize: absolutize, - relativize: relativize, - scrollTo: scrollTo, - makePositioned: makePositioned, - undoPositioned: undoPositioned, - makeClipping: makeClipping, - undoClipping: undoClipping, - clonePosition: clonePosition - }); - - function isBody(element) { - return element.nodeName.toUpperCase() === 'BODY'; - } - - function isHtml(element) { - return element.nodeName.toUpperCase() === 'HTML'; - } - - function isDocument(element) { - return element.nodeType === Node.DOCUMENT_NODE; - } - - function isDetached(element) { - return element !== document.body && - !Element.descendantOf(element, document.body); - } - - if ('getBoundingClientRect' in document.documentElement) { - Element.addMethods({ - viewportOffset: function(element) { - element = $(element); - if (isDetached(element)) return new Element.Offset(0, 0); - - var rect = element.getBoundingClientRect(), - docEl = document.documentElement; - return new Element.Offset(rect.left - docEl.clientLeft, - rect.top - docEl.clientTop); - } - }); - } - - -})(); - -(function() { - - var IS_OLD_OPERA = Prototype.Browser.Opera && - (window.parseFloat(window.opera.version()) < 9.5); - var ROOT = null; - function getRootElement() { - if (ROOT) return ROOT; - ROOT = IS_OLD_OPERA ? document.body : document.documentElement; - return ROOT; - } - - function getDimensions() { - return { width: this.getWidth(), height: this.getHeight() }; - } - - function getWidth() { - return getRootElement().clientWidth; - } - - function getHeight() { - return getRootElement().clientHeight; - } - - function getScrollOffsets() { - var x = window.pageXOffset || document.documentElement.scrollLeft || - document.body.scrollLeft; - var y = window.pageYOffset || document.documentElement.scrollTop || - document.body.scrollTop; - - return new Element.Offset(x, y); - } - - document.viewport = { - getDimensions: getDimensions, - getWidth: getWidth, - getHeight: getHeight, - getScrollOffsets: getScrollOffsets - }; - -})(); -window.$$ = function() { - var expression = $A(arguments).join(', '); - return Prototype.Selector.select(expression, document); -}; - -Prototype.Selector = (function() { - - function select() { - throw new Error('Method "Prototype.Selector.select" must be defined.'); - } - - function match() { - throw new Error('Method "Prototype.Selector.match" must be defined.'); - } - - function find(elements, expression, index) { - index = index || 0; - var match = Prototype.Selector.match, length = elements.length, matchIndex = 0, i; - - for (i = 0; i < length; i++) { - if (match(elements[i], expression) && index == matchIndex++) { - return Element.extend(elements[i]); - } - } - } - - function extendElements(elements) { - for (var i = 0, length = elements.length; i < length; i++) { - Element.extend(elements[i]); - } - return elements; - } - - - var K = Prototype.K; - - return { - select: select, - match: match, - find: find, - extendElements: (Element.extend === K) ? K : extendElements, - extendElement: Element.extend - }; -})(); -Prototype._original_property = window.Sizzle; - -;(function () { - function fakeDefine(fn) { - Prototype._actual_sizzle = fn(); - } - fakeDefine.amd = true; - - if (typeof define !== 'undefined' && define.amd) { - Prototype._original_define = define; - Prototype._actual_sizzle = null; - window.define = fakeDefine; - } -})(); - -/*! - * Sizzle CSS Selector Engine v1.10.18 - * http://sizzlejs.com/ - * - * Copyright 2013 jQuery Foundation, Inc. and other contributors - * Released under the MIT license - * http://jquery.org/license - * - * Date: 2014-02-05 - */ -(function( window ) { - -var i, - support, - Expr, - getText, - isXML, - compile, - select, - outermostContext, - sortInput, - hasDuplicate, - - setDocument, - document, - docElem, - documentIsHTML, - rbuggyQSA, - rbuggyMatches, - matches, - contains, - - expando = "sizzle" + -(new Date()), - preferredDoc = window.document, - dirruns = 0, - done = 0, - classCache = createCache(), - tokenCache = createCache(), - compilerCache = createCache(), - sortOrder = function( a, b ) { - if ( a === b ) { - hasDuplicate = true; - } - return 0; - }, - - strundefined = typeof undefined, - MAX_NEGATIVE = 1 << 31, - - hasOwn = ({}).hasOwnProperty, - arr = [], - pop = arr.pop, - push_native = arr.push, - push = arr.push, - slice = arr.slice, - indexOf = arr.indexOf || function( elem ) { - var i = 0, - len = this.length; - for ( ; i < len; i++ ) { - if ( this[i] === elem ) { - return i; - } - } - return -1; - }, - - booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped", - - - whitespace = "[\\x20\\t\\r\\n\\f]", - characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+", - - identifier = characterEncoding.replace( "w", "w#" ), - - attributes = "\\[" + whitespace + "*(" + characterEncoding + ")" + whitespace + - "*(?:([*^$|!~]?=)" + whitespace + "*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|)" + whitespace + "*\\]", - - pseudos = ":(" + characterEncoding + ")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|" + attributes.replace( 3, 8 ) + ")*)|.*)\\)|)", - - rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", "g" ), - - rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), - rcombinators = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + "*" ), - - rattributeQuotes = new RegExp( "=" + whitespace + "*([^\\]'\"]*?)" + whitespace + "*\\]", "g" ), - - rpseudo = new RegExp( pseudos ), - ridentifier = new RegExp( "^" + identifier + "$" ), - - matchExpr = { - "ID": new RegExp( "^#(" + characterEncoding + ")" ), - "CLASS": new RegExp( "^\\.(" + characterEncoding + ")" ), - "TAG": new RegExp( "^(" + characterEncoding.replace( "w", "w*" ) + ")" ), - "ATTR": new RegExp( "^" + attributes ), - "PSEUDO": new RegExp( "^" + pseudos ), - "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace + - "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace + - "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), - "bool": new RegExp( "^(?:" + booleans + ")$", "i" ), - "needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + - whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) - }, - - rinputs = /^(?:input|select|textarea|button)$/i, - rheader = /^h\d$/i, - - rnative = /^[^{]+\{\s*\[native \w/, - - rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, - - rsibling = /[+~]/, - rescape = /'|\\/g, - - runescape = new RegExp( "\\\\([\\da-f]{1,6}" + whitespace + "?|(" + whitespace + ")|.)", "ig" ), - funescape = function( _, escaped, escapedWhitespace ) { - var high = "0x" + escaped - 0x10000; - return high !== high || escapedWhitespace ? - escaped : - high < 0 ? - String.fromCharCode( high + 0x10000 ) : - String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); - }; - -try { - push.apply( - (arr = slice.call( preferredDoc.childNodes )), - preferredDoc.childNodes - ); - arr[ preferredDoc.childNodes.length ].nodeType; -} catch ( e ) { - push = { apply: arr.length ? - - function( target, els ) { - push_native.apply( target, slice.call(els) ); - } : - - function( target, els ) { - var j = target.length, - i = 0; - while ( (target[j++] = els[i++]) ) {} - target.length = j - 1; - } - }; -} - -function Sizzle( selector, context, results, seed ) { - var match, elem, m, nodeType, - i, groups, old, nid, newContext, newSelector; - - if ( ( context ? context.ownerDocument || context : preferredDoc ) !== document ) { - setDocument( context ); - } - - context = context || document; - results = results || []; - - if ( !selector || typeof selector !== "string" ) { - return results; - } - - if ( (nodeType = context.nodeType) !== 1 && nodeType !== 9 ) { - return []; - } - - if ( documentIsHTML && !seed ) { - - if ( (match = rquickExpr.exec( selector )) ) { - if ( (m = match[1]) ) { - if ( nodeType === 9 ) { - elem = context.getElementById( m ); - if ( elem && elem.parentNode ) { - if ( elem.id === m ) { - results.push( elem ); - return results; - } - } else { - return results; - } - } else { - if ( context.ownerDocument && (elem = context.ownerDocument.getElementById( m )) && - contains( context, elem ) && elem.id === m ) { - results.push( elem ); - return results; - } - } - - } else if ( match[2] ) { - push.apply( results, context.getElementsByTagName( selector ) ); - return results; - - } else if ( (m = match[3]) && support.getElementsByClassName && context.getElementsByClassName ) { - push.apply( results, context.getElementsByClassName( m ) ); - return results; - } - } - - if ( support.qsa && (!rbuggyQSA || !rbuggyQSA.test( selector )) ) { - nid = old = expando; - newContext = context; - newSelector = nodeType === 9 && selector; - - if ( nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) { - groups = tokenize( selector ); - - if ( (old = context.getAttribute("id")) ) { - nid = old.replace( rescape, "\\$&" ); - } else { - context.setAttribute( "id", nid ); - } - nid = "[id='" + nid + "'] "; - - i = groups.length; - while ( i-- ) { - groups[i] = nid + toSelector( groups[i] ); - } - newContext = rsibling.test( selector ) && testContext( context.parentNode ) || context; - newSelector = groups.join(","); - } - - if ( newSelector ) { - try { - push.apply( results, - newContext.querySelectorAll( newSelector ) - ); - return results; - } catch(qsaError) { - } finally { - if ( !old ) { - context.removeAttribute("id"); - } - } - } - } - } - - return select( selector.replace( rtrim, "$1" ), context, results, seed ); -} - -/** - * Create key-value caches of limited size - * @returns {Function(string, Object)} Returns the Object data after storing it on itself with - * property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength) - * deleting the oldest entry - */ -function createCache() { - var keys = []; - - function cache( key, value ) { - if ( keys.push( key + " " ) > Expr.cacheLength ) { - delete cache[ keys.shift() ]; - } - return (cache[ key + " " ] = value); - } - return cache; -} - -/** - * Mark a function for special use by Sizzle - * @param {Function} fn The function to mark - */ -function markFunction( fn ) { - fn[ expando ] = true; - return fn; -} - -/** - * Support testing using an element - * @param {Function} fn Passed the created div and expects a boolean result - */ -function assert( fn ) { - var div = document.createElement("div"); - - try { - return !!fn( div ); - } catch (e) { - return false; - } finally { - if ( div.parentNode ) { - div.parentNode.removeChild( div ); - } - div = null; - } -} - -/** - * Adds the same handler for all of the specified attrs - * @param {String} attrs Pipe-separated list of attributes - * @param {Function} handler The method that will be applied - */ -function addHandle( attrs, handler ) { - var arr = attrs.split("|"), - i = attrs.length; - - while ( i-- ) { - Expr.attrHandle[ arr[i] ] = handler; - } -} - -/** - * Checks document order of two siblings - * @param {Element} a - * @param {Element} b - * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b - */ -function siblingCheck( a, b ) { - var cur = b && a, - diff = cur && a.nodeType === 1 && b.nodeType === 1 && - ( ~b.sourceIndex || MAX_NEGATIVE ) - - ( ~a.sourceIndex || MAX_NEGATIVE ); - - if ( diff ) { - return diff; - } - - if ( cur ) { - while ( (cur = cur.nextSibling) ) { - if ( cur === b ) { - return -1; - } - } - } - - return a ? 1 : -1; -} - -/** - * Returns a function to use in pseudos for input types - * @param {String} type - */ -function createInputPseudo( type ) { - return function( elem ) { - var name = elem.nodeName.toLowerCase(); - return name === "input" && elem.type === type; - }; -} - -/** - * Returns a function to use in pseudos for buttons - * @param {String} type - */ -function createButtonPseudo( type ) { - return function( elem ) { - var name = elem.nodeName.toLowerCase(); - return (name === "input" || name === "button") && elem.type === type; - }; -} - -/** - * Returns a function to use in pseudos for positionals - * @param {Function} fn - */ -function createPositionalPseudo( fn ) { - return markFunction(function( argument ) { - argument = +argument; - return markFunction(function( seed, matches ) { - var j, - matchIndexes = fn( [], seed.length, argument ), - i = matchIndexes.length; - - while ( i-- ) { - if ( seed[ (j = matchIndexes[i]) ] ) { - seed[j] = !(matches[j] = seed[j]); - } - } - }); - }); -} - -/** - * 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; -} - -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 ) { - var documentElement = elem && (elem.ownerDocument || elem).documentElement; - return documentElement ? documentElement.nodeName !== "HTML" : false; -}; - -/** - * 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 hasCompare, - doc = node ? node.ownerDocument || node : preferredDoc, - parent = doc.defaultView; - - if ( doc === document || doc.nodeType !== 9 || !doc.documentElement ) { - return document; - } - - document = doc; - docElem = doc.documentElement; - - documentIsHTML = !isXML( doc ); - - if ( parent && parent !== parent.top ) { - if ( parent.addEventListener ) { - parent.addEventListener( "unload", function() { - setDocument(); - }, false ); - } else if ( parent.attachEvent ) { - parent.attachEvent( "onunload", function() { - setDocument(); - }); - } - } - - /* Attributes - ---------------------------------------------------------------------- */ - - support.attributes = assert(function( div ) { - div.className = "i"; - return !div.getAttribute("className"); - }); - - /* getElement(s)By* - ---------------------------------------------------------------------- */ - - support.getElementsByTagName = assert(function( div ) { - div.appendChild( doc.createComment("") ); - return !div.getElementsByTagName("*").length; - }); - - support.getElementsByClassName = rnative.test( doc.getElementsByClassName ) && assert(function( div ) { - div.innerHTML = "
    "; - - div.firstChild.className = "i"; - return div.getElementsByClassName("i").length === 2; - }); - - support.getById = assert(function( div ) { - docElem.appendChild( div ).id = expando; - return !doc.getElementsByName || !doc.getElementsByName( expando ).length; - }); - - if ( support.getById ) { - Expr.find["ID"] = function( id, context ) { - if ( typeof context.getElementById !== strundefined && documentIsHTML ) { - var m = context.getElementById( id ); - return m && m.parentNode ? [m] : []; - } - }; - Expr.filter["ID"] = function( id ) { - var attrId = id.replace( runescape, funescape ); - return function( elem ) { - return elem.getAttribute("id") === attrId; - }; - }; - } else { - delete Expr.find["ID"]; - - Expr.filter["ID"] = function( id ) { - var attrId = id.replace( runescape, funescape ); - return function( elem ) { - var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id"); - return node && node.value === attrId; - }; - }; - } - - Expr.find["TAG"] = support.getElementsByTagName ? - function( tag, context ) { - if ( typeof context.getElementsByTagName !== strundefined ) { - return context.getElementsByTagName( tag ); - } - } : - function( tag, context ) { - var elem, - tmp = [], - i = 0, - results = context.getElementsByTagName( tag ); - - if ( tag === "*" ) { - while ( (elem = results[i++]) ) { - if ( elem.nodeType === 1 ) { - tmp.push( elem ); - } - } - - return tmp; - } - return results; - }; - - Expr.find["CLASS"] = support.getElementsByClassName && function( className, context ) { - if ( typeof context.getElementsByClassName !== strundefined && documentIsHTML ) { - return context.getElementsByClassName( className ); - } - }; - - /* QSA/matchesSelector - ---------------------------------------------------------------------- */ - - - rbuggyMatches = []; - - rbuggyQSA = []; - - if ( (support.qsa = rnative.test( doc.querySelectorAll )) ) { - assert(function( div ) { - div.innerHTML = ""; - - if ( div.querySelectorAll("[t^='']").length ) { - rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); - } - - if ( !div.querySelectorAll("[selected]").length ) { - rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); - } - - if ( !div.querySelectorAll(":checked").length ) { - rbuggyQSA.push(":checked"); - } - }); - - assert(function( div ) { - var input = doc.createElement("input"); - input.setAttribute( "type", "hidden" ); - div.appendChild( input ).setAttribute( "name", "D" ); - - if ( div.querySelectorAll("[name=d]").length ) { - rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" ); - } - - if ( !div.querySelectorAll(":enabled").length ) { - rbuggyQSA.push( ":enabled", ":disabled" ); - } - - div.querySelectorAll("*,:x"); - rbuggyQSA.push(",.*:"); - }); - } - - if ( (support.matchesSelector = rnative.test( (matches = docElem.webkitMatchesSelector || - docElem.mozMatchesSelector || - docElem.oMatchesSelector || - docElem.msMatchesSelector) )) ) { - - assert(function( div ) { - support.disconnectedMatch = matches.call( div, "div" ); - - matches.call( div, "[s!='']:x" ); - rbuggyMatches.push( "!=", pseudos ); - }); - } - - rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join("|") ); - rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join("|") ); - - /* Contains - ---------------------------------------------------------------------- */ - hasCompare = rnative.test( docElem.compareDocumentPosition ); - - contains = hasCompare || rnative.test( docElem.contains ) ? - function( a, b ) { - var adown = a.nodeType === 9 ? a.documentElement : a, - bup = b && b.parentNode; - return a === bup || !!( bup && bup.nodeType === 1 && ( - adown.contains ? - adown.contains( bup ) : - a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 - )); - } : - function( a, b ) { - if ( b ) { - while ( (b = b.parentNode) ) { - if ( b === a ) { - return true; - } - } - } - return false; - }; - - /* Sorting - ---------------------------------------------------------------------- */ - - sortOrder = hasCompare ? - function( a, b ) { - - if ( a === b ) { - hasDuplicate = true; - return 0; - } - - var compare = !a.compareDocumentPosition - !b.compareDocumentPosition; - if ( compare ) { - return compare; - } - - compare = ( a.ownerDocument || a ) === ( b.ownerDocument || b ) ? - a.compareDocumentPosition( b ) : - - 1; - - if ( compare & 1 || - (!support.sortDetached && b.compareDocumentPosition( a ) === compare) ) { - - if ( a === doc || a.ownerDocument === preferredDoc && contains(preferredDoc, a) ) { - return -1; - } - if ( b === doc || b.ownerDocument === preferredDoc && contains(preferredDoc, b) ) { - return 1; - } - - return sortInput ? - ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) : - 0; - } - - return compare & 4 ? -1 : 1; - } : - function( a, b ) { - if ( a === b ) { - hasDuplicate = true; - return 0; - } - - var cur, - i = 0, - aup = a.parentNode, - bup = b.parentNode, - ap = [ a ], - bp = [ b ]; - - if ( !aup || !bup ) { - return a === doc ? -1 : - b === doc ? 1 : - aup ? -1 : - bup ? 1 : - sortInput ? - ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) : - 0; - - } else if ( aup === bup ) { - return siblingCheck( a, b ); - } - - cur = a; - while ( (cur = cur.parentNode) ) { - ap.unshift( cur ); - } - cur = b; - while ( (cur = cur.parentNode) ) { - bp.unshift( cur ); - } - - while ( ap[i] === bp[i] ) { - i++; - } - - return i ? - siblingCheck( ap[i], bp[i] ) : - - ap[i] === preferredDoc ? -1 : - bp[i] === preferredDoc ? 1 : - 0; - }; - - return doc; -}; - -Sizzle.matches = function( expr, elements ) { - return Sizzle( expr, null, null, elements ); -}; - -Sizzle.matchesSelector = function( elem, expr ) { - if ( ( elem.ownerDocument || elem ) !== document ) { - setDocument( elem ); - } - - expr = expr.replace( rattributeQuotes, "='$1']" ); - - if ( support.matchesSelector && documentIsHTML && - ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) && - ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) { - - try { - var ret = matches.call( elem, expr ); - - if ( ret || support.disconnectedMatch || - elem.document && elem.document.nodeType !== 11 ) { - return ret; - } - } catch(e) {} - } - - return Sizzle( expr, document, null, [elem] ).length > 0; -}; - -Sizzle.contains = function( context, elem ) { - if ( ( context.ownerDocument || context ) !== document ) { - setDocument( context ); - } - return contains( context, elem ); -}; - -Sizzle.attr = function( elem, name ) { - if ( ( elem.ownerDocument || elem ) !== document ) { - setDocument( elem ); - } - - var fn = Expr.attrHandle[ name.toLowerCase() ], - val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ? - fn( elem, name, !documentIsHTML ) : - undefined; - - return val !== undefined ? - val : - support.attributes || !documentIsHTML ? - elem.getAttribute( name ) : - (val = elem.getAttributeNode(name)) && val.specified ? - val.value : - null; -}; - -Sizzle.error = function( msg ) { - throw new Error( "Syntax error, unrecognized expression: " + msg ); -}; - -/** - * Document sorting and removing duplicates - * @param {ArrayLike} results - */ -Sizzle.uniqueSort = function( results ) { - var elem, - duplicates = [], - j = 0, - i = 0; - - hasDuplicate = !support.detectDuplicates; - sortInput = !support.sortStable && results.slice( 0 ); - results.sort( sortOrder ); - - if ( hasDuplicate ) { - while ( (elem = results[i++]) ) { - if ( elem === results[ i ] ) { - j = duplicates.push( i ); - } - } - while ( j-- ) { - results.splice( duplicates[ j ], 1 ); - } - } - - sortInput = null; - - return results; -}; - -/** - * Utility function for retrieving the text value of an array of DOM nodes - * @param {Array|Element} elem - */ -getText = Sizzle.getText = function( elem ) { - var node, - ret = "", - i = 0, - nodeType = elem.nodeType; - - if ( !nodeType ) { - while ( (node = elem[i++]) ) { - ret += getText( node ); - } - } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { - if ( typeof elem.textContent === "string" ) { - return elem.textContent; - } else { - for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { - ret += getText( elem ); - } - } - } else if ( nodeType === 3 || nodeType === 4 ) { - return elem.nodeValue; - } - - return ret; -}; - -Expr = Sizzle.selectors = { - - cacheLength: 50, - - createPseudo: markFunction, - - match: matchExpr, - - attrHandle: {}, - - find: {}, - - relative: { - ">": { dir: "parentNode", first: true }, - " ": { dir: "parentNode" }, - "+": { dir: "previousSibling", first: true }, - "~": { dir: "previousSibling" } - }, - - preFilter: { - "ATTR": function( match ) { - match[1] = match[1].replace( runescape, funescape ); - - match[3] = ( match[4] || match[5] || "" ).replace( runescape, funescape ); - - if ( match[2] === "~=" ) { - match[3] = " " + match[3] + " "; - } - - return match.slice( 0, 4 ); - }, - - "CHILD": function( match ) { - /* matches from matchExpr["CHILD"] - 1 type (only|nth|...) - 2 what (child|of-type) - 3 argument (even|odd|\d*|\d*n([+-]\d+)?|...) - 4 xn-component of xn+y argument ([+-]?\d*n|) - 5 sign of xn-component - 6 x of xn-component - 7 sign of y-component - 8 y of y-component - */ - match[1] = match[1].toLowerCase(); - - if ( match[1].slice( 0, 3 ) === "nth" ) { - if ( !match[3] ) { - Sizzle.error( match[0] ); - } - - match[4] = +( match[4] ? match[5] + (match[6] || 1) : 2 * ( match[3] === "even" || match[3] === "odd" ) ); - match[5] = +( ( match[7] + match[8] ) || match[3] === "odd" ); - - } else if ( match[3] ) { - Sizzle.error( match[0] ); - } - - return match; - }, - - "PSEUDO": function( match ) { - var excess, - unquoted = !match[5] && match[2]; - - if ( matchExpr["CHILD"].test( match[0] ) ) { - return null; - } - - if ( match[3] && match[4] !== undefined ) { - match[2] = match[4]; - - } else if ( unquoted && rpseudo.test( unquoted ) && - (excess = tokenize( unquoted, true )) && - (excess = unquoted.indexOf( ")", unquoted.length - excess ) - unquoted.length) ) { - - match[0] = match[0].slice( 0, excess ); - match[2] = unquoted.slice( 0, excess ); - } - - return match.slice( 0, 3 ); - } - }, - - filter: { - - "TAG": function( nodeNameSelector ) { - var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase(); - return nodeNameSelector === "*" ? - function() { return true; } : - function( elem ) { - return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; - }; - }, - - "CLASS": function( className ) { - var pattern = classCache[ className + " " ]; - - return pattern || - (pattern = new RegExp( "(^|" + whitespace + ")" + className + "(" + whitespace + "|$)" )) && - classCache( className, function( elem ) { - return pattern.test( typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== strundefined && elem.getAttribute("class") || "" ); - }); - }, - - "ATTR": function( name, operator, check ) { - return function( elem ) { - var result = Sizzle.attr( elem, name ); - - if ( result == null ) { - return operator === "!="; - } - if ( !operator ) { - return true; - } - - result += ""; - - return operator === "=" ? result === check : - operator === "!=" ? result !== check : - operator === "^=" ? check && result.indexOf( check ) === 0 : - operator === "*=" ? check && result.indexOf( check ) > -1 : - operator === "$=" ? check && result.slice( -check.length ) === check : - operator === "~=" ? ( " " + result + " " ).indexOf( check ) > -1 : - operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" : - false; - }; - }, - - "CHILD": function( type, what, argument, first, last ) { - var simple = type.slice( 0, 3 ) !== "nth", - forward = type.slice( -4 ) !== "last", - ofType = what === "of-type"; - - return first === 1 && last === 0 ? - - function( elem ) { - return !!elem.parentNode; - } : - - function( elem, context, xml ) { - var cache, outerCache, node, diff, nodeIndex, start, - dir = simple !== forward ? "nextSibling" : "previousSibling", - parent = elem.parentNode, - name = ofType && elem.nodeName.toLowerCase(), - useCache = !xml && !ofType; - - if ( parent ) { - - if ( simple ) { - while ( dir ) { - node = elem; - while ( (node = node[ dir ]) ) { - if ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) { - return false; - } - } - start = dir = type === "only" && !start && "nextSibling"; - } - return true; - } - - start = [ forward ? parent.firstChild : parent.lastChild ]; - - if ( forward && useCache ) { - outerCache = parent[ expando ] || (parent[ expando ] = {}); - cache = outerCache[ type ] || []; - nodeIndex = cache[0] === dirruns && cache[1]; - diff = cache[0] === dirruns && cache[2]; - node = nodeIndex && parent.childNodes[ nodeIndex ]; - - while ( (node = ++nodeIndex && node && node[ dir ] || - - (diff = nodeIndex = 0) || start.pop()) ) { - - if ( node.nodeType === 1 && ++diff && node === elem ) { - outerCache[ type ] = [ dirruns, nodeIndex, diff ]; - break; - } - } - - } else if ( useCache && (cache = (elem[ expando ] || (elem[ expando ] = {}))[ type ]) && cache[0] === dirruns ) { - diff = cache[1]; - - } else { - while ( (node = ++nodeIndex && node && node[ dir ] || - (diff = nodeIndex = 0) || start.pop()) ) { - - if ( ( ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1 ) && ++diff ) { - if ( useCache ) { - (node[ expando ] || (node[ expando ] = {}))[ type ] = [ dirruns, diff ]; - } - - if ( node === elem ) { - break; - } - } - } - } - - diff -= last; - return diff === first || ( diff % first === 0 && diff / first >= 0 ); - } - }; - }, - - "PSEUDO": function( pseudo, argument ) { - var args, - fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || - Sizzle.error( "unsupported pseudo: " + pseudo ); - - if ( fn[ expando ] ) { - return fn( argument ); - } - - if ( fn.length > 1 ) { - args = [ pseudo, pseudo, "", argument ]; - return Expr.setFilters.hasOwnProperty( pseudo.toLowerCase() ) ? - markFunction(function( seed, matches ) { - var idx, - matched = fn( seed, argument ), - i = matched.length; - while ( i-- ) { - idx = indexOf.call( seed, matched[i] ); - seed[ idx ] = !( matches[ idx ] = matched[i] ); - } - }) : - function( elem ) { - return fn( elem, 0, args ); - }; - } - - return fn; - } - }, - - pseudos: { - "not": markFunction(function( selector ) { - var input = [], - results = [], - matcher = compile( selector.replace( rtrim, "$1" ) ); - - return matcher[ expando ] ? - markFunction(function( seed, matches, context, xml ) { - var elem, - unmatched = matcher( seed, null, xml, [] ), - i = seed.length; - - while ( i-- ) { - if ( (elem = unmatched[i]) ) { - seed[i] = !(matches[i] = elem); - } - } - }) : - function( elem, context, xml ) { - input[0] = elem; - matcher( input, null, xml, results ); - return !results.pop(); - }; - }), - - "has": markFunction(function( selector ) { - return function( elem ) { - return Sizzle( selector, elem ).length > 0; - }; - }), - - "contains": markFunction(function( text ) { - return function( elem ) { - return ( elem.textContent || elem.innerText || getText( elem ) ).indexOf( text ) > -1; - }; - }), - - "lang": markFunction( function( lang ) { - if ( !ridentifier.test(lang || "") ) { - Sizzle.error( "unsupported lang: " + lang ); - } - lang = lang.replace( runescape, funescape ).toLowerCase(); - return function( elem ) { - var elemLang; - do { - if ( (elemLang = documentIsHTML ? - elem.lang : - elem.getAttribute("xml:lang") || elem.getAttribute("lang")) ) { - - elemLang = elemLang.toLowerCase(); - return elemLang === lang || elemLang.indexOf( lang + "-" ) === 0; - } - } while ( (elem = elem.parentNode) && elem.nodeType === 1 ); - return false; - }; - }), - - "target": function( elem ) { - var hash = window.location && window.location.hash; - return hash && hash.slice( 1 ) === elem.id; - }, - - "root": function( elem ) { - return elem === docElem; - }, - - "focus": function( elem ) { - return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex); - }, - - "enabled": function( elem ) { - return elem.disabled === false; - }, - - "disabled": function( elem ) { - return elem.disabled === true; - }, - - "checked": function( elem ) { - var nodeName = elem.nodeName.toLowerCase(); - return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected); - }, - - "selected": function( elem ) { - if ( elem.parentNode ) { - elem.parentNode.selectedIndex; - } - - return elem.selected === true; - }, - - "empty": function( elem ) { - for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { - if ( elem.nodeType < 6 ) { - return false; - } - } - return true; - }, - - "parent": function( elem ) { - return !Expr.pseudos["empty"]( elem ); - }, - - "header": function( elem ) { - return rheader.test( elem.nodeName ); - }, - - "input": function( elem ) { - return rinputs.test( elem.nodeName ); - }, - - "button": function( elem ) { - var name = elem.nodeName.toLowerCase(); - return name === "input" && elem.type === "button" || name === "button"; - }, - - "text": function( elem ) { - var attr; - return elem.nodeName.toLowerCase() === "input" && - elem.type === "text" && - - ( (attr = elem.getAttribute("type")) == null || attr.toLowerCase() === "text" ); - }, - - "first": createPositionalPseudo(function() { - return [ 0 ]; - }), - - "last": createPositionalPseudo(function( matchIndexes, length ) { - return [ length - 1 ]; - }), - - "eq": createPositionalPseudo(function( matchIndexes, length, argument ) { - return [ argument < 0 ? argument + length : argument ]; - }), - - "even": createPositionalPseudo(function( matchIndexes, length ) { - var i = 0; - for ( ; i < length; i += 2 ) { - matchIndexes.push( i ); - } - return matchIndexes; - }), - - "odd": createPositionalPseudo(function( matchIndexes, length ) { - var i = 1; - for ( ; i < length; i += 2 ) { - matchIndexes.push( i ); - } - return matchIndexes; - }), - - "lt": createPositionalPseudo(function( matchIndexes, length, argument ) { - var i = argument < 0 ? argument + length : argument; - for ( ; --i >= 0; ) { - matchIndexes.push( i ); - } - return matchIndexes; - }), - - "gt": createPositionalPseudo(function( matchIndexes, length, argument ) { - var i = argument < 0 ? argument + length : argument; - for ( ; ++i < length; ) { - matchIndexes.push( i ); - } - return matchIndexes; - }) - } -}; - -Expr.pseudos["nth"] = Expr.pseudos["eq"]; - -for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) { - Expr.pseudos[ i ] = createInputPseudo( i ); -} -for ( i in { submit: true, reset: true } ) { - Expr.pseudos[ i ] = createButtonPseudo( i ); -} - -function setFilters() {} -setFilters.prototype = Expr.filters = Expr.pseudos; -Expr.setFilters = new setFilters(); - -function tokenize( selector, parseOnly ) { - var matched, match, tokens, type, - soFar, groups, preFilters, - cached = tokenCache[ selector + " " ]; - - if ( cached ) { - return parseOnly ? 0 : cached.slice( 0 ); - } - - soFar = selector; - groups = []; - preFilters = Expr.preFilter; - - while ( soFar ) { - - if ( !matched || (match = rcomma.exec( soFar )) ) { - if ( match ) { - soFar = soFar.slice( match[0].length ) || soFar; - } - groups.push( (tokens = []) ); - } - - matched = false; - - if ( (match = rcombinators.exec( soFar )) ) { - matched = match.shift(); - tokens.push({ - value: matched, - type: match[0].replace( rtrim, " " ) - }); - soFar = soFar.slice( matched.length ); - } - - for ( type in Expr.filter ) { - if ( (match = matchExpr[ type ].exec( soFar )) && (!preFilters[ type ] || - (match = preFilters[ type ]( match ))) ) { - matched = match.shift(); - tokens.push({ - value: matched, - type: type, - matches: match - }); - soFar = soFar.slice( matched.length ); - } - } - - if ( !matched ) { - break; - } - } - - return parseOnly ? - soFar.length : - soFar ? - Sizzle.error( selector ) : - tokenCache( selector, groups ).slice( 0 ); -} - -function toSelector( tokens ) { - var i = 0, - len = tokens.length, - selector = ""; - for ( ; i < len; i++ ) { - selector += tokens[i].value; - } - return selector; -} - -function addCombinator( matcher, combinator, base ) { - var dir = combinator.dir, - checkNonElements = base && dir === "parentNode", - doneName = done++; - - return combinator.first ? - function( elem, context, xml ) { - while ( (elem = elem[ dir ]) ) { - if ( elem.nodeType === 1 || checkNonElements ) { - return matcher( elem, context, xml ); - } - } - } : - - function( elem, context, xml ) { - var oldCache, outerCache, - newCache = [ dirruns, doneName ]; - - if ( xml ) { - while ( (elem = elem[ dir ]) ) { - if ( elem.nodeType === 1 || checkNonElements ) { - if ( matcher( elem, context, xml ) ) { - return true; - } - } - } - } else { - while ( (elem = elem[ dir ]) ) { - if ( elem.nodeType === 1 || checkNonElements ) { - outerCache = elem[ expando ] || (elem[ expando ] = {}); - if ( (oldCache = outerCache[ dir ]) && - oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) { - - return (newCache[ 2 ] = oldCache[ 2 ]); - } else { - outerCache[ dir ] = newCache; - - if ( (newCache[ 2 ] = matcher( elem, context, xml )) ) { - return true; - } - } - } - } - } - }; -} - -function elementMatcher( matchers ) { - return matchers.length > 1 ? - function( elem, context, xml ) { - var i = matchers.length; - while ( i-- ) { - if ( !matchers[i]( elem, context, xml ) ) { - return false; - } - } - return true; - } : - matchers[0]; -} - -function multipleContexts( selector, contexts, results ) { - var i = 0, - len = contexts.length; - for ( ; i < len; i++ ) { - Sizzle( selector, contexts[i], results ); - } - return results; -} - -function condense( unmatched, map, filter, context, xml ) { - var elem, - newUnmatched = [], - i = 0, - len = unmatched.length, - mapped = map != null; - - for ( ; i < len; i++ ) { - if ( (elem = unmatched[i]) ) { - if ( !filter || filter( elem, context, xml ) ) { - newUnmatched.push( elem ); - if ( mapped ) { - map.push( i ); - } - } - } - } - - return newUnmatched; -} - -function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postSelector ) { - if ( postFilter && !postFilter[ expando ] ) { - postFilter = setMatcher( postFilter ); - } - if ( postFinder && !postFinder[ expando ] ) { - postFinder = setMatcher( postFinder, postSelector ); - } - return markFunction(function( seed, results, context, xml ) { - var temp, i, elem, - preMap = [], - postMap = [], - preexisting = results.length, - - elems = seed || multipleContexts( selector || "*", context.nodeType ? [ context ] : context, [] ), - - matcherIn = preFilter && ( seed || !selector ) ? - condense( elems, preMap, preFilter, context, xml ) : - elems, - - matcherOut = matcher ? - postFinder || ( seed ? preFilter : preexisting || postFilter ) ? - - [] : - - results : - matcherIn; - - if ( matcher ) { - matcher( matcherIn, matcherOut, context, xml ); - } - - if ( postFilter ) { - temp = condense( matcherOut, postMap ); - postFilter( temp, [], context, xml ); - - i = temp.length; - while ( i-- ) { - if ( (elem = temp[i]) ) { - matcherOut[ postMap[i] ] = !(matcherIn[ postMap[i] ] = elem); - } - } - } - - if ( seed ) { - if ( postFinder || preFilter ) { - if ( postFinder ) { - temp = []; - i = matcherOut.length; - while ( i-- ) { - if ( (elem = matcherOut[i]) ) { - temp.push( (matcherIn[i] = elem) ); - } - } - postFinder( null, (matcherOut = []), temp, xml ); - } - - i = matcherOut.length; - while ( i-- ) { - if ( (elem = matcherOut[i]) && - (temp = postFinder ? indexOf.call( seed, elem ) : preMap[i]) > -1 ) { - - seed[temp] = !(results[temp] = elem); - } - } - } - - } else { - matcherOut = condense( - matcherOut === results ? - matcherOut.splice( preexisting, matcherOut.length ) : - matcherOut - ); - if ( postFinder ) { - postFinder( null, results, matcherOut, xml ); - } else { - push.apply( results, matcherOut ); - } - } - }); -} - -function matcherFromTokens( tokens ) { - var checkContext, matcher, j, - len = tokens.length, - leadingRelative = Expr.relative[ tokens[0].type ], - implicitRelative = leadingRelative || Expr.relative[" "], - i = leadingRelative ? 1 : 0, - - matchContext = addCombinator( function( elem ) { - return elem === checkContext; - }, implicitRelative, true ), - matchAnyContext = addCombinator( function( elem ) { - return indexOf.call( checkContext, elem ) > -1; - }, implicitRelative, true ), - matchers = [ function( elem, context, xml ) { - return ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( - (checkContext = context).nodeType ? - matchContext( elem, context, xml ) : - matchAnyContext( elem, context, xml ) ); - } ]; - - for ( ; i < len; i++ ) { - if ( (matcher = Expr.relative[ tokens[i].type ]) ) { - matchers = [ addCombinator(elementMatcher( matchers ), matcher) ]; - } else { - matcher = Expr.filter[ tokens[i].type ].apply( null, tokens[i].matches ); - - if ( matcher[ expando ] ) { - j = ++i; - for ( ; j < len; j++ ) { - if ( Expr.relative[ tokens[j].type ] ) { - break; - } - } - return setMatcher( - i > 1 && elementMatcher( matchers ), - i > 1 && toSelector( - tokens.slice( 0, i - 1 ).concat({ value: tokens[ i - 2 ].type === " " ? "*" : "" }) - ).replace( rtrim, "$1" ), - matcher, - i < j && matcherFromTokens( tokens.slice( i, j ) ), - j < len && matcherFromTokens( (tokens = tokens.slice( j )) ), - j < len && toSelector( tokens ) - ); - } - matchers.push( matcher ); - } - } - - return elementMatcher( matchers ); -} - -function matcherFromGroupMatchers( elementMatchers, setMatchers ) { - var bySet = setMatchers.length > 0, - byElement = elementMatchers.length > 0, - superMatcher = function( seed, context, xml, results, outermost ) { - var elem, j, matcher, - matchedCount = 0, - i = "0", - unmatched = seed && [], - setMatched = [], - contextBackup = outermostContext, - elems = seed || byElement && Expr.find["TAG"]( "*", outermost ), - dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1), - len = elems.length; - - if ( outermost ) { - outermostContext = context !== document && context; - } - - for ( ; i !== len && (elem = elems[i]) != null; i++ ) { - if ( byElement && elem ) { - j = 0; - while ( (matcher = elementMatchers[j++]) ) { - if ( matcher( elem, context, xml ) ) { - results.push( elem ); - break; - } - } - if ( outermost ) { - dirruns = dirrunsUnique; - } - } - - if ( bySet ) { - if ( (elem = !matcher && elem) ) { - matchedCount--; - } - - if ( seed ) { - unmatched.push( elem ); - } - } - } - - matchedCount += i; - if ( bySet && i !== matchedCount ) { - j = 0; - while ( (matcher = setMatchers[j++]) ) { - matcher( unmatched, setMatched, context, xml ); - } - - if ( seed ) { - if ( matchedCount > 0 ) { - while ( i-- ) { - if ( !(unmatched[i] || setMatched[i]) ) { - setMatched[i] = pop.call( results ); - } - } - } - - setMatched = condense( setMatched ); - } - - push.apply( results, setMatched ); - - if ( outermost && !seed && setMatched.length > 0 && - ( matchedCount + setMatchers.length ) > 1 ) { - - Sizzle.uniqueSort( results ); - } - } - - if ( outermost ) { - dirruns = dirrunsUnique; - outermostContext = contextBackup; - } - - return unmatched; - }; - - return bySet ? - markFunction( superMatcher ) : - superMatcher; -} - -compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) { - var i, - setMatchers = [], - elementMatchers = [], - cached = compilerCache[ selector + " " ]; - - if ( !cached ) { - if ( !match ) { - match = tokenize( selector ); - } - i = match.length; - while ( i-- ) { - cached = matcherFromTokens( match[i] ); - if ( cached[ expando ] ) { - setMatchers.push( cached ); - } else { - elementMatchers.push( cached ); - } - } - - cached = compilerCache( selector, matcherFromGroupMatchers( elementMatchers, setMatchers ) ); - - cached.selector = selector; - } - return cached; -}; - -/** - * 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, - compiled = typeof selector === "function" && selector, - match = !seed && tokenize( (selector = compiled.selector || selector) ); - - results = results || []; - - if ( match.length === 1 ) { - - tokens = match[0] = match[0].slice( 0 ); - if ( tokens.length > 2 && (token = tokens[0]).type === "ID" && - 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; - - } else if ( compiled ) { - context = context.parentNode; - } - - selector = selector.slice( tokens.shift().value.length ); - } - - i = matchExpr["needsContext"].test( selector ) ? 0 : tokens.length; - while ( i-- ) { - token = tokens[i]; - - if ( Expr.relative[ (type = token.type) ] ) { - break; - } - if ( (find = Expr.find[ type ]) ) { - if ( (seed = find( - token.matches[0].replace( runescape, funescape ), - rsibling.test( tokens[0].type ) && testContext( context.parentNode ) || context - )) ) { - - tokens.splice( i, 1 ); - selector = seed.length && toSelector( tokens ); - if ( !selector ) { - push.apply( results, seed ); - return results; - } - - break; - } - } - } - } - - ( compiled || compile( selector, match ) )( - seed, - context, - !documentIsHTML, - results, - rsibling.test( selector ) && testContext( context.parentNode ) || context - ); - return results; -}; - - -support.sortStable = expando.split("").sort( sortOrder ).join("") === expando; - -support.detectDuplicates = !!hasDuplicate; - -setDocument(); - -support.sortDetached = assert(function( div1 ) { - return div1.compareDocumentPosition( document.createElement("div") ) & 1; -}); - -if ( !assert(function( div ) { - div.innerHTML = ""; - return div.firstChild.getAttribute("href") === "#" ; -}) ) { - addHandle( "type|href|height|width", function( elem, name, isXML ) { - if ( !isXML ) { - return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 ); - } - }); -} - -if ( !support.attributes || !assert(function( div ) { - div.innerHTML = ""; - div.firstChild.setAttribute( "value", "" ); - return div.firstChild.getAttribute( "value" ) === ""; -}) ) { - addHandle( "value", function( elem, name, isXML ) { - if ( !isXML && elem.nodeName.toLowerCase() === "input" ) { - return elem.defaultValue; - } - }); -} - -if ( !assert(function( div ) { - return div.getAttribute("disabled") == null; -}) ) { - addHandle( booleans, function( elem, name, isXML ) { - var val; - if ( !isXML ) { - return elem[ name ] === true ? name.toLowerCase() : - (val = elem.getAttributeNode( name )) && val.specified ? - val.value : - null; - } - }); -} - -if ( typeof define === "function" && define.amd ) { - define(function() { return Sizzle; }); -} else if ( typeof module !== "undefined" && module.exports ) { - module.exports = Sizzle; -} else { - window.Sizzle = Sizzle; -} - -})( window ); - -;(function() { - if (typeof Sizzle !== 'undefined') { - return; - } - - if (typeof define !== 'undefined' && define.amd) { - window.Sizzle = Prototype._actual_sizzle; - window.define = Prototype._original_define; - delete Prototype._actual_sizzle; - delete Prototype._original_define; - } else if (typeof module !== 'undefined' && module.exports) { - window.Sizzle = module.exports; - module.exports = {}; - } -})(); - -;(function(engine) { - var extendElements = Prototype.Selector.extendElements; - - function select(selector, scope) { - return extendElements(engine(selector, scope || document)); - } - - function match(element, selector) { - return engine.matches(selector, [element]).length == 1; - } - - Prototype.Selector.engine = engine; - Prototype.Selector.select = select; - Prototype.Selector.match = match; -})(Sizzle); - -window.Sizzle = Prototype._original_property; -delete Prototype._original_property; - -var Form = { - reset: function(form) { - form = $(form); - form.reset(); - return form; - }, - - serializeElements: function(elements, options) { - if (typeof options != 'object') options = { hash: !!options }; - else if (Object.isUndefined(options.hash)) options.hash = true; - var key, value, submitted = false, submit = options.submit, accumulator, initial; - - if (options.hash) { - initial = {}; - accumulator = function(result, key, value) { - if (key in result) { - if (!Object.isArray(result[key])) result[key] = [result[key]]; - result[key] = result[key].concat(value); - } else result[key] = value; - return result; - }; - } else { - initial = ''; - accumulator = function(result, key, values) { - if (!Object.isArray(values)) {values = [values];} - if (!values.length) {return result;} - var encodedKey = encodeURIComponent(key).gsub(/%20/, '+'); - return result + (result ? "&" : "") + values.map(function (value) { - value = value.gsub(/(\r)?\n/, '\r\n'); - value = encodeURIComponent(value); - value = value.gsub(/%20/, '+'); - return encodedKey + "=" + value; - }).join("&"); - }; - } - - return elements.inject(initial, function(result, element) { - if (!element.disabled && element.name) { - key = element.name; value = $(element).getValue(); - if (value != null && element.type != 'file' && (element.type != 'submit' || (!submitted && - submit !== false && (!submit || key == submit) && (submitted = true)))) { - result = accumulator(result, key, value); - } - } - return result; - }); - } -}; - -Form.Methods = { - serialize: function(form, options) { - return Form.serializeElements(Form.getElements(form), options); - }, - - - getElements: function(form) { - var elements = $(form).getElementsByTagName('*'); - var element, results = [], serializers = Form.Element.Serializers; - - for (var i = 0; element = elements[i]; i++) { - if (serializers[element.tagName.toLowerCase()]) - results.push(Element.extend(element)); - } - return results; - }, - - getInputs: function(form, typeName, name) { - form = $(form); - var inputs = form.getElementsByTagName('input'); - - if (!typeName && !name) return $A(inputs).map(Element.extend); - - for (var i = 0, matchingInputs = [], length = inputs.length; i < length; i++) { - var input = inputs[i]; - if ((typeName && input.type != typeName) || (name && input.name != name)) - continue; - matchingInputs.push(Element.extend(input)); - } - - return matchingInputs; - }, - - disable: function(form) { - form = $(form); - Form.getElements(form).invoke('disable'); - return form; - }, - - enable: function(form) { - form = $(form); - Form.getElements(form).invoke('enable'); - return form; - }, - - findFirstElement: function(form) { - var elements = $(form).getElements().findAll(function(element) { - return 'hidden' != element.type && !element.disabled; - }); - var firstByIndex = elements.findAll(function(element) { - return element.hasAttribute('tabIndex') && element.tabIndex >= 0; - }).sortBy(function(element) { return element.tabIndex }).first(); - - return firstByIndex ? firstByIndex : elements.find(function(element) { - return /^(?:input|select|textarea)$/i.test(element.tagName); - }); - }, - - focusFirstElement: function(form) { - form = $(form); - var element = form.findFirstElement(); - if (element) element.activate(); - return form; - }, - - request: function(form, options) { - form = $(form), options = Object.clone(options || { }); - - var params = options.parameters, action = form.readAttribute('action') || ''; - if (action.blank()) action = window.location.href; - options.parameters = form.serialize(true); - - if (params) { - if (Object.isString(params)) params = params.toQueryParams(); - Object.extend(options.parameters, params); - } - - if (form.hasAttribute('method') && !options.method) - options.method = form.method; - - return new Ajax.Request(action, options); - } -}; - -/*--------------------------------------------------------------------------*/ - - -Form.Element = { - focus: function(element) { - $(element).focus(); - return element; - }, - - select: function(element) { - $(element).select(); - return element; - } -}; - -Form.Element.Methods = { - - serialize: function(element) { - element = $(element); - if (!element.disabled && element.name) { - var value = element.getValue(); - if (value != undefined) { - var pair = { }; - pair[element.name] = value; - return Object.toQueryString(pair); - } - } - return ''; - }, - - getValue: function(element) { - element = $(element); - var method = element.tagName.toLowerCase(); - return Form.Element.Serializers[method](element); - }, - - setValue: function(element, value) { - element = $(element); - var method = element.tagName.toLowerCase(); - Form.Element.Serializers[method](element, value); - return element; - }, - - clear: function(element) { - $(element).value = ''; - return element; - }, - - present: function(element) { - return $(element).value != ''; - }, - - activate: function(element) { - element = $(element); - try { - element.focus(); - if (element.select && (element.tagName.toLowerCase() != 'input' || - !(/^(?:button|reset|submit)$/i.test(element.type)))) - element.select(); - } catch (e) { } - return element; - }, - - disable: function(element) { - element = $(element); - element.disabled = true; - return element; - }, - - enable: function(element) { - element = $(element); - element.disabled = false; - return element; - } -}; - -/*--------------------------------------------------------------------------*/ - -var Field = Form.Element; - -var $F = Form.Element.Methods.getValue; - -/*--------------------------------------------------------------------------*/ - -Form.Element.Serializers = (function() { - function input(element, value) { - switch (element.type.toLowerCase()) { - case 'checkbox': - case 'radio': - return inputSelector(element, value); - default: - return valueSelector(element, value); - } - } - - function inputSelector(element, value) { - if (Object.isUndefined(value)) - return element.checked ? element.value : null; - else element.checked = !!value; - } - - function valueSelector(element, value) { - if (Object.isUndefined(value)) return element.value; - else element.value = value; - } - - function select(element, value) { - if (Object.isUndefined(value)) - return (element.type === 'select-one' ? selectOne : selectMany)(element); - - var opt, currentValue, single = !Object.isArray(value); - for (var i = 0, length = element.length; i < length; i++) { - opt = element.options[i]; - currentValue = this.optionValue(opt); - if (single) { - if (currentValue == value) { - opt.selected = true; - return; - } - } - else opt.selected = value.include(currentValue); - } - } - - function selectOne(element) { - var index = element.selectedIndex; - return index >= 0 ? optionValue(element.options[index]) : null; - } - - function selectMany(element) { - var values, length = element.length; - if (!length) return null; - - for (var i = 0, values = []; i < length; i++) { - var opt = element.options[i]; - if (opt.selected) values.push(optionValue(opt)); - } - return values; - } - - function optionValue(opt) { - return Element.hasAttribute(opt, 'value') ? opt.value : opt.text; - } - - return { - input: input, - inputSelector: inputSelector, - textarea: valueSelector, - select: select, - selectOne: selectOne, - selectMany: selectMany, - optionValue: optionValue, - button: valueSelector - }; -})(); - -/*--------------------------------------------------------------------------*/ - - -Abstract.TimedObserver = Class.create(PeriodicalExecuter, { - initialize: function($super, element, frequency, callback) { - $super(callback, frequency); - this.element = $(element); - this.lastValue = this.getValue(); - }, - - execute: function() { - var value = this.getValue(); - if (Object.isString(this.lastValue) && Object.isString(value) ? - this.lastValue != value : String(this.lastValue) != String(value)) { - this.callback(this.element, value); - this.lastValue = value; - } - } -}); - -Form.Element.Observer = Class.create(Abstract.TimedObserver, { - getValue: function() { - return Form.Element.getValue(this.element); - } -}); - -Form.Observer = Class.create(Abstract.TimedObserver, { - getValue: function() { - return Form.serialize(this.element); - } -}); - -/*--------------------------------------------------------------------------*/ - -Abstract.EventObserver = Class.create({ - initialize: function(element, callback) { - this.element = $(element); - this.callback = callback; - - this.lastValue = this.getValue(); - if (this.element.tagName.toLowerCase() == 'form') - this.registerFormCallbacks(); - else - this.registerCallback(this.element); - }, - - onElementEvent: function() { - var value = this.getValue(); - if (this.lastValue != value) { - this.callback(this.element, value); - this.lastValue = value; - } - }, - - registerFormCallbacks: function() { - Form.getElements(this.element).each(this.registerCallback, this); - }, - - registerCallback: function(element) { - if (element.type) { - switch (element.type.toLowerCase()) { - case 'checkbox': - case 'radio': - Event.observe(element, 'click', this.onElementEvent.bind(this)); - break; - default: - Event.observe(element, 'change', this.onElementEvent.bind(this)); - break; - } - } - } -}); - -Form.Element.EventObserver = Class.create(Abstract.EventObserver, { - getValue: function() { - return Form.Element.getValue(this.element); - } -}); - -Form.EventObserver = Class.create(Abstract.EventObserver, { - getValue: function() { - return Form.serialize(this.element); - } -}); -(function(GLOBAL) { - var DIV = document.createElement('div'); - var docEl = document.documentElement; - var MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED = 'onmouseenter' in docEl - && 'onmouseleave' in docEl; - - var Event = { - KEY_BACKSPACE: 8, - KEY_TAB: 9, - KEY_RETURN: 13, - KEY_ESC: 27, - KEY_LEFT: 37, - KEY_UP: 38, - KEY_RIGHT: 39, - KEY_DOWN: 40, - KEY_DELETE: 46, - KEY_HOME: 36, - KEY_END: 35, - KEY_PAGEUP: 33, - KEY_PAGEDOWN: 34, - KEY_INSERT: 45 - }; - - - var isIELegacyEvent = function(event) { return false; }; - - if (window.attachEvent) { - if (window.addEventListener) { - isIELegacyEvent = function(event) { - return !(event instanceof window.Event); - }; - } else { - isIELegacyEvent = function(event) { return true; }; - } - } - - var _isButton; - - function _isButtonForDOMEvents(event, code) { - return event.which ? (event.which === code + 1) : (event.button === code); - } - - var legacyButtonMap = { 0: 1, 1: 4, 2: 2 }; - function _isButtonForLegacyEvents(event, code) { - return event.button === legacyButtonMap[code]; - } - - function _isButtonForWebKit(event, code) { - switch (code) { - case 0: return event.which == 1 && !event.metaKey; - case 1: return event.which == 2 || (event.which == 1 && event.metaKey); - case 2: return event.which == 3; - default: return false; - } - } - - if (window.attachEvent) { - if (!window.addEventListener) { - _isButton = _isButtonForLegacyEvents; - } else { - _isButton = function(event, code) { - return isIELegacyEvent(event) ? _isButtonForLegacyEvents(event, code) : - _isButtonForDOMEvents(event, code); - } - } - } else if (Prototype.Browser.WebKit) { - _isButton = _isButtonForWebKit; - } else { - _isButton = _isButtonForDOMEvents; - } - - function isLeftClick(event) { return _isButton(event, 0) } - - function isMiddleClick(event) { return _isButton(event, 1) } - - function isRightClick(event) { return _isButton(event, 2) } - - function element(event) { - return Element.extend(_element(event)); - } - - function _element(event) { - event = Event.extend(event); - - var node = event.target, type = event.type, - currentTarget = event.currentTarget; - - if (currentTarget && currentTarget.tagName) { - if (type === 'load' || type === 'error' || - (type === 'click' && currentTarget.tagName.toLowerCase() === 'input' - && currentTarget.type === 'radio')) - node = currentTarget; - } - - return node.nodeType == Node.TEXT_NODE ? node.parentNode : node; - } - - function findElement(event, expression) { - var element = _element(event), selector = Prototype.Selector; - if (!expression) return Element.extend(element); - while (element) { - if (Object.isElement(element) && selector.match(element, expression)) - return Element.extend(element); - element = element.parentNode; - } - } - - function pointer(event) { - return { x: pointerX(event), y: pointerY(event) }; - } - - function pointerX(event) { - var docElement = document.documentElement, - body = document.body || { scrollLeft: 0 }; - - return event.pageX || (event.clientX + - (docElement.scrollLeft || body.scrollLeft) - - (docElement.clientLeft || 0)); - } - - function pointerY(event) { - var docElement = document.documentElement, - body = document.body || { scrollTop: 0 }; - - return event.pageY || (event.clientY + - (docElement.scrollTop || body.scrollTop) - - (docElement.clientTop || 0)); - } - - - function stop(event) { - Event.extend(event); - event.preventDefault(); - event.stopPropagation(); - - event.stopped = true; - } - - - Event.Methods = { - isLeftClick: isLeftClick, - isMiddleClick: isMiddleClick, - isRightClick: isRightClick, - - element: element, - findElement: findElement, - - pointer: pointer, - pointerX: pointerX, - pointerY: pointerY, - - stop: stop - }; - - var methods = Object.keys(Event.Methods).inject({ }, function(m, name) { - m[name] = Event.Methods[name].methodize(); - return m; - }); - - if (window.attachEvent) { - function _relatedTarget(event) { - var element; - switch (event.type) { - case 'mouseover': - case 'mouseenter': - element = event.fromElement; - break; - case 'mouseout': - case 'mouseleave': - element = event.toElement; - break; - default: - return null; - } - return Element.extend(element); - } - - var additionalMethods = { - stopPropagation: function() { this.cancelBubble = true }, - preventDefault: function() { this.returnValue = false }, - inspect: function() { return '[object Event]' } - }; - - Event.extend = function(event, element) { - if (!event) return false; - - if (!isIELegacyEvent(event)) return event; - - if (event._extendedByPrototype) return event; - event._extendedByPrototype = Prototype.emptyFunction; - - var pointer = Event.pointer(event); - - Object.extend(event, { - target: event.srcElement || element, - relatedTarget: _relatedTarget(event), - pageX: pointer.x, - pageY: pointer.y - }); - - Object.extend(event, methods); - Object.extend(event, additionalMethods); - - return event; - }; - } else { - Event.extend = Prototype.K; - } - - if (window.addEventListener) { - Event.prototype = window.Event.prototype || document.createEvent('HTMLEvents').__proto__; - Object.extend(Event.prototype, methods); - } - - var EVENT_TRANSLATIONS = { - mouseenter: 'mouseover', - mouseleave: 'mouseout' - }; - - function getDOMEventName(eventName) { - return EVENT_TRANSLATIONS[eventName] || eventName; - } - - if (MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED) - getDOMEventName = Prototype.K; - - function getUniqueElementID(element) { - if (element === window) return 0; - - if (typeof element._prototypeUID === 'undefined') - element._prototypeUID = Element.Storage.UID++; - return element._prototypeUID; - } - - function getUniqueElementID_IE(element) { - if (element === window) return 0; - if (element == document) return 1; - return element.uniqueID; - } - - if ('uniqueID' in DIV) - getUniqueElementID = getUniqueElementID_IE; - - function isCustomEvent(eventName) { - return eventName.include(':'); - } - - Event._isCustomEvent = isCustomEvent; - - function getOrCreateRegistryFor(element, uid) { - var CACHE = GLOBAL.Event.cache; - if (Object.isUndefined(uid)) - uid = getUniqueElementID(element); - if (!CACHE[uid]) CACHE[uid] = { element: element }; - return CACHE[uid]; - } - - function destroyRegistryForElement(element, uid) { - if (Object.isUndefined(uid)) - uid = getUniqueElementID(element); - delete GLOBAL.Event.cache[uid]; - } - - - function register(element, eventName, handler) { - var registry = getOrCreateRegistryFor(element); - if (!registry[eventName]) registry[eventName] = []; - var entries = registry[eventName]; - - var i = entries.length; - while (i--) - if (entries[i].handler === handler) return null; - - var uid = getUniqueElementID(element); - var responder = GLOBAL.Event._createResponder(uid, eventName, handler); - var entry = { - responder: responder, - handler: handler - }; - - entries.push(entry); - return entry; - } - - function unregister(element, eventName, handler) { - var registry = getOrCreateRegistryFor(element); - var entries = registry[eventName] || []; - - var i = entries.length, entry; - while (i--) { - if (entries[i].handler === handler) { - entry = entries[i]; - break; - } - } - - if (entry) { - var index = entries.indexOf(entry); - entries.splice(index, 1); - } - - if (entries.length === 0) { - delete registry[eventName]; - if (Object.keys(registry).length === 1 && ('element' in registry)) - destroyRegistryForElement(element); - } - - return entry; - } - - - function observe(element, eventName, handler) { - element = $(element); - var entry = register(element, eventName, handler); - - if (entry === null) return element; - - var responder = entry.responder; - if (isCustomEvent(eventName)) - observeCustomEvent(element, eventName, responder); - else - observeStandardEvent(element, eventName, responder); - - return element; - } - - function observeStandardEvent(element, eventName, responder) { - var actualEventName = getDOMEventName(eventName); - if (element.addEventListener) { - element.addEventListener(actualEventName, responder, false); - } else { - element.attachEvent('on' + actualEventName, responder); - } - } - - function observeCustomEvent(element, eventName, responder) { - if (element.addEventListener) { - element.addEventListener('dataavailable', responder, false); - } else { - element.attachEvent('ondataavailable', responder); - element.attachEvent('onlosecapture', responder); - } - } - - function stopObserving(element, eventName, handler) { - element = $(element); - var handlerGiven = !Object.isUndefined(handler), - eventNameGiven = !Object.isUndefined(eventName); - - if (!eventNameGiven && !handlerGiven) { - stopObservingElement(element); - return element; - } - - if (!handlerGiven) { - stopObservingEventName(element, eventName); - return element; - } - - var entry = unregister(element, eventName, handler); - - if (!entry) return element; - removeEvent(element, eventName, entry.responder); - return element; - } - - function stopObservingStandardEvent(element, eventName, responder) { - var actualEventName = getDOMEventName(eventName); - if (element.removeEventListener) { - element.removeEventListener(actualEventName, responder, false); - } else { - element.detachEvent('on' + actualEventName, responder); - } - } - - function stopObservingCustomEvent(element, eventName, responder) { - if (element.removeEventListener) { - element.removeEventListener('dataavailable', responder, false); - } else { - element.detachEvent('ondataavailable', responder); - element.detachEvent('onlosecapture', responder); - } - } - - - - function stopObservingElement(element) { - var uid = getUniqueElementID(element), registry = GLOBAL.Event.cache[uid]; - if (!registry) return; - - destroyRegistryForElement(element, uid); - - var entries, i; - for (var eventName in registry) { - if (eventName === 'element') continue; - - entries = registry[eventName]; - i = entries.length; - while (i--) - removeEvent(element, eventName, entries[i].responder); - } - } - - function stopObservingEventName(element, eventName) { - var registry = getOrCreateRegistryFor(element); - var entries = registry[eventName]; - if (entries) { - delete registry[eventName]; - } - - entries = entries || []; - - var i = entries.length; - while (i--) - removeEvent(element, eventName, entries[i].responder); - - for (var name in registry) { - if (name === 'element') continue; - return; // There is another registered event - } - - destroyRegistryForElement(element); - } - - - function removeEvent(element, eventName, handler) { - if (isCustomEvent(eventName)) - stopObservingCustomEvent(element, eventName, handler); - else - stopObservingStandardEvent(element, eventName, handler); - } - - - - function getFireTarget(element) { - if (element !== document) return element; - if (document.createEvent && !element.dispatchEvent) - return document.documentElement; - return element; - } - - function fire(element, eventName, memo, bubble) { - element = getFireTarget($(element)); - if (Object.isUndefined(bubble)) bubble = true; - memo = memo || {}; - - var event = fireEvent(element, eventName, memo, bubble); - return Event.extend(event); - } - - function fireEvent_DOM(element, eventName, memo, bubble) { - var event = document.createEvent('HTMLEvents'); - event.initEvent('dataavailable', bubble, true); - - event.eventName = eventName; - event.memo = memo; - - element.dispatchEvent(event); - return event; - } - - function fireEvent_IE(element, eventName, memo, bubble) { - var event = document.createEventObject(); - event.eventType = bubble ? 'ondataavailable' : 'onlosecapture'; - - event.eventName = eventName; - event.memo = memo; - - element.fireEvent(event.eventType, event); - return event; - } - - var fireEvent = document.createEvent ? fireEvent_DOM : fireEvent_IE; - - - - Event.Handler = Class.create({ - initialize: function(element, eventName, selector, callback) { - this.element = $(element); - this.eventName = eventName; - this.selector = selector; - this.callback = callback; - this.handler = this.handleEvent.bind(this); - }, - - - start: function() { - Event.observe(this.element, this.eventName, this.handler); - return this; - }, - - stop: function() { - Event.stopObserving(this.element, this.eventName, this.handler); - return this; - }, - - handleEvent: function(event) { - var element = Event.findElement(event, this.selector); - if (element) this.callback.call(this.element, event, element); - } - }); - - function on(element, eventName, selector, callback) { - element = $(element); - if (Object.isFunction(selector) && Object.isUndefined(callback)) { - callback = selector, selector = null; - } - - return new Event.Handler(element, eventName, selector, callback).start(); - } - - Object.extend(Event, Event.Methods); - - Object.extend(Event, { - fire: fire, - observe: observe, - stopObserving: stopObserving, - p_on: on - }); - - Element.addMethods({ - fire: fire, - - observe: observe, - - stopObserving: stopObserving, - - p_on: on - }); - - Object.extend(document, { - fire: fire.methodize(), - - observe: observe.methodize(), - - stopObserving: stopObserving.methodize(), - - p_on: on.methodize(), - - loaded: false - }); - - if (GLOBAL.Event) Object.extend(window.Event, Event); - else GLOBAL.Event = Event; - - GLOBAL.Event.cache = {}; - - function destroyCache_IE() { - GLOBAL.Event.cache = null; - } - - if (window.attachEvent) - window.attachEvent('onunload', destroyCache_IE); - - DIV = null; - docEl = null; -})(this); - -(function(GLOBAL) { - /* Code for creating leak-free event responders is based on work by - John-David Dalton. */ - - var docEl = document.documentElement; - var MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED = 'onmouseenter' in docEl - && 'onmouseleave' in docEl; - - function isSimulatedMouseEnterLeaveEvent(eventName) { - return !MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED && - (eventName === 'mouseenter' || eventName === 'mouseleave'); - } - - function createResponder(uid, eventName, handler) { - if (Event._isCustomEvent(eventName)) - return createResponderForCustomEvent(uid, eventName, handler); - if (isSimulatedMouseEnterLeaveEvent(eventName)) - return createMouseEnterLeaveResponder(uid, eventName, handler); - - return function(event) { - if (!Event.cache) return; - - var element = Event.cache[uid].element; - Event.extend(event, element); - handler.call(element, event); - }; - } - - function createResponderForCustomEvent(uid, eventName, handler) { - return function(event) { - var cache = Event.cache[uid]; - var element = cache && cache.element; - - if (Object.isUndefined(event.eventName)) - return false; - - if (event.eventName !== eventName) - return false; - - Event.extend(event, element); - handler.call(element, event); - }; - } - - function createMouseEnterLeaveResponder(uid, eventName, handler) { - return function(event) { - var element = Event.cache[uid].element; - - Event.extend(event, element); - var parent = event.relatedTarget; - - while (parent && parent !== element) { - try { parent = parent.parentNode; } - catch(e) { parent = element; } - } - - if (parent === element) return; - handler.call(element, event); - } - } - - GLOBAL.Event._createResponder = createResponder; - docEl = null; -})(this); - -(function(GLOBAL) { - /* Support for the DOMContentLoaded event is based on work by Dan Webb, - Matthias Miller, Dean Edwards, John Resig, and Diego Perini. */ - - var TIMER; - - function fireContentLoadedEvent() { - if (document.loaded) return; - if (TIMER) window.clearTimeout(TIMER); - document.loaded = true; - document.fire('dom:loaded'); - } - - function checkReadyState() { - if (document.readyState === 'complete') { - document.detachEvent('onreadystatechange', checkReadyState); - fireContentLoadedEvent(); - } - } - - function pollDoScroll() { - try { - document.documentElement.doScroll('left'); - } catch (e) { - TIMER = pollDoScroll.defer(); - return; - } - - fireContentLoadedEvent(); - } - - - if (document.readyState === 'complete') { - fireContentLoadedEvent(); - return; - } - - if (document.addEventListener) { - document.addEventListener('DOMContentLoaded', fireContentLoadedEvent, false); - } else { - document.attachEvent('onreadystatechange', checkReadyState); - if (window == top) TIMER = pollDoScroll.defer(); - } - - Event.observe(window, 'load', fireContentLoadedEvent); -})(this); - - -Element.addMethods(); -/*------------------------------- DEPRECATED -------------------------------*/ - -Hash.toQueryString = Object.toQueryString; - -var Toggle = { display: Element.toggle }; - -Element.addMethods({ - childOf: Element.Methods.descendantOf -}); - -var Insertion = { - Before: function(element, content) { - return Element.insert(element, {before:content}); - }, - - Top: function(element, content) { - return Element.insert(element, {top:content}); - }, - - Bottom: function(element, content) { - return Element.insert(element, {bottom:content}); - }, - - After: function(element, content) { - return Element.insert(element, {after:content}); - } -}; - -var $continue = new Error('"throw $continue" is deprecated, use "return" instead'); - -var Position = { - includeScrollOffsets: false, - - prepare: function() { - this.deltaX = window.pageXOffset - || document.documentElement.scrollLeft - || document.body.scrollLeft - || 0; - this.deltaY = window.pageYOffset - || document.documentElement.scrollTop - || document.body.scrollTop - || 0; - }, - - within: function(element, x, y) { - if (this.includeScrollOffsets) - return this.withinIncludingScrolloffsets(element, x, y); - this.xcomp = x; - this.ycomp = y; - this.offset = Element.cumulativeOffset(element); - - return (y >= this.offset[1] && - y < this.offset[1] + element.offsetHeight && - x >= this.offset[0] && - x < this.offset[0] + element.offsetWidth); - }, - - withinIncludingScrolloffsets: function(element, x, y) { - var offsetcache = Element.cumulativeScrollOffset(element); - - this.xcomp = x + offsetcache[0] - this.deltaX; - this.ycomp = y + offsetcache[1] - this.deltaY; - this.offset = Element.cumulativeOffset(element); - - return (this.ycomp >= this.offset[1] && - this.ycomp < this.offset[1] + element.offsetHeight && - this.xcomp >= this.offset[0] && - this.xcomp < this.offset[0] + element.offsetWidth); - }, - - overlap: function(mode, element) { - if (!mode) return 0; - if (mode == 'vertical') - return ((this.offset[1] + element.offsetHeight) - this.ycomp) / - element.offsetHeight; - if (mode == 'horizontal') - return ((this.offset[0] + element.offsetWidth) - this.xcomp) / - element.offsetWidth; - }, - - - cumulativeOffset: Element.Methods.cumulativeOffset, - - positionedOffset: Element.Methods.positionedOffset, - - absolutize: function(element) { - Position.prepare(); - return Element.absolutize(element); - }, - - relativize: function(element) { - Position.prepare(); - return Element.relativize(element); - }, - - realOffset: Element.Methods.cumulativeScrollOffset, - - offsetParent: Element.Methods.getOffsetParent, - - page: Element.Methods.viewportOffset, - - clone: function(source, target, options) { - options = options || { }; - return Element.clonePosition(target, source, options); - } -}; - -/*--------------------------------------------------------------------------*/ - -if (!document.getElementsByClassName) document.getElementsByClassName = function(instanceMethods){ - function iter(name) { - return name.blank() ? null : "[contains(concat(' ', @class, ' '), ' " + name + " ')]"; - } - - instanceMethods.getElementsByClassName = Prototype.BrowserFeatures.XPath ? - function(element, className) { - className = className.toString().strip(); - var cond = /\s/.test(className) ? $w(className).map(iter).join('') : iter(className); - return cond ? document._getElementsByXPath('.//*' + cond, element) : []; - } : function(element, className) { - className = className.toString().strip(); - var elements = [], classNames = (/\s/.test(className) ? $w(className) : null); - if (!classNames && !className) return elements; - - var nodes = $(element).getElementsByTagName('*'); - className = ' ' + className + ' '; - - for (var i = 0, child, cn; child = nodes[i]; i++) { - if (child.className && (cn = ' ' + child.className + ' ') && (cn.include(className) || - (classNames && classNames.all(function(name) { - return !name.toString().blank() && cn.include(' ' + name + ' '); - })))) - elements.push(Element.extend(child)); - } - return elements; - }; - - return function(className, parentElement) { - return $(parentElement || document.body).getElementsByClassName(className); - }; -}(Element.Methods); - -/*--------------------------------------------------------------------------*/ - -Element.ClassNames = Class.create(); -Element.ClassNames.prototype = { - initialize: function(element) { - this.element = $(element); - }, - - _each: function(iterator, context) { - this.element.className.split(/\s+/).select(function(name) { - return name.length > 0; - })._each(iterator, context); - }, - - set: function(className) { - this.element.className = className; - }, - - add: function(classNameToAdd) { - if (this.include(classNameToAdd)) return; - this.set($A(this).concat(classNameToAdd).join(' ')); - }, - - remove: function(classNameToRemove) { - if (!this.include(classNameToRemove)) return; - this.set($A(this).without(classNameToRemove).join(' ')); - }, - - toString: function() { - return $A(this).join(' '); - } -}; - -Object.extend(Element.ClassNames.prototype, Enumerable); - -/*--------------------------------------------------------------------------*/ - -(function() { - window.Selector = Class.create({ - initialize: function(expression) { - this.expression = expression.strip(); - }, - - findElements: function(rootElement) { - return Prototype.Selector.select(this.expression, rootElement); - }, - - match: function(element) { - return Prototype.Selector.match(element, this.expression); - }, - - toString: function() { - return this.expression; - }, - - inspect: function() { - return "#"; - } - }); - - Object.extend(Selector, { - matchElements: function(elements, expression) { - var match = Prototype.Selector.match, - results = []; - - for (var i = 0, length = elements.length; i < length; i++) { - var element = elements[i]; - if (match(element, expression)) { - results.push(Element.extend(element)); - } - } - return results; - }, - - findElement: function(elements, expression, index) { - index = index || 0; - var matchIndex = 0, element; - for (var i = 0, length = elements.length; i < length; i++) { - element = elements[i]; - if (Prototype.Selector.match(element, expression) && index === matchIndex++) { - return Element.extend(element); - } - } - }, - - findChildElements: function(element, expressions) { - var selector = expressions.toArray().join(', '); - return Prototype.Selector.select(selector, element || document); - } - }); -})(); diff --git a/plugins/share/init.php b/plugins/share/init.php index 4c47e29d3..0c975cfdd 100644 --- a/plugins/share/init.php +++ b/plugins/share/init.php @@ -156,7 +156,6 @@ class Share extends Plugin { <?= $line["title"] ?> - - - - -
    - - "; - print "window.close();"; - print ""; - - } else { - $title = htmlspecialchars(clean($_REQUEST["title"])); - $url = htmlspecialchars(clean($_REQUEST["url"])); - - ?> - - - - - -
    - - -
    - -
    - - -
    - -
    - - -
    - -
    - - -
    -
    - -
    - -
    - - - -
    - - - - - - -
    - - - -
    - - " /> -
    - -
    - - - "/> -
    - -
    - -
    - - - -
    - -
    - "; - } - function login() { if (!SINGLE_USER_MODE) { @@ -565,160 +406,6 @@ class Handler_Public extends Handler { } } - function subscribe() { - if (SINGLE_USER_MODE) { - UserHelper::login_sequence(); - } - - if (!empty($_SESSION["uid"])) { - - $feed_url = clean($_REQUEST["feed_url"] ?? ""); - $csrf_token = clean($_POST["csrf_token"] ?? ""); - - header('Content-Type: text/html; charset=utf-8'); - ?> - - - - Tiny Tiny RSS - - - - - - - - -
    -

    -
    - -
    - - -
    - - -
    - - - - -
    - %s.", $feed_url)); - break; - case 1: - print_notice(T_sprintf("Subscribed to %s.", $feed_url)); - break; - case 2: - print_error(T_sprintf("Could not subscribe to %s.", $feed_url)); - break; - case 3: - print_error(T_sprintf("No feeds found in %s.", $feed_url)); - break; - case 4: - $feed_urls = $rc["feeds"]; - break; - case 5: - print_error(T_sprintf("Could not subscribe to %s.
    Can't download the Feed URL.", $feed_url)); - break; - } - - if ($feed_urls) { - - print "
    "; - print ""; - print \Controls\hidden_tag("csrf_token", $_SESSION["csrf_token"]); - - print "
    "; - print ""; - print ""; - print "
    "; - - print ""; - print "".__("Return to Tiny Tiny RSS").""; - - print "
    "; - } - - $tp_uri = get_self_url_prefix() . "/prefs.php"; - - if ($rc['code'] <= 2){ - $sth = $this->pdo->prepare("SELECT id FROM ttrss_feeds WHERE - feed_url = ? AND owner_uid = ?"); - $sth->execute([$feed_url, $_SESSION['uid']]); - $row = $sth->fetch(); - - $feed_id = $row["id"]; - } else { - $feed_id = 0; - } - - if ($feed_id) { - print "
    - - - - - ".__("Return to Tiny Tiny RSS")." -
    "; - } - } - - print "
    "; - - } else { - $this->render_login_form(); - } - } - function index() { header("Content-Type: text/plain"); print error_json(13); @@ -928,7 +615,7 @@ class Handler_Public extends Handler { if (!SINGLE_USER_MODE && $_SESSION["access_level"] < 10) { $_SESSION["login_error_msg"] = __("Your access level is insufficient to run this script."); - $this->render_login_form(); + $this->_render_login_form(); exit; } @@ -1066,7 +753,7 @@ class Handler_Public extends Handler { } } - private function make_article_tag_uri($id, $timestamp) { + private function _make_article_tag_uri($id, $timestamp) { $timestamp = date("Y-m-d", strtotime($timestamp)); @@ -1108,7 +795,7 @@ class Handler_Public extends Handler { } } - static function render_login_form() { + static function _render_login_form() { header('Cache-Control: public'); require_once "login_form.php"; diff --git a/classes/pluginhost.php b/classes/pluginhost.php index 065fa99c4..d50c5a706 100755 --- a/classes/pluginhost.php +++ b/classes/pluginhost.php @@ -599,7 +599,7 @@ class PluginHost { } // handled by classes/pluginhandler.php, requires valid session - function get_method_url(Plugin $sender, string $method, $params) { + function get_method_url(Plugin $sender, string $method, $params = []) { return get_self_url_prefix() . "/backend.php?" . http_build_query( array_merge( @@ -623,7 +623,7 @@ class PluginHost { } */ // WARNING: endpoint in public.php, exposed to unauthenticated users - function get_public_method_url(Plugin $sender, string $method, $params) { + function get_public_method_url(Plugin $sender, string $method, $params = []) { if ($sender->is_public_method($method)) { return get_self_url_prefix() . "/public.php?" . http_build_query( diff --git a/classes/userhelper.php b/classes/userhelper.php index 42d50a0f4..7fe1e5557 100644 --- a/classes/userhelper.php +++ b/classes/userhelper.php @@ -107,7 +107,7 @@ class UserHelper { if (empty($_SESSION["uid"])) { UserHelper::logout(); - Handler_Public::render_login_form(); + Handler_Public::_render_login_form(); exit; } diff --git a/include/controls.php b/include/controls.php index a60b1e0b0..ae5fba739 100755 --- a/include/controls.php +++ b/include/controls.php @@ -16,6 +16,10 @@ return hidden_tag("op", strtolower(get_class($plugin) . \PluginHost::PUBLIC_METHOD_DELIMITER . $method)); } */ + function public_method_tags(\Plugin $plugin, string $method) { + return hidden_tag("op", strtolower(get_class($plugin) . \PluginHost::PUBLIC_METHOD_DELIMITER . $method)); + } + function pluginhandler_tags(\Plugin $plugin, string $method) { return hidden_tag("op", "pluginhandler") . hidden_tag("plugin", strtolower(get_class($plugin))) . diff --git a/plugins/bookmarklets/init.php b/plugins/bookmarklets/init.php index a9dc3e69d..4c3bbf4cc 100644 --- a/plugins/bookmarklets/init.php +++ b/plugins/bookmarklets/init.php @@ -16,21 +16,338 @@ class Bookmarklets extends Plugin { $host->add_hook($host::HOOK_PREFS_TAB, $this); } - private function subscribe_to_feed_url() { - $url_path = get_self_url_prefix() . - "/public.php?op=subscribe&feed_url=%s"; - return $url_path; + function is_public_method($method) { + return in_array($method, ["subscribe", "sharepopup"]); } + function subscribe() { + if (SINGLE_USER_MODE) { + UserHelper::login_sequence(); + } + + if (!empty($_SESSION["uid"])) { + + $feed_url = clean($_REQUEST["feed_url"] ?? ""); + $csrf_token = clean($_POST["csrf_token"] ?? ""); + + header('Content-Type: text/html; charset=utf-8'); + ?> + + + + <?= __("Subscribe to feed...") ?> + + + + + + + + + + + +
    +

    +
    + +
    + + + +
    + + +
    + + + + +
    + %s.", $feed_url)); + break; + case 1: + print_notice(T_sprintf("Subscribed to %s.", $feed_url)); + break; + case 2: + print_error(T_sprintf("Could not subscribe to %s.", $feed_url)); + break; + case 3: + print_error(T_sprintf("No feeds found in %s.", $feed_url)); + break; + case 4: + $feed_urls = $rc["feeds"]; + break; + case 5: + print_error(T_sprintf("Could not subscribe to %s.
    Can't download the Feed URL.", $feed_url)); + break; + } + + if ($feed_urls) { + + print "
    "; + print \Controls\public_method_tags($this, "subscribe"); + print \Controls\hidden_tag("csrf_token", $_SESSION["csrf_token"]); + + print "
    "; + print ""; + print ""; + print "
    "; + + print ""; + print "".__("Return to Tiny Tiny RSS").""; + + print "
    "; + } + + $tp_uri = get_self_url_prefix() . "/prefs.php"; + + if ($rc['code'] <= 2){ + $sth = $this->pdo->prepare("SELECT id FROM ttrss_feeds WHERE + feed_url = ? AND owner_uid = ?"); + $sth->execute([$feed_url, $_SESSION['uid']]); + $row = $sth->fetch(); + + $feed_id = $row["id"]; + } else { + $feed_id = 0; + } + + if ($feed_id) { + print "
    + + + + + ".__("Return to Tiny Tiny RSS")." +
    "; + } + } + + print "
    "; + } else { + Handler_Public::_render_login_form(); + } + } + + function sharepopup() { + if (SINGLE_USER_MODE) { + UserHelper::login_sequence(); + } + + header('Content-Type: text/html; charset=utf-8'); + ?> + + + + <?= __("Share with Tiny Tiny RSS") ?> + + + + + + + + + + + +
    + + "; + print "window.close();"; + print ""; + + } else { + $title = htmlspecialchars(clean($_REQUEST["title"])); + $url = htmlspecialchars(clean($_REQUEST["url"])); + + ?> +
    + + + + + +
    + + +
    + +
    + + +
    + +
    + + +
    + +
    + + + +
    + +
    + +
    + + "window.close()"]) ?> + +
    + +
    + + +
    + + + +
    + + " /> +
    + +
    + + + "/> +
    + +
    + +
    + + + +
    + +
    + +
    + + "; + subscribe_to_feed_url()); + $bm_subscribe_url = $this->host->get_public_method_url($this, "subscribe"); + $bm_share_url = $this->host->get_public_method_url($this, "sharepopup"); + $confirm_str = str_replace("'", "\'", __('Subscribe to %s in Tiny Tiny RSS?')); - $bm_subscribe_url = htmlspecialchars("javascript:{if(confirm('$confirm_str'.replace('%s',window.location.href)))window.location.href='$bm_subscribe_url'+encodeURIComponent(window.location.href)}"); - $bm_share_url = htmlspecialchars("javascript:(function(){var d=document,w=window,e=w.getSelection,k=d.getSelection,x=d.selection,s=(e?e():(k)?k():(x?x.createRange().text:0)),f='".get_self_url_prefix()."/public.php?op=sharepopup',l=d.location,e=encodeURIComponent,g=f+'&title='+((e(s))?e(s):e(document.title))+'&url='+e(l.href);function a(){if(!w.open(g,'t','toolbar=0,resizable=0,scrollbars=1,status=1,width=500,height=250')){l.href=g;}}a();})()"); + $bm_subscribe_url = htmlspecialchars("javascript:{if(confirm('$confirm_str'.replace('%s',window.location.href)))window.location.href='$bm_subscribe_url&feed_url='+encodeURIComponent(window.location.href)}"); + $bm_share_url = htmlspecialchars("javascript:(function(){var d=document,w=window,e=w.getSelection,k=d.getSelection,x=d.selection,s=(e?e():(k)?k():(x?x.createRange().text:0)),f='$bm_share_url',l=d.location,e=encodeURIComponent,g=f+'&title='+((e(s))?e(s):e(document.title))+'&url='+e(l.href);function a(){if(!w.open(g,'t','toolbar=0,resizable=0,scrollbars=1,status=1,width=500,height=250')){l.href=g;}}a();})()"); + + //$bm_subscribe_url = str_replace('%s', '', $this->subscribe_to_feed_url()); + //$confirm_str = str_replace("'", "\'", __('Subscribe to %s in Tiny Tiny RSS?')); + //$bm_subscribe_url = htmlspecialchars("javascript:{if(confirm('$confirm_str'.replace('%s',window.location.href)))window.location.href='$bm_subscribe_url'+encodeURIComponent(window.location.href)}"); + + //$bm_share_url = htmlspecialchars("javascript:(function(){var d=document,w=window,e=w.getSelection,k=d.getSelection,x=d.selection,s=(e?e():(k)?k():(x?x.createRange().text:0)),f='".get_self_url_prefix()."/public.php?op=sharepopup',l=d.location,e=encodeURIComponent,g=f+'&title='+((e(s))?e(s):e(document.title))+'&url='+e(l.href);function a(){if(!w.open(g,'t','toolbar=0,resizable=0,scrollbars=1,status=1,width=500,height=250')){l.href=g;}}a();})()"); ?>
    Date: Sun, 21 Feb 2021 09:35:07 +0300 Subject: for the most part, deal with filter rules UI --- classes/labels.php | 15 +- classes/pref/filters.php | 231 +++++++++++++++++++--------- classes/rpc.php | 4 +- include/controls.php | 16 +- js/CommonFilters.js | 294 +++++++++++++++++++++++++++++++----- plugins/auto_assign_labels/init.php | 1 + themes/compact.css | 20 +-- themes/compact_night.css | 20 +-- themes/light.css | 20 +-- themes/light/tt-rss.less | 8 +- themes/night.css | 20 +-- themes/night_blue.css | 20 +-- 12 files changed, 495 insertions(+), 174 deletions(-) (limited to 'include') diff --git a/classes/labels.php b/classes/labels.php index f72d7d84b..570f24f4f 100644 --- a/classes/labels.php +++ b/classes/labels.php @@ -37,7 +37,18 @@ class Labels } } - static function get_all_labels($owner_uid) { + static function get_as_hash($owner_uid) { + $rv = []; + $labels = Labels::get_all($owner_uid); + + foreach ($labels as $i => $label) { + $rv[$label["id"]] = $labels[$i]; + } + + return $rv; + } + + static function get_all($owner_uid) { $rv = array(); $pdo = Db::pdo(); @@ -46,7 +57,7 @@ class Labels WHERE owner_uid = ? ORDER BY caption"); $sth->execute([$owner_uid]); - while ($line = $sth->fetch()) { + while ($line = $sth->fetch(PDO::FETCH_ASSOC)) { array_push($rv, $line); } diff --git a/classes/pref/filters.php b/classes/pref/filters.php index 9c250be19..4e52260c9 100755 --- a/classes/pref/filters.php +++ b/classes/pref/filters.php @@ -318,6 +318,84 @@ class Pref_Filters extends Handler_Protected { WHERE id = ? AND owner_uid = ?"); $sth->execute([$filter_id, $_SESSION['uid']]); + if (empty($filter_id) || $row = $sth->fetch()) { + $rv = [ + "id" => $filter_id, + "enabled" => $row["enabled"] ?? true, + "match_any_rule" => $row["match_any_rule"] ?? false, + "inverse" => $row["inverse"] ?? false, + "title" => $row["title"] ?? "", + "rules" => [], + "actions" => [], + "filter_types" => [], + "filter_actions" => [], + "labels" => Labels::get_all($_SESSION["uid"]) + ]; + + $res = $this->pdo->query("SELECT id,description + FROM ttrss_filter_types WHERE id != 5 ORDER BY description"); + + while ($line = $res->fetch()) { + $rv["filter_types"][$line["id"]] = __($line["description"]); + } + + $res = $this->pdo->query("SELECT id,description FROM ttrss_filter_actions + ORDER BY name"); + + while ($line = $res->fetch()) { + $rv["filter_actions"][$line["id"]] = __($line["description"]); + } + + if ($filter_id) { + $rules_sth = $this->pdo->prepare("SELECT * FROM ttrss_filters2_rules + WHERE filter_id = ? ORDER BY reg_exp, id"); + $rules_sth->execute([$filter_id]); + + while ($rrow = $rules_sth->fetch(PDO::FETCH_ASSOC)) { + if ($rrow["match_on"]) { + $rrow["feed_id"] = json_decode($rrow["match_on"], true); + } else { + if ($rrow["cat_filter"]) { + $feed_id = "CAT:" . (int)$rrow["cat_id"]; + } else { + $feed_id = (int)$rrow["feed_id"]; + } + + $rrow["feed_id"] = ["" . $feed_id]; // set item type to string for in_array() + } + + unset($rrow["cat_filter"]); + unset($rrow["cat_id"]); + unset($rrow["filter_id"]); + unset($rrow["id"]); + if (!$rrow["inverse"]) unset($rrow["inverse"]); + unset($rrow["match_on"]); + + $rrow["name"] = $this->_get_rule_name($rrow); + + array_push($rv["rules"], $rrow); + } + + $actions_sth = $this->pdo->prepare("SELECT * FROM ttrss_filters2_actions + WHERE filter_id = ? ORDER BY id"); + $actions_sth->execute([$filter_id]); + + while ($arow = $actions_sth->fetch(PDO::FETCH_ASSOC)) { + $arow["action_param_label"] = $arow["action_param"]; + + unset($arow["filter_id"]); + unset($arow["id"]); + + $arow["name"] = $this->_get_action_name($arow); + + array_push($rv["actions"], $arow); + } + } + print json_encode($rv); + } + + /*return; + if (empty($filter_id) || $row = $sth->fetch()) { $enabled = $row["enabled"] ?? true; @@ -475,7 +553,7 @@ class Pref_Filters extends Handler_Protected { } print ""; - } + } */ } private function _get_rule_name($rule) { @@ -592,8 +670,7 @@ class Pref_Filters extends Handler_Protected { $sth->execute(array_merge($ids, [$_SESSION['uid']])); } - private function _save_rules_and_actions($filter_id) - { + private function _save_rules_and_actions($filter_id) { $sth = $this->pdo->prepare("DELETE FROM ttrss_filters2_rules WHERE filter_id = ?"); $sth->execute([$filter_id]); @@ -670,7 +747,7 @@ class Pref_Filters extends Handler_Protected { } } - function add() { + function add () { $enabled = checkbox_to_sql_bool(clean($_REQUEST["enabled"] ?? false)); $match_any_rule = checkbox_to_sql_bool(clean($_REQUEST["match_any_rule"] ?? false)); $title = clean($_REQUEST["title"]); @@ -764,7 +841,15 @@ class Pref_Filters extends Handler_Protected { $this->feed_multi_select("feed_id", $feed_ids, 'style="width : 500px; height : 300px" dojoType="dijit.form.MultiSelect"') + ]); + + /*return; + $rule = json_decode(clean($_REQUEST["rule"]), true); if ($rule) { @@ -818,7 +903,7 @@ class Pref_Filters extends Handler_Protected { print "
    "; print ""; - $this->feed_multi_select("feed_id", + print $this->feed_multi_select("feed_id", $feed_id, 'style="width : 500px; height : 300px" dojoType="dijit.form.MultiSelect"'); print ""; @@ -840,7 +925,7 @@ class Pref_Filters extends Handler_Protected { print ""; - print ""; + print "";*/ } function newaction() { @@ -1071,102 +1156,106 @@ class Pref_Filters extends Handler_Protected { $attributes = "", $include_all_feeds = true, $root_id = null, $nest_level = 0) { - $pdo = Db::pdo(); + $pdo = Db::pdo(); - // print_r(in_array("CAT:6",$default_ids)); + $rv = ""; - if (!$root_id) { - print ""; + if ($include_all_feeds) { + $is_selected = (in_array("0", $default_ids)) ? "selected=\"1\"" : ""; + $rv .= ""; + } + } - if (!$root_id) $root_id = null; + if (get_pref('ENABLE_FEED_CATS')) { - $sth = $pdo->prepare("SELECT id,title, - (SELECT COUNT(id) FROM ttrss_feed_categories AS c2 WHERE - c2.parent_cat = ttrss_feed_categories.id) AS num_children - FROM ttrss_feed_categories - WHERE owner_uid = :uid AND - (parent_cat = :root_id OR (:root_id IS NULL AND parent_cat IS NULL)) ORDER BY title"); + if (!$root_id) $root_id = null; - $sth->execute([":uid" => $_SESSION['uid'], ":root_id" => $root_id]); + $sth = $pdo->prepare("SELECT id,title, + (SELECT COUNT(id) FROM ttrss_feed_categories AS c2 WHERE + c2.parent_cat = ttrss_feed_categories.id) AS num_children + FROM ttrss_feed_categories + WHERE owner_uid = :uid AND + (parent_cat = :root_id OR (:root_id IS NULL AND parent_cat IS NULL)) ORDER BY title"); - while ($line = $sth->fetch()) { + $sth->execute([":uid" => $_SESSION['uid'], ":root_id" => $root_id]); - for ($i = 0; $i < $nest_level; $i++) - $line["title"] = " " . $line["title"]; + while ($line = $sth->fetch()) { - $is_selected = in_array("CAT:".$line["id"], $default_ids) ? "selected=\"1\"" : ""; + for ($i = 0; $i < $nest_level; $i++) + $line["title"] = " " . $line["title"]; - printf("", - $line["id"], htmlspecialchars($line["title"])); + $is_selected = in_array("CAT:".$line["id"], $default_ids) ? "selected=\"1\"" : ""; - if ($line["num_children"] > 0) - $this->feed_multi_select($id, $default_ids, $attributes, - $include_all_feeds, $line["id"], $nest_level+1); + $rv .= sprintf("", + $line["id"], htmlspecialchars($line["title"])); - $f_sth = $pdo->prepare("SELECT id,title FROM ttrss_feeds - WHERE cat_id = ? AND owner_uid = ? ORDER BY title"); + if ($line["num_children"] > 0) + $rv .= $this->feed_multi_select($id, $default_ids, $attributes, + $include_all_feeds, $line["id"], $nest_level+1); - $f_sth->execute([$line['id'], $_SESSION['uid']]); + $f_sth = $pdo->prepare("SELECT id,title FROM ttrss_feeds + WHERE cat_id = ? AND owner_uid = ? ORDER BY title"); - while ($fline = $f_sth->fetch()) { - $is_selected = (in_array($fline["id"], $default_ids)) ? "selected=\"1\"" : ""; + $f_sth->execute([$line['id'], $_SESSION['uid']]); - $fline["title"] = " " . $fline["title"]; + while ($fline = $f_sth->fetch()) { + $is_selected = (in_array($fline["id"], $default_ids)) ? "selected=\"1\"" : ""; - for ($i = 0; $i < $nest_level; $i++) $fline["title"] = " " . $fline["title"]; - printf("", - $fline["id"], htmlspecialchars($fline["title"])); - } - } + for ($i = 0; $i < $nest_level; $i++) + $fline["title"] = " " . $fline["title"]; - if (!$root_id) { - $is_selected = in_array("CAT:0", $default_ids) ? "selected=\"1\"" : ""; + $rv .= sprintf("", + $fline["id"], htmlspecialchars($fline["title"])); + } + } - printf("", - __("Uncategorized")); + if (!$root_id) { + $is_selected = in_array("CAT:0", $default_ids) ? "selected=\"1\"" : ""; - $f_sth = $pdo->prepare("SELECT id,title FROM ttrss_feeds - WHERE cat_id IS NULL AND owner_uid = ? ORDER BY title"); - $f_sth->execute([$_SESSION['uid']]); + $rv .= sprintf("", + __("Uncategorized")); - while ($fline = $f_sth->fetch()) { - $is_selected = in_array($fline["id"], $default_ids) ? "selected=\"1\"" : ""; + $f_sth = $pdo->prepare("SELECT id,title FROM ttrss_feeds + WHERE cat_id IS NULL AND owner_uid = ? ORDER BY title"); + $f_sth->execute([$_SESSION['uid']]); - $fline["title"] = " " . $fline["title"]; + while ($fline = $f_sth->fetch()) { + $is_selected = in_array($fline["id"], $default_ids) ? "selected=\"1\"" : ""; - for ($i = 0; $i < $nest_level; $i++) $fline["title"] = " " . $fline["title"]; - printf("", - $fline["id"], htmlspecialchars($fline["title"])); + for ($i = 0; $i < $nest_level; $i++) + $fline["title"] = " " . $fline["title"]; + + $rv .= sprintf("", + $fline["id"], htmlspecialchars($fline["title"])); + } } - } - } else { - $sth = $pdo->prepare("SELECT id,title FROM ttrss_feeds - WHERE owner_uid = ? ORDER BY title"); - $sth->execute([$_SESSION['uid']]); + } else { + $sth = $pdo->prepare("SELECT id,title FROM ttrss_feeds + WHERE owner_uid = ? ORDER BY title"); + $sth->execute([$_SESSION['uid']]); - while ($line = $sth->fetch()) { + while ($line = $sth->fetch()) { - $is_selected = (in_array($line["id"], $default_ids)) ? "selected=\"1\"" : ""; + $is_selected = (in_array($line["id"], $default_ids)) ? "selected=\"1\"" : ""; - printf("", - $line["id"], htmlspecialchars($line["title"])); + $rv .= sprintf("", + $line["id"], htmlspecialchars($line["title"])); + } } - } - if (!$root_id) { - print ""; + if (!$root_id) { + $rv .= ""; + } + + return $rv; } } -} diff --git a/classes/rpc.php b/classes/rpc.php index 643ad29d8..20a11b994 100755 --- a/classes/rpc.php +++ b/classes/rpc.php @@ -399,7 +399,7 @@ class RPC extends Handler_Protected { $params["icon_indicator_white"] = $this->image_to_base64("images/indicator_white.gif"); - $params["labels"] = Labels::get_all_labels($_SESSION["uid"]); + $params["labels"] = Labels::get_all($_SESSION["uid"]); return $params; } @@ -430,7 +430,7 @@ class RPC extends Handler_Protected { $data["max_feed_id"] = (int) $max_feed_id; $data["num_feeds"] = (int) $num_feeds; $data['cdm_expanded'] = get_pref('CDM_EXPANDED'); - $data["labels"] = Labels::get_all_labels($_SESSION["uid"]); + $data["labels"] = Labels::get_all($_SESSION["uid"]); if (LOG_DESTINATION == 'sql' && $_SESSION['access_level'] >= 10) { if (DB_TYPE == 'pgsql') { diff --git a/include/controls.php b/include/controls.php index ae5fba739..e3349d31b 100755 --- a/include/controls.php +++ b/include/controls.php @@ -60,21 +60,11 @@ return $rv; } - function select_labels(string $name, string $value, array $attributes = [], string $id = "") { - $pdo = \Db::pdo(); - - $sth = $pdo->prepare("SELECT caption FROM ttrss_labels2 - WHERE owner_uid = ? ORDER BY caption"); - $sth->execute([$_SESSION['uid']]); - - $values = []; - - while ($row = $sth->fetch()) { - array_push($values, $row["caption"]); - } + /*function select_labels(string $name, string $value, array $attributes = [], string $id = "") { + $values = \Labels::get_as_hash($_SESSION["uid"]); return select_tag($name, $value, $values, $attributes, $id); - } + }*/ function select_hash(string $name, $value, array $values, array $attributes = [], string $id = "") { $attributes_str = attributes_to_string($attributes); diff --git a/js/CommonFilters.js b/js/CommonFilters.js index bd6808f59..75e1fa8af 100644 --- a/js/CommonFilters.js +++ b/js/CommonFilters.js @@ -7,26 +7,17 @@ /* exported Filters */ const Filters = { - edit: function(id) { // if no id, new filter dialog - let query; - - if (!App.isPrefs()) { - query = { - op: "pref-filters", method: "edit", - feed: Feeds.getActive(), is_cat: Feeds.activeIsCat() - }; - } else { - query = {op: "pref-filters", method: "edit", id: id}; - } + edit: function(filter_id = null) { // if no id, new filter dialog const dialog = new fox.SingleUseDialog({ id: "filterEditDlg", - title: id ? __("Edit Filter") : __("Create Filter"), + title: filter_id ? __("Edit Filter") : __("Create Filter"), ACTION_TAG: 4, ACTION_SCORE: 6, ACTION_LABEL: 7, ACTION_PLUGIN: 9, PARAM_ACTIONS: [4, 6, 7, 9], + filter_info: {}, test: function() { const test_dialog = new fox.SingleUseDialog({ title: "Test Filter", @@ -116,16 +107,17 @@ const Filters = { test_dialog.show(); }, - createNewRuleElement: function(parentNode, replaceNode) { + insertRule: function(parentNode, replaceNode) { const rule = dojo.formToJson("filter_new_rule_form"); xhr.post("backend.php", {op: "pref-filters", method: "printrulename", rule: rule}, (reply) => { try { const li = document.createElement('li'); + li.addClassName("rule"); - li.innerHTML = ` - ${reply} - ${App.FormFields.hidden_tag("rule[]", rule)}`; + li.innerHTML = `${App.FormFields.checkbox_tag("", false, {onclick: 'Lists.onRowChecked(this)'})} + ${reply} + ${App.FormFields.hidden_tag("rule[]", rule)}`; dojo.parser.parse(li); @@ -139,7 +131,7 @@ const Filters = { } }); }, - createNewActionElement: function(parentNode, replaceNode) { + insertAction: function(parentNode, replaceNode) { const form = document.forms["filter_new_action_form"]; if (form.action_id.value == 7) { @@ -153,10 +145,11 @@ const Filters = { xhr.post("backend.php", { op: "pref-filters", method: "printactionname", action: action }, (reply) => { try { const li = document.createElement('li'); + li.addClassName("action"); - li.innerHTML = ` - ${reply} - ${App.FormFields.hidden_tag("action[]", action)}`; + li.innerHTML = `${App.FormFields.checkbox_tag("", false, {onclick: 'Lists.onRowChecked(this)'})} + ${reply} + ${App.FormFields.hidden_tag("action[]", action)}`; dojo.parser.parse(li); @@ -171,30 +164,84 @@ const Filters = { } }); }, - editRule: function(replaceNode, ruleStr) { + editRule: function(replaceNode, ruleStr = null) { const edit_rule_dialog = new fox.SingleUseDialog({ id: "filterNewRuleDlg", title: ruleStr ? __("Edit rule") : __("Add rule"), execute: function () { if (this.validate()) { - dialog.createNewRuleElement(App.byId("filterDlg_Matches"), replaceNode); + dialog.insertRule(App.byId("filterDlg_Matches"), replaceNode); this.hide(); } }, content: __('Loading, please wait...'), }); - const tmph = dojo.connect(edit_rule_dialog, "onShow", null, function (/* e */) { + const tmph = dojo.connect(edit_rule_dialog, "onShow", null, function () { dojo.disconnect(tmph); - xhr.post("backend.php", {op: 'pref-filters', method: 'newrule', rule: ruleStr}, (reply) => { - edit_rule_dialog.attr('content', reply); + let rule; + + if (ruleStr) { + rule = JSON.parse(ruleStr); + } else { + rule = { + reg_exp: "", + filter_type: 1, + feed_id: ["0"], + inverse: false, + }; + } + + console.log(rule, dialog.filter_info); + + xhr.json("backend.php", {op: "pref-filters", method: "editrule", ids: rule.feed_id.join(",")}, function (editrule) { + edit_rule_dialog.attr('content', + ` +
    + +
    + + +
    + +
    + +
    +
    + + ${App.FormFields.select_hash("filter_type", rule.filter_type, dialog.filter_info.filter_types)} + +
    +
    + + ${editrule.multiselect} + +
    +
    + +
    + ${App.FormFields.button_tag(App.FormFields.icon("help") + " " + __("More info"), "", {class: 'pull-left alt-info', + onclick: "window.open('https://tt-rss.org/wiki/ContentFilters')"})} + ${App.FormFields.submit_tag(__("Save rule"), {onclick: "App.dialogOf(this).execute()"})} + ${App.FormFields.cancel_dialog_tag(__("Cancel"))} +
    + +
    + `); }); + }); edit_rule_dialog.show(); }, - editAction: function(replaceNode, actionStr) { + /*editAction: function(replaceNode, actionStr) { const edit_action_dialog = new fox.SingleUseDialog({ title: actionStr ? __("Edit action") : __("Add action"), hideOrShowActionParam: function(sender) { @@ -221,7 +268,7 @@ const Filters = { } }); - const tmph = dojo.connect(edit_action_dialog, "onShow", null, function (/* e */) { + const tmph = dojo.connect(edit_action_dialog, "onShow", null, function () { dojo.disconnect(tmph); xhr.post("backend.php", {op: 'pref-filters', method: 'newaction', action: actionStr}, (reply) => { @@ -234,22 +281,65 @@ const Filters = { }); edit_action_dialog.show(); - }, + }, */ + /*editAction: function(replaceNode, actionStr) { + const edit_action_dialog = new fox.SingleUseDialog({ + title: actionStr ? __("Edit action") : __("Add action"), + hideOrShowActionParam: function(sender) { + const action = parseInt(sender.value); + + dijit.byId("filterDlg_actionParam").domNode.hide(); + dijit.byId("filterDlg_actionParamLabel").domNode.hide(); + dijit.byId("filterDlg_actionParamPlugin").domNode.hide(); + + // if selected action supports parameters, enable params field + if (action == dialog.ACTION_LABEL) { + dijit.byId("filterDlg_actionParamLabel").domNode.show(); + } else if (action == dialog.ACTION_PLUGIN) { + dijit.byId("filterDlg_actionParamPlugin").domNode.show(); + } else if (dialog.PARAM_ACTIONS.indexOf(action) != -1) { + dijit.byId("filterDlg_actionParam").domNode.show(); + } + }, + execute: function () { + if (this.validate()) { + dialog.createNewActionElement(App.byId("filterDlg_Actions"), replaceNode); + this.hide(); + } + } + }); + + const tmph = dojo.connect(edit_action_dialog, "onShow", null, function () { + dojo.disconnect(tmph); + + xhr.post("backend.php", {op: 'pref-filters', method: 'newaction', action: actionStr}, (reply) => { + edit_action_dialog.attr('content', reply); + + setTimeout(() => { + edit_action_dialog.hideOrShowActionParam(dijit.byId("filterDlg_actionSelect").attr('value')); + }, 250); + }); + }); + + edit_action_dialog.show(); + },*/ selectRules: function (select) { Lists.select("filterDlg_Matches", select); }, selectActions: function (select) { Lists.select("filterDlg_Actions", select); }, - onRuleClicked: function (e) { - const li = e.closest('li'); - const rule = li.querySelector('input[name="rule[]"]').value + onRuleClicked: function (elem) { + + const li = elem.closest('li'); + const rule = li.querySelector('input[name="rule[]"]').value; this.editRule(li, rule); }, - onActionClicked: function (e) { - const li = e.closest('li'); - const action = li.querySelector('input[name="action[]"]').value + onActionClicked: function (elem) { + + const li = elem.closest('li'); + const action = li.querySelector('input[name="action[]"]').value; this.editAction(li, action); }, @@ -302,13 +392,141 @@ const Filters = { content: __("Loading, please wait...") }); - - const tmph = dojo.connect(dialog, 'onShow', function () { dojo.disconnect(tmph); - xhr.post("backend.php", query, function (reply) { - dialog.attr('content', reply); + const query = {op: "pref-filters", method: "edit", id: filter_id}; + + /*if (!App.isPrefs()) { + query = { + op: "pref-filters", method: "edit", + feed: Feeds.getActive(), is_cat: Feeds.activeIsCat() + }; + } else { + query = {op: "pref-filters", method: "edit", id: id}; + }*/ + + xhr.json("backend.php", query, function (filter) { + + dialog.filter_info = filter; + + const options = { + enabled: [ filter.enabled, __('Enabled') ], + match_any_rule: [ filter.match_any_rule, __('Match any rule') ], + inverse: [ filter.inverse, __('Inverse matching') ], + }; + + dialog.attr('content', + ` +
    + + ${App.FormFields.hidden_tag("op", "pref-filters")} + ${App.FormFields.hidden_tag("id", filter_id)} + ${App.FormFields.hidden_tag("method", filter_id ? "editSave" : "add")} + ${App.FormFields.hidden_tag("csrf_token", App.getInitParam('csrf_token'))} + +
    + +
    + +
    +
    +
    +
    +
    + ${__("Select")} +
    + +
    ${__("All")}
    +
    ${__("None")}
    +
    +
    + + +
    +
    +
      + ${filter.rules.map((rule) => ` +
    • + ${App.FormFields.checkbox_tag("", false, "", {onclick: 'Lists.onRowChecked(this)'})} + ${rule.name} + ${App.FormFields.hidden_tag("rule[]", JSON.stringify(rule))} +
    • + `).join("")} +
    +
    +
    +
    +
    +
    +
    +
    + ${__("Select")} +
    +
    ${__("All")}
    +
    ${__("None")}
    +
    +
    + + +
    +
    +
      + ${filter.actions.map((action) => ` +
    • + ${App.FormFields.checkbox_tag("", false, "", {onclick: 'Lists.onRowChecked(this)'})} + ${App.escapeHtml(action.name)} + ${App.FormFields.hidden_tag("action[]", JSON.stringify(action))} +
    • + `).join("")} +
    +
    +
    +
    +
    + +
    + +
    + ${Object.keys(options).map((name) => + ` +
    + +
    + `).join("")} +
    + +
    + ${filter_id ? + ` + ${App.FormFields.button_tag(__("Remove"), "", {class: "pull-left alt-danger", onclick: "App.dialogOf(this).removeFilter()"})} + ${App.FormFields.button_tag(__("Test"), "", {class: "alt-info", onclick: "App.dialogOf(this).test()"})} + ${App.FormFields.submit_tag(__("Save"), {onclick: "App.dialogOf(this).execute()"})} + ${App.FormFields.cancel_dialog_tag(__("Cancel"))} + ` : ` + ${App.FormFields.button_tag(__("Test"), "", {class: "alt-info", onclick: "App.dialogOf(this).test()"})} + ${App.FormFields.submit_tag(__("Create"), {onclick: "App.dialogOf(this).execute()"})} + ${App.FormFields.cancel_dialog_tag(__("Cancel"))} + `} +
    +
    + `); if (!App.isPrefs()) { const selectedText = App.getSelectedText(); diff --git a/plugins/auto_assign_labels/init.php b/plugins/auto_assign_labels/init.php index 3fa4ad8c0..341895cef 100755 --- a/plugins/auto_assign_labels/init.php +++ b/plugins/auto_assign_labels/init.php @@ -19,6 +19,7 @@ class Auto_Assign_Labels extends Plugin { function get_all_labels_filter_format($owner_uid) { $rv = array(); + // TODO: use Labels::get_all() $sth = $this->pdo->prepare("SELECT id, fg_color, bg_color, caption FROM ttrss_labels2 WHERE owner_uid = ?"); $sth->execute([$owner_uid]); diff --git a/themes/compact.css b/themes/compact.css index 92e9928c8..16bdcf1f0 100644 --- a/themes/compact.css +++ b/themes/compact.css @@ -822,16 +822,18 @@ body.ttrss_main #headlines-spacer a:hover { } body.ttrss_main ul#filterDlg_Matches, body.ttrss_main ul#filterDlg_Actions { - max-height: 100px; - overflow: auto; list-style-type: none; - border-style: solid; - border-color: #ddd; - border-width: 1px 1px 1px 1px; - background-color: white; - margin: 0px 0px 5px 0px; - padding: 4px; - min-height: 16px; + margin: 0; + padding: 0; + /*max-height : 100px; + overflow : auto; + border-style : solid; + border-color : @border-default; + border-width : 1px 1px 1px 1px; + background-color : @default-bg; + margin : 0px 0px 5px 0px; + padding : 4px; + min-height : 16px;*/ } body.ttrss_main ul#filterDlg_Matches li, body.ttrss_main ul#filterDlg_Actions li { diff --git a/themes/compact_night.css b/themes/compact_night.css index 29eff9cb9..37adf3fda 100644 --- a/themes/compact_night.css +++ b/themes/compact_night.css @@ -822,16 +822,18 @@ body.ttrss_main #headlines-spacer a:hover { } body.ttrss_main ul#filterDlg_Matches, body.ttrss_main ul#filterDlg_Actions { - max-height: 100px; - overflow: auto; list-style-type: none; - border-style: solid; - border-color: #222; - border-width: 1px 1px 1px 1px; - background-color: #333; - margin: 0px 0px 5px 0px; - padding: 4px; - min-height: 16px; + margin: 0; + padding: 0; + /*max-height : 100px; + overflow : auto; + border-style : solid; + border-color : @border-default; + border-width : 1px 1px 1px 1px; + background-color : @default-bg; + margin : 0px 0px 5px 0px; + padding : 4px; + min-height : 16px;*/ } body.ttrss_main ul#filterDlg_Matches li, body.ttrss_main ul#filterDlg_Actions li { diff --git a/themes/light.css b/themes/light.css index 583f9cabb..0f2ffc1b6 100644 --- a/themes/light.css +++ b/themes/light.css @@ -822,16 +822,18 @@ body.ttrss_main #headlines-spacer a:hover { } body.ttrss_main ul#filterDlg_Matches, body.ttrss_main ul#filterDlg_Actions { - max-height: 100px; - overflow: auto; list-style-type: none; - border-style: solid; - border-color: #ddd; - border-width: 1px 1px 1px 1px; - background-color: white; - margin: 0px 0px 5px 0px; - padding: 4px; - min-height: 16px; + margin: 0; + padding: 0; + /*max-height : 100px; + overflow : auto; + border-style : solid; + border-color : @border-default; + border-width : 1px 1px 1px 1px; + background-color : @default-bg; + margin : 0px 0px 5px 0px; + padding : 4px; + min-height : 16px;*/ } body.ttrss_main ul#filterDlg_Matches li, body.ttrss_main ul#filterDlg_Actions li { diff --git a/themes/light/tt-rss.less b/themes/light/tt-rss.less index b1895f318..2794d8177 100644 --- a/themes/light/tt-rss.less +++ b/themes/light/tt-rss.less @@ -955,16 +955,18 @@ body.ttrss_main { } ul#filterDlg_Matches, ul#filterDlg_Actions { - max-height : 100px; - overflow : auto; list-style-type : none; + margin : 0; + padding: 0; + /*max-height : 100px; + overflow : auto; border-style : solid; border-color : @border-default; border-width : 1px 1px 1px 1px; background-color : @default-bg; margin : 0px 0px 5px 0px; padding : 4px; - min-height : 16px; + min-height : 16px;*/ } ul#filterDlg_Matches li, ul#filterDlg_Actions li { diff --git a/themes/night.css b/themes/night.css index e428d8aa7..e012c92b2 100644 --- a/themes/night.css +++ b/themes/night.css @@ -823,16 +823,18 @@ body.ttrss_main #headlines-spacer a:hover { } body.ttrss_main ul#filterDlg_Matches, body.ttrss_main ul#filterDlg_Actions { - max-height: 100px; - overflow: auto; list-style-type: none; - border-style: solid; - border-color: #222; - border-width: 1px 1px 1px 1px; - background-color: #333; - margin: 0px 0px 5px 0px; - padding: 4px; - min-height: 16px; + margin: 0; + padding: 0; + /*max-height : 100px; + overflow : auto; + border-style : solid; + border-color : @border-default; + border-width : 1px 1px 1px 1px; + background-color : @default-bg; + margin : 0px 0px 5px 0px; + padding : 4px; + min-height : 16px;*/ } body.ttrss_main ul#filterDlg_Matches li, body.ttrss_main ul#filterDlg_Actions li { diff --git a/themes/night_blue.css b/themes/night_blue.css index c9ccaf737..b49a496e3 100644 --- a/themes/night_blue.css +++ b/themes/night_blue.css @@ -823,16 +823,18 @@ body.ttrss_main #headlines-spacer a:hover { } body.ttrss_main ul#filterDlg_Matches, body.ttrss_main ul#filterDlg_Actions { - max-height: 100px; - overflow: auto; list-style-type: none; - border-style: solid; - border-color: #222; - border-width: 1px 1px 1px 1px; - background-color: #333; - margin: 0px 0px 5px 0px; - padding: 4px; - min-height: 16px; + margin: 0; + padding: 0; + /*max-height : 100px; + overflow : auto; + border-style : solid; + border-color : @border-default; + border-width : 1px 1px 1px 1px; + background-color : @default-bg; + margin : 0px 0px 5px 0px; + padding : 4px; + min-height : 16px;*/ } body.ttrss_main ul#filterDlg_Matches li, body.ttrss_main ul#filterDlg_Actions li { -- cgit v1.2.3-54-g00ecf From f6bfb89b2912933f638416135dff7bb8cb28890d Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Sun, 21 Feb 2021 23:18:32 +0300 Subject: pref-prefs: switch to new control shorthand in a few places --- classes/pref/prefs.php | 51 +++++++++++++++++++++----------------------------- include/controls.php | 17 +++++++++++++++++ 2 files changed, 38 insertions(+), 30 deletions(-) (limited to 'include') diff --git a/classes/pref/prefs.php b/classes/pref/prefs.php index 2ea2e9f01..a26281fee 100644 --- a/classes/pref/prefs.php +++ b/classes/pref/prefs.php @@ -712,59 +712,50 @@ class Pref_Prefs extends Handler_Protected { array_push($listed_boolean_prefs, $pref_name); - $is_checked = ($value == "true") ? "checked=\"checked\"" : ""; - if ($pref_name == "PURGE_UNREAD_ARTICLES" && FORCE_ARTICLE_PURGE != 0) { - $is_disabled = "disabled=\"1\""; - $is_checked = "checked=\"checked\""; + $is_disabled = true; + $is_checked = true; } else { - $is_disabled = ""; + $is_disabled = false; + $is_checked = ($value == "true"); } - print ""; + print \Controls\checkbox_tag($pref_name, $is_checked, "true", + ["disabled" => $is_disabled], "CB_$pref_name"); } else if (in_array($pref_name, ['FRESH_ARTICLE_MAX_AGE', 'PURGE_OLD_DAYS', 'LONG_DATE_FORMAT', 'SHORT_DATE_FORMAT'])) { - $regexp = ($type_name == 'integer') ? 'regexp="^\d*$"' : ''; - if ($pref_name == "PURGE_OLD_DAYS" && FORCE_ARTICLE_PURGE != 0) { - $is_disabled = "disabled='1'"; + $attributes = ["disabled" => true, "required" => true]; $value = FORCE_ARTICLE_PURGE; } else { - $is_disabled = ""; + $attributes = ["required" => true]; } if ($type_name == 'integer') - print ""; + print \Controls\number_spinner_tag($pref_name, $value, $attributes); else - print ""; + print \Controls\input_tag($pref_name, $value, "text", $attributes); } else if ($pref_name == "SSL_CERT_SERIAL") { - print ""; + print \Controls\input_tag($pref_name, $value, "text", ["readonly" => true], "SSL_CERT_SERIAL"); $cert_serial = htmlspecialchars(get_ssl_certificate_id()); - $has_serial = ($cert_serial) ? "false" : "true"; + $has_serial = ($cert_serial) ? true : false; - print ""; + print \Controls\button_tag(__('Register'), "", [ + "disabled" => !$has_serial, + "onclick" => "dijit.byId('SSL_CERT_SERIAL').attr('value', '$cert_serial')"]); - print ""; + print \Controls\button_tag(__('Clear'), "", [ + "class" => "alt-danger", + "onclick" => "dijit.byId('SSL_CERT_SERIAL').attr('value', '')"]); - print ""; + print \Controls\button_tag(\Controls\icon("help") . " " . __("More info..."), "", [ + "class" => "alt-info", + "onclick" => "window.open('https://tt-rss.org/wiki/SSL%20Certificate%20Authentication')"]); } else if ($pref_name == 'DIGEST_PREFERRED_TIME') { print " $v) { + + // special handling for "disabled" + if ($k === "disabled" && !sql_bool_to_bool($v)) + continue; + $rv .= "$k=\"" . htmlspecialchars($v) . "\""; } @@ -30,6 +35,18 @@ return ""; } + function input_tag(string $name, string $value, string $type = "text", array $attributes = [], string $id = "") { + $attributes_str = attributes_to_string($attributes); + $dojo_type = strpos($attributes_str, "dojoType") === false ? "dojoType='dijit.form.TextBox'" : ""; + + return ""; + } + + function number_spinner_tag(string $name, string $value, array $attributes = [], string $id = "") { + return input_tag($name, $value, "text", array_merge(["dojoType" => "dijit.form.NumberSpinner"], $attributes), $id); + } + function submit_tag(string $value, array $attributes = []) { return button_tag($value, "submit", array_merge(["class" => "alt-primary"], $attributes)); } -- cgit v1.2.3-54-g00ecf From 02a9485966dbbac1ed52ecbfb29fcc15125cba43 Mon Sep 17 00:00:00 2001 From: wn_ Date: Sun, 21 Feb 2021 23:30:31 +0000 Subject: Try to limit max favicon size, don't store current/old in a var. --- classes/rssutils.php | 12 ++++++++---- include/functions.php | 2 ++ utils/phpstan_tunables.php | 2 ++ 3 files changed, 12 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/classes/rssutils.php b/classes/rssutils.php index 13f63bc7a..6785ab3f5 100755 --- a/classes/rssutils.php +++ b/classes/rssutils.php @@ -1652,15 +1652,19 @@ class RSSUtils { } // Limiting to "image" type misses those served with text/plain - $contents = UrlHelper::fetch(['url' => $favicon_url]); // , "image"); + $contents = UrlHelper::fetch([ + 'url' => $favicon_url, + 'max_size' => MAX_FAVICON_FILE_SIZE, + //'type' => 'image', + ]); if (!$contents) { Debug::log("fetching favicon $favicon_url failed", Debug::$LOG_VERBOSE); return false; } - $original_contents = file_exists($icon_file) ? file_get_contents($icon_file) : null; - if ($original_contents) { - if (strcmp($contents, $original_contents) === 0) { + $original_contents_md5 = file_exists($icon_file) ? md5_file($icon_file) : null; + if ($original_contents_md5) { + if (md5($contents) == $original_contents_md5) { Debug::log("favicon content has not changed", Debug::$LOG_VERBOSE); return $icon_file; } diff --git a/include/functions.php b/include/functions.php index 174ef39f0..df8730aca 100644 --- a/include/functions.php +++ b/include/functions.php @@ -68,6 +68,8 @@ // do not cache files larger than that (bytes) define_default('MAX_DOWNLOAD_FILE_SIZE', 16*1024*1024); // do not download general files larger than that (bytes) + define_default('MAX_FAVICON_FILE_SIZE', 1*1024*1024); + // do not download favicon files larger than that (bytes) define_default('CACHE_MAX_DAYS', 7); // max age in days for various automatically cached (temporary) files define_default('MAX_CONDITIONAL_INTERVAL', 3600*12); diff --git a/utils/phpstan_tunables.php b/utils/phpstan_tunables.php index 7d5d8f03a..e192bcdba 100644 --- a/utils/phpstan_tunables.php +++ b/utils/phpstan_tunables.php @@ -27,6 +27,8 @@ // do not cache files larger than that (bytes) define('MAX_DOWNLOAD_FILE_SIZE', 16*1024*1024); // do not download general files larger than that (bytes) + define('MAX_FAVICON_FILE_SIZE', 1*1024*1024); + // do not download favicon files larger than that (bytes) define('CACHE_MAX_DAYS', 7); // max age in days for various automatically cached (temporary) files define('MAX_CONDITIONAL_INTERVAL', 3600*12); -- cgit v1.2.3-54-g00ecf From be4e7b13403666fc477d4b563ea8c075d0fd2022 Mon Sep 17 00:00:00 2001 From: Andrew Dolgov Date: Mon, 22 Feb 2021 14:41:09 +0300 Subject: fix several issues reported by phpstan --- api/index.php | 4 ++-- classes/api.php | 2 +- classes/digest.php | 9 --------- classes/feeds.php | 6 ------ classes/handler/public.php | 10 +++++----- classes/pluginhost.php | 3 ++- classes/pref/prefs.php | 7 ------- include/functions.php | 8 ++++---- include/sanity_check.php | 6 +++--- include/sanity_config.php | 4 ++-- include/sessions.php | 8 ++++---- phpstan.neon | 4 +++- plugins/af_fsckportal/init.php | 5 +---- plugins/af_redditimgur/init.php | 5 ++++- update.php | 4 ++-- utils/regen_config_checks.sh | 4 ++-- 16 files changed, 35 insertions(+), 54 deletions(-) (limited to 'include') diff --git a/api/index.php b/api/index.php index eb79422f9..1b713d561 100644 --- a/api/index.php +++ b/api/index.php @@ -18,8 +18,8 @@ require_once "functions.php"; require_once "sessions.php"; - ini_set('session.use_cookies', 0); - ini_set("session.gc_maxlifetime", 86400); + ini_set('session.use_cookies', "0"); + ini_set("session.gc_maxlifetime", "86400"); ob_start(); diff --git a/classes/api.php b/classes/api.php index 03eea1927..5677cb908 100755 --- a/classes/api.php +++ b/classes/api.php @@ -292,7 +292,7 @@ class API extends Handler { $sanitize_content = !isset($_REQUEST["sanitize"]) || self::_param_to_bool($_REQUEST["sanitize"]); - if ($article_ids) { + if (count($article_ids) > 0) { $article_qmarks = arr_qmarks($article_ids); diff --git a/classes/digest.php b/classes/digest.php index 77eb92c54..e0c23d705 100644 --- a/classes/digest.php +++ b/classes/digest.php @@ -1,12 +1,6 @@ addBlock('enclosure'); } } else { - $tpl->setVariable('ARTICLE_ENCLOSURE_URL', null, true); - $tpl->setVariable('ARTICLE_ENCLOSURE_TYPE', null, true); - $tpl->setVariable('ARTICLE_ENCLOSURE_LENGTH', null, true); + $tpl->setVariable('ARTICLE_ENCLOSURE_URL', "", true); + $tpl->setVariable('ARTICLE_ENCLOSURE_TYPE', "", true); + $tpl->setVariable('ARTICLE_ENCLOSURE_LENGTH', "", true); } list ($og_image, $og_stream) = Article::_get_image($enclosures, $line['content'], $feed_site_url); @@ -207,8 +207,8 @@ class Handler_Public extends Handler { $article['content'] = Sanitizer::sanitize($line["content"], false, $owner_uid, $feed_site_url, false, $line["id"]); $article['updated'] = date('c', strtotime($line["updated"])); - if ($line['note']) $article['note'] = $line['note']; - if ($article['author']) $article['author'] = $line['author']; + if (!empty($line['note'])) $article['note'] = $line['note']; + if (!empty($line['author'])) $article['author'] = $line['author']; if (count($line["tags"]) > 0) { $article['tags'] = array(); diff --git a/classes/pluginhost.php b/classes/pluginhost.php index a05938111..5121c8491 100755 --- a/classes/pluginhost.php +++ b/classes/pluginhost.php @@ -108,8 +108,9 @@ class PluginHost { return false; } + // needed for compatibility with API 2 (?) function get_dbh() { - return Db::get(); + return false; } function get_pdo(): PDO { diff --git a/classes/pref/prefs.php b/classes/pref/prefs.php index a26281fee..adb249dac 100644 --- a/classes/pref/prefs.php +++ b/classes/pref/prefs.php @@ -311,13 +311,6 @@ class Pref_Prefs extends Handler_Protected {
    - -
    - - -
    - -