diff options
author | Chris Buechler <cmb@pfsense.org> | 2011-07-25 05:23:31 -0400 |
---|---|---|
committer | Chris Buechler <cmb@pfsense.org> | 2011-07-25 05:23:31 -0400 |
commit | 581e4f7afd2f6297f81fa08ca1698210597d1e64 (patch) | |
tree | 5013760332cd0011f945a7d76e2f2d052d88992b /usr/local | |
parent | cb2518bf5a57d6459753e205e91e7f157ca60cd3 (diff) | |
parent | 9ee5069a078d050313c290d5c24eeff1ad07154e (diff) | |
download | pfsense-581e4f7afd2f6297f81fa08ca1698210597d1e64.zip pfsense-581e4f7afd2f6297f81fa08ca1698210597d1e64.tar.gz |
Merge branch 'master' of github.com:bsdperimeter/pfsense
Diffstat (limited to 'usr/local')
41 files changed, 6392 insertions, 1983 deletions
diff --git a/usr/local/captiveportal/index.php b/usr/local/captiveportal/index.php index 0170a7a..1bd9366 100755 --- a/usr/local/captiveportal/index.php +++ b/usr/local/captiveportal/index.php @@ -77,12 +77,12 @@ if ($orig_host != $ourhostname) { exit; } -if (preg_match("/redirurl=(.*)/", $orig_request, $matches)) - $redirurl = urldecode($matches[1]); -if ($_POST['redirurl']) - $redirurl = $_POST['redirurl']; if (!empty($config['captiveportal']['redirurl'])) $redirurl = $config['captiveportal']['redirurl']; +else if (preg_match("/redirurl=(.*)/", $orig_request, $matches)) + $redirurl = urldecode($matches[1]); +else if ($_REQUEST['redirurl']) + $redirurl = $_REQUEST['redirurl']; $macfilter = !isset($config['captiveportal']['nomacfilter']); $passthrumac = isset($config['captiveportal']['passthrumacadd']); @@ -193,7 +193,7 @@ EOD; captiveportal_logportalauth($_POST['auth_user'],$clientmac,$clientip,"FAILURE"); portal_reply_page($redirurl, "error", $errormsg); } -} else if ($_POST['accept'] && $clientip) { +} else if ($_POST['accept'] && $clientip && $config['captiveportal']['auth_method'] == "none") { captiveportal_logportalauth("unauthenticated",$clientmac,$clientip,"ACCEPT"); portal_allow($clientip, $clientmac, "unauthenticated"); } else { diff --git a/usr/local/www/csrf/csrf-magic.js b/usr/local/www/csrf/csrf-magic.js index 820f6e8..6992402 100644 --- a/usr/local/www/csrf/csrf-magic.js +++ b/usr/local/www/csrf/csrf-magic.js @@ -1,177 +1,177 @@ -/**
- * @file
- *
- * Rewrites XMLHttpRequest to automatically send CSRF token with it. In theory
- * plays nice with other JavaScript libraries, needs testing though.
- */
-
-// Here are the basic overloaded method definitions
-// The wrapper must be set BEFORE onreadystatechange is written to, since
-// a bug in ActiveXObject prevents us from properly testing for it.
-CsrfMagic = function(real) {
- // try to make it ourselves, if you didn't pass it
- if (!real) try { real = new XMLHttpRequest; } catch (e) {;}
- if (!real) try { real = new ActiveXObject('Msxml2.XMLHTTP'); } catch (e) {;}
- if (!real) try { real = new ActiveXObject('Microsoft.XMLHTTP'); } catch (e) {;}
- if (!real) try { real = new ActiveXObject('Msxml2.XMLHTTP.4.0'); } catch (e) {;}
- this.csrf = real;
- // properties
- var csrfMagic = this;
- real.onreadystatechange = function() {
- csrfMagic._updateProps();
- return csrfMagic.onreadystatechange ? csrfMagic.onreadystatechange() : null;
- };
- csrfMagic._updateProps();
-}
-
-CsrfMagic.prototype = {
-
- open: function(method, url, async, username, password) {
- if (method == 'POST') this.csrf_isPost = true;
- // deal with Opera bug, thanks jQuery
- if (username) return this.csrf_open(method, url, async, username, password);
- else return this.csrf_open(method, url, async);
- },
- csrf_open: function(method, url, async, username, password) {
- if (username) return this.csrf.open(method, url, async, username, password);
- else return this.csrf.open(method, url, async);
- },
-
- send: function(data) {
- if (!this.csrf_isPost) return this.csrf_send(data);
- prepend = csrfMagicName + '=' + csrfMagicToken + '&';
- if (this.csrf_purportedLength === undefined) {
- this.csrf_setRequestHeader("Content-length", this.csrf_purportedLength + prepend.length);
- delete this.csrf_purportedLength;
- }
- delete this.csrf_isPost;
- return this.csrf_send(prepend + data);
- },
- csrf_send: function(data) {
- return this.csrf.send(data);
- },
-
- setRequestHeader: function(header, value) {
- // We have to auto-set this at the end, since we don't know how long the
- // nonce is when added to the data.
- if (this.csrf_isPost && header == "Content-length") {
- this.csrf_purportedLength = value;
- return;
- }
- return this.csrf_setRequestHeader(header, value);
- },
- csrf_setRequestHeader: function(header, value) {
- return this.csrf.setRequestHeader(header, value);
- },
-
- abort: function() {
- return this.csrf.abort();
- },
- getAllResponseHeaders: function() {
- return this.csrf.getAllResponseHeaders();
- },
- getResponseHeader: function(header) {
- return this.csrf.getResponseHeader(header);
- } // ,
-}
-
-// proprietary
-CsrfMagic.prototype._updateProps = function() {
- this.readyState = this.csrf.readyState;
- if (this.readyState == 4) {
- this.responseText = this.csrf.responseText;
- this.responseXML = this.csrf.responseXML;
- this.status = this.csrf.status;
- this.statusText = this.csrf.statusText;
- }
-}
-CsrfMagic.process = function(base) {
- var prepend = csrfMagicName + '=' + csrfMagicToken;
- if (base) return prepend + '&' + base;
- return prepend;
-}
-// callback function for when everything on the page has loaded
-CsrfMagic.end = function() {
- // This rewrites forms AGAIN, so in case buffering didn't work this
- // certainly will.
- forms = document.getElementsByTagName('form');
- for (var i = 0; i < forms.length; i++) {
- form = forms[i];
- if (form.method.toUpperCase() !== 'POST') continue;
- if (form.elements[csrfMagicName]) continue;
- var input = document.createElement('input');
- input.setAttribute('name', csrfMagicName);
- input.setAttribute('value', csrfMagicToken);
- input.setAttribute('type', 'hidden');
- form.appendChild(input);
- }
-}
-
-// Sets things up for Mozilla/Opera/nice browsers
-if (window.XMLHttpRequest && window.XMLHttpRequest.prototype) {
- var x = XMLHttpRequest.prototype;
- var c = CsrfMagic.prototype;
-
- // Save the original functions
- x.csrf_open = x.open;
- x.csrf_send = x.send;
- x.csrf_setRequestHeader = x.setRequestHeader;
-
- // Notice that CsrfMagic is itself an instantiatable object, but only
- // open, send and setRequestHeader are necessary as decorators.
- x.open = c.open;
- x.send = c.send;
- x.setRequestHeader = c.setRequestHeader;
-} else {
- // The only way we can do this is by modifying a library you have been
- // using. We support YUI, script.aculo.us, prototype, MooTools,
- // jQuery, Ext and Dojo.
- if (window.jQuery) {
- // jQuery didn't implement a new XMLHttpRequest function, so we have
- // to do this the hard way.
- jQuery.csrf_ajax = jQuery.ajax;
- jQuery.ajax = function( s ) {
- if (s.type && s.type.toUpperCase() == 'POST') {
- s = jQuery.extend(true, s, jQuery.extend(true, {}, jQuery.ajaxSettings, s));
- if ( s.data && s.processData && typeof s.data != "string" ) {
- s.data = jQuery.param(s.data);
- }
- s.data = CsrfMagic.process(s.data);
- }
- return jQuery.csrf_ajax( s );
- }
- } else if (window.Prototype) {
- // This works for script.aculo.us too
- Ajax.csrf_getTransport = Ajax.getTransport;
- Ajax.getTransport = function() {
- return new CsrfMagic(Ajax.csrf_getTransport());
- }
- } else if (window.MooTools) {
- Browser.csrf_Request = Browser.Request;
- Browser.Request = function () {
- return new CsrfMagic(Browser.csrf_Request());
- }
- } else if (window.YAHOO) {
- YAHOO.util.Connect.csrf_createXhrObject = YAHOO.util.Connect.createXhrObject;
- YAHOO.util.Connect.createXhrObject = function (transaction) {
- obj = YAHOO.util.Connect.csrf_createXhrObject(transaction);
- obj.conn = new CsrfMagic(obj.conn);
- return obj;
- }
- } else if (window.Ext) {
- // Ext can use other js libraries as loaders, so it has to come last
- // Ext's implementation is pretty identical to Yahoo's, but we duplicate
- // it for comprehensiveness's sake.
- Ext.lib.Ajax.csrf_createXhrObject = Ext.lib.Ajax.createXhrObject;
- Ext.lib.Ajax.createXhrObject = function (transaction) {
- obj = Ext.lib.Ajax.csrf_createXhrObject(transaction);
- obj.conn = new CsrfMagic(obj.conn);
- return obj;
- }
- } else if (window.dojo) {
- dojo.csrf__xhrObj = dojo._xhrObj;
- dojo._xhrObj = function () {
- return new CsrfMagic(dojo.csrf__xhrObj());
- }
- }
-}
+/** + * @file + * + * Rewrites XMLHttpRequest to automatically send CSRF token with it. In theory + * plays nice with other JavaScript libraries, needs testing though. + */ + +// Here are the basic overloaded method definitions +// The wrapper must be set BEFORE onreadystatechange is written to, since +// a bug in ActiveXObject prevents us from properly testing for it. +CsrfMagic = function(real) { + // try to make it ourselves, if you didn't pass it + if (!real) try { real = new XMLHttpRequest; } catch (e) {;} + if (!real) try { real = new ActiveXObject('Msxml2.XMLHTTP'); } catch (e) {;} + if (!real) try { real = new ActiveXObject('Microsoft.XMLHTTP'); } catch (e) {;} + if (!real) try { real = new ActiveXObject('Msxml2.XMLHTTP.4.0'); } catch (e) {;} + this.csrf = real; + // properties + var csrfMagic = this; + real.onreadystatechange = function() { + csrfMagic._updateProps(); + return csrfMagic.onreadystatechange ? csrfMagic.onreadystatechange() : null; + }; + csrfMagic._updateProps(); +} + +CsrfMagic.prototype = { + + open: function(method, url, async, username, password) { + if (method == 'POST') this.csrf_isPost = true; + // deal with Opera bug, thanks jQuery + if (username) return this.csrf_open(method, url, async, username, password); + else return this.csrf_open(method, url, async); + }, + csrf_open: function(method, url, async, username, password) { + if (username) return this.csrf.open(method, url, async, username, password); + else return this.csrf.open(method, url, async); + }, + + send: function(data) { + if (!this.csrf_isPost) return this.csrf_send(data); + prepend = csrfMagicName + '=' + csrfMagicToken + '&'; + if (this.csrf_purportedLength === undefined) { + this.csrf_setRequestHeader("Content-length", this.csrf_purportedLength + prepend.length); + delete this.csrf_purportedLength; + } + delete this.csrf_isPost; + return this.csrf_send(prepend + data); + }, + csrf_send: function(data) { + return this.csrf.send(data); + }, + + setRequestHeader: function(header, value) { + // We have to auto-set this at the end, since we don't know how long the + // nonce is when added to the data. + if (this.csrf_isPost && header == "Content-length") { + this.csrf_purportedLength = value; + return; + } + return this.csrf_setRequestHeader(header, value); + }, + csrf_setRequestHeader: function(header, value) { + return this.csrf.setRequestHeader(header, value); + }, + + abort: function() { + return this.csrf.abort(); + }, + getAllResponseHeaders: function() { + return this.csrf.getAllResponseHeaders(); + }, + getResponseHeader: function(header) { + return this.csrf.getResponseHeader(header); + } // , +} + +// proprietary +CsrfMagic.prototype._updateProps = function() { + this.readyState = this.csrf.readyState; + if (this.readyState == 4) { + this.responseText = this.csrf.responseText; + this.responseXML = this.csrf.responseXML; + this.status = this.csrf.status; + this.statusText = this.csrf.statusText; + } +} +CsrfMagic.process = function(base) { + var prepend = csrfMagicName + '=' + csrfMagicToken; + if (base) return prepend + '&' + base; + return prepend; +} +// callback function for when everything on the page has loaded +CsrfMagic.end = function() { + // This rewrites forms AGAIN, so in case buffering didn't work this + // certainly will. + forms = document.getElementsByTagName('form'); + for (var i = 0; i < forms.length; i++) { + form = forms[i]; + if (form.method.toUpperCase() !== 'POST') continue; + if (form.elements[csrfMagicName]) continue; + var input = document.createElement('input'); + input.setAttribute('name', csrfMagicName); + input.setAttribute('value', csrfMagicToken); + input.setAttribute('type', 'hidden'); + form.appendChild(input); + } +} + +// Sets things up for Mozilla/Opera/nice browsers +if (window.XMLHttpRequest && window.XMLHttpRequest.prototype) { + var x = XMLHttpRequest.prototype; + var c = CsrfMagic.prototype; + + // Save the original functions + x.csrf_open = x.open; + x.csrf_send = x.send; + x.csrf_setRequestHeader = x.setRequestHeader; + + // Notice that CsrfMagic is itself an instantiatable object, but only + // open, send and setRequestHeader are necessary as decorators. + x.open = c.open; + x.send = c.send; + x.setRequestHeader = c.setRequestHeader; +} else { + // The only way we can do this is by modifying a library you have been + // using. We support YUI, script.aculo.us, prototype, MooTools, + // jQuery, Ext and Dojo. + if (window.jQuery) { + // jQuery didn't implement a new XMLHttpRequest function, so we have + // to do this the hard way. + jQuery.csrf_ajax = jQuery.ajax; + jQuery.ajax = function( s ) { + if (s.type && s.type.toUpperCase() == 'POST') { + s = jQuery.extend(true, s, jQuery.extend(true, {}, jQuery.ajaxSettings, s)); + if ( s.data && s.processData && typeof s.data != "string" ) { + s.data = jQuery.param(s.data); + } + s.data = CsrfMagic.process(s.data); + } + return jQuery.csrf_ajax( s ); + } + } else if (window.Prototype) { + // This works for script.aculo.us too + Ajax.csrf_getTransport = Ajax.getTransport; + Ajax.getTransport = function() { + return new CsrfMagic(Ajax.csrf_getTransport()); + } + } else if (window.MooTools) { + Browser.csrf_Request = Browser.Request; + Browser.Request = function () { + return new CsrfMagic(Browser.csrf_Request()); + } + } else if (window.YAHOO) { + YAHOO.util.Connect.csrf_createXhrObject = YAHOO.util.Connect.createXhrObject; + YAHOO.util.Connect.createXhrObject = function (transaction) { + obj = YAHOO.util.Connect.csrf_createXhrObject(transaction); + obj.conn = new CsrfMagic(obj.conn); + return obj; + } + } else if (window.Ext) { + // Ext can use other js libraries as loaders, so it has to come last + // Ext's implementation is pretty identical to Yahoo's, but we duplicate + // it for comprehensiveness's sake. + Ext.lib.Ajax.csrf_createXhrObject = Ext.lib.Ajax.createXhrObject; + Ext.lib.Ajax.createXhrObject = function (transaction) { + obj = Ext.lib.Ajax.csrf_createXhrObject(transaction); + obj.conn = new CsrfMagic(obj.conn); + return obj; + } + } else if (window.dojo) { + dojo.csrf__xhrObj = dojo._xhrObj; + dojo._xhrObj = function () { + return new CsrfMagic(dojo.csrf__xhrObj()); + } + } +} diff --git a/usr/local/www/csrf/csrf-magic.php b/usr/local/www/csrf/csrf-magic.php index ccb1617..befad0a 100644 --- a/usr/local/www/csrf/csrf-magic.php +++ b/usr/local/www/csrf/csrf-magic.php @@ -1,372 +1,372 @@ -<?php
-
-/**
- * @file
- *
- * csrf-magic is a PHP library that makes adding CSRF-protection to your
- * web applications a snap. No need to modify every form or create a database
- * of valid nonces; just include this file at the top of every
- * web-accessible page (or even better, your common include file included
- * in every page), and forget about it! (There are, of course, configuration
- * options for advanced users).
- *
- * This library is PHP4 and PHP5 compatible.
- */
-
-// CONFIGURATION:
-
-/**
- * By default, when you include this file csrf-magic will automatically check
- * and exit if the CSRF token is invalid. This will defer executing
- * csrf_check() until you're ready. You can also pass false as a parameter to
- * that function, in which case the function will not exit but instead return
- * a boolean false if the CSRF check failed. This allows for tighter integration
- * with your system.
- */
-$GLOBALS['csrf']['defer'] = false;
-
-/**
- * This is the amount of seconds you wish to allow before any token becomes
- * invalid; the default is two hours, which should be more than enough for
- * most websites.
- */
-$GLOBALS['csrf']['expires'] = 7200;
-
-/**
- * Callback function to execute when there's the CSRF check fails and
- * $fatal == true (see csrf_check). This will usually output an error message
- * about the failure.
- */
-$GLOBALS['csrf']['callback'] = 'csrf_callback';
-
-/**
- * Whether or not to include our JavaScript library which also rewrites
- * AJAX requests on this domain. Set this to the web path. This setting only works
- * with supported JavaScript libraries in Internet Explorer; see README.txt for
- * a list of supported libraries.
- */
-$GLOBALS['csrf']['rewrite-js'] = false;
-
-/**
- * A secret key used when hashing items. Please generate a random string and
- * place it here. If you change this value, all previously generated tokens
- * will become invalid.
- */
-$GLOBALS['csrf']['secret'] = '';
-
-/**
- * Set this to false to disable csrf-magic's output handler, and therefore,
- * its rewriting capabilities. If you're serving non HTML content, you should
- * definitely set this false.
- */
-$GLOBALS['csrf']['rewrite'] = true;
-
-/**
- * Whether or not to use IP addresses when binding a user to a token. This is
- * less reliable and less secure than sessions, but is useful when you need
- * to give facilities to anonymous users and do not wish to maintain a database
- * of valid keys.
- */
-$GLOBALS['csrf']['allow-ip'] = true;
-
-/**
- * If this information is available, use the cookie by this name to determine
- * whether or not to allow the request. This is a shortcut implementation
- * very similar to 'key', but we randomly set the cookie ourselves.
- */
-$GLOBALS['csrf']['cookie'] = '__csrf_cookie';
-
-/**
- * If this information is available, set this to a unique identifier (it
- * can be an integer or a unique username) for the current "user" of this
- * application. The token will then be globally valid for all of that user's
- * operations, but no one else. This requires that 'secret' be set.
- */
-$GLOBALS['csrf']['user'] = false;
-
-/**
- * This is an arbitrary secret value associated with the user's session. This
- * will most probably be the contents of a cookie, as an attacker cannot easily
- * determine this information. Warning: If the attacker knows this value, they
- * can easily spoof a token. This is a generic implementation; sessions should
- * work in most cases.
- *
- * Why would you want to use this? Lets suppose you have a squid cache for your
- * website, and the presence of a session cookie bypasses it. Let's also say
- * you allow anonymous users to interact with the website; submitting forms
- * and AJAX. Previously, you didn't have any CSRF protection for anonymous users
- * and so they never got sessions; you don't want to start using sessions either,
- * otherwise you'll bypass the Squid cache. Setup a different cookie for CSRF
- * tokens, and have Squid ignore that cookie for get requests, for anonymous
- * users. (If you haven't guessed, this scheme was(?) used for MediaWiki).
- */
-$GLOBALS['csrf']['key'] = false;
-
-/**
- * The name of the magic CSRF token that will be placed in all forms, i.e.
- * the contents of <input type="hidden" name="$name" value="CSRF-TOKEN" />
- */
-$GLOBALS['csrf']['input-name'] = '__csrf_magic';
-
-/**
- * Set this to false if your site must work inside of frame/iframe elements,
- * but do so at your own risk: this configuration protects you against CSS
- * overlay attacks that defeat tokens.
- */
-$GLOBALS['csrf']['frame-breaker'] = true;
-
-/**
- * Whether or not CSRF Magic should be allowed to start a new session in order
- * to determine the key.
- */
-$GLOBALS['csrf']['auto-session'] = true;
-
-/**
- * Whether or not csrf-magic should produce XHTML style tags.
- */
-$GLOBALS['csrf']['xhtml'] = true;
-
-// FUNCTIONS:
-
-// Don't edit this!
-$GLOBALS['csrf']['version'] = '1.0.1';
-
-/**
- * Rewrites <form> on the fly to add CSRF tokens to them. This can also
- * inject our JavaScript library.
- */
-function csrf_ob_handler($buffer, $flags) {
- // Even though the user told us to rewrite, we should do a quick heuristic
- // to check if the page is *actually* HTML. We don't begin rewriting until
- // we hit the first <html tag.
- static $is_html = false;
- if (!$is_html) {
- // not HTML until proven otherwise
- if (stripos($buffer, '<html') !== false) {
- $is_html = true;
- } else {
- return $buffer;
- }
- }
- $tokens = csrf_get_tokens();
- $name = $GLOBALS['csrf']['input-name'];
- $endslash = $GLOBALS['csrf']['xhtml'] ? ' /' : '';
- $input = "<input type='hidden' name='$name' value=\"$tokens\"$endslash>";
- $buffer = preg_replace('#(<form[^>]*method\s*=\s*["\']post["\'][^>]*>)#i', '$1' . $input, $buffer);
- if ($GLOBALS['csrf']['frame-breaker']) {
- $buffer = str_ireplace('</head>', '<script type="text/javascript">if (top != self) {top.location.href = self.location.href;}</script></head>', $buffer);
- }
- if ($js = $GLOBALS['csrf']['rewrite-js']) {
- $buffer = str_ireplace(
- '</head>',
- '<script type="text/javascript">'.
- 'var csrfMagicToken = "'.$tokens.'";'.
- 'var csrfMagicName = "'.$name.'";</script>'.
- '<script src="'.$js.'" type="text/javascript"></script></head>',
- $buffer
- );
- $script = '<script type="text/javascript">CsrfMagic.end();</script>';
- $buffer = str_ireplace('</body>', $script . '</body>', $buffer, $count);
- if (!$count) {
- $buffer .= $script;
- }
- }
- return $buffer;
-}
-
-/**
- * Checks if this is a post request, and if it is, checks if the nonce is valid.
- * @param bool $fatal Whether or not to fatally error out if there is a problem.
- * @return True if check passes or is not necessary, false if failure.
- */
-function csrf_check($fatal = true) {
- if ($_SERVER['REQUEST_METHOD'] !== 'POST') return true;
- csrf_start();
- $name = $GLOBALS['csrf']['input-name'];
- $ok = false;
- $tokens = '';
- do {
- if (!isset($_POST[$name])) break;
- // we don't regenerate a token and check it because some token creation
- // schemes are volatile.
- $tokens = $_POST[$name];
- if (!csrf_check_tokens($tokens)) break;
- $ok = true;
- } while (false);
- if ($fatal && !$ok) {
- $callback = $GLOBALS['csrf']['callback'];
- if (trim($tokens, 'A..Za..z0..9:;,') !== '') $tokens = 'hidden';
- $callback($tokens);
- exit;
- }
- return $ok;
-}
-
-/**
- * Retrieves a valid token(s) for a particular context. Tokens are separated
- * by semicolons.
- */
-function csrf_get_tokens() {
- $has_cookies = !empty($_COOKIE);
-
- // $ip implements a composite key, which is sent if the user hasn't sent
- // any cookies. It may or may not be used, depending on whether or not
- // the cookies "stick"
- if (!$has_cookies && $secret) {
- // :TODO: Harden this against proxy-spoofing attacks
- $ip = ';ip:' . csrf_hash($_SERVER['IP_ADDRESS']);
- } else {
- $ip = '';
- }
- csrf_start();
-
- // These are "strong" algorithms that don't require per se a secret
- if (session_id()) return 'sid:' . csrf_hash(session_id()) . $ip;
- if ($GLOBALS['csrf']['cookie']) {
- $val = csrf_generate_secret();
- setcookie($GLOBALS['csrf']['cookie'], $val);
- return 'cookie:' . csrf_hash($val) . $ip;
- }
- if ($GLOBALS['csrf']['key']) return 'key:' . csrf_hash($GLOBALS['csrf']['key']) . $ip;
- // These further algorithms require a server-side secret
- if ($secret === '') return 'invalid';
- if ($GLOBALS['csrf']['user'] !== false) {
- return 'user:' . csrf_hash($GLOBALS['csrf']['user']);
- }
- if ($GLOBALS['csrf']['allow-ip']) {
- return ltrim($ip, ';');
- }
- return 'invalid';
-}
-
-/**
- * @param $tokens is safe for HTML consumption
- */
-function csrf_callback($tokens) {
- header($_SERVER['SERVER_PROTOCOL'] . ' 403 Forbidden');
- echo "<html><head><title>CSRF check failed</title></head><body>CSRF check failed. Either your session has expired, this page has been inactive too long, or you need to enable cookies.<br />Debug: ".$tokens."</body></html>
-";
-}
-
-/**
- * Checks if a composite token is valid. Outward facing code should use this
- * instead of csrf_check_token()
- */
-function csrf_check_tokens($tokens) {
- if (is_string($tokens)) $tokens = explode(';', $tokens);
- foreach ($tokens as $token) {
- if (csrf_check_token($token)) return true;
- }
- return false;
-}
-
-/**
- * Checks if a token is valid.
- */
-function csrf_check_token($token) {
- if (strpos($token, ':') === false) return false;
- list($type, $value) = explode(':', $token, 2);
- if (strpos($value, ',') === false) return false;
- list($x, $time) = explode(',', $token, 2);
- if ($GLOBALS['csrf']['expires']) {
- if (time() > $time + $GLOBALS['csrf']['expires']) return false;
- }
- switch ($type) {
- case 'sid':
- return $value === csrf_hash(session_id(), $time);
- case 'cookie':
- $n = $GLOBALS['csrf']['cookie'];
- if (!$n) return false;
- if (!isset($_COOKIE[$n])) return false;
- return $value === csrf_hash($_COOKIE[$n], $time);
- case 'key':
- if (!$GLOBALS['csrf']['key']) return false;
- return $value === csrf_hash($GLOBALS['csrf']['key'], $time);
- // We could disable these 'weaker' checks if 'key' was set, but
- // that doesn't make me feel good then about the cookie-based
- // implementation.
- case 'user':
- if ($GLOBALS['csrf']['secret'] === '') return false;
- if ($GLOBALS['csrf']['user'] === false) return false;
- return $value === csrf_hash($GLOBALS['csrf']['user'], $time);
- case 'ip':
- if (csrf_get_secret() === '') return false;
- // do not allow IP-based checks if the username is set, or if
- // the browser sent cookies
- if ($GLOBALS['csrf']['user'] !== false) return false;
- if (!empty($_COOKIE)) return false;
- if (!$GLOBALS['csrf']['allow-ip']) return false;
- return $value === csrf_hash($_SERVER['IP_ADDRESS'], $time);
- }
- return false;
-}
-
-/**
- * Sets a configuration value.
- */
-function csrf_conf($key, $val) {
- if (!isset($GLOBALS['csrf'][$key])) {
- trigger_error('No such configuration ' . $key, E_USER_WARNING);
- return;
- }
- $GLOBALS['csrf'][$key] = $val;
-}
-
-/**
- * Starts a session if we're allowed to.
- */
-function csrf_start() {
- if ($GLOBALS['csrf']['auto-session'] && !session_id()) {
- session_start();
- }
-}
-
-/**
- * Retrieves the secret, and generates one if necessary.
- */
-function csrf_get_secret() {
- if ($GLOBALS['csrf']['secret']) return $GLOBALS['csrf']['secret'];
- $dir = dirname(__FILE__);
- $file = $dir . '/csrf-secret.php';
- $secret = '';
- if (file_exists($file)) {
- include $file;
- return $secret;
- }
- if (is_writable($dir)) {
- $secret = csrf_generate_secret();
- $fh = fopen($file, 'w');
- fwrite($fh, '<?php $secret = "'.$secret.'";' . PHP_EOL);
- fclose($fh);
- return $secret;
- }
- return '';
-}
-
-/**
- * Generates a random string as the hash of time, microtime, and mt_rand.
- */
-function csrf_generate_secret($len = 32) {
- $secret = '';
- for ($i = 0; $i < 32; $i++) {
- $secret .= chr(mt_rand(0, 255));
- }
- $secret .= time() . microtime();
- return sha1($secret);
-}
-
-/**
- * Generates a hash/expiry double. If time isn't set it will be calculated
- * from the current time.
- */
-function csrf_hash($value, $time = null) {
- if (!$time) $time = time();
- return sha1($secret . $value . $time) . ',' . $time;
-}
-
-// Load user configuration
-if (function_exists('csrf_startup')) csrf_startup();
-// Initialize our handler
-if ($GLOBALS['csrf']['rewrite']) ob_start('csrf_ob_handler');
-// Perform check
-if (!$GLOBALS['csrf']['defer']) csrf_check();
+<?php + +/** + * @file + * + * csrf-magic is a PHP library that makes adding CSRF-protection to your + * web applications a snap. No need to modify every form or create a database + * of valid nonces; just include this file at the top of every + * web-accessible page (or even better, your common include file included + * in every page), and forget about it! (There are, of course, configuration + * options for advanced users). + * + * This library is PHP4 and PHP5 compatible. + */ + +// CONFIGURATION: + +/** + * By default, when you include this file csrf-magic will automatically check + * and exit if the CSRF token is invalid. This will defer executing + * csrf_check() until you're ready. You can also pass false as a parameter to + * that function, in which case the function will not exit but instead return + * a boolean false if the CSRF check failed. This allows for tighter integration + * with your system. + */ +$GLOBALS['csrf']['defer'] = false; + +/** + * This is the amount of seconds you wish to allow before any token becomes + * invalid; the default is two hours, which should be more than enough for + * most websites. + */ +$GLOBALS['csrf']['expires'] = 7200; + +/** + * Callback function to execute when there's the CSRF check fails and + * $fatal == true (see csrf_check). This will usually output an error message + * about the failure. + */ +$GLOBALS['csrf']['callback'] = 'csrf_callback'; + +/** + * Whether or not to include our JavaScript library which also rewrites + * AJAX requests on this domain. Set this to the web path. This setting only works + * with supported JavaScript libraries in Internet Explorer; see README.txt for + * a list of supported libraries. + */ +$GLOBALS['csrf']['rewrite-js'] = false; + +/** + * A secret key used when hashing items. Please generate a random string and + * place it here. If you change this value, all previously generated tokens + * will become invalid. + */ +$GLOBALS['csrf']['secret'] = ''; + +/** + * Set this to false to disable csrf-magic's output handler, and therefore, + * its rewriting capabilities. If you're serving non HTML content, you should + * definitely set this false. + */ +$GLOBALS['csrf']['rewrite'] = true; + +/** + * Whether or not to use IP addresses when binding a user to a token. This is + * less reliable and less secure than sessions, but is useful when you need + * to give facilities to anonymous users and do not wish to maintain a database + * of valid keys. + */ +$GLOBALS['csrf']['allow-ip'] = true; + +/** + * If this information is available, use the cookie by this name to determine + * whether or not to allow the request. This is a shortcut implementation + * very similar to 'key', but we randomly set the cookie ourselves. + */ +$GLOBALS['csrf']['cookie'] = '__csrf_cookie'; + +/** + * If this information is available, set this to a unique identifier (it + * can be an integer or a unique username) for the current "user" of this + * application. The token will then be globally valid for all of that user's + * operations, but no one else. This requires that 'secret' be set. + */ +$GLOBALS['csrf']['user'] = false; + +/** + * This is an arbitrary secret value associated with the user's session. This + * will most probably be the contents of a cookie, as an attacker cannot easily + * determine this information. Warning: If the attacker knows this value, they + * can easily spoof a token. This is a generic implementation; sessions should + * work in most cases. + * + * Why would you want to use this? Lets suppose you have a squid cache for your + * website, and the presence of a session cookie bypasses it. Let's also say + * you allow anonymous users to interact with the website; submitting forms + * and AJAX. Previously, you didn't have any CSRF protection for anonymous users + * and so they never got sessions; you don't want to start using sessions either, + * otherwise you'll bypass the Squid cache. Setup a different cookie for CSRF + * tokens, and have Squid ignore that cookie for get requests, for anonymous + * users. (If you haven't guessed, this scheme was(?) used for MediaWiki). + */ +$GLOBALS['csrf']['key'] = false; + +/** + * The name of the magic CSRF token that will be placed in all forms, i.e. + * the contents of <input type="hidden" name="$name" value="CSRF-TOKEN" /> + */ +$GLOBALS['csrf']['input-name'] = '__csrf_magic'; + +/** + * Set this to false if your site must work inside of frame/iframe elements, + * but do so at your own risk: this configuration protects you against CSS + * overlay attacks that defeat tokens. + */ +$GLOBALS['csrf']['frame-breaker'] = true; + +/** + * Whether or not CSRF Magic should be allowed to start a new session in order + * to determine the key. + */ +$GLOBALS['csrf']['auto-session'] = true; + +/** + * Whether or not csrf-magic should produce XHTML style tags. + */ +$GLOBALS['csrf']['xhtml'] = true; + +// FUNCTIONS: + +// Don't edit this! +$GLOBALS['csrf']['version'] = '1.0.1'; + +/** + * Rewrites <form> on the fly to add CSRF tokens to them. This can also + * inject our JavaScript library. + */ +function csrf_ob_handler($buffer, $flags) { + // Even though the user told us to rewrite, we should do a quick heuristic + // to check if the page is *actually* HTML. We don't begin rewriting until + // we hit the first <html tag. + static $is_html = false; + if (!$is_html) { + // not HTML until proven otherwise + if (stripos($buffer, '<html') !== false) { + $is_html = true; + } else { + return $buffer; + } + } + $tokens = csrf_get_tokens(); + $name = $GLOBALS['csrf']['input-name']; + $endslash = $GLOBALS['csrf']['xhtml'] ? ' /' : ''; + $input = "<input type='hidden' name='$name' value=\"$tokens\"$endslash>"; + $buffer = preg_replace('#(<form[^>]*method\s*=\s*["\']post["\'][^>]*>)#i', '$1' . $input, $buffer); + if ($GLOBALS['csrf']['frame-breaker']) { + $buffer = str_ireplace('</head>', '<script type="text/javascript">if (top != self) {top.location.href = self.location.href;}</script></head>', $buffer); + } + if ($js = $GLOBALS['csrf']['rewrite-js']) { + $buffer = str_ireplace( + '</head>', + '<script type="text/javascript">'. + 'var csrfMagicToken = "'.$tokens.'";'. + 'var csrfMagicName = "'.$name.'";</script>'. + '<script src="'.$js.'" type="text/javascript"></script></head>', + $buffer + ); + $script = '<script type="text/javascript">CsrfMagic.end();</script>'; + $buffer = str_ireplace('</body>', $script . '</body>', $buffer, $count); + if (!$count) { + $buffer .= $script; + } + } + return $buffer; +} + +/** + * Checks if this is a post request, and if it is, checks if the nonce is valid. + * @param bool $fatal Whether or not to fatally error out if there is a problem. + * @return True if check passes or is not necessary, false if failure. + */ +function csrf_check($fatal = true) { + if ($_SERVER['REQUEST_METHOD'] !== 'POST') return true; + csrf_start(); + $name = $GLOBALS['csrf']['input-name']; + $ok = false; + $tokens = ''; + do { + if (!isset($_POST[$name])) break; + // we don't regenerate a token and check it because some token creation + // schemes are volatile. + $tokens = $_POST[$name]; + if (!csrf_check_tokens($tokens)) break; + $ok = true; + } while (false); + if ($fatal && !$ok) { + $callback = $GLOBALS['csrf']['callback']; + if (trim($tokens, 'A..Za..z0..9:;,') !== '') $tokens = 'hidden'; + $callback($tokens); + exit; + } + return $ok; +} + +/** + * Retrieves a valid token(s) for a particular context. Tokens are separated + * by semicolons. + */ +function csrf_get_tokens() { + $has_cookies = !empty($_COOKIE); + + // $ip implements a composite key, which is sent if the user hasn't sent + // any cookies. It may or may not be used, depending on whether or not + // the cookies "stick" + if (!$has_cookies && $secret) { + // :TODO: Harden this against proxy-spoofing attacks + $ip = ';ip:' . csrf_hash($_SERVER['IP_ADDRESS']); + } else { + $ip = ''; + } + csrf_start(); + + // These are "strong" algorithms that don't require per se a secret + if (session_id()) return 'sid:' . csrf_hash(session_id()) . $ip; + if ($GLOBALS['csrf']['cookie']) { + $val = csrf_generate_secret(); + setcookie($GLOBALS['csrf']['cookie'], $val); + return 'cookie:' . csrf_hash($val) . $ip; + } + if ($GLOBALS['csrf']['key']) return 'key:' . csrf_hash($GLOBALS['csrf']['key']) . $ip; + // These further algorithms require a server-side secret + if ($secret === '') return 'invalid'; + if ($GLOBALS['csrf']['user'] !== false) { + return 'user:' . csrf_hash($GLOBALS['csrf']['user']); + } + if ($GLOBALS['csrf']['allow-ip']) { + return ltrim($ip, ';'); + } + return 'invalid'; +} + +/** + * @param $tokens is safe for HTML consumption + */ +function csrf_callback($tokens) { + header($_SERVER['SERVER_PROTOCOL'] . ' 403 Forbidden'); + echo "<html><head><title>CSRF check failed</title></head><body>CSRF check failed. Either your session has expired, this page has been inactive too long, or you need to enable cookies.<br />Debug: ".$tokens."</body></html> +"; +} + +/** + * Checks if a composite token is valid. Outward facing code should use this + * instead of csrf_check_token() + */ +function csrf_check_tokens($tokens) { + if (is_string($tokens)) $tokens = explode(';', $tokens); + foreach ($tokens as $token) { + if (csrf_check_token($token)) return true; + } + return false; +} + +/** + * Checks if a token is valid. + */ +function csrf_check_token($token) { + if (strpos($token, ':') === false) return false; + list($type, $value) = explode(':', $token, 2); + if (strpos($value, ',') === false) return false; + list($x, $time) = explode(',', $token, 2); + if ($GLOBALS['csrf']['expires']) { + if (time() > $time + $GLOBALS['csrf']['expires']) return false; + } + switch ($type) { + case 'sid': + return $value === csrf_hash(session_id(), $time); + case 'cookie': + $n = $GLOBALS['csrf']['cookie']; + if (!$n) return false; + if (!isset($_COOKIE[$n])) return false; + return $value === csrf_hash($_COOKIE[$n], $time); + case 'key': + if (!$GLOBALS['csrf']['key']) return false; + return $value === csrf_hash($GLOBALS['csrf']['key'], $time); + // We could disable these 'weaker' checks if 'key' was set, but + // that doesn't make me feel good then about the cookie-based + // implementation. + case 'user': + if ($GLOBALS['csrf']['secret'] === '') return false; + if ($GLOBALS['csrf']['user'] === false) return false; + return $value === csrf_hash($GLOBALS['csrf']['user'], $time); + case 'ip': + if (csrf_get_secret() === '') return false; + // do not allow IP-based checks if the username is set, or if + // the browser sent cookies + if ($GLOBALS['csrf']['user'] !== false) return false; + if (!empty($_COOKIE)) return false; + if (!$GLOBALS['csrf']['allow-ip']) return false; + return $value === csrf_hash($_SERVER['IP_ADDRESS'], $time); + } + return false; +} + +/** + * Sets a configuration value. + */ +function csrf_conf($key, $val) { + if (!isset($GLOBALS['csrf'][$key])) { + trigger_error('No such configuration ' . $key, E_USER_WARNING); + return; + } + $GLOBALS['csrf'][$key] = $val; +} + +/** + * Starts a session if we're allowed to. + */ +function csrf_start() { + if ($GLOBALS['csrf']['auto-session'] && !session_id()) { + session_start(); + } +} + +/** + * Retrieves the secret, and generates one if necessary. + */ +function csrf_get_secret() { + if ($GLOBALS['csrf']['secret']) return $GLOBALS['csrf']['secret']; + $dir = dirname(__FILE__); + $file = $dir . '/csrf-secret.php'; + $secret = ''; + if (file_exists($file)) { + include $file; + return $secret; + } + if (is_writable($dir)) { + $secret = csrf_generate_secret(); + $fh = fopen($file, 'w'); + fwrite($fh, '<?php $secret = "'.$secret.'";' . PHP_EOL); + fclose($fh); + return $secret; + } + return ''; +} + +/** + * Generates a random string as the hash of time, microtime, and mt_rand. + */ +function csrf_generate_secret($len = 32) { + $secret = ''; + for ($i = 0; $i < 32; $i++) { + $secret .= chr(mt_rand(0, 255)); + } + $secret .= time() . microtime(); + return sha1($secret); +} + +/** + * Generates a hash/expiry double. If time isn't set it will be calculated + * from the current time. + */ +function csrf_hash($value, $time = null) { + if (!$time) $time = time(); + return sha1($secret . $value . $time) . ',' . $time; +} + +// Load user configuration +if (function_exists('csrf_startup')) csrf_startup(); +// Initialize our handler +if ($GLOBALS['csrf']['rewrite']) ob_start('csrf_ob_handler'); +// Perform check +if (!$GLOBALS['csrf']['defer']) csrf_check(); diff --git a/usr/local/www/diag_logs_ppp.php b/usr/local/www/diag_logs_ppp.php index 9609132..c58b589 100755 --- a/usr/local/www/diag_logs_ppp.php +++ b/usr/local/www/diag_logs_ppp.php @@ -87,7 +87,7 @@ include("head.inc"); <div id="mainarea"> <table class="tabcont" width="100%" border="0" cellspacing="0" cellpadding="0"> <tr> - <td colspan="2" class="listtopic"><?php printf (gettext("Last $nentries PPP log entries"),$nentries);?></td> + <td colspan="2" class="listtopic"><?php printf(gettext("Last %s PPP log entries"),$nentries);?></td> </tr> <?php foreach($ppp_logarr as $logent){ diff --git a/usr/local/www/firewall_aliases.php b/usr/local/www/firewall_aliases.php index 575240c..03d0539 100755 --- a/usr/local/www/firewall_aliases.php +++ b/usr/local/www/firewall_aliases.php @@ -159,7 +159,7 @@ include("head.inc"); <?php if (is_subsystem_dirty('aliases')): ?><p> <?php print_info_box_np(gettext("The alias list has been changed.") . "<br>" . gettext("You must apply the changes in order for them to take effect."));?> <?php endif; ?> - +<?php pfSense_handle_custom_code("/usr/local/pkg/firewall_aliases/pre_table"); ?> <table width="100%" border="0" cellpadding="0" cellspacing="0" class="tabcont"> <tr> <td width="25%" class="listhdrr"><?=gettext("Name"); ?></td> diff --git a/usr/local/www/firewall_aliases_edit.php b/usr/local/www/firewall_aliases_edit.php index 3d44694..adaa2c7 100755 --- a/usr/local/www/firewall_aliases_edit.php +++ b/usr/local/www/firewall_aliases_edit.php @@ -558,6 +558,7 @@ EOD; rowsize[2] = "50"; </script> +<?php pfSense_handle_custom_code("/usr/local/pkg/firewall_aliases_edit/pre_input_errors"); ?> <?php if ($input_errors) print_input_errors($input_errors); ?> <div id="inputerrors"></div> diff --git a/usr/local/www/firewall_nat.php b/usr/local/www/firewall_nat.php index ac67291..d2a9caa 100755 --- a/usr/local/www/firewall_nat.php +++ b/usr/local/www/firewall_nat.php @@ -70,6 +70,8 @@ if ($_POST) { $retval |= filter_configure(); $savemsg = get_std_save_message($retval); + pfSense_handle_custom_code("/usr/local/pkg/firewall_nat/apply"); + if ($retval == 0) { clear_subsystem_dirty('natconf'); clear_subsystem_dirty('filter'); diff --git a/usr/local/www/firewall_nat_edit.php b/usr/local/www/firewall_nat_edit.php index 5aa069c..95286eb 100755 --- a/usr/local/www/firewall_nat_edit.php +++ b/usr/local/www/firewall_nat_edit.php @@ -439,8 +439,11 @@ $pgtitle = array(gettext("Firewall"),gettext("NAT"),gettext("Port Forward"),gett include("head.inc"); ?> +<link rel="stylesheet" href="/javascript/chosen/chosen.css" /> +</head> <body link="#0000CC" vlink="#0000CC" alink="#0000CC"> +<script src="/javascript/chosen/chosen.proto.js" type="text/javascript"></script> <?php include("fbegin.inc"); ?> <?php if ($input_errors) print_input_errors($input_errors); ?> @@ -476,6 +479,8 @@ include("fbegin.inc"); ?> <?php $iflist = get_configured_interface_with_descr(false, true); + // Allow extending of the firewall edit interfaces + pfSense_handle_custom_code("/usr/local/pkg/firewall_nat/pre_interfaces_edit"); foreach ($iflist as $if => $ifdesc) if(have_ruleint_access($if)) $interfaces[$if] = $ifdesc; diff --git a/usr/local/www/firewall_rules.php b/usr/local/www/firewall_rules.php index 1aaea16..da9abd1 100755 --- a/usr/local/www/firewall_rules.php +++ b/usr/local/www/firewall_rules.php @@ -177,6 +177,8 @@ if (isset($config['ipsec']['enable']) || isset($config['ipsec']['client']['enabl if ($config['openvpn']["openvpn-server"] || $config['openvpn']["openvpn-client"]) $iflist["openvpn"] = "OpenVPN"; +pfSense_handle_custom_code("/usr/local/pkg/firewall_rules/interfaces_override"); + if (!$if || !isset($iflist[$if])) { if ("any" == $if) $if = "FloatingRules"; @@ -194,6 +196,8 @@ if ($_POST) { clear_subsystem_dirty('filter'); + pfSense_handle_custom_code("/usr/local/pkg/firewall_rules/apply"); + $savemsg = sprintf(gettext("The settings have been applied. The firewall rules are now reloading in the background.<br/>You can also %s monitor %s the reload progress"),"<a href='status_filter_reload.php'>","</a>"); } } @@ -292,9 +296,11 @@ echo "<script type=\"text/javascript\" language=\"javascript\" src=\"/javascript echo "<script type=\"text/javascript\" language=\"javascript\" src=\"/javascript/domTT/behaviour.js\"></script>"; echo "<script type=\"text/javascript\" language=\"javascript\" src=\"/javascript/domTT/fadomatic.js\"></script>"; ?> +<link rel="stylesheet" href="/javascript/chosen/chosen.css" /> </head> <body link="#0000CC" vlink="#0000CC" alink="#0000CC"> +<script src="/javascript/chosen/chosen.proto.js" type="text/javascript"></script> <?php include("fbegin.inc"); ?> <form action="firewall_rules.php" method="post"> @@ -317,6 +323,9 @@ if($_REQUEST['undodrag']) { <img src="/themes/<?=$g['theme']?>/images/misc/loader.gif"> Loading, please wait... <p/> </div> +<?php + pfSense_handle_custom_code("/usr/local/pkg/firewall_rules/before_table"); +?> <table width="100%" border="0" cellpadding="0" cellspacing="0"> <tr><td class="tabnavtbl"> <?php @@ -340,21 +349,30 @@ if($_REQUEST['undodrag']) { <tr> <td> <div id="mainarea"> - <table class="tabcont" width="100%" border="0" cellpadding="0" cellspacing="0"> - <tr id="frheader"> - <td width="3%" class="list"> </td> - <td width="5%" class="list"> </td> - <td width="3%" class="listhdrr"><?=gettext("ID");?></td> - <td width="6%" class="listhdrr"><?=gettext("Proto");?></td> - <td width="12%" class="listhdrr"><?=gettext("Source");?></td> - <td width="6%" class="listhdrr"><?=gettext("Port");?></td> - <td width="12%" class="listhdrr"><?=gettext("Destination");?></td> - <td width="6%" class="listhdrr"><?=gettext("Port");?></td> - <td width="5%" class="listhdrr"><?=gettext("Gateway");?></td> - <td width="8%" class="listhdrr"><?=gettext("Queue");?></td> - <td width="5%" class="listhdrr"><?=gettext("Schedule");?></td> - <td width="19%" class="listhdr"><?=gettext("Description");?></td> - <td width="10%" class="list"> + <table class="tabcont" width="100%" border="0" cellpadding="0" cellspacing="0"> +<?php + pfSense_handle_custom_code("/usr/local/pkg/firewall_rules/before_first_tr"); +?> + <tr id="frheader"> + <td width="3%" class="list"> </td> + <td width="5%" class="list"> </td> + <td width="3%" class="listhdrr"><?=gettext("ID");?></td> +<?php + pfSense_handle_custom_code("/usr/local/pkg/firewall_rules/pre_id_tablehead"); +?> + <td width="6%" class="listhdrr"><?=gettext("Proto");?></td> + <td width="12%" class="listhdrr"><?=gettext("Source");?></td> + <td width="6%" class="listhdrr"><?=gettext("Port");?></td> + <td width="12%" class="listhdrr"><?=gettext("Destination");?></td> + <td width="6%" class="listhdrr"><?=gettext("Port");?></td> + <td width="5%" class="listhdrr"><?=gettext("Gateway");?></td> + <td width="8%" class="listhdrr"><?=gettext("Queue");?></td> + <td width="5%" class="listhdrr"><?=gettext("Schedule");?></td> +<?php + pfSense_handle_custom_code("/usr/local/pkg/firewall_rules/pre_desc_tablehead"); +?> + <td width="19%" class="listhdr"><?=gettext("Description");?></td> + <td width="10%" class="list"> <table border="0" cellspacing="0" cellpadding="1"> <tr> <?php @@ -401,6 +419,9 @@ if($_REQUEST['undodrag']) { <td class="list"> </td> <td class="listt" align="center"><img src="./themes/<?= $g['theme']; ?>/images/icons/icon_pass.gif" width="11" height="11" border="0"></td> <td class="listlr" style="background-color: #E0E0E0"></td> +<?php + pfSense_handle_custom_code("/usr/local/pkg/firewall_rules/pre_id_tr_antilockout"); +?> <td class="listr" style="background-color: #E0E0E0">*</td> <td class="listr" style="background-color: #E0E0E0">*</td> <td class="listr" style="background-color: #E0E0E0">*</td> @@ -483,6 +504,7 @@ if($_REQUEST['undodrag']) { <?php endif; ?> <tbody id="dragtable" width="100%"> <?php $nrules = 0; for ($i = 0; isset($a_filter[$i]); $i++): + pfSense_handle_custom_code("/usr/local/pkg/firewall_rules/row_start"); $filterent = $a_filter[$i]; if ($filterent['interface'] != $if && !isset($filterent['floating'])) continue; @@ -492,7 +514,7 @@ if($_REQUEST['undodrag']) { if($isadvset) $advanced_set = "<img src=\"./themes/{$g['theme']}/images/icons/icon_advanced.gif\" title=\"" . gettext("advanced settings set") . ": {$isadvset}\" border=\"0\">"; else - $advanced_set = "" + $advanced_set = ""; ?> <tr valign="top" id="fr<?=$nrules;?>"> <td class="listt"> @@ -685,6 +707,9 @@ if($_REQUEST['undodrag']) { <td class="listlr" onClick="fr_toggle(<?=$nrules;?>)" id="frd<?=$nrules;?>" ondblclick="document.location='firewall_rules_edit.php?id=<?=$i;?>';"> <?=$textss;?><?php if (isset($filterent['id'])) echo $filterent['id']; else echo ""; ?><?=$textse;?> </td> +<?php + pfSense_handle_custom_code("/usr/local/pkg/firewall_rules/pre_id_tr"); +?> <td class="listr" onClick="fr_toggle(<?=$nrules;?>)" id="frd<?=$nrules;?>" ondblclick="document.location='firewall_rules_edit.php?id=<?=$i;?>';"> <?=$textss;?><?php if (isset($filterent['ipprotocol'])) { @@ -740,6 +765,9 @@ if($_REQUEST['undodrag']) { <td class="listr" onClick="fr_toggle(<?=$nrules;?>)" id="frd<?=$nrules;?>" ondblclick="document.location='firewall_rules_edit.php?id=<?=$i;?>';"><font color="black"> <?php if ($printicon) { ?><img src="./themes/<?= $g['theme']; ?>/images/icons/<?php echo $image; ?>.gif" title="<?php echo $alttext;?>" border="0"><?php } ?> <?=$textss;?><?php echo $schedule_span_begin;?><?=htmlspecialchars($filterent['sched']);?><?php echo $schedule_span_end; ?><?=$textse;?> </td> +<?php + pfSense_handle_custom_code("/usr/local/pkg/firewall_rules/pre_descr_tr"); +?> <td class="listbg" onClick="fr_toggle(<?=$nrules;?>)" ondblclick="document.location='firewall_rules_edit.php?id=<?=$i;?>';" class="descr"> <?=$textss;?><?=htmlspecialchars($filterent['descr']);?> <?=$textse;?> </td> @@ -771,6 +799,9 @@ if($_REQUEST['undodrag']) { <tr id="fr<?=$nrules;?>"> <td class="list"></td> <td class="list"></td> +<?php + pfSense_handle_custom_code("/usr/local/pkg/firewall_rules/pre_id_tr_belowtable"); +?> <td class="list"> </td> <td class="list"> </td> <td class="list"> </td> diff --git a/usr/local/www/firewall_rules_edit.php b/usr/local/www/firewall_rules_edit.php index 0e61806..8857d8d 100755 --- a/usr/local/www/firewall_rules_edit.php +++ b/usr/local/www/firewall_rules_edit.php @@ -603,11 +603,13 @@ $page_filename = "firewall_rules_edit.php"; include("head.inc"); ?> - +<link rel="stylesheet" href="/javascript/chosen/chosen.css" /> </head> <body link="#0000CC" vlink="#0000CC" alink="#0000CC"> +<script src="/javascript/chosen/chosen.proto.js" type="text/javascript"></script> <?php include("fbegin.inc"); ?> +<?php pfSense_handle_custom_code("/usr/local/pkg/firewall_rules/pre_input_errors"); ?> <?php if ($input_errors) print_input_errors($input_errors); ?> <form action="firewall_rules_edit.php" method="post" name="iform" id="iform"> @@ -693,7 +695,7 @@ include("head.inc"); <td width="22%" valign="top" class="vncellreq"><?=gettext("Interface");?></td> <td width="78%" class="vtable"> <?php if ($if == "FloatingRules" || isset($pconfig['floating'])): ?> - <select name="interface[]" multiple="true" class="formselect" size="3" <?=$edit_disabled;?>> + <select name="interface[]" title="Select interfaces..." multiple style="width:350px;" class="chzn-select" tabindex="2" <?=$edit_disabled;?>> <?php else: ?> <select name="interface" class="formselect" <?=$edit_disabled;?>> <?php diff --git a/usr/local/www/firewall_shaper_vinterface.php b/usr/local/www/firewall_shaper_vinterface.php index fae5429..b325a30 100644 --- a/usr/local/www/firewall_shaper_vinterface.php +++ b/usr/local/www/firewall_shaper_vinterface.php @@ -111,7 +111,7 @@ if ($_GET) { } $output_form .= $queue->build_form(); } else { - $input_errors[] = gettext("No queue with name {$qname} was found!"); + $input_errors[] = sprintf(gettext("No queue with name %s was found!"),$qname); $output_form .= "<p class=\"pgtitle\">" . $dn_default_shaper_msg."</p>"; $dontshow = true; } diff --git a/usr/local/www/guiconfig.inc b/usr/local/www/guiconfig.inc index 63fadab..6b579db 100755 --- a/usr/local/www/guiconfig.inc +++ b/usr/local/www/guiconfig.inc @@ -914,7 +914,7 @@ function display_top_tabs(& $tab_array, $no_drop_down = false) { } if ($tab_array_char_limit == '') { - $tab_array_char_limit = 82; + $tab_array_char_limit = 92; } $tab_temp = array (); diff --git a/usr/local/www/index.php b/usr/local/www/index.php index 2b9be16..1638196 100755 --- a/usr/local/www/index.php +++ b/usr/local/www/index.php @@ -475,6 +475,8 @@ echo $jscriptstr; if ($savemsg) print_info_box($savemsg); +pfSense_handle_custom_code("/usr/local/pkg/dashboard/pre_dashboard"); + ?> <div id="widgetcontainer" style="display:none"> <div id="content1"><h1><?=gettext("Available Widgets"); ?></h1><p><?php diff --git a/usr/local/www/interfaces.php b/usr/local/www/interfaces.php index 66aed5b..a62fee5 100755 --- a/usr/local/www/interfaces.php +++ b/usr/local/www/interfaces.php @@ -451,9 +451,9 @@ if ($_POST['apply']) { } case "dhcp": if (in_array($wancfg['ipaddr'], array("ppp", "pppoe", "pptp", "l2tp"))) - $input_errors[] = gettext("You have to reassign the interface to be able to configure as {$_POST['type']}."); + $input_errors[] = sprintf(gettext("You have to reassign the interface to be able to configure as %s."),$_POST['type']); if (in_array($wancfg['ipaddrv6'], array("ppp", "pppoe", "pptp", "l2tp"))) - $input_errors[] = gettext("You have to reassign the interface to be able to configure as {$_POST['type']}."); + $input_errors[] = sprintf(gettext("You have to reassign the interface to be able to configure as %s."),$_POST['type']); break; case "ppp": $reqdfields = explode(" ", "port phone"); @@ -657,7 +657,7 @@ if ($_POST['apply']) { } if($skip == false) { $gateway_item['gateway'] = "dynamic"; - $gateway_item['descr'] = gettext("Interface") . $if . gettext("dynamic gateway"); + $gateway_item['descr'] = sprintf(gettext("Interface %s dynamic gateway"),$if); $gateway_item['name'] = "GW_" . strtoupper($if); $gateway_item['interface'] = "{$if}"; } else { @@ -1617,7 +1617,7 @@ $types = array("none" => gettext("None"), "staticv4" => gettext("Static IPv4"), <?php if(is_readable("/var/db/dhcp6c_duid")) { // $current_duid = file_get_contents("/var/db/dhcp6c_duid"); } - echo gettext("The current DUID is: '") . $current_duid ."'"; + printf(gettext("The current DUID is: '%s'"),$current_duid); ?> </td> diff --git a/usr/local/www/interfaces_assign.php b/usr/local/www/interfaces_assign.php index cf0203c..230ce1a 100755 --- a/usr/local/www/interfaces_assign.php +++ b/usr/local/www/interfaces_assign.php @@ -392,6 +392,7 @@ if(file_exists("/var/run/interface_mismatch_reboot_needed")) <?php print_info_box($savemsg); ?> <?php endif; ?> +<?php pfSense_handle_custom_code("/usr/local/pkg/interfaces_assign/pre_input_errors"); ?> <?php if ($input_errors) print_input_errors($input_errors); ?> <table width="100%" border="0" cellpadding="0" cellspacing="0"> diff --git a/usr/local/www/interfaces_groups.php b/usr/local/www/interfaces_groups.php index df628c8..00bc613 100755 --- a/usr/local/www/interfaces_groups.php +++ b/usr/local/www/interfaces_groups.php @@ -47,7 +47,7 @@ $a_ifgroups = &$config['ifgroups']['ifgroupentry']; if ($_GET['act'] == "del") { if ($a_ifgroups[$_GET['id']]) { - $members = explode(" ", $a_ifgroups[$_GET[$id]]); + $members = explode(" ", $a_ifgroups[$_GET[$id]]['members']); foreach ($members as $ifs) { $realif = get_real_interface($ifs); if ($realif) diff --git a/usr/local/www/javascript/chosen/chosen-sprite.png b/usr/local/www/javascript/chosen/chosen-sprite.png Binary files differnew file mode 100644 index 0000000..6ec9bbf --- /dev/null +++ b/usr/local/www/javascript/chosen/chosen-sprite.png diff --git a/usr/local/www/javascript/chosen/chosen.css b/usr/local/www/javascript/chosen/chosen.css new file mode 100644 index 0000000..d2b1eb4 --- /dev/null +++ b/usr/local/www/javascript/chosen/chosen.css @@ -0,0 +1,317 @@ +div.chzn-container { + font-size: 13px; + position: relative; +} + +div.chzn-container input { + background: #fff; + background: -webkit-gradient(linear, left bottom, left top, color-stop(0.85, white), color-stop(0.99, #eeeeee)); + background: -moz-linear-gradient(center bottom, white 85%, #eeeeee 99%); + background: -o-linear-gradient(bottom, white 85%, #eeeeee 99%); + border: 1px solid #aaa; + font-family: sans-serif; + font-size: 1em; + margin: 0px; + padding: 4px 5px; + outline: none; + -moz-border-radius: 3px; + -webkit-border-radius: 3px; + -o-border-radius: 3px; + -ms-border-radius: 3px; + -khtml-border-radius: 3px; + border-radius: 3px; +} +div.chzn-container textarea:focus { + border-color: #058cf5; + -moz-box-shadow: 0px 0px 3px #aaa; + -webkit-box-shadow: 0px 0px 3px #aaa; + box-shadow: 0px 0px 3px #aaa; +} + + +div.chzn-container div.chzn-drop { + background: #FFF; + border: 1px solid #aaa; + border-width: 0 1px 1px; + left: 0; + position: absolute; + top: 29px; + -webkit-box-shadow: 0px 4px 5px rgba(0, 0, 0, 0.15); + -moz-box-shadow: 0px 4px 5px rgba(0, 0, 0, 0.15); + box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.15); + z-index: 20; +} +div.chzn-container-single div.chzn-drop { + -moz-border-radius: 0 0 4px 4px; + -webkit-border-radius: 0 0 4px 4px; + -o-border-radius: 0 0 4px 4px; + -ms-border-radius: 0 0 4px 4px; + -khtml-border-radius: 0 0 4px 4px; + border-radius: 0 0 4px 4px; +} + + +/* SINGLE */ +div.chzn-container a.chzn-single { + background: #FFF; + background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #eeeeee), color-stop(0.5, white)); + background-image: -moz-linear-gradient(center bottom, #eeeeee 0%, white 50%); + background-image: -o-linear-gradient(bottom, #eeeeee 0%, white 50%); + border: 1px solid #aaa; + display: block; + overflow: hidden; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + -o-border-radius: 4px; + -ms-border-radius: 4px; + -khtml-border-radius: 4px; + border-radius: 4px; + height: 25px; + color: #444; + line-height: 26px; + padding: 0px 0px 0px 8px; + position: relative; + text-decoration: none; + z-index: 19; + white-space: nowrap; +} +div.chzn-container a.chzn-single span { + display: block; + margin-right: 26px; + overflow: hidden; + text-overflow: ellipsis; +} +div.chzn-container a.chzn-single div { + -moz-border-radius-topright: 4px; + -webkit-border-top-right-radius: 4px; + -o-border-top-right-radius: 4px; + -ms-border-top-right-radius: 4px; + -khtml-border-top-right-radius: 4px; + border-top-right-radius: 4px; + -moz-border-radius-bottomright: 4px; + -webkit-border-bottom-right-radius: 4px; + -o-border-bottom-right-radius: 4px; + -ms-border-bottom-right-radius: 4px; + -khtml-border-bottom-right-radius: 4px; + border-bottom-right-radius: 4px; + background: #ccc; + background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #ccc), color-stop(0.6, #eee)); + background-image: -moz-linear-gradient(center bottom, #ccc 0%, #eee 60%); + background-image: -o-linear-gradient(bottom, #ccc 0%, #eee 60%); + border-left: 1px solid #aaa; + display: block; + height: 100%; + position: absolute; + right: 0; + top: 0; + width: 18px; +} +div.chzn-container a.chzn-single div b { + background: url('chosen-sprite.png') no-repeat 0 1px; + display: block; + width: 100%; + height: 100%; +} +div.chzn-container div.chzn-search { + padding: 3px 4px; + margin: 0px; + white-space: nowrap; +} +div.chzn-container div.chzn-search input { + background: url('chosen-sprite.png') no-repeat 97% -35px, #ffffff; + background: url('chosen-sprite.png') no-repeat 97% -35px, -webkit-gradient(linear, left bottom, left top, color-stop(0.85, white), color-stop(0.99, #eeeeee)); + background: url('chosen-sprite.png') no-repeat 97% -35px, -moz-linear-gradient(center bottom, white 85%, #eeeeee 99%); + background: url('chosen-sprite.png') no-repeat 97% -35px, -o-linear-gradient(bottom, white 85%, #eeeeee 99%); + -moz-border-radius: 0px; + -webkit-border-radius: 0px; + -o-border-radius: 0px; + -ms-border-radius: 0px; + -khtml-border-radius: 0px; + border-radius: 0px; + margin: 1px 0; + outline: 0; +} + + +/* Multi */ +div.chzn-container ul.chzn-choices { + background: #fff; + background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0.85, white), color-stop(0.99, #eeeeee)); + background-image: -moz-linear-gradient(center bottom, white 85%, #eeeeee 99%); + background-image: -o-linear-gradient(bottom, white 85%, #eeeeee 99%); + margin: 0; + cursor: text; + border: 1px solid #aaa; + overflow: hidden; + height: auto !important; + height: 1%; + padding: 0; + position: relative; +} +div.chzn-container ul.chzn-choices:focus { + border-color: #058cf5; + -moz-box-shadow: 0px 0px 5px #999; + -webkit-box-shadow: 0px 0px 5px #999; + box-shadow: 0px 0px 5px #999; +} +div.chzn-container ul.chzn-choices li { + float: left; + list-style-type: none; + margin: 0px; +} +div.chzn-container ul.chzn-choices li.search-field { + margin: 0px; + white-space: nowrap; + padding: 0px; +} +div.chzn-container ul.chzn-choices li.search-field input { + color: #666; + background: transparent !important; + border: 0px !important; + padding: 5px; + margin: 1px 0; + outline: 0; + -webkit-box-shadow: none; + -moz-box-shadow: none; + box-shadow: none; +} +div.chzn-container ul.chzn-choices li.search-field input.default { + color: #999; +} +div.chzn-container ul.chzn-choices li.search-choice { + -moz-border-radius: 3px; + -webkit-border-radius: 3px; + border-radius: 3px; + background: #e4e4e4; + background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, #e4e4e4), color-stop(0.7, #eeeeee)); + background-image: -moz-linear-gradient(center bottom, #e4e4e4 0%, #eeeeee 70%); + background-image: -o-linear-gradient(bottom, #e4e4e4 0%, #eeeeee 70%); + color: #333; + border: 1px solid #b4b4b4; + line-height: 13px; + padding: 3px 19px 3px 6px; + position: relative; + margin: 3px 0px 3px 5px; +} +div.chzn-container ul.chzn-choices li.search-choice span { + cursor: default; +} +div.chzn-container ul.chzn-choices li.search-choice.search-choice-focus { + background: #d4d4d4; +} +div.chzn-container ul.chzn-choices li.search-choice a.search-choice-close { + position: absolute; + right: 5px; + top: 6px; + display: block; + width: 8px; + height: 9px; + font-size: 1px; + background: url(chosen-sprite.png) right top no-repeat; +} +div.chzn-container ul.chzn-choices li.search-choice a.search-choice-close:hover { + background-position: right -9px; +} +div.chzn-container ul.chzn-choices li.search-choice.search-choice-focus a.search-choice-close { + background-position: right -9px; +} + + +/* Results */ +div.chzn-container ul.chzn-results { + margin: 0 4px 4px 0; + max-height: 190px; + padding: 0 0 0 4px; + position: relative; + overflow-x: hidden; + overflow-y: auto; + z-index: 20; +} +div.chzn-container-multi ul.chzn-results { + margin: -1px 0 0; + padding: 0; +} +div.chzn-container-multi ul.chzn-results li { + border-left: 0px !important; + border-right: 0px !important; +} +div.chzn-container ul.chzn-results li { + line-height: 80%; + padding: 7px 7px 8px; + z-index: 22; + margin: 0; + list-style-type: none; +} +div.chzn-container ul.chzn-results li.active-result { + cursor: pointer; +} +div.chzn-container ul.chzn-results li em { + font-style: normal; + background: #FEFFDC; +} +div.chzn-container ul.chzn-results li.highlighted { + background: #3875d7; + color: #fff; +} +div.chzn-container ul.chzn-results li.highlighted em { + background: transparent; +} +div.chzn-container ul.chzn-results li.no-results { + background: #F4F4F4; +} +div.chzn-container ul.chzn-results li.group-result { + cursor: default; + color: #999; + font-weight: bold; +} +div.chzn-container ul.chzn-results li.group-option { + padding-left: 20px; +} + +div.chzn-container-multi div.chzn-drop li.result-selected { + display: none; +} + + + +/* Active */ +div.chzn-container-active a.chzn-single { + -webkit-box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.3); + -moz-box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.3); + box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.3); + border: 1px solid #5897fb; +} +div.chzn-container-active a.chzn-single-with-drop { + border: 1px solid #aaa; + border-width: 1px 1px 1px; + -moz-box-shadow: 0px 1px 0px #FFF inset; + -webkit-box-shadow: 0px 1px 0px #FFF inset; + box-shadow: 0px 1px 0px #FFF inset; + background: #EEE; + background-image: -webkit-gradient(linear, left bottom, left top, color-stop(0, white), color-stop(0.5, #eeeeee)); + background-image: -moz-linear-gradient(center bottom, white 0%, #eeeeee 50%); + background-image: -o-linear-gradient(bottom, white 0%, #eeeeee 50%); + -webkit-border-bottom-left-radius: 0px; + -webkit-border-bottom-right-radius: 0px; + -moz-border-radius-bottomleft: 0px; + -moz-border-radius-bottomright: 0px; + border-bottom-left-radius: 0px; + border-bottom-right-radius: 0px; +} +div.chzn-container-active a.chzn-single-with-drop div { + background: transparent; + border-left: none; +} +div.chzn-container-active a.chzn-single-with-drop div b { + background-position: -18px 1px; +} +div.chzn-container-active ul.chzn-choices { + z-index: 21; + -webkit-box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.3); + -moz-box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.3); + box-shadow: 0px 0px 5px rgba(0, 0, 0, 0.3); + border: 1px solid #5897fb; +} +div.chzn-container-active ul.chzn-choices input { + color: #111 !important; +} diff --git a/usr/local/www/javascript/chosen/chosen.jquery.js b/usr/local/www/javascript/chosen/chosen.jquery.js new file mode 100644 index 0000000..21e822a --- /dev/null +++ b/usr/local/www/javascript/chosen/chosen.jquery.js @@ -0,0 +1,755 @@ +(function() { + /* + Chosen, a Select Box Enhancer for jQuery and Protoype + by Patrick Filler for Harvest, http://getharvest.com + + Available for use under the MIT License, http://en.wikipedia.org/wiki/MIT_License + + Copyright (c) 2011 by Harvest + */ var $, Chosen, SelectParser, get_side_border_padding, root; + var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; + root = typeof exports !== "undefined" && exports !== null ? exports : this; + $ = jQuery; + $.fn.extend({ + chosen: function(data, options) { + return $(this).each(function(input_field) { + if (!($(this)).hasClass("chzn-done")) { + return new Chosen(this, data, options); + } + }); + } + }); + Chosen = (function() { + function Chosen(elmn) { + this.set_default_values(); + this.form_field = elmn; + this.form_field_jq = $(this.form_field); + this.is_multiple = this.form_field.multiple; + this.default_text_default = this.form_field.multiple ? "Select Some Options" : "Select an Option"; + this.set_up_html(); + this.register_observers(); + this.form_field_jq.addClass("chzn-done"); + } + Chosen.prototype.set_default_values = function() { + this.click_test_action = __bind(function(evt) { + return this.test_active_click(evt); + }, this); + this.active_field = false; + this.mouse_on_container = false; + this.results_showing = false; + this.result_highlighted = null; + this.result_single_selected = null; + return this.choices = 0; + }; + Chosen.prototype.set_up_html = function() { + var container_div, dd_top, dd_width, sf_width; + this.container_id = this.form_field.id + "_chzn"; + this.f_width = this.form_field_jq.width(); + this.default_text = this.form_field_jq.attr('title') ? this.form_field_jq.attr('title') : this.default_text_default; + container_div = $("<div />", { + id: this.container_id, + "class": 'chzn-container', + style: 'width: ' + this.f_width + 'px;' + }); + if (this.is_multiple) { + container_div.html('<ul class="chzn-choices"><li class="search-field"><input type="text" value="' + this.default_text + '" class="default" style="width:25px;" /></li></ul><div class="chzn-drop" style="left:-9000px;"><ul class="chzn-results"></ul></div>'); + } else { + container_div.html('<a href="javascript:void(0)" class="chzn-single"><span>' + this.default_text + '</span><div><b></b></div></a><div class="chzn-drop" style="left:-9000px;"><div class="chzn-search"><input type="text" /></div><ul class="chzn-results"></ul></div>'); + } + this.form_field_jq.hide().after(container_div); + this.container = $('#' + this.container_id); + this.container.addClass("chzn-container-" + (this.is_multiple ? "multi" : "single")); + this.dropdown = this.container.find('div.chzn-drop').first(); + dd_top = this.container.height(); + dd_width = this.f_width - get_side_border_padding(this.dropdown); + this.dropdown.css({ + "width": dd_width + "px", + "top": dd_top + "px" + }); + this.search_field = this.container.find('input').first(); + this.search_results = this.container.find('ul.chzn-results').first(); + this.search_field_scale(); + this.search_no_results = this.container.find('li.no-results').first(); + if (this.is_multiple) { + this.search_choices = this.container.find('ul.chzn-choices').first(); + this.search_container = this.container.find('li.search-field').first(); + } else { + this.search_container = this.container.find('div.chzn-search').first(); + this.selected_item = this.container.find('.chzn-single').first(); + sf_width = dd_width - get_side_border_padding(this.search_container) - get_side_border_padding(this.search_field); + this.search_field.css({ + "width": sf_width + "px" + }); + } + this.results_build(); + return this.set_tab_index(); + }; + Chosen.prototype.register_observers = function() { + this.container.click(__bind(function(evt) { + return this.container_click(evt); + }, this)); + this.container.mouseenter(__bind(function(evt) { + return this.mouse_enter(evt); + }, this)); + this.container.mouseleave(__bind(function(evt) { + return this.mouse_leave(evt); + }, this)); + this.search_results.click(__bind(function(evt) { + return this.search_results_click(evt); + }, this)); + this.search_results.mouseover(__bind(function(evt) { + return this.search_results_mouseover(evt); + }, this)); + this.search_results.mouseout(__bind(function(evt) { + return this.search_results_mouseout(evt); + }, this)); + this.form_field_jq.bind("liszt:updated", __bind(function(evt) { + return this.results_update_field(evt); + }, this)); + this.search_field.blur(__bind(function(evt) { + return this.input_blur(evt); + }, this)); + this.search_field.keyup(__bind(function(evt) { + return this.keyup_checker(evt); + }, this)); + this.search_field.keydown(__bind(function(evt) { + return this.keydown_checker(evt); + }, this)); + if (this.is_multiple) { + this.search_choices.click(__bind(function(evt) { + return this.choices_click(evt); + }, this)); + return this.search_field.focus(__bind(function(evt) { + return this.input_focus(evt); + }, this)); + } else { + return this.selected_item.focus(__bind(function(evt) { + return this.activate_field(evt); + }, this)); + } + }; + Chosen.prototype.container_click = function(evt) { + if (evt && evt.type === "click") { + evt.stopPropagation(); + } + if (!this.pending_destroy_click) { + if (!this.active_field) { + if (this.is_multiple) { + this.search_field.val(""); + } + $(document).click(this.click_test_action); + this.results_show(); + } else if (!this.is_multiple && evt && ($(evt.target) === this.selected_item || $(evt.target).parents("a.chzn-single").length)) { + evt.preventDefault(); + this.results_toggle(); + } + return this.activate_field(); + } else { + return this.pending_destroy_click = false; + } + }; + Chosen.prototype.mouse_enter = function() { + return this.mouse_on_container = true; + }; + Chosen.prototype.mouse_leave = function() { + return this.mouse_on_container = false; + }; + Chosen.prototype.input_focus = function(evt) { + if (!this.active_field) { + return setTimeout((__bind(function() { + return this.container_click(); + }, this)), 50); + } + }; + Chosen.prototype.input_blur = function(evt) { + if (!this.mouse_on_container) { + this.active_field = false; + return setTimeout((__bind(function() { + return this.blur_test(); + }, this)), 100); + } + }; + Chosen.prototype.blur_test = function(evt) { + if (!this.active_field && this.container.hasClass("chzn-container-active")) { + return this.close_field(); + } + }; + Chosen.prototype.close_field = function() { + $(document).unbind("click", this.click_test_action); + if (!this.is_multiple) { + this.selected_item.attr("tabindex", this.search_field.attr("tabindex")); + this.search_field.attr("tabindex", -1); + } + this.active_field = false; + this.results_hide(); + this.container.removeClass("chzn-container-active"); + this.winnow_results_clear(); + this.clear_backstroke(); + this.show_search_field_default(); + return this.search_field_scale(); + }; + Chosen.prototype.activate_field = function() { + if (!this.is_multiple && !this.active_field) { + this.search_field.attr("tabindex", this.selected_item.attr("tabindex")); + this.selected_item.attr("tabindex", -1); + } + this.container.addClass("chzn-container-active"); + this.active_field = true; + this.search_field.val(this.search_field.val()); + return this.search_field.focus(); + }; + Chosen.prototype.test_active_click = function(evt) { + if ($(evt.target).parents('#' + this.container.id).length) { + return this.active_field = true; + } else { + return this.close_field(); + } + }; + Chosen.prototype.results_build = function() { + var content, data, startTime, _i, _len, _ref; + startTime = new Date(); + this.parsing = true; + this.results_data = SelectParser.select_to_array(this.form_field); + if (this.is_multiple && this.choices > 0) { + this.search_choices.find("li.search-choice").remove(); + this.choices = 0; + } else if (!this.is_multiple) { + this.selected_item.find("span").text(this.default_text); + } + content = ''; + _ref = this.results_data; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + data = _ref[_i]; + if (data.group) { + content += this.result_add_group(data); + } else if (!data.empty) { + content += this.result_add_option(data); + if (data.selected && this.is_multiple) { + this.choice_build(data); + } else if (data.selected && !this.is_multiple) { + this.selected_item.find("span").text(data.text); + } + } + } + this.show_search_field_default(); + this.search_field_scale(); + this.search_results.html(content); + return this.parsing = false; + }; + Chosen.prototype.result_add_group = function(group) { + if (!group.disabled) { + group.dom_id = this.form_field.id + "chzn_g_" + group.array_index; + return '<li id="' + group.dom_id + '" class="group-result">' + $("<div />").text(group.label).html() + '</li>'; + } else { + return ""; + } + }; + Chosen.prototype.result_add_option = function(option) { + var classes; + if (!option.disabled) { + option.dom_id = this.form_field.id + "chzn_o_" + option.array_index; + classes = option.selected && this.is_multiple ? [] : ["active-result"]; + if (option.selected) { + classes.push("result-selected"); + } + if (option.group_array_index != null) { + classes.push("group-option"); + } + return '<li id="' + option.dom_id + '" class="' + classes.join(' ') + '">' + $("<div />").text(option.text).html() + '</li>'; + } else { + return ""; + } + }; + Chosen.prototype.results_update_field = function() { + this.result_clear_highlight(); + this.result_single_selected = null; + return this.results_build(); + }; + Chosen.prototype.result_do_highlight = function(el) { + var high_bottom, high_top, maxHeight, visible_bottom, visible_top; + if (el.length) { + this.result_clear_highlight(); + this.result_highlight = el; + this.result_highlight.addClass("highlighted"); + maxHeight = parseInt(this.search_results.css("maxHeight"), 10); + visible_top = this.search_results.scrollTop(); + visible_bottom = maxHeight + visible_top; + high_top = this.result_highlight.position().top + this.search_results.scrollTop(); + high_bottom = high_top + this.result_highlight.outerHeight(); + if (high_bottom >= visible_bottom) { + return this.search_results.scrollTop((high_bottom - maxHeight) > 0 ? high_bottom - maxHeight : 0); + } else if (high_top < visible_top) { + return this.search_results.scrollTop(high_top); + } + } + }; + Chosen.prototype.result_clear_highlight = function() { + if (this.result_highlight) { + this.result_highlight.removeClass("highlighted"); + } + return this.result_highlight = null; + }; + Chosen.prototype.results_toggle = function() { + if (this.results_showing) { + return this.results_hide(); + } else { + return this.results_show(); + } + }; + Chosen.prototype.results_show = function() { + var dd_top; + if (!this.is_multiple) { + this.selected_item.addClass("chzn-single-with-drop"); + if (this.result_single_selected) { + this.result_do_highlight(this.result_single_selected); + } + } + dd_top = this.is_multiple ? this.container.height() : this.container.height() - 1; + this.dropdown.css({ + "top": dd_top + "px", + "left": 0 + }); + this.results_showing = true; + this.search_field.focus(); + this.search_field.val(this.search_field.val()); + return this.winnow_results(); + }; + Chosen.prototype.results_hide = function() { + if (!this.is_multiple) { + this.selected_item.removeClass("chzn-single-with-drop"); + } + this.result_clear_highlight(); + this.dropdown.css({ + "left": "-9000px" + }); + return this.results_showing = false; + }; + Chosen.prototype.set_tab_index = function(el) { + var ti; + if (this.form_field_jq.attr("tabindex")) { + ti = this.form_field_jq.attr("tabindex"); + this.form_field_jq.attr("tabindex", -1); + if (this.is_multiple) { + return this.search_field.attr("tabindex", ti); + } else { + this.selected_item.attr("tabindex", ti); + return this.search_field.attr("tabindex", -1); + } + } + }; + Chosen.prototype.show_search_field_default = function() { + if (this.is_multiple && this.choices < 1 && !this.active_field) { + this.search_field.val(this.default_text); + return this.search_field.addClass("default"); + } else { + this.search_field.val(""); + return this.search_field.removeClass("default"); + } + }; + Chosen.prototype.search_results_click = function(evt) { + var target; + target = $(evt.target).hasClass("active-result") ? $(evt.target) : $(evt.target).parents(".active-result").first(); + if (target.length) { + this.result_highlight = target; + return this.result_select(); + } + }; + Chosen.prototype.search_results_mouseover = function(evt) { + var target; + target = $(evt.target).hasClass("active-result") ? $(evt.target) : $(evt.target).parents(".active-result").first(); + if (target) { + return this.result_do_highlight(target); + } + }; + Chosen.prototype.search_results_mouseout = function(evt) { + if ($(evt.target).hasClass("active-result" || $(evt.target).parents('.active-result').first())) { + return this.result_clear_highlight(); + } + }; + Chosen.prototype.choices_click = function(evt) { + evt.preventDefault(); + if (this.active_field && !($(evt.target).hasClass("search-choice" || $(evt.target).parents('.search-choice').first)) && !this.results_showing) { + return this.results_show(); + } + }; + Chosen.prototype.choice_build = function(item) { + var choice_id, link; + choice_id = this.form_field.id + "_chzn_c_" + item.array_index; + this.choices += 1; + this.search_container.before('<li class="search-choice" id="' + choice_id + '"><span>' + item.text + '</span><a href="javascript:void(0)" class="search-choice-close" rel="' + item.array_index + '"></a></li>'); + link = $('#' + choice_id).find("a").first(); + return link.click(__bind(function(evt) { + return this.choice_destroy_link_click(evt); + }, this)); + }; + Chosen.prototype.choice_destroy_link_click = function(evt) { + evt.preventDefault(); + this.pending_destroy_click = true; + return this.choice_destroy($(evt.target)); + }; + Chosen.prototype.choice_destroy = function(link) { + this.choices -= 1; + this.show_search_field_default(); + if (this.is_multiple && this.choices > 0 && this.search_field.val().length < 1) { + this.results_hide(); + } + this.result_deselect(link.attr("rel")); + return link.parents('li').first().remove(); + }; + Chosen.prototype.result_select = function() { + var high, high_id, item, position; + if (this.result_highlight) { + high = this.result_highlight; + high_id = high.attr("id"); + this.result_clear_highlight(); + high.addClass("result-selected"); + if (this.is_multiple) { + this.result_deactivate(high); + } else { + this.result_single_selected = high; + } + position = high_id.substr(high_id.lastIndexOf("_") + 1); + item = this.results_data[position]; + item.selected = true; + this.form_field.options[item.options_index].selected = true; + if (this.is_multiple) { + this.choice_build(item); + } else { + this.selected_item.find("span").first().text(item.text); + } + this.results_hide(); + this.search_field.val(""); + this.form_field_jq.trigger("change"); + return this.search_field_scale(); + } + }; + Chosen.prototype.result_activate = function(el) { + return el.addClass("active-result").show(); + }; + Chosen.prototype.result_deactivate = function(el) { + return el.removeClass("active-result").hide(); + }; + Chosen.prototype.result_deselect = function(pos) { + var result, result_data; + result_data = this.results_data[pos]; + result_data.selected = false; + this.form_field.options[result_data.options_index].selected = false; + result = $("#" + this.form_field.id + "chzn_o_" + pos); + result.removeClass("result-selected").addClass("active-result").show(); + this.result_clear_highlight(); + this.winnow_results(); + this.form_field_jq.trigger("change"); + return this.search_field_scale(); + }; + Chosen.prototype.results_search = function(evt) { + if (this.results_showing) { + return this.winnow_results(); + } else { + return this.results_show(); + } + }; + Chosen.prototype.winnow_results = function() { + var found, option, part, parts, regex, result_id, results, searchText, startTime, startpos, text, zregex, _i, _j, _len, _len2, _ref; + startTime = new Date(); + this.no_results_clear(); + results = 0; + searchText = this.search_field.val() === this.default_text ? "" : $.trim(this.search_field.val()); + regex = new RegExp('^' + searchText.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"), 'i'); + zregex = new RegExp(searchText.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"), 'i'); + _ref = this.results_data; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + option = _ref[_i]; + if (!option.disabled && !option.empty) { + if (option.group) { + $('#' + option.dom_id).hide(); + } else if (!(this.is_multiple && option.selected)) { + found = false; + result_id = option.dom_id; + if (regex.test(option.text)) { + found = true; + results += 1; + } else if (option.text.indexOf(" ") >= 0 || option.text.indexOf("[") === 0) { + parts = option.text.replace(/\[|\]/g, "").split(" "); + if (parts.length) { + for (_j = 0, _len2 = parts.length; _j < _len2; _j++) { + part = parts[_j]; + if (regex.test(part)) { + found = true; + results += 1; + } + } + } + } + if (found) { + if (searchText.length) { + startpos = option.text.search(zregex); + text = option.text.substr(0, startpos + searchText.length) + '</em>' + option.text.substr(startpos + searchText.length); + text = text.substr(0, startpos) + '<em>' + text.substr(startpos); + } else { + text = option.text; + } + if ($("#" + result_id).html !== text) { + $("#" + result_id).html(text); + } + this.result_activate($("#" + result_id)); + if (option.group_array_index != null) { + $("#" + this.results_data[option.group_array_index].dom_id).show(); + } + } else { + if (this.result_highlight && result_id === this.result_highlight.attr('id')) { + this.result_clear_highlight(); + } + this.result_deactivate($("#" + result_id)); + } + } + } + } + if (results < 1 && searchText.length) { + return this.no_results(searchText); + } else { + return this.winnow_results_set_highlight(); + } + }; + Chosen.prototype.winnow_results_clear = function() { + var li, lis, _i, _len, _results; + this.search_field.val(""); + lis = this.search_results.find("li"); + _results = []; + for (_i = 0, _len = lis.length; _i < _len; _i++) { + li = lis[_i]; + li = $(li); + _results.push(li.hasClass("group-result") ? li.show() : !this.is_multiple || !li.hasClass("result-selected") ? this.result_activate(li) : void 0); + } + return _results; + }; + Chosen.prototype.winnow_results_set_highlight = function() { + var do_high; + if (!this.result_highlight) { + do_high = this.search_results.find(".active-result").first(); + if (do_high) { + return this.result_do_highlight(do_high); + } + } + }; + Chosen.prototype.no_results = function(terms) { + var no_results_html; + no_results_html = $('<li class="no-results">No results match "<span></span>"</li>'); + no_results_html.find("span").first().text(terms); + return this.search_results.append(no_results_html); + }; + Chosen.prototype.no_results_clear = function() { + return this.search_results.find(".no-results").remove(); + }; + Chosen.prototype.keydown_arrow = function() { + var first_active, next_sib; + if (!this.result_highlight) { + first_active = this.search_results.find("li.active-result").first(); + if (first_active) { + this.result_do_highlight($(first_active)); + } + } else if (this.results_showing) { + next_sib = this.result_highlight.nextAll("li.active-result").first(); + if (next_sib) { + this.result_do_highlight(next_sib); + } + } + if (!this.results_showing) { + return this.results_show(); + } + }; + Chosen.prototype.keyup_arrow = function() { + var prev_sibs; + if (!this.results_showing && !this.is_multiple) { + return this.results_show(); + } else if (this.result_highlight) { + prev_sibs = this.result_highlight.prevAll("li.active-result"); + if (prev_sibs.length) { + return this.result_do_highlight(prev_sibs.first()); + } else { + if (this.choices > 0) { + this.results_hide(); + } + return this.result_clear_highlight(); + } + } + }; + Chosen.prototype.keydown_backstroke = function() { + if (this.pending_backstroke) { + this.choice_destroy(this.pending_backstroke.find("a").first()); + return this.clear_backstroke(); + } else { + this.pending_backstroke = this.search_container.siblings("li.search-choice").last(); + return this.pending_backstroke.addClass("search-choice-focus"); + } + }; + Chosen.prototype.clear_backstroke = function() { + if (this.pending_backstroke) { + this.pending_backstroke.removeClass("search-choice-focus"); + } + return this.pending_backstroke = null; + }; + Chosen.prototype.keyup_checker = function(evt) { + var stroke, _ref; + stroke = (_ref = evt.which) != null ? _ref : evt.keyCode; + this.search_field_scale(); + switch (stroke) { + case 8: + if (this.is_multiple && this.backstroke_length < 1 && this.choices > 0) { + return this.keydown_backstroke(); + } else if (!this.pending_backstroke) { + this.result_clear_highlight(); + return this.results_search(); + } + break; + case 13: + evt.preventDefault(); + if (this.results_showing) { + return this.result_select(); + } + break; + case 27: + if (this.results_showing) { + return this.results_hide(); + } + break; + case 9: + case 38: + case 40: + case 16: + break; + default: + return this.results_search(); + } + }; + Chosen.prototype.keydown_checker = function(evt) { + var stroke, _ref; + stroke = (_ref = evt.which) != null ? _ref : evt.keyCode; + this.search_field_scale(); + if (stroke !== 8 && this.pending_backstroke) { + this.clear_backstroke(); + } + switch (stroke) { + case 8: + this.backstroke_length = this.search_field.val().length; + break; + case 9: + this.mouse_on_container = false; + break; + case 13: + evt.preventDefault(); + break; + case 38: + evt.preventDefault(); + this.keyup_arrow(); + break; + case 40: + this.keydown_arrow(); + break; + } + }; + Chosen.prototype.search_field_scale = function() { + var dd_top, div, h, style, style_block, styles, w, _i, _len; + if (this.is_multiple) { + h = 0; + w = 0; + style_block = "position:absolute; left: -1000px; top: -1000px; display:none;"; + styles = ['font-size', 'font-style', 'font-weight', 'font-family', 'line-height', 'text-transform', 'letter-spacing']; + for (_i = 0, _len = styles.length; _i < _len; _i++) { + style = styles[_i]; + style_block += style + ":" + this.search_field.css(style) + ";"; + } + div = $('<div />', { + 'style': style_block + }); + div.text(this.search_field.val()); + $('body').append(div); + w = div.width() + 25; + div.remove(); + if (w > this.f_width - 10) { + w = this.f_width - 10; + } + this.search_field.css({ + 'width': w + 'px' + }); + dd_top = this.container.height(); + return this.dropdown.css({ + "top": dd_top + "px" + }); + } + }; + return Chosen; + })(); + get_side_border_padding = function(elmt) { + var side_border_padding; + return side_border_padding = elmt.outerWidth() - elmt.width(); + }; + root.get_side_border_padding = get_side_border_padding; + SelectParser = (function() { + function SelectParser() { + this.options_index = 0; + this.parsed = []; + } + SelectParser.prototype.add_node = function(child) { + if (child.nodeName === "OPTGROUP") { + return this.add_group(child); + } else { + return this.add_option(child); + } + }; + SelectParser.prototype.add_group = function(group) { + var group_position, option, _i, _len, _ref, _results; + group_position = this.parsed.length; + this.parsed.push({ + array_index: group_position, + group: true, + label: group.label, + children: 0, + disabled: group.disabled + }); + _ref = group.childNodes; + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + option = _ref[_i]; + _results.push(this.add_option(option, group_position, group.disabled)); + } + return _results; + }; + SelectParser.prototype.add_option = function(option, group_position, group_disabled) { + if (option.nodeName === "OPTION") { + if (option.text !== "") { + if (group_position != null) { + this.parsed[group_position].children += 1; + } + this.parsed.push({ + array_index: this.parsed.length, + options_index: this.options_index, + value: option.value, + text: option.text, + selected: option.selected, + disabled: group_disabled === true ? group_disabled : option.disabled, + group_array_index: group_position + }); + } else { + this.parsed.push({ + array_index: this.parsed.length, + options_index: this.options_index, + empty: true + }); + } + return this.options_index += 1; + } + }; + return SelectParser; + })(); + SelectParser.select_to_array = function(select) { + var child, parser, _i, _len, _ref; + parser = new SelectParser(); + _ref = select.childNodes; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + child = _ref[_i]; + parser.add_node(child); + } + return parser.parsed; + }; + root.SelectParser = SelectParser; +}).call(this); diff --git a/usr/local/www/javascript/chosen/chosen.jquery.min.js b/usr/local/www/javascript/chosen/chosen.jquery.min.js new file mode 100644 index 0000000..ae69f22 --- /dev/null +++ b/usr/local/www/javascript/chosen/chosen.jquery.min.js @@ -0,0 +1,9 @@ +/* +Chosen, a Select Box Enhancer for jQuery and Protoype +by Patrick Filler for Harvest, http://getharvest.com + +Available for use under the MIT License, http://en.wikipedia.org/wiki/MIT_License + +Copyright (c) 2011 by Harvest +*/ +(function(){var $,Chosen,SelectParser,get_side_border_padding,root;var __bind=function(fn,me){return function(){return fn.apply(me,arguments);};};root=typeof exports!=="undefined"&&exports!==null?exports:this;$=jQuery;$.fn.extend({chosen:function(data,options){return $(this).each(function(input_field){if(!($(this)).hasClass("chzn-done")){return new Chosen(this,data,options);}});}});Chosen=(function(){function Chosen(elmn){this.set_default_values();this.form_field=elmn;this.form_field_jq=$(this.form_field);this.is_multiple=this.form_field.multiple;this.default_text_default=this.form_field.multiple?"Select Some Options":"Select an Option";this.set_up_html();this.register_observers();this.form_field_jq.addClass("chzn-done");}Chosen.prototype.set_default_values=function(){this.click_test_action=__bind(function(evt){return this.test_active_click(evt);},this);this.active_field=false;this.mouse_on_container=false;this.results_showing=false;this.result_highlighted=null;this.result_single_selected=null;return this.choices=0;};Chosen.prototype.set_up_html=function(){var container_div,dd_top,dd_width,sf_width;this.container_id=this.form_field.id+"_chzn";this.f_width=this.form_field_jq.width();this.default_text=this.form_field_jq.attr("title")?this.form_field_jq.attr("title"):this.default_text_default;container_div=$("<div />",{id:this.container_id,"class":"chzn-container",style:"width: "+this.f_width+"px;"});if(this.is_multiple){container_div.html('<ul class="chzn-choices"><li class="search-field"><input type="text" value="'+this.default_text+'" class="default" style="width:25px;" /></li></ul><div class="chzn-drop" style="left:-9000px;"><ul class="chzn-results"></ul></div>');}else{container_div.html('<a href="javascript:void(0)" class="chzn-single"><span>'+this.default_text+'</span><div><b></b></div></a><div class="chzn-drop" style="left:-9000px;"><div class="chzn-search"><input type="text" /></div><ul class="chzn-results"></ul></div>');}this.form_field_jq.hide().after(container_div);this.container=$("#"+this.container_id);this.container.addClass("chzn-container-"+(this.is_multiple?"multi":"single"));this.dropdown=this.container.find("div.chzn-drop").first();dd_top=this.container.height();dd_width=this.f_width-get_side_border_padding(this.dropdown);this.dropdown.css({width:dd_width+"px",top:dd_top+"px"});this.search_field=this.container.find("input").first();this.search_results=this.container.find("ul.chzn-results").first();this.search_field_scale();this.search_no_results=this.container.find("li.no-results").first();if(this.is_multiple){this.search_choices=this.container.find("ul.chzn-choices").first();this.search_container=this.container.find("li.search-field").first();}else{this.search_container=this.container.find("div.chzn-search").first();this.selected_item=this.container.find(".chzn-single").first();sf_width=dd_width-get_side_border_padding(this.search_container)-get_side_border_padding(this.search_field);this.search_field.css({width:sf_width+"px"});}this.results_build();return this.set_tab_index();};Chosen.prototype.register_observers=function(){this.container.click(__bind(function(evt){return this.container_click(evt);},this));this.container.mouseenter(__bind(function(evt){return this.mouse_enter(evt);},this));this.container.mouseleave(__bind(function(evt){return this.mouse_leave(evt);},this));this.search_results.click(__bind(function(evt){return this.search_results_click(evt);},this));this.search_results.mouseover(__bind(function(evt){return this.search_results_mouseover(evt);},this));this.search_results.mouseout(__bind(function(evt){return this.search_results_mouseout(evt);},this));this.form_field_jq.bind("liszt:updated",__bind(function(evt){return this.results_update_field(evt);},this));this.search_field.blur(__bind(function(evt){return this.input_blur(evt);},this));this.search_field.keyup(__bind(function(evt){return this.keyup_checker(evt);},this));this.search_field.keydown(__bind(function(evt){return this.keydown_checker(evt);},this));if(this.is_multiple){this.search_choices.click(__bind(function(evt){return this.choices_click(evt);},this));return this.search_field.focus(__bind(function(evt){return this.input_focus(evt);},this));}else{return this.selected_item.focus(__bind(function(evt){return this.activate_field(evt);},this));}};Chosen.prototype.container_click=function(evt){if(evt&&evt.type==="click"){evt.stopPropagation();}if(!this.pending_destroy_click){if(!this.active_field){if(this.is_multiple){this.search_field.val("");}$(document).click(this.click_test_action);this.results_show();}else{if(!this.is_multiple&&evt&&($(evt.target)===this.selected_item||$(evt.target).parents("a.chzn-single").length)){evt.preventDefault();this.results_toggle();}}return this.activate_field();}else{return this.pending_destroy_click=false;}};Chosen.prototype.mouse_enter=function(){return this.mouse_on_container=true;};Chosen.prototype.mouse_leave=function(){return this.mouse_on_container=false;};Chosen.prototype.input_focus=function(evt){if(!this.active_field){return setTimeout((__bind(function(){return this.container_click();},this)),50);}};Chosen.prototype.input_blur=function(evt){if(!this.mouse_on_container){this.active_field=false;return setTimeout((__bind(function(){return this.blur_test();},this)),100);}};Chosen.prototype.blur_test=function(evt){if(!this.active_field&&this.container.hasClass("chzn-container-active")){return this.close_field();}};Chosen.prototype.close_field=function(){$(document).unbind("click",this.click_test_action);if(!this.is_multiple){this.selected_item.attr("tabindex",this.search_field.attr("tabindex"));this.search_field.attr("tabindex",-1);}this.active_field=false;this.results_hide();this.container.removeClass("chzn-container-active");this.winnow_results_clear();this.clear_backstroke();this.show_search_field_default();return this.search_field_scale();};Chosen.prototype.activate_field=function(){if(!this.is_multiple&&!this.active_field){this.search_field.attr("tabindex",this.selected_item.attr("tabindex"));this.selected_item.attr("tabindex",-1);}this.container.addClass("chzn-container-active");this.active_field=true;this.search_field.val(this.search_field.val());return this.search_field.focus();};Chosen.prototype.test_active_click=function(evt){if($(evt.target).parents("#"+this.container.id).length){return this.active_field=true;}else{return this.close_field();}};Chosen.prototype.results_build=function(){var content,data,startTime,_i,_len,_ref;startTime=new Date();this.parsing=true;this.results_data=SelectParser.select_to_array(this.form_field);if(this.is_multiple&&this.choices>0){this.search_choices.find("li.search-choice").remove();this.choices=0;}else{if(!this.is_multiple){this.selected_item.find("span").text(this.default_text);}}content="";_ref=this.results_data;for(_i=0,_len=_ref.length;_i<_len;_i++){data=_ref[_i];if(data.group){content+=this.result_add_group(data);}else{if(!data.empty){content+=this.result_add_option(data);if(data.selected&&this.is_multiple){this.choice_build(data);}else{if(data.selected&&!this.is_multiple){this.selected_item.find("span").text(data.text);}}}}}this.show_search_field_default();this.search_field_scale();this.search_results.html(content);return this.parsing=false;};Chosen.prototype.result_add_group=function(group){if(!group.disabled){group.dom_id=this.form_field.id+"chzn_g_"+group.array_index;return'<li id="'+group.dom_id+'" class="group-result">'+$("<div />").text(group.label).html()+"</li>";}else{return"";}};Chosen.prototype.result_add_option=function(option){var classes;if(!option.disabled){option.dom_id=this.form_field.id+"chzn_o_"+option.array_index;classes=option.selected&&this.is_multiple?[]:["active-result"];if(option.selected){classes.push("result-selected");}if(option.group_array_index!=null){classes.push("group-option");}return'<li id="'+option.dom_id+'" class="'+classes.join(" ")+'">'+$("<div />").text(option.text).html()+"</li>";}else{return"";}};Chosen.prototype.results_update_field=function(){this.result_clear_highlight();this.result_single_selected=null;return this.results_build();};Chosen.prototype.result_do_highlight=function(el){var high_bottom,high_top,maxHeight,visible_bottom,visible_top;if(el.length){this.result_clear_highlight();this.result_highlight=el;this.result_highlight.addClass("highlighted");maxHeight=parseInt(this.search_results.css("maxHeight"),10);visible_top=this.search_results.scrollTop();visible_bottom=maxHeight+visible_top;high_top=this.result_highlight.position().top+this.search_results.scrollTop();high_bottom=high_top+this.result_highlight.outerHeight();if(high_bottom>=visible_bottom){return this.search_results.scrollTop((high_bottom-maxHeight)>0?high_bottom-maxHeight:0);}else{if(high_top<visible_top){return this.search_results.scrollTop(high_top);}}}};Chosen.prototype.result_clear_highlight=function(){if(this.result_highlight){this.result_highlight.removeClass("highlighted");}return this.result_highlight=null;};Chosen.prototype.results_toggle=function(){if(this.results_showing){return this.results_hide();}else{return this.results_show();}};Chosen.prototype.results_show=function(){var dd_top;if(!this.is_multiple){this.selected_item.addClass("chzn-single-with-drop");if(this.result_single_selected){this.result_do_highlight(this.result_single_selected);}}dd_top=this.is_multiple?this.container.height():this.container.height()-1;this.dropdown.css({top:dd_top+"px",left:0});this.results_showing=true;this.search_field.focus();this.search_field.val(this.search_field.val());return this.winnow_results();};Chosen.prototype.results_hide=function(){if(!this.is_multiple){this.selected_item.removeClass("chzn-single-with-drop");}this.result_clear_highlight();this.dropdown.css({left:"-9000px"});return this.results_showing=false;};Chosen.prototype.set_tab_index=function(el){var ti;if(this.form_field_jq.attr("tabindex")){ti=this.form_field_jq.attr("tabindex");this.form_field_jq.attr("tabindex",-1);if(this.is_multiple){return this.search_field.attr("tabindex",ti);}else{this.selected_item.attr("tabindex",ti);return this.search_field.attr("tabindex",-1);}}};Chosen.prototype.show_search_field_default=function(){if(this.is_multiple&&this.choices<1&&!this.active_field){this.search_field.val(this.default_text);return this.search_field.addClass("default");}else{this.search_field.val("");return this.search_field.removeClass("default");}};Chosen.prototype.search_results_click=function(evt){var target;target=$(evt.target).hasClass("active-result")?$(evt.target):$(evt.target).parents(".active-result").first();if(target.length){this.result_highlight=target;return this.result_select();}};Chosen.prototype.search_results_mouseover=function(evt){var target;target=$(evt.target).hasClass("active-result")?$(evt.target):$(evt.target).parents(".active-result").first();if(target){return this.result_do_highlight(target);}};Chosen.prototype.search_results_mouseout=function(evt){if($(evt.target).hasClass("active-result"||$(evt.target).parents(".active-result").first())){return this.result_clear_highlight();}};Chosen.prototype.choices_click=function(evt){evt.preventDefault();if(this.active_field&&!($(evt.target).hasClass("search-choice"||$(evt.target).parents(".search-choice").first))&&!this.results_showing){return this.results_show();}};Chosen.prototype.choice_build=function(item){var choice_id,link;choice_id=this.form_field.id+"_chzn_c_"+item.array_index;this.choices+=1;this.search_container.before('<li class="search-choice" id="'+choice_id+'"><span>'+item.text+'</span><a href="javascript:void(0)" class="search-choice-close" rel="'+item.array_index+'"></a></li>');link=$("#"+choice_id).find("a").first();return link.click(__bind(function(evt){return this.choice_destroy_link_click(evt);},this));};Chosen.prototype.choice_destroy_link_click=function(evt){evt.preventDefault();this.pending_destroy_click=true;return this.choice_destroy($(evt.target));};Chosen.prototype.choice_destroy=function(link){this.choices-=1;this.show_search_field_default();if(this.is_multiple&&this.choices>0&&this.search_field.val().length<1){this.results_hide();}this.result_deselect(link.attr("rel"));return link.parents("li").first().remove();};Chosen.prototype.result_select=function(){var high,high_id,item,position;if(this.result_highlight){high=this.result_highlight;high_id=high.attr("id");this.result_clear_highlight();high.addClass("result-selected");if(this.is_multiple){this.result_deactivate(high);}else{this.result_single_selected=high;}position=high_id.substr(high_id.lastIndexOf("_")+1);item=this.results_data[position];item.selected=true;this.form_field.options[item.options_index].selected=true;if(this.is_multiple){this.choice_build(item);}else{this.selected_item.find("span").first().text(item.text);}this.results_hide();this.search_field.val("");this.form_field_jq.trigger("change");return this.search_field_scale();}};Chosen.prototype.result_activate=function(el){return el.addClass("active-result").show();};Chosen.prototype.result_deactivate=function(el){return el.removeClass("active-result").hide();};Chosen.prototype.result_deselect=function(pos){var result,result_data;result_data=this.results_data[pos];result_data.selected=false;this.form_field.options[result_data.options_index].selected=false;result=$("#"+this.form_field.id+"chzn_o_"+pos);result.removeClass("result-selected").addClass("active-result").show();this.result_clear_highlight();this.winnow_results();this.form_field_jq.trigger("change");return this.search_field_scale();};Chosen.prototype.results_search=function(evt){if(this.results_showing){return this.winnow_results();}else{return this.results_show();}};Chosen.prototype.winnow_results=function(){var found,option,part,parts,regex,result_id,results,searchText,startTime,startpos,text,zregex,_i,_j,_len,_len2,_ref;startTime=new Date();this.no_results_clear();results=0;searchText=this.search_field.val()===this.default_text?"":$.trim(this.search_field.val());regex=new RegExp("^"+searchText.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&"),"i");zregex=new RegExp(searchText.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&"),"i");_ref=this.results_data;for(_i=0,_len=_ref.length;_i<_len;_i++){option=_ref[_i];if(!option.disabled&&!option.empty){if(option.group){$("#"+option.dom_id).hide();}else{if(!(this.is_multiple&&option.selected)){found=false;result_id=option.dom_id;if(regex.test(option.text)){found=true;results+=1;}else{if(option.text.indexOf(" ")>=0||option.text.indexOf("[")===0){parts=option.text.replace(/\[|\]/g,"").split(" ");if(parts.length){for(_j=0,_len2=parts.length;_j<_len2;_j++){part=parts[_j];if(regex.test(part)){found=true;results+=1;}}}}}if(found){if(searchText.length){startpos=option.text.search(zregex);text=option.text.substr(0,startpos+searchText.length)+"</em>"+option.text.substr(startpos+searchText.length);text=text.substr(0,startpos)+"<em>"+text.substr(startpos);}else{text=option.text;}if($("#"+result_id).html!==text){$("#"+result_id).html(text);}this.result_activate($("#"+result_id));if(option.group_array_index!=null){$("#"+this.results_data[option.group_array_index].dom_id).show();}}else{if(this.result_highlight&&result_id===this.result_highlight.attr("id")){this.result_clear_highlight();}this.result_deactivate($("#"+result_id));}}}}}if(results<1&&searchText.length){return this.no_results(searchText);}else{return this.winnow_results_set_highlight();}};Chosen.prototype.winnow_results_clear=function(){var li,lis,_i,_len,_results;this.search_field.val("");lis=this.search_results.find("li");_results=[];for(_i=0,_len=lis.length;_i<_len;_i++){li=lis[_i];li=$(li);_results.push(li.hasClass("group-result")?li.show():!this.is_multiple||!li.hasClass("result-selected")?this.result_activate(li):void 0);}return _results;};Chosen.prototype.winnow_results_set_highlight=function(){var do_high;if(!this.result_highlight){do_high=this.search_results.find(".active-result").first();if(do_high){return this.result_do_highlight(do_high);}}};Chosen.prototype.no_results=function(terms){var no_results_html;no_results_html=$('<li class="no-results">No results match "<span></span>"</li>');no_results_html.find("span").first().text(terms);return this.search_results.append(no_results_html);};Chosen.prototype.no_results_clear=function(){return this.search_results.find(".no-results").remove();};Chosen.prototype.keydown_arrow=function(){var first_active,next_sib;if(!this.result_highlight){first_active=this.search_results.find("li.active-result").first();if(first_active){this.result_do_highlight($(first_active));}}else{if(this.results_showing){next_sib=this.result_highlight.nextAll("li.active-result").first();if(next_sib){this.result_do_highlight(next_sib);}}}if(!this.results_showing){return this.results_show();}};Chosen.prototype.keyup_arrow=function(){var prev_sibs;if(!this.results_showing&&!this.is_multiple){return this.results_show();}else{if(this.result_highlight){prev_sibs=this.result_highlight.prevAll("li.active-result");if(prev_sibs.length){return this.result_do_highlight(prev_sibs.first());}else{if(this.choices>0){this.results_hide();}return this.result_clear_highlight();}}}};Chosen.prototype.keydown_backstroke=function(){if(this.pending_backstroke){this.choice_destroy(this.pending_backstroke.find("a").first());return this.clear_backstroke();}else{this.pending_backstroke=this.search_container.siblings("li.search-choice").last();return this.pending_backstroke.addClass("search-choice-focus");}};Chosen.prototype.clear_backstroke=function(){if(this.pending_backstroke){this.pending_backstroke.removeClass("search-choice-focus");}return this.pending_backstroke=null;};Chosen.prototype.keyup_checker=function(evt){var stroke,_ref;stroke=(_ref=evt.which)!=null?_ref:evt.keyCode;this.search_field_scale();switch(stroke){case 8:if(this.is_multiple&&this.backstroke_length<1&&this.choices>0){return this.keydown_backstroke();}else{if(!this.pending_backstroke){this.result_clear_highlight();return this.results_search();}}break;case 13:evt.preventDefault();if(this.results_showing){return this.result_select();}break;case 27:if(this.results_showing){return this.results_hide();}break;case 9:case 38:case 40:case 16:break;default:return this.results_search();}};Chosen.prototype.keydown_checker=function(evt){var stroke,_ref;stroke=(_ref=evt.which)!=null?_ref:evt.keyCode;this.search_field_scale();if(stroke!==8&&this.pending_backstroke){this.clear_backstroke();}switch(stroke){case 8:this.backstroke_length=this.search_field.val().length;break;case 9:this.mouse_on_container=false;break;case 13:evt.preventDefault();break;case 38:evt.preventDefault();this.keyup_arrow();break;case 40:this.keydown_arrow();break;}};Chosen.prototype.search_field_scale=function(){var dd_top,div,h,style,style_block,styles,w,_i,_len;if(this.is_multiple){h=0;w=0;style_block="position:absolute; left: -1000px; top: -1000px; display:none;";styles=["font-size","font-style","font-weight","font-family","line-height","text-transform","letter-spacing"];for(_i=0,_len=styles.length;_i<_len;_i++){style=styles[_i];style_block+=style+":"+this.search_field.css(style)+";";}div=$("<div />",{style:style_block});div.text(this.search_field.val());$("body").append(div);w=div.width()+25;div.remove();if(w>this.f_width-10){w=this.f_width-10;}this.search_field.css({width:w+"px"});dd_top=this.container.height();return this.dropdown.css({top:dd_top+"px"});}};return Chosen;})();get_side_border_padding=function(elmt){var side_border_padding;return side_border_padding=elmt.outerWidth()-elmt.width();};root.get_side_border_padding=get_side_border_padding;SelectParser=(function(){function SelectParser(){this.options_index=0;this.parsed=[];}SelectParser.prototype.add_node=function(child){if(child.nodeName==="OPTGROUP"){return this.add_group(child);}else{return this.add_option(child);}};SelectParser.prototype.add_group=function(group){var group_position,option,_i,_len,_ref,_results;group_position=this.parsed.length;this.parsed.push({array_index:group_position,group:true,label:group.label,children:0,disabled:group.disabled});_ref=group.childNodes;_results=[];for(_i=0,_len=_ref.length;_i<_len;_i++){option=_ref[_i];_results.push(this.add_option(option,group_position,group.disabled));}return _results;};SelectParser.prototype.add_option=function(option,group_position,group_disabled){if(option.nodeName==="OPTION"){if(option.text!==""){if(group_position!=null){this.parsed[group_position].children+=1;}this.parsed.push({array_index:this.parsed.length,options_index:this.options_index,value:option.value,text:option.text,selected:option.selected,disabled:group_disabled===true?group_disabled:option.disabled,group_array_index:group_position});}else{this.parsed.push({array_index:this.parsed.length,options_index:this.options_index,empty:true});}return this.options_index+=1;}};return SelectParser;})();SelectParser.select_to_array=function(select){var child,parser,_i,_len,_ref;parser=new SelectParser();_ref=select.childNodes;for(_i=0,_len=_ref.length;_i<_len;_i++){child=_ref[_i];parser.add_node(child);}return parser.parsed;};root.SelectParser=SelectParser;}).call(this);
\ No newline at end of file diff --git a/usr/local/www/javascript/chosen/chosen.proto.js b/usr/local/www/javascript/chosen/chosen.proto.js new file mode 100644 index 0000000..e3c0fbd --- /dev/null +++ b/usr/local/www/javascript/chosen/chosen.proto.js @@ -0,0 +1,765 @@ +(function() { + /* + Chosen, a Select Box Enhancer for jQuery and Protoype + by Patrick Filler for Harvest, http://getharvest.com + + Available for use under the MIT License, http://en.wikipedia.org/wiki/MIT_License + + Copyright (c) 2011 by Harvest + */ var Chosen, SelectParser, get_side_border_padding, root; + var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; + root = typeof exports !== "undefined" && exports !== null ? exports : this; + Chosen = (function() { + function Chosen(elmn) { + this.set_default_values(); + this.form_field = elmn; + this.is_multiple = this.form_field.multiple; + this.default_text_default = this.form_field.multiple ? "Select Some Options" : "Select an Option"; + this.set_up_html(); + this.register_observers(); + } + Chosen.prototype.set_default_values = function() { + this.click_test_action = __bind(function(evt) { + return this.test_active_click(evt); + }, this); + this.active_field = false; + this.mouse_on_container = false; + this.results_showing = false; + this.result_highlighted = null; + this.result_single_selected = null; + this.choices = 0; + this.single_temp = new Template('<a href="javascript:void(0)" class="chzn-single"><span>#{default}</span><div><b></b></div></a><div class="chzn-drop" style="left:-9000px;"><div class="chzn-search"><input type="text" /></div><ul class="chzn-results"></ul></div>'); + this.multi_temp = new Template('<ul class="chzn-choices"><li class="search-field"><input type="text" value="#{default}" class="default" style="width:25px;" /></li></ul><div class="chzn-drop" style="left:-9000px;"><ul class="chzn-results"></ul></div>'); + this.choice_temp = new Template('<li class="search-choice" id="#{id}"><span>#{choice}</span><a href="javascript:void(0)" class="search-choice-close" rel="#{position}"></a></li>'); + return this.no_results_temp = new Template('<li class="no-results">No results match "<span>#{terms}</span>"</li>'); + }; + Chosen.prototype.set_up_html = function() { + var base_template, container_props, dd_top, dd_width, sf_width; + this.container_id = this.form_field.id + "_chzn"; + this.f_width = this.form_field.getStyle("width") ? parseInt(this.form_field.getStyle("width"), 10) : this.form_field.getWidth(); + container_props = { + 'id': this.container_id, + 'class': 'chzn-container', + 'style': 'width: ' + this.f_width + 'px' + }; + this.default_text = this.form_field.readAttribute('title') ? this.form_field.readAttribute('title') : this.default_text_default; + base_template = this.is_multiple ? new Element('div', container_props).update(this.multi_temp.evaluate({ + "default": this.default_text + })) : new Element('div', container_props).update(this.single_temp.evaluate({ + "default": this.default_text + })); + this.form_field.hide().insert({ + after: base_template + }); + this.container = $(this.container_id); + this.container.addClassName("chzn-container-" + (this.is_multiple ? "multi" : "single")); + this.dropdown = this.container.down('div.chzn-drop'); + dd_top = this.container.getHeight(); + dd_width = this.f_width - get_side_border_padding(this.dropdown); + this.dropdown.setStyle({ + "width": dd_width + "px", + "top": dd_top + "px" + }); + this.search_field = this.container.down('input'); + this.search_results = this.container.down('ul.chzn-results'); + this.search_field_scale(); + this.search_no_results = this.container.down('li.no-results'); + if (this.is_multiple) { + this.search_choices = this.container.down('ul.chzn-choices'); + this.search_container = this.container.down('li.search-field'); + } else { + this.search_container = this.container.down('div.chzn-search'); + this.selected_item = this.container.down('.chzn-single'); + sf_width = dd_width - get_side_border_padding(this.search_container) - get_side_border_padding(this.search_field); + this.search_field.setStyle({ + "width": sf_width + "px" + }); + } + this.results_build(); + return this.set_tab_index(); + }; + Chosen.prototype.register_observers = function() { + this.container.observe("click", __bind(function(evt) { + return this.container_click(evt); + }, this)); + this.container.observe("mouseenter", __bind(function(evt) { + return this.mouse_enter(evt); + }, this)); + this.container.observe("mouseleave", __bind(function(evt) { + return this.mouse_leave(evt); + }, this)); + this.search_results.observe("click", __bind(function(evt) { + return this.search_results_click(evt); + }, this)); + this.search_results.observe("mouseover", __bind(function(evt) { + return this.search_results_mouseover(evt); + }, this)); + this.search_results.observe("mouseout", __bind(function(evt) { + return this.search_results_mouseout(evt); + }, this)); + this.form_field.observe("liszt:updated", __bind(function(evt) { + return this.results_update_field(evt); + }, this)); + this.search_field.observe("blur", __bind(function(evt) { + return this.input_blur(evt); + }, this)); + this.search_field.observe("keyup", __bind(function(evt) { + return this.keyup_checker(evt); + }, this)); + this.search_field.observe("keydown", __bind(function(evt) { + return this.keydown_checker(evt); + }, this)); + if (this.is_multiple) { + this.search_choices.observe("click", __bind(function(evt) { + return this.choices_click(evt); + }, this)); + return this.search_field.observe("focus", __bind(function(evt) { + return this.input_focus(evt); + }, this)); + } else { + return this.selected_item.observe("focus", __bind(function(evt) { + return this.activate_field(evt); + }, this)); + } + }; + Chosen.prototype.container_click = function(evt) { + if (evt && evt.type === "click") { + evt.stop(); + } + if (!this.pending_destroy_click) { + if (!this.active_field) { + if (this.is_multiple) { + this.search_field.clear(); + } + document.observe("click", this.click_test_action); + this.results_show(); + } else if (!this.is_multiple && evt && (evt.target === this.selected_item || evt.target.up("a.chzn-single"))) { + this.results_toggle(); + } + return this.activate_field(); + } else { + return this.pending_destroy_click = false; + } + }; + Chosen.prototype.mouse_enter = function() { + return this.mouse_on_container = true; + }; + Chosen.prototype.mouse_leave = function() { + return this.mouse_on_container = false; + }; + Chosen.prototype.input_focus = function(evt) { + if (!this.active_field) { + return setTimeout(this.container_click.bind(this), 50); + } + }; + Chosen.prototype.input_blur = function(evt) { + if (!this.mouse_on_container) { + this.active_field = false; + return setTimeout(this.blur_test.bind(this), 100); + } + }; + Chosen.prototype.blur_test = function(evt) { + if (!this.active_field && this.container.hasClassName("chzn-container-active")) { + return this.close_field(); + } + }; + Chosen.prototype.close_field = function() { + document.stopObserving("click", this.click_test_action); + if (!this.is_multiple) { + this.selected_item.tabIndex = this.search_field.tabIndex; + this.search_field.tabIndex = -1; + } + this.active_field = false; + this.results_hide(); + this.container.removeClassName("chzn-container-active"); + this.winnow_results_clear(); + this.clear_backstroke(); + this.show_search_field_default(); + return this.search_field_scale(); + }; + Chosen.prototype.activate_field = function() { + if (!this.is_multiple && !this.active_field) { + this.search_field.tabIndex = this.selected_item.tabIndex; + this.selected_item.tabIndex = -1; + } + this.container.addClassName("chzn-container-active"); + this.active_field = true; + this.search_field.value = this.search_field.value; + return this.search_field.focus(); + }; + Chosen.prototype.test_active_click = function(evt) { + if (evt.target.up('#' + this.container.id)) { + return this.active_field = true; + } else { + return this.close_field(); + } + }; + Chosen.prototype.results_build = function() { + var content, data, startTime, _i, _len, _ref; + startTime = new Date(); + this.parsing = true; + this.results_data = SelectParser.select_to_array(this.form_field); + if (this.is_multiple && this.choices > 0) { + this.search_choices.select("li.search-choice").invoke("remove"); + this.choices = 0; + } else if (!this.is_multiple) { + this.selected_item.down("span").update(this.default_text); + } + content = ''; + _ref = this.results_data; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + data = _ref[_i]; + if (data.group) { + content += this.result_add_group(data); + } else if (!data.empty) { + content += this.result_add_option(data); + if (data.selected && this.is_multiple) { + this.choice_build(data); + } else if (data.selected && !this.is_multiple) { + this.selected_item.down("span").update(data.text); + } + } + } + this.show_search_field_default(); + this.search_field_scale(); + this.search_results.update(content); + return this.parsing = false; + }; + Chosen.prototype.result_add_group = function(group) { + if (!group.disabled) { + group.dom_id = this.form_field.id + "chzn_g_" + group.array_index; + return '<li id="' + group.dom_id + '" class="group-result">' + group.label.escapeHTML() + '</li>'; + } else { + return ""; + } + }; + Chosen.prototype.result_add_option = function(option) { + var classes; + if (!option.disabled) { + option.dom_id = this.form_field.id + "chzn_o_" + option.array_index; + classes = option.selected && this.is_multiple ? [] : ["active-result"]; + if (option.selected) { + classes.push("result-selected"); + } + if (option.group_array_index != null) { + classes.push("group-option"); + } + return '<li id="' + option.dom_id + '" class="' + classes.join(' ') + '">' + option.text.escapeHTML() + '</li>'; + } else { + return ""; + } + }; + Chosen.prototype.results_update_field = function() { + this.result_clear_highlight(); + this.result_single_selected = null; + return this.results_build(); + }; + Chosen.prototype.result_do_highlight = function(el) { + var high_bottom, high_top, maxHeight, visible_bottom, visible_top; + this.result_clear_highlight(); + this.result_highlight = el; + this.result_highlight.addClassName("highlighted"); + maxHeight = parseInt(this.search_results.getStyle('maxHeight'), 10); + visible_top = this.search_results.scrollTop; + visible_bottom = maxHeight + visible_top; + high_top = this.result_highlight.positionedOffset().top; + high_bottom = high_top + this.result_highlight.getHeight(); + if (high_bottom >= visible_bottom) { + return this.search_results.scrollTop = (high_bottom - maxHeight) > 0 ? high_bottom - maxHeight : 0; + } else if (high_top < visible_top) { + return this.search_results.scrollTop = high_top; + } + }; + Chosen.prototype.result_clear_highlight = function() { + if (this.result_highlight) { + this.result_highlight.removeClassName('highlighted'); + } + return this.result_highlight = null; + }; + Chosen.prototype.results_toggle = function() { + if (this.results_showing) { + return this.results_hide(); + } else { + return this.results_show(); + } + }; + Chosen.prototype.results_show = function() { + var dd_top; + if (!this.is_multiple) { + this.selected_item.addClassName('chzn-single-with-drop'); + if (this.result_single_selected) { + this.result_do_highlight(this.result_single_selected); + } + } + dd_top = this.is_multiple ? this.container.getHeight() : this.container.getHeight() - 1; + this.dropdown.setStyle({ + "top": dd_top + "px", + "left": 0 + }); + this.results_showing = true; + this.search_field.focus(); + this.search_field.value = this.search_field.value; + return this.winnow_results(); + }; + Chosen.prototype.results_hide = function() { + if (!this.is_multiple) { + this.selected_item.removeClassName('chzn-single-with-drop'); + } + this.result_clear_highlight(); + this.dropdown.setStyle({ + "left": "-9000px" + }); + return this.results_showing = false; + }; + Chosen.prototype.set_tab_index = function(el) { + var ti; + if (this.form_field.tabIndex) { + ti = this.form_field.tabIndex; + this.form_field.tabIndex = -1; + if (this.is_multiple) { + return this.search_field.tabIndex = ti; + } else { + this.selected_item.tabIndex = ti; + return this.search_field.tabIndex = -1; + } + } + }; + Chosen.prototype.show_search_field_default = function() { + if (this.is_multiple && this.choices < 1 && !this.active_field) { + this.search_field.value = this.default_text; + return this.search_field.addClassName("default"); + } else { + this.search_field.value = ""; + return this.search_field.removeClassName("default"); + } + }; + Chosen.prototype.search_results_click = function(evt) { + var target; + target = evt.target.hasClassName("active-result") ? evt.target : evt.target.up(".active-result"); + if (target) { + this.result_highlight = target; + return this.result_select(); + } + }; + Chosen.prototype.search_results_mouseover = function(evt) { + var target; + target = evt.target.hasClassName("active-result") ? evt.target : evt.target.up(".active-result"); + if (target) { + return this.result_do_highlight(target); + } + }; + Chosen.prototype.search_results_mouseout = function(evt) { + if (evt.target.hasClassName('active-result') || evt.target.up('.active-result')) { + return this.result_clear_highlight(); + } + }; + Chosen.prototype.choices_click = function(evt) { + evt.preventDefault(); + if (this.active_field && !(evt.target.hasClassName('search-choice') || evt.target.up('.search-choice')) && !this.results_showing) { + return this.results_show(); + } + }; + Chosen.prototype.choice_build = function(item) { + var choice_id, link; + choice_id = this.form_field.id + "_chzn_c_" + item.array_index; + this.choices += 1; + this.search_container.insert({ + before: this.choice_temp.evaluate({ + "id": choice_id, + "choice": item.text, + "position": item.array_index + }) + }); + link = $(choice_id).down('a'); + return link.observe("click", __bind(function(evt) { + return this.choice_destroy_link_click(evt); + }, this)); + }; + Chosen.prototype.choice_destroy_link_click = function(evt) { + evt.preventDefault(); + this.pending_destroy_click = true; + return this.choice_destroy(evt.target); + }; + Chosen.prototype.choice_destroy = function(link) { + this.choices -= 1; + this.show_search_field_default(); + if (this.is_multiple && this.choices > 0 && this.search_field.value.length < 1) { + this.results_hide(); + } + this.result_deselect(link.readAttribute("rel")); + return link.up('li').remove(); + }; + Chosen.prototype.result_select = function() { + var high, item, position; + if (this.result_highlight) { + high = this.result_highlight; + this.result_clear_highlight(); + high.addClassName("result-selected"); + if (this.is_multiple) { + this.result_deactivate(high); + } else { + this.result_single_selected = high; + } + position = high.id.substr(high.id.lastIndexOf("_") + 1); + item = this.results_data[position]; + item.selected = true; + this.form_field.options[item.options_index].selected = true; + if (this.is_multiple) { + this.choice_build(item); + } else { + this.selected_item.down("span").update(item.text); + } + this.results_hide(); + this.search_field.value = ""; + if (typeof Event.simulate === 'function') { + this.form_field.simulate("change"); + } + return this.search_field_scale(); + } + }; + Chosen.prototype.result_activate = function(el) { + return el.addClassName("active-result").show(); + }; + Chosen.prototype.result_deactivate = function(el) { + return el.removeClassName("active-result").hide(); + }; + Chosen.prototype.result_deselect = function(pos) { + var result, result_data; + result_data = this.results_data[pos]; + result_data.selected = false; + this.form_field.options[result_data.options_index].selected = false; + result = $(this.form_field.id + "chzn_o_" + pos); + result.removeClassName("result-selected").addClassName("active-result").show(); + this.result_clear_highlight(); + this.winnow_results(); + if (typeof Event.simulate === 'function') { + this.form_field.simulate("change"); + } + return this.search_field_scale(); + }; + Chosen.prototype.results_search = function(evt) { + if (this.results_showing) { + return this.winnow_results(); + } else { + return this.results_show(); + } + }; + Chosen.prototype.winnow_results = function() { + var found, option, part, parts, regex, result_id, results, searchText, startTime, startpos, text, zregex, _i, _j, _len, _len2, _ref; + startTime = new Date(); + this.no_results_clear(); + results = 0; + searchText = this.search_field.value === this.default_text ? "" : this.search_field.value.strip(); + regex = new RegExp('^' + searchText.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"), 'i'); + zregex = new RegExp(searchText.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"), 'i'); + _ref = this.results_data; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + option = _ref[_i]; + if (!option.disabled && !option.empty) { + if (option.group) { + $(option.dom_id).hide(); + } else if (!(this.is_multiple && option.selected)) { + found = false; + result_id = option.dom_id; + if (regex.test(option.text)) { + found = true; + results += 1; + } else if (option.text.indexOf(" ") >= 0 || option.text.indexOf("[") === 0) { + parts = option.text.replace(/\[|\]/g, "").split(" "); + if (parts.length) { + for (_j = 0, _len2 = parts.length; _j < _len2; _j++) { + part = parts[_j]; + if (regex.test(part)) { + found = true; + results += 1; + } + } + } + } + if (found) { + if (searchText.length) { + startpos = option.text.search(zregex); + text = option.text.substr(0, startpos + searchText.length) + '</em>' + option.text.substr(startpos + searchText.length); + text = text.substr(0, startpos) + '<em>' + text.substr(startpos); + } else { + text = option.text; + } + if ($(result_id).innerHTML !== text) { + $(result_id).update(text); + } + this.result_activate($(result_id)); + if (option.group_array_index != null) { + $(this.results_data[option.group_array_index].dom_id).show(); + } + } else { + if ($(result_id) === this.result_highlight) { + this.result_clear_highlight(); + } + this.result_deactivate($(result_id)); + } + } + } + } + if (results < 1 && searchText.length) { + return this.no_results(searchText); + } else { + return this.winnow_results_set_highlight(); + } + }; + Chosen.prototype.winnow_results_clear = function() { + var li, lis, _i, _len, _results; + this.search_field.clear(); + lis = this.search_results.select("li"); + _results = []; + for (_i = 0, _len = lis.length; _i < _len; _i++) { + li = lis[_i]; + _results.push(li.hasClassName("group-result") ? li.show() : !this.is_multiple || !li.hasClassName("result-selected") ? this.result_activate(li) : void 0); + } + return _results; + }; + Chosen.prototype.winnow_results_set_highlight = function() { + var do_high; + if (!this.result_highlight) { + do_high = this.search_results.down(".active-result"); + if (do_high) { + return this.result_do_highlight(do_high); + } + } + }; + Chosen.prototype.no_results = function(terms) { + return this.search_results.insert(this.no_results_temp.evaluate({ + "terms": terms.escapeHTML() + })); + }; + Chosen.prototype.no_results_clear = function() { + var nr, _results; + nr = null; + _results = []; + while (nr = this.search_results.down(".no-results")) { + _results.push(nr.remove()); + } + return _results; + }; + Chosen.prototype.keydown_arrow = function() { + var actives, nexts, sibs; + actives = this.search_results.select("li.active-result"); + if (actives.length) { + if (!this.result_highlight) { + this.result_do_highlight(actives.first()); + } else if (this.results_showing) { + sibs = this.result_highlight.nextSiblings(); + nexts = sibs.intersect(actives); + if (nexts.length) { + this.result_do_highlight(nexts.first()); + } + } + if (!this.results_showing) { + return this.results_show(); + } + } + }; + Chosen.prototype.keyup_arrow = function() { + var actives, prevs, sibs; + if (!this.results_showing && !this.is_multiple) { + return this.results_show(); + } else if (this.result_highlight) { + sibs = this.result_highlight.previousSiblings(); + actives = this.search_results.select("li.active-result"); + prevs = sibs.intersect(actives); + if (prevs.length) { + return this.result_do_highlight(prevs.first()); + } else { + if (this.choices > 0) { + this.results_hide(); + } + return this.result_clear_highlight(); + } + } + }; + Chosen.prototype.keydown_backstroke = function() { + if (this.pending_backstroke) { + this.choice_destroy(this.pending_backstroke.down("a")); + return this.clear_backstroke(); + } else { + this.pending_backstroke = this.search_container.siblings("li.search-choice").last(); + return this.pending_backstroke.addClassName("search-choice-focus"); + } + }; + Chosen.prototype.clear_backstroke = function() { + if (this.pending_backstroke) { + this.pending_backstroke.removeClassName("search-choice-focus"); + } + return this.pending_backstroke = null; + }; + Chosen.prototype.keyup_checker = function(evt) { + var stroke, _ref; + stroke = (_ref = evt.which) != null ? _ref : evt.keyCode; + this.search_field_scale(); + switch (stroke) { + case 8: + if (this.is_multiple && this.backstroke_length < 1 && this.choices > 0) { + return this.keydown_backstroke(); + } else if (!this.pending_backstroke) { + this.result_clear_highlight(); + return this.results_search(); + } + break; + case 13: + evt.preventDefault(); + if (this.results_showing) { + return this.result_select(); + } + break; + case 27: + if (this.results_showing) { + return this.results_hide(); + } + break; + case 9: + case 38: + case 40: + case 16: + break; + default: + return this.results_search(); + } + }; + Chosen.prototype.keydown_checker = function(evt) { + var stroke, _ref; + stroke = (_ref = evt.which) != null ? _ref : evt.keyCode; + this.search_field_scale(); + if (stroke !== 8 && this.pending_backstroke) { + this.clear_backstroke(); + } + switch (stroke) { + case 8: + return this.backstroke_length = this.search_field.value.length; + case 9: + return this.mouse_on_container = false; + case 13: + return evt.preventDefault(); + case 38: + evt.preventDefault(); + return this.keyup_arrow(); + case 40: + return this.keydown_arrow(); + } + }; + Chosen.prototype.search_field_scale = function() { + var dd_top, div, h, style, style_block, styles, w, _i, _len; + if (this.is_multiple) { + h = 0; + w = 0; + style_block = "position:absolute; left: -1000px; top: -1000px; display:none;"; + styles = ['font-size', 'font-style', 'font-weight', 'font-family', 'line-height', 'text-transform', 'letter-spacing']; + for (_i = 0, _len = styles.length; _i < _len; _i++) { + style = styles[_i]; + style_block += style + ":" + this.search_field.getStyle(style) + ";"; + } + div = new Element('div', { + 'style': style_block + }).update(this.search_field.value); + document.body.appendChild(div); + w = Element.measure(div, 'width') + 25; + div.remove(); + if (w > this.f_width - 10) { + w = this.f_width - 10; + } + this.search_field.setStyle({ + 'width': w + 'px' + }); + dd_top = this.container.getHeight(); + return this.dropdown.setStyle({ + "top": dd_top + "px" + }); + } + }; + return Chosen; + })(); + root.Chosen = Chosen; + document.observe('dom:loaded', function(evt) { + var select, selects, _i, _len, _results; + selects = $$(".chzn-select"); + _results = []; + for (_i = 0, _len = selects.length; _i < _len; _i++) { + select = selects[_i]; + _results.push(new Chosen(select)); + } + return _results; + }); + get_side_border_padding = function(elmt) { + var layout, side_border_padding; + layout = new Element.Layout(elmt); + return side_border_padding = layout.get("border-left") + layout.get("border-right") + layout.get("padding-left") + layout.get("padding-right"); + }; + root.get_side_border_padding = get_side_border_padding; + root = typeof exports !== "undefined" && exports !== null ? exports : this; + SelectParser = (function() { + function SelectParser() { + this.options_index = 0; + this.parsed = []; + } + SelectParser.prototype.add_node = function(child) { + if (child.nodeName === "OPTGROUP") { + return this.add_group(child); + } else { + return this.add_option(child); + } + }; + SelectParser.prototype.add_group = function(group) { + var group_position, option, _i, _len, _ref, _results; + group_position = this.parsed.length; + this.parsed.push({ + array_index: group_position, + group: true, + label: group.label, + children: 0, + disabled: group.disabled + }); + _ref = group.childNodes; + _results = []; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + option = _ref[_i]; + _results.push(this.add_option(option, group_position, group.disabled)); + } + return _results; + }; + SelectParser.prototype.add_option = function(option, group_position, group_disabled) { + if (option.nodeName === "OPTION") { + if (option.text !== "") { + if (group_position != null) { + this.parsed[group_position].children += 1; + } + this.parsed.push({ + array_index: this.parsed.length, + options_index: this.options_index, + value: option.value, + text: option.text, + selected: option.selected, + disabled: group_disabled === true ? group_disabled : option.disabled, + group_array_index: group_position + }); + } else { + this.parsed.push({ + array_index: this.parsed.length, + options_index: this.options_index, + empty: true + }); + } + return this.options_index += 1; + } + }; + return SelectParser; + })(); + SelectParser.select_to_array = function(select) { + var child, parser, _i, _len, _ref; + parser = new SelectParser(); + _ref = select.childNodes; + for (_i = 0, _len = _ref.length; _i < _len; _i++) { + child = _ref[_i]; + parser.add_node(child); + } + return parser.parsed; + }; + root.SelectParser = SelectParser; +}).call(this); diff --git a/usr/local/www/javascript/chosen/chosen.proto.min.js b/usr/local/www/javascript/chosen/chosen.proto.min.js new file mode 100644 index 0000000..79109c5 --- /dev/null +++ b/usr/local/www/javascript/chosen/chosen.proto.min.js @@ -0,0 +1,9 @@ +/* +Chosen, a Select Box Enhancer for jQuery and Protoype +by Patrick Filler for Harvest, http://getharvest.com + +Available for use under the MIT License, http://en.wikipedia.org/wiki/MIT_License + +Copyright (c) 2011 by Harvest +*/ +(function(){var Chosen,SelectParser,get_side_border_padding,root;var __bind=function(fn,me){return function(){return fn.apply(me,arguments);};};root=typeof exports!=="undefined"&&exports!==null?exports:this;Chosen=(function(){function Chosen(elmn){this.set_default_values();this.form_field=elmn;this.is_multiple=this.form_field.multiple;this.default_text_default=this.form_field.multiple?"Select Some Options":"Select an Option";this.set_up_html();this.register_observers();}Chosen.prototype.set_default_values=function(){this.click_test_action=__bind(function(evt){return this.test_active_click(evt);},this);this.active_field=false;this.mouse_on_container=false;this.results_showing=false;this.result_highlighted=null;this.result_single_selected=null;this.choices=0;this.single_temp=new Template('<a href="javascript:void(0)" class="chzn-single"><span>#{default}</span><div><b></b></div></a><div class="chzn-drop" style="left:-9000px;"><div class="chzn-search"><input type="text" /></div><ul class="chzn-results"></ul></div>');this.multi_temp=new Template('<ul class="chzn-choices"><li class="search-field"><input type="text" value="#{default}" class="default" style="width:25px;" /></li></ul><div class="chzn-drop" style="left:-9000px;"><ul class="chzn-results"></ul></div>');this.choice_temp=new Template('<li class="search-choice" id="#{id}"><span>#{choice}</span><a href="javascript:void(0)" class="search-choice-close" rel="#{position}"></a></li>');return this.no_results_temp=new Template('<li class="no-results">No results match "<span>#{terms}</span>"</li>');};Chosen.prototype.set_up_html=function(){var base_template,container_props,dd_top,dd_width,sf_width;this.container_id=this.form_field.id+"_chzn";this.f_width=this.form_field.getStyle("width")?parseInt(this.form_field.getStyle("width"),10):this.form_field.getWidth();container_props={id:this.container_id,"class":"chzn-container",style:"width: "+this.f_width+"px"};this.default_text=this.form_field.readAttribute("title")?this.form_field.readAttribute("title"):this.default_text_default;base_template=this.is_multiple?new Element("div",container_props).update(this.multi_temp.evaluate({"default":this.default_text})):new Element("div",container_props).update(this.single_temp.evaluate({"default":this.default_text}));this.form_field.hide().insert({after:base_template});this.container=$(this.container_id);this.container.addClassName("chzn-container-"+(this.is_multiple?"multi":"single"));this.dropdown=this.container.down("div.chzn-drop");dd_top=this.container.getHeight();dd_width=this.f_width-get_side_border_padding(this.dropdown);this.dropdown.setStyle({width:dd_width+"px",top:dd_top+"px"});this.search_field=this.container.down("input");this.search_results=this.container.down("ul.chzn-results");this.search_field_scale();this.search_no_results=this.container.down("li.no-results");if(this.is_multiple){this.search_choices=this.container.down("ul.chzn-choices");this.search_container=this.container.down("li.search-field");}else{this.search_container=this.container.down("div.chzn-search");this.selected_item=this.container.down(".chzn-single");sf_width=dd_width-get_side_border_padding(this.search_container)-get_side_border_padding(this.search_field);this.search_field.setStyle({width:sf_width+"px"});}this.results_build();return this.set_tab_index();};Chosen.prototype.register_observers=function(){this.container.observe("click",__bind(function(evt){return this.container_click(evt);},this));this.container.observe("mouseenter",__bind(function(evt){return this.mouse_enter(evt);},this));this.container.observe("mouseleave",__bind(function(evt){return this.mouse_leave(evt);},this));this.search_results.observe("click",__bind(function(evt){return this.search_results_click(evt);},this));this.search_results.observe("mouseover",__bind(function(evt){return this.search_results_mouseover(evt);},this));this.search_results.observe("mouseout",__bind(function(evt){return this.search_results_mouseout(evt);},this));this.form_field.observe("liszt:updated",__bind(function(evt){return this.results_update_field(evt);},this));this.search_field.observe("blur",__bind(function(evt){return this.input_blur(evt);},this));this.search_field.observe("keyup",__bind(function(evt){return this.keyup_checker(evt);},this));this.search_field.observe("keydown",__bind(function(evt){return this.keydown_checker(evt);},this));if(this.is_multiple){this.search_choices.observe("click",__bind(function(evt){return this.choices_click(evt);},this));return this.search_field.observe("focus",__bind(function(evt){return this.input_focus(evt);},this));}else{return this.selected_item.observe("focus",__bind(function(evt){return this.activate_field(evt);},this));}};Chosen.prototype.container_click=function(evt){if(evt&&evt.type==="click"){evt.stop();}if(!this.pending_destroy_click){if(!this.active_field){if(this.is_multiple){this.search_field.clear();}document.observe("click",this.click_test_action);this.results_show();}else{if(!this.is_multiple&&evt&&(evt.target===this.selected_item||evt.target.up("a.chzn-single"))){this.results_toggle();}}return this.activate_field();}else{return this.pending_destroy_click=false;}};Chosen.prototype.mouse_enter=function(){return this.mouse_on_container=true;};Chosen.prototype.mouse_leave=function(){return this.mouse_on_container=false;};Chosen.prototype.input_focus=function(evt){if(!this.active_field){return setTimeout(this.container_click.bind(this),50);}};Chosen.prototype.input_blur=function(evt){if(!this.mouse_on_container){this.active_field=false;return setTimeout(this.blur_test.bind(this),100);}};Chosen.prototype.blur_test=function(evt){if(!this.active_field&&this.container.hasClassName("chzn-container-active")){return this.close_field();}};Chosen.prototype.close_field=function(){document.stopObserving("click",this.click_test_action);if(!this.is_multiple){this.selected_item.tabIndex=this.search_field.tabIndex;this.search_field.tabIndex=-1;}this.active_field=false;this.results_hide();this.container.removeClassName("chzn-container-active");this.winnow_results_clear();this.clear_backstroke();this.show_search_field_default();return this.search_field_scale();};Chosen.prototype.activate_field=function(){if(!this.is_multiple&&!this.active_field){this.search_field.tabIndex=this.selected_item.tabIndex;this.selected_item.tabIndex=-1;}this.container.addClassName("chzn-container-active");this.active_field=true;this.search_field.value=this.search_field.value;return this.search_field.focus();};Chosen.prototype.test_active_click=function(evt){if(evt.target.up("#"+this.container.id)){return this.active_field=true;}else{return this.close_field();}};Chosen.prototype.results_build=function(){var content,data,startTime,_i,_len,_ref;startTime=new Date();this.parsing=true;this.results_data=SelectParser.select_to_array(this.form_field);if(this.is_multiple&&this.choices>0){this.search_choices.select("li.search-choice").invoke("remove");this.choices=0;}else{if(!this.is_multiple){this.selected_item.down("span").update(this.default_text);}}content="";_ref=this.results_data;for(_i=0,_len=_ref.length;_i<_len;_i++){data=_ref[_i];if(data.group){content+=this.result_add_group(data);}else{if(!data.empty){content+=this.result_add_option(data);if(data.selected&&this.is_multiple){this.choice_build(data);}else{if(data.selected&&!this.is_multiple){this.selected_item.down("span").update(data.text);}}}}}this.show_search_field_default();this.search_field_scale();this.search_results.update(content);return this.parsing=false;};Chosen.prototype.result_add_group=function(group){if(!group.disabled){group.dom_id=this.form_field.id+"chzn_g_"+group.array_index;return'<li id="'+group.dom_id+'" class="group-result">'+group.label.escapeHTML()+"</li>";}else{return"";}};Chosen.prototype.result_add_option=function(option){var classes;if(!option.disabled){option.dom_id=this.form_field.id+"chzn_o_"+option.array_index;classes=option.selected&&this.is_multiple?[]:["active-result"];if(option.selected){classes.push("result-selected");}if(option.group_array_index!=null){classes.push("group-option");}return'<li id="'+option.dom_id+'" class="'+classes.join(" ")+'">'+option.text.escapeHTML()+"</li>";}else{return"";}};Chosen.prototype.results_update_field=function(){this.result_clear_highlight();this.result_single_selected=null;return this.results_build();};Chosen.prototype.result_do_highlight=function(el){var high_bottom,high_top,maxHeight,visible_bottom,visible_top;this.result_clear_highlight();this.result_highlight=el;this.result_highlight.addClassName("highlighted");maxHeight=parseInt(this.search_results.getStyle("maxHeight"),10);visible_top=this.search_results.scrollTop;visible_bottom=maxHeight+visible_top;high_top=this.result_highlight.positionedOffset().top;high_bottom=high_top+this.result_highlight.getHeight();if(high_bottom>=visible_bottom){return this.search_results.scrollTop=(high_bottom-maxHeight)>0?high_bottom-maxHeight:0;}else{if(high_top<visible_top){return this.search_results.scrollTop=high_top;}}};Chosen.prototype.result_clear_highlight=function(){if(this.result_highlight){this.result_highlight.removeClassName("highlighted");}return this.result_highlight=null;};Chosen.prototype.results_toggle=function(){if(this.results_showing){return this.results_hide();}else{return this.results_show();}};Chosen.prototype.results_show=function(){var dd_top;if(!this.is_multiple){this.selected_item.addClassName("chzn-single-with-drop");if(this.result_single_selected){this.result_do_highlight(this.result_single_selected);}}dd_top=this.is_multiple?this.container.getHeight():this.container.getHeight()-1;this.dropdown.setStyle({top:dd_top+"px",left:0});this.results_showing=true;this.search_field.focus();this.search_field.value=this.search_field.value;return this.winnow_results();};Chosen.prototype.results_hide=function(){if(!this.is_multiple){this.selected_item.removeClassName("chzn-single-with-drop");}this.result_clear_highlight();this.dropdown.setStyle({left:"-9000px"});return this.results_showing=false;};Chosen.prototype.set_tab_index=function(el){var ti;if(this.form_field.tabIndex){ti=this.form_field.tabIndex;this.form_field.tabIndex=-1;if(this.is_multiple){return this.search_field.tabIndex=ti;}else{this.selected_item.tabIndex=ti;return this.search_field.tabIndex=-1;}}};Chosen.prototype.show_search_field_default=function(){if(this.is_multiple&&this.choices<1&&!this.active_field){this.search_field.value=this.default_text;return this.search_field.addClassName("default");}else{this.search_field.value="";return this.search_field.removeClassName("default");}};Chosen.prototype.search_results_click=function(evt){var target;target=evt.target.hasClassName("active-result")?evt.target:evt.target.up(".active-result");if(target){this.result_highlight=target;return this.result_select();}};Chosen.prototype.search_results_mouseover=function(evt){var target;target=evt.target.hasClassName("active-result")?evt.target:evt.target.up(".active-result");if(target){return this.result_do_highlight(target);}};Chosen.prototype.search_results_mouseout=function(evt){if(evt.target.hasClassName("active-result")||evt.target.up(".active-result")){return this.result_clear_highlight();}};Chosen.prototype.choices_click=function(evt){evt.preventDefault();if(this.active_field&&!(evt.target.hasClassName("search-choice")||evt.target.up(".search-choice"))&&!this.results_showing){return this.results_show();}};Chosen.prototype.choice_build=function(item){var choice_id,link;choice_id=this.form_field.id+"_chzn_c_"+item.array_index;this.choices+=1;this.search_container.insert({before:this.choice_temp.evaluate({id:choice_id,choice:item.text,position:item.array_index})});link=$(choice_id).down("a");return link.observe("click",__bind(function(evt){return this.choice_destroy_link_click(evt);},this));};Chosen.prototype.choice_destroy_link_click=function(evt){evt.preventDefault();this.pending_destroy_click=true;return this.choice_destroy(evt.target);};Chosen.prototype.choice_destroy=function(link){this.choices-=1;this.show_search_field_default();if(this.is_multiple&&this.choices>0&&this.search_field.value.length<1){this.results_hide();}this.result_deselect(link.readAttribute("rel"));return link.up("li").remove();};Chosen.prototype.result_select=function(){var high,item,position;if(this.result_highlight){high=this.result_highlight;this.result_clear_highlight();high.addClassName("result-selected");if(this.is_multiple){this.result_deactivate(high);}else{this.result_single_selected=high;}position=high.id.substr(high.id.lastIndexOf("_")+1);item=this.results_data[position];item.selected=true;this.form_field.options[item.options_index].selected=true;if(this.is_multiple){this.choice_build(item);}else{this.selected_item.down("span").update(item.text);}this.results_hide();this.search_field.value="";if(typeof Event.simulate==="function"){this.form_field.simulate("change");}return this.search_field_scale();}};Chosen.prototype.result_activate=function(el){return el.addClassName("active-result").show();};Chosen.prototype.result_deactivate=function(el){return el.removeClassName("active-result").hide();};Chosen.prototype.result_deselect=function(pos){var result,result_data;result_data=this.results_data[pos];result_data.selected=false;this.form_field.options[result_data.options_index].selected=false;result=$(this.form_field.id+"chzn_o_"+pos);result.removeClassName("result-selected").addClassName("active-result").show();this.result_clear_highlight();this.winnow_results();if(typeof Event.simulate==="function"){this.form_field.simulate("change");}return this.search_field_scale();};Chosen.prototype.results_search=function(evt){if(this.results_showing){return this.winnow_results();}else{return this.results_show();}};Chosen.prototype.winnow_results=function(){var found,option,part,parts,regex,result_id,results,searchText,startTime,startpos,text,zregex,_i,_j,_len,_len2,_ref;startTime=new Date();this.no_results_clear();results=0;searchText=this.search_field.value===this.default_text?"":this.search_field.value.strip();regex=new RegExp("^"+searchText.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&"),"i");zregex=new RegExp(searchText.replace(/[-[\]{}()*+?.,\\^$|#\s]/g,"\\$&"),"i");_ref=this.results_data;for(_i=0,_len=_ref.length;_i<_len;_i++){option=_ref[_i];if(!option.disabled&&!option.empty){if(option.group){$(option.dom_id).hide();}else{if(!(this.is_multiple&&option.selected)){found=false;result_id=option.dom_id;if(regex.test(option.text)){found=true;results+=1;}else{if(option.text.indexOf(" ")>=0||option.text.indexOf("[")===0){parts=option.text.replace(/\[|\]/g,"").split(" ");if(parts.length){for(_j=0,_len2=parts.length;_j<_len2;_j++){part=parts[_j];if(regex.test(part)){found=true;results+=1;}}}}}if(found){if(searchText.length){startpos=option.text.search(zregex);text=option.text.substr(0,startpos+searchText.length)+"</em>"+option.text.substr(startpos+searchText.length);text=text.substr(0,startpos)+"<em>"+text.substr(startpos);}else{text=option.text;}if($(result_id).innerHTML!==text){$(result_id).update(text);}this.result_activate($(result_id));if(option.group_array_index!=null){$(this.results_data[option.group_array_index].dom_id).show();}}else{if($(result_id)===this.result_highlight){this.result_clear_highlight();}this.result_deactivate($(result_id));}}}}}if(results<1&&searchText.length){return this.no_results(searchText);}else{return this.winnow_results_set_highlight();}};Chosen.prototype.winnow_results_clear=function(){var li,lis,_i,_len,_results;this.search_field.clear();lis=this.search_results.select("li");_results=[];for(_i=0,_len=lis.length;_i<_len;_i++){li=lis[_i];_results.push(li.hasClassName("group-result")?li.show():!this.is_multiple||!li.hasClassName("result-selected")?this.result_activate(li):void 0);}return _results;};Chosen.prototype.winnow_results_set_highlight=function(){var do_high;if(!this.result_highlight){do_high=this.search_results.down(".active-result");if(do_high){return this.result_do_highlight(do_high);}}};Chosen.prototype.no_results=function(terms){return this.search_results.insert(this.no_results_temp.evaluate({terms:terms.escapeHTML()}));};Chosen.prototype.no_results_clear=function(){var nr,_results;nr=null;_results=[];while(nr=this.search_results.down(".no-results")){_results.push(nr.remove());}return _results;};Chosen.prototype.keydown_arrow=function(){var actives,nexts,sibs;actives=this.search_results.select("li.active-result");if(actives.length){if(!this.result_highlight){this.result_do_highlight(actives.first());}else{if(this.results_showing){sibs=this.result_highlight.nextSiblings();nexts=sibs.intersect(actives);if(nexts.length){this.result_do_highlight(nexts.first());}}}if(!this.results_showing){return this.results_show();}}};Chosen.prototype.keyup_arrow=function(){var actives,prevs,sibs;if(!this.results_showing&&!this.is_multiple){return this.results_show();}else{if(this.result_highlight){sibs=this.result_highlight.previousSiblings();actives=this.search_results.select("li.active-result");prevs=sibs.intersect(actives);if(prevs.length){return this.result_do_highlight(prevs.first());}else{if(this.choices>0){this.results_hide();}return this.result_clear_highlight();}}}};Chosen.prototype.keydown_backstroke=function(){if(this.pending_backstroke){this.choice_destroy(this.pending_backstroke.down("a"));return this.clear_backstroke();}else{this.pending_backstroke=this.search_container.siblings("li.search-choice").last();return this.pending_backstroke.addClassName("search-choice-focus");}};Chosen.prototype.clear_backstroke=function(){if(this.pending_backstroke){this.pending_backstroke.removeClassName("search-choice-focus");}return this.pending_backstroke=null;};Chosen.prototype.keyup_checker=function(evt){var stroke,_ref;stroke=(_ref=evt.which)!=null?_ref:evt.keyCode;this.search_field_scale();switch(stroke){case 8:if(this.is_multiple&&this.backstroke_length<1&&this.choices>0){return this.keydown_backstroke();}else{if(!this.pending_backstroke){this.result_clear_highlight();return this.results_search();}}break;case 13:evt.preventDefault();if(this.results_showing){return this.result_select();}break;case 27:if(this.results_showing){return this.results_hide();}break;case 9:case 38:case 40:case 16:break;default:return this.results_search();}};Chosen.prototype.keydown_checker=function(evt){var stroke,_ref;stroke=(_ref=evt.which)!=null?_ref:evt.keyCode;this.search_field_scale();if(stroke!==8&&this.pending_backstroke){this.clear_backstroke();}switch(stroke){case 8:return this.backstroke_length=this.search_field.value.length;case 9:return this.mouse_on_container=false;case 13:return evt.preventDefault();case 38:evt.preventDefault();return this.keyup_arrow();case 40:return this.keydown_arrow();}};Chosen.prototype.search_field_scale=function(){var dd_top,div,h,style,style_block,styles,w,_i,_len;if(this.is_multiple){h=0;w=0;style_block="position:absolute; left: -1000px; top: -1000px; display:none;";styles=["font-size","font-style","font-weight","font-family","line-height","text-transform","letter-spacing"];for(_i=0,_len=styles.length;_i<_len;_i++){style=styles[_i];style_block+=style+":"+this.search_field.getStyle(style)+";";}div=new Element("div",{style:style_block}).update(this.search_field.value);document.body.appendChild(div);w=Element.measure(div,"width")+25;div.remove();if(w>this.f_width-10){w=this.f_width-10;}this.search_field.setStyle({width:w+"px"});dd_top=this.container.getHeight();return this.dropdown.setStyle({top:dd_top+"px"});}};return Chosen;})();root.Chosen=Chosen;document.observe("dom:loaded",function(evt){var select,selects,_i,_len,_results;selects=$$(".chzn-select");_results=[];for(_i=0,_len=selects.length;_i<_len;_i++){select=selects[_i];_results.push(new Chosen(select));}return _results;});get_side_border_padding=function(elmt){var layout,side_border_padding;layout=new Element.Layout(elmt);return side_border_padding=layout.get("border-left")+layout.get("border-right")+layout.get("padding-left")+layout.get("padding-right");};root.get_side_border_padding=get_side_border_padding;root=typeof exports!=="undefined"&&exports!==null?exports:this;SelectParser=(function(){function SelectParser(){this.options_index=0;this.parsed=[];}SelectParser.prototype.add_node=function(child){if(child.nodeName==="OPTGROUP"){return this.add_group(child);}else{return this.add_option(child);}};SelectParser.prototype.add_group=function(group){var group_position,option,_i,_len,_ref,_results;group_position=this.parsed.length;this.parsed.push({array_index:group_position,group:true,label:group.label,children:0,disabled:group.disabled});_ref=group.childNodes;_results=[];for(_i=0,_len=_ref.length;_i<_len;_i++){option=_ref[_i];_results.push(this.add_option(option,group_position,group.disabled));}return _results;};SelectParser.prototype.add_option=function(option,group_position,group_disabled){if(option.nodeName==="OPTION"){if(option.text!==""){if(group_position!=null){this.parsed[group_position].children+=1;}this.parsed.push({array_index:this.parsed.length,options_index:this.options_index,value:option.value,text:option.text,selected:option.selected,disabled:group_disabled===true?group_disabled:option.disabled,group_array_index:group_position});}else{this.parsed.push({array_index:this.parsed.length,options_index:this.options_index,empty:true});}return this.options_index+=1;}};return SelectParser;})();SelectParser.select_to_array=function(select){var child,parser,_i,_len,_ref;parser=new SelectParser();_ref=select.childNodes;for(_i=0,_len=_ref.length;_i<_len;_i++){child=_ref[_i];parser.add_node(child);}return parser.parsed;};root.SelectParser=SelectParser;}).call(this);
\ No newline at end of file diff --git a/usr/local/www/javascript/chosen/coffee/chosen.jquery.coffee b/usr/local/www/javascript/chosen/coffee/chosen.jquery.coffee new file mode 100644 index 0000000..0d6596a --- /dev/null +++ b/usr/local/www/javascript/chosen/coffee/chosen.jquery.coffee @@ -0,0 +1,633 @@ +### +Chosen, a Select Box Enhancer for jQuery and Protoype +by Patrick Filler for Harvest, http://getharvest.com + +Available for use under the MIT License, http://en.wikipedia.org/wiki/MIT_License + +Copyright (c) 2011 by Harvest +### + +root = exports ? this +$ = jQuery + +$.fn.extend({ + chosen: (data, options) -> + $(this).each((input_field) -> + new Chosen(this, data, options) unless ($ this).hasClass "chzn-done" + ) +}) + +class Chosen + + constructor: (elmn) -> + this.set_default_values() + + @form_field = elmn + @form_field_jq = $ @form_field + @is_multiple = @form_field.multiple + + @default_text_default = if @form_field.multiple then "Select Some Options" else "Select an Option" + + this.set_up_html() + this.register_observers() + @form_field_jq.addClass "chzn-done" + + set_default_values: -> + + @click_test_action = (evt) => this.test_active_click(evt) + @active_field = false + @mouse_on_container = false + @results_showing = false + @result_highlighted = null + @result_single_selected = null + @choices = 0 + + set_up_html: -> + @container_id = @form_field.id + "_chzn" + + @f_width = @form_field_jq.width() + + @default_text = if @form_field_jq.attr 'title' then @form_field_jq.attr 'title' else @default_text_default + + container_div = ($ "<div />", { + id: @container_id + class: 'chzn-container' + style: 'width: ' + (@f_width) + 'px;' #use parens around @f_width so coffeescript doesn't think + ' px' is a function parameter + }) + + if @is_multiple + container_div.html '<ul class="chzn-choices"><li class="search-field"><input type="text" value="' + @default_text + '" class="default" style="width:25px;" /></li></ul><div class="chzn-drop" style="left:-9000px;"><ul class="chzn-results"></ul></div>' + else + container_div.html '<a href="javascript:void(0)" class="chzn-single"><span>' + @default_text + '</span><div><b></b></div></a><div class="chzn-drop" style="left:-9000px;"><div class="chzn-search"><input type="text" /></div><ul class="chzn-results"></ul></div>' + + @form_field_jq.hide().after container_div + @container = ($ '#' + @container_id) + @container.addClass( "chzn-container-" + (if @is_multiple then "multi" else "single") ) + @dropdown = @container.find('div.chzn-drop').first() + + dd_top = @container.height() + dd_width = (@f_width - get_side_border_padding(@dropdown)) + + @dropdown.css({"width": dd_width + "px", "top": dd_top + "px"}) + + @search_field = @container.find('input').first() + @search_results = @container.find('ul.chzn-results').first() + this.search_field_scale() + + @search_no_results = @container.find('li.no-results').first() + + if @is_multiple + @search_choices = @container.find('ul.chzn-choices').first() + @search_container = @container.find('li.search-field').first() + else + @search_container = @container.find('div.chzn-search').first() + @selected_item = @container.find('.chzn-single').first() + sf_width = dd_width - get_side_border_padding(@search_container) - get_side_border_padding(@search_field) + @search_field.css( {"width" : sf_width + "px"} ) + + this.results_build() + this.set_tab_index() + + + register_observers: -> + @container.click (evt) => this.container_click(evt) + @container.mouseenter (evt) => this.mouse_enter(evt) + @container.mouseleave (evt) => this.mouse_leave(evt) + + @search_results.click (evt) => this.search_results_click(evt) + @search_results.mouseover (evt) => this.search_results_mouseover(evt) + @search_results.mouseout (evt) => this.search_results_mouseout(evt) + + @form_field_jq.bind "liszt:updated", (evt) => this.results_update_field(evt) + + @search_field.blur (evt) => this.input_blur(evt) + @search_field.keyup (evt) => this.keyup_checker(evt) + @search_field.keydown (evt) => this.keydown_checker(evt) + + if @is_multiple + @search_choices.click (evt) => this.choices_click(evt) + @search_field.focus (evt) => this.input_focus(evt) + else + @selected_item.focus (evt) => this.activate_field(evt) + + container_click: (evt) -> + if evt and evt.type is "click" + evt.stopPropagation() + if not @pending_destroy_click + if not @active_field + @search_field.val "" if @is_multiple + $(document).click @click_test_action + this.results_show() + else if not @is_multiple and evt and ($(evt.target) is @selected_item || $(evt.target).parents("a.chzn-single").length) + evt.preventDefault() + this.results_toggle() + + this.activate_field() + else + @pending_destroy_click = false + + mouse_enter: -> @mouse_on_container = true + mouse_leave: -> @mouse_on_container = false + + input_focus: (evt) -> + setTimeout (=> this.container_click()), 50 unless @active_field + + input_blur: (evt) -> + if not @mouse_on_container + @active_field = false + setTimeout (=> this.blur_test()), 100 + + blur_test: (evt) -> + this.close_field() if not @active_field and @container.hasClass "chzn-container-active" + + close_field: -> + $(document).unbind "click", @click_test_action + + if not @is_multiple + @selected_item.attr "tabindex", @search_field.attr("tabindex") + @search_field.attr "tabindex", -1 + + @active_field = false + this.results_hide() + + @container.removeClass "chzn-container-active" + this.winnow_results_clear() + this.clear_backstroke() + + this.show_search_field_default() + this.search_field_scale() + + activate_field: -> + if not @is_multiple and not @active_field + @search_field.attr "tabindex", (@selected_item.attr "tabindex") + @selected_item.attr "tabindex", -1 + + @container.addClass "chzn-container-active" + @active_field = true + + @search_field.val(@search_field.val()) + @search_field.focus() + + + test_active_click: (evt) -> + if $(evt.target).parents('#' + @container.id).length + @active_field = true + else + this.close_field() + + results_build: -> + startTime = new Date() + @parsing = true + @results_data = SelectParser.select_to_array @form_field + + if @is_multiple and @choices > 0 + @search_choices.find("li.search-choice").remove() + @choices = 0 + else if not @is_multiple + @selected_item.find("span").text @default_text + + content = '' + for data in @results_data + if data.group + content += this.result_add_group data + else if !data.empty + content += this.result_add_option data + if data.selected and @is_multiple + this.choice_build data + else if data.selected and not @is_multiple + @selected_item.find("span").text data.text + + this.show_search_field_default() + this.search_field_scale() + + @search_results.html content + @parsing = false + + + result_add_group: (group) -> + if not group.disabled + group.dom_id = @form_field.id + "chzn_g_" + group.array_index + '<li id="' + group.dom_id + '" class="group-result">' + $("<div />").text(group.label).html() + '</li>' + else + "" + + result_add_option: (option) -> + if not option.disabled + option.dom_id = @form_field.id + "chzn_o_" + option.array_index + + classes = if option.selected and @is_multiple then [] else ["active-result"] + classes.push "result-selected" if option.selected + classes.push "group-option" if option.group_array_index? + + '<li id="' + option.dom_id + '" class="' + classes.join(' ') + '">' + $("<div />").text(option.text).html() + '</li>' + else + "" + + results_update_field: -> + this.result_clear_highlight() + @result_single_selected = null + this.results_build() + + result_do_highlight: (el) -> + if el.length + this.result_clear_highlight() + + @result_highlight = el + @result_highlight.addClass "highlighted" + + maxHeight = parseInt @search_results.css("maxHeight"), 10 + visible_top = @search_results.scrollTop() + visible_bottom = maxHeight + visible_top + + high_top = @result_highlight.position().top + @search_results.scrollTop() + high_bottom = high_top + @result_highlight.outerHeight() + + if high_bottom >= visible_bottom + @search_results.scrollTop if (high_bottom - maxHeight) > 0 then (high_bottom - maxHeight) else 0 + else if high_top < visible_top + @search_results.scrollTop high_top + + result_clear_highlight: -> + @result_highlight.removeClass "highlighted" if @result_highlight + @result_highlight = null + + results_toggle: -> + if @results_showing + this.results_hide() + else + this.results_show() + + results_show: -> + if not @is_multiple + @selected_item.addClass "chzn-single-with-drop" + if @result_single_selected + this.result_do_highlight( @result_single_selected ) + + dd_top = if @is_multiple then @container.height() else (@container.height() - 1) + @dropdown.css {"top": dd_top + "px", "left":0} + @results_showing = true + + @search_field.focus() + @search_field.val @search_field.val() + + this.winnow_results() + + results_hide: -> + @selected_item.removeClass "chzn-single-with-drop" unless @is_multiple + this.result_clear_highlight() + @dropdown.css {"left":"-9000px"} + @results_showing = false + + + set_tab_index: (el) -> + if @form_field_jq.attr "tabindex" + ti = @form_field_jq.attr "tabindex" + @form_field_jq.attr "tabindex", -1 + + if @is_multiple + @search_field.attr "tabindex", ti + else + @selected_item.attr "tabindex", ti + @search_field.attr "tabindex", -1 + + show_search_field_default: -> + if @is_multiple and @choices < 1 and not @active_field + @search_field.val(@default_text) + @search_field.addClass "default" + else + @search_field.val("") + @search_field.removeClass "default" + + search_results_click: (evt) -> + target = if $(evt.target).hasClass "active-result" then $(evt.target) else $(evt.target).parents(".active-result").first() + if target.length + @result_highlight = target + this.result_select() + + search_results_mouseover: (evt) -> + target = if $(evt.target).hasClass "active-result" then $(evt.target) else $(evt.target).parents(".active-result").first() + this.result_do_highlight( target ) if target + + search_results_mouseout: (evt) -> + this.result_clear_highlight() if $(evt.target).hasClass "active-result" or $(evt.target).parents('.active-result').first() + + + choices_click: (evt) -> + evt.preventDefault() + if( @active_field and not($(evt.target).hasClass "search-choice" or $(evt.target).parents('.search-choice').first) and not @results_showing ) + this.results_show() + + choice_build: (item) -> + choice_id = @form_field.id + "_chzn_c_" + item.array_index + @choices += 1 + @search_container.before '<li class="search-choice" id="' + choice_id + '"><span>' + item.text + '</span><a href="javascript:void(0)" class="search-choice-close" rel="' + item.array_index + '"></a></li>' + link = $('#' + choice_id).find("a").first() + link.click (evt) => this.choice_destroy_link_click(evt) + + choice_destroy_link_click: (evt) -> + evt.preventDefault() + @pending_destroy_click = true + this.choice_destroy $(evt.target) + + choice_destroy: (link) -> + @choices -= 1 + this.show_search_field_default() + + this.results_hide() if @is_multiple and @choices > 0 and @search_field.val().length < 1 + + this.result_deselect (link.attr "rel") + link.parents('li').first().remove() + + result_select: -> + if @result_highlight + high = @result_highlight + high_id = high.attr "id" + + this.result_clear_highlight() + + high.addClass "result-selected" + + if @is_multiple + this.result_deactivate high + else + @result_single_selected = high + + position = high_id.substr(high_id.lastIndexOf("_") + 1 ) + item = @results_data[position] + item.selected = true + + @form_field.options[item.options_index].selected = true + + if @is_multiple + this.choice_build item + else + @selected_item.find("span").first().text item.text + + this.results_hide() + @search_field.val "" + + @form_field_jq.trigger "change" + this.search_field_scale() + + result_activate: (el) -> + el.addClass("active-result").show() + + result_deactivate: (el) -> + el.removeClass("active-result").hide() + + result_deselect: (pos) -> + result_data = @results_data[pos] + result_data.selected = false + + @form_field.options[result_data.options_index].selected = false + result = $("#" + @form_field.id + "chzn_o_" + pos) + result.removeClass("result-selected").addClass("active-result").show() + + this.result_clear_highlight() + this.winnow_results() + + @form_field_jq.trigger "change" + this.search_field_scale() + + results_search: (evt) -> + if @results_showing + this.winnow_results() + else + this.results_show() + + winnow_results: -> + startTime = new Date() + this.no_results_clear() + + results = 0 + + searchText = if @search_field.val() is @default_text then "" else $.trim @search_field.val() + regex = new RegExp('^' + searchText.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"), 'i') + zregex = new RegExp(searchText.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"), 'i') + + for option in @results_data + if not option.disabled and not option.empty + if option.group + $('#' + option.dom_id).hide() + else if not (@is_multiple and option.selected) + found = false + result_id = option.dom_id + + if regex.test option.text + found = true + results += 1 + else if option.text.indexOf(" ") >= 0 or option.text.indexOf("[") == 0 + #TODO: replace this substitution of /\[\]/ with a list of characters to skip. + parts = option.text.replace(/\[|\]/g, "").split(" ") + if parts.length + for part in parts + if regex.test part + found = true + results += 1 + + if found + if searchText.length + startpos = option.text.search zregex + text = option.text.substr(0, startpos + searchText.length) + '</em>' + option.text.substr(startpos + searchText.length) + text = text.substr(0, startpos) + '<em>' + text.substr(startpos) + else + text = option.text + + $("#" + result_id).html text if $("#" + result_id).html != text + + this.result_activate $("#" + result_id) + + $("#" + @results_data[option.group_array_index].dom_id).show() if option.group_array_index? + else + this.result_clear_highlight() if @result_highlight and result_id is @result_highlight.attr 'id' + this.result_deactivate $("#" + result_id) + + if results < 1 and searchText.length + this.no_results searchText + else + this.winnow_results_set_highlight() + + winnow_results_clear: -> + @search_field.val "" + lis = @search_results.find("li") + + for li in lis + li = $(li) + if li.hasClass "group-result" + li.show() + else if not @is_multiple or not li.hasClass "result-selected" + this.result_activate li + + winnow_results_set_highlight: -> + if not @result_highlight + do_high = @search_results.find(".active-result").first() + if(do_high) + this.result_do_highlight do_high + + no_results: (terms) -> + no_results_html = $('<li class="no-results">No results match "<span></span>"</li>') + no_results_html.find("span").first().text(terms) + + @search_results.append no_results_html + + no_results_clear: -> + @search_results.find(".no-results").remove() + + keydown_arrow: -> + if not @result_highlight + first_active = @search_results.find("li.active-result").first() + this.result_do_highlight $(first_active) if first_active + else if @results_showing + next_sib = @result_highlight.nextAll("li.active-result").first() + this.result_do_highlight next_sib if next_sib + this.results_show() if not @results_showing + + keyup_arrow: -> + if not @results_showing and not @is_multiple + this.results_show() + else if @result_highlight + prev_sibs = @result_highlight.prevAll("li.active-result") + + if prev_sibs.length + this.result_do_highlight prev_sibs.first() + else + this.results_hide() if @choices > 0 + this.result_clear_highlight() + + keydown_backstroke: -> + if @pending_backstroke + this.choice_destroy @pending_backstroke.find("a").first() + this.clear_backstroke() + else + @pending_backstroke = @search_container.siblings("li.search-choice").last() + @pending_backstroke.addClass "search-choice-focus" + + clear_backstroke: -> + @pending_backstroke.removeClass "search-choice-focus" if @pending_backstroke + @pending_backstroke = null + + keyup_checker: (evt) -> + stroke = evt.which ? evt.keyCode + this.search_field_scale() + + switch stroke + when 8 + if @is_multiple and @backstroke_length < 1 and @choices > 0 + this.keydown_backstroke() + else if not @pending_backstroke + this.result_clear_highlight() + this.results_search() + when 13 + evt.preventDefault() + this.result_select() if this.results_showing + when 27 + this.results_hide() if @results_showing + when 9, 38, 40, 16 + # don't do anything on these keys + else this.results_search() + + + keydown_checker: (evt) -> + stroke = evt.which ? evt.keyCode + this.search_field_scale() + + this.clear_backstroke() if stroke != 8 and this.pending_backstroke + + switch stroke + when 8 + @backstroke_length = this.search_field.val().length + break + when 9 + @mouse_on_container = false + break + when 13 + evt.preventDefault() + break + when 38 + evt.preventDefault() + this.keyup_arrow() + break + when 40 + this.keydown_arrow() + break + + + search_field_scale: -> + if @is_multiple + h = 0 + w = 0 + + style_block = "position:absolute; left: -1000px; top: -1000px; display:none;" + styles = ['font-size','font-style', 'font-weight', 'font-family','line-height', 'text-transform', 'letter-spacing'] + + for style in styles + style_block += style + ":" + @search_field.css(style) + ";" + + div = $('<div />', { 'style' : style_block }) + div.text @search_field.val() + $('body').append div + + w = div.width() + 25 + div.remove() + + if( w > @f_width-10 ) + w = @f_width - 10 + + @search_field.css({'width': w + 'px'}) + + dd_top = @container.height() + @dropdown.css({"top": dd_top + "px"}) + +get_side_border_padding = (elmt) -> + side_border_padding = elmt.outerWidth() - elmt.width() + +root.get_side_border_padding = get_side_border_padding + +class SelectParser + + constructor: -> + @options_index = 0 + @parsed = [] + + add_node: (child) -> + if child.nodeName is "OPTGROUP" + this.add_group child + else + this.add_option child + + add_group: (group) -> + group_position = @parsed.length + @parsed.push + array_index: group_position + group: true + label: group.label + children: 0 + disabled: group.disabled + this.add_option( option, group_position, group.disabled ) for option in group.childNodes + + add_option: (option, group_position, group_disabled) -> + if option.nodeName is "OPTION" + if option.text != "" + if group_position? + @parsed[group_position].children += 1 + @parsed.push + array_index: @parsed.length + options_index: @options_index + value: option.value + text: option.text + selected: option.selected + disabled: if group_disabled is true then group_disabled else option.disabled + group_array_index: group_position + else + @parsed.push + array_index: @parsed.length + options_index: @options_index + empty: true + @options_index += 1 + +SelectParser.select_to_array = (select) -> + parser = new SelectParser() + parser.add_node( child ) for child in select.childNodes + parser.parsed + +root.SelectParser = SelectParser diff --git a/usr/local/www/javascript/chosen/coffee/chosen.proto.coffee b/usr/local/www/javascript/chosen/coffee/chosen.proto.coffee new file mode 100644 index 0000000..87a22b6 --- /dev/null +++ b/usr/local/www/javascript/chosen/coffee/chosen.proto.coffee @@ -0,0 +1,629 @@ +### +Chosen, a Select Box Enhancer for jQuery and Protoype +by Patrick Filler for Harvest, http://getharvest.com + +Available for use under the MIT License, http://en.wikipedia.org/wiki/MIT_License + +Copyright (c) 2011 by Harvest +### + +root = exports ? this + +class Chosen + + constructor: (elmn) -> + this.set_default_values() + + @form_field = elmn + @is_multiple = @form_field.multiple + + @default_text_default = if @form_field.multiple then "Select Some Options" else "Select an Option" + + this.set_up_html() + this.register_observers() + + + set_default_values: -> + + @click_test_action = (evt) => this.test_active_click(evt) + @active_field = false + @mouse_on_container = false + @results_showing = false + @result_highlighted = null + @result_single_selected = null + @choices = 0 + + # HTML Templates + @single_temp = new Template('<a href="javascript:void(0)" class="chzn-single"><span>#{default}</span><div><b></b></div></a><div class="chzn-drop" style="left:-9000px;"><div class="chzn-search"><input type="text" /></div><ul class="chzn-results"></ul></div>') + @multi_temp = new Template('<ul class="chzn-choices"><li class="search-field"><input type="text" value="#{default}" class="default" style="width:25px;" /></li></ul><div class="chzn-drop" style="left:-9000px;"><ul class="chzn-results"></ul></div>') + @choice_temp = new Template('<li class="search-choice" id="#{id}"><span>#{choice}</span><a href="javascript:void(0)" class="search-choice-close" rel="#{position}"></a></li>') + @no_results_temp = new Template('<li class="no-results">No results match "<span>#{terms}</span>"</li>') + + + set_up_html: -> + @container_id = @form_field.id + "_chzn" + + @f_width = if @form_field.getStyle("width") then parseInt @form_field.getStyle("width"), 10 else @form_field.getWidth() + + container_props = + 'id': @container_id + 'class': 'chzn-container' + 'style': 'width: ' + (@f_width) + 'px' #use parens around @f_width so coffeescript doesn't think + ' px' is a function parameter + + @default_text = if @form_field.readAttribute 'title' then @form_field.readAttribute 'title' else @default_text_default + + base_template = if @is_multiple then new Element('div', container_props).update( @multi_temp.evaluate({ "default": @default_text}) ) else new Element('div', container_props).update( @single_temp.evaluate({ "default":@default_text }) ) + + @form_field.hide().insert({ after: base_template }) + @container = $(@container_id) + @container.addClassName( "chzn-container-" + (if @is_multiple then "multi" else "single") ) + @dropdown = @container.down('div.chzn-drop') + + dd_top = @container.getHeight() + dd_width = (@f_width - get_side_border_padding(@dropdown)) + + @dropdown.setStyle({"width": dd_width + "px", "top": dd_top + "px"}) + + @search_field = @container.down('input') + @search_results = @container.down('ul.chzn-results') + this.search_field_scale() + + @search_no_results = @container.down('li.no-results') + + if @is_multiple + @search_choices = @container.down('ul.chzn-choices') + @search_container = @container.down('li.search-field') + else + @search_container = @container.down('div.chzn-search') + @selected_item = @container.down('.chzn-single') + sf_width = dd_width - get_side_border_padding(@search_container) - get_side_border_padding(@search_field) + @search_field.setStyle( {"width" : sf_width + "px"} ) + + this.results_build() + this.set_tab_index() + + + register_observers: -> + @container.observe "click", (evt) => this.container_click(evt) + @container.observe "mouseenter", (evt) => this.mouse_enter(evt) + @container.observe "mouseleave", (evt) => this.mouse_leave(evt) + + @search_results.observe "click", (evt) => this.search_results_click(evt) + @search_results.observe "mouseover", (evt) => this.search_results_mouseover(evt) + @search_results.observe "mouseout", (evt) => this.search_results_mouseout(evt) + + @form_field.observe "liszt:updated", (evt) => this.results_update_field(evt) + + @search_field.observe "blur", (evt) => this.input_blur(evt) + @search_field.observe "keyup", (evt) => this.keyup_checker(evt) + @search_field.observe "keydown", (evt) => this.keydown_checker(evt) + + if @is_multiple + @search_choices.observe "click", (evt) => this.choices_click(evt) + @search_field.observe "focus", (evt) => this.input_focus(evt) + else + @selected_item.observe "focus", (evt) => this.activate_field(evt) + + + container_click: (evt) -> + if evt and evt.type is "click" + evt.stop() + if not @pending_destroy_click + if not @active_field + @search_field.clear() if @is_multiple + document.observe "click", @click_test_action + this.results_show() + else if not @is_multiple and evt and (evt.target is @selected_item || evt.target.up("a.chzn-single")) + this.results_toggle() + + this.activate_field() + else + @pending_destroy_click = false + + mouse_enter: -> @mouse_on_container = true + mouse_leave: -> @mouse_on_container = false + + input_focus: (evt) -> + setTimeout this.container_click.bind(this), 50 unless @active_field + + input_blur: (evt) -> + if not @mouse_on_container + @active_field = false + setTimeout this.blur_test.bind(this), 100 + + blur_test: (evt) -> + this.close_field() if not @active_field and @container.hasClassName("chzn-container-active") + + close_field: -> + document.stopObserving "click", @click_test_action + + if not @is_multiple + @selected_item.tabIndex = @search_field.tabIndex + @search_field.tabIndex = -1 + + @active_field = false + this.results_hide() + + @container.removeClassName "chzn-container-active" + this.winnow_results_clear() + this.clear_backstroke() + + this.show_search_field_default() + this.search_field_scale() + + activate_field: -> + if not @is_multiple and not @active_field + @search_field.tabIndex = @selected_item.tabIndex + @selected_item.tabIndex = -1 + + @container.addClassName "chzn-container-active" + @active_field = true + + @search_field.value = @search_field.value + @search_field.focus() + + + test_active_click: (evt) -> + if evt.target.up('#' + @container.id) + @active_field = true + else + this.close_field() + + results_build: -> + startTime = new Date() + @parsing = true + @results_data = SelectParser.select_to_array @form_field + + if @is_multiple and @choices > 0 + @search_choices.select("li.search-choice").invoke("remove") + @choices = 0 + else if not @is_multiple + @selected_item.down("span").update(@default_text) + + content = '' + for data in @results_data + if data.group + content += this.result_add_group data + else if !data.empty + content += this.result_add_option data + if data.selected and @is_multiple + this.choice_build data + else if data.selected and not @is_multiple + @selected_item.down("span").update( data.text ) + + this.show_search_field_default() + this.search_field_scale() + + @search_results.update content + @parsing = false + + + result_add_group: (group) -> + if not group.disabled + group.dom_id = @form_field.id + "chzn_g_" + group.array_index + '<li id="' + group.dom_id + '" class="group-result">' + group.label.escapeHTML() + '</li>' + else + "" + + result_add_option: (option) -> + if not option.disabled + option.dom_id = @form_field.id + "chzn_o_" + option.array_index + + classes = if option.selected and @is_multiple then [] else ["active-result"] + classes.push "result-selected" if option.selected + classes.push "group-option" if option.group_array_index? + + '<li id="' + option.dom_id + '" class="' + classes.join(' ') + '">' + option.text.escapeHTML() + '</li>' + else + "" + + results_update_field: -> + this.result_clear_highlight() + @result_single_selected = null + this.results_build() + + result_do_highlight: (el) -> + this.result_clear_highlight() + + @result_highlight = el + @result_highlight.addClassName "highlighted" + + maxHeight = parseInt @search_results.getStyle('maxHeight'), 10 + visible_top = @search_results.scrollTop + visible_bottom = maxHeight + visible_top + + high_top = @result_highlight.positionedOffset().top + high_bottom = high_top + @result_highlight.getHeight() + + if high_bottom >= visible_bottom + @search_results.scrollTop = if (high_bottom - maxHeight) > 0 then (high_bottom - maxHeight) else 0 + else if high_top < visible_top + @search_results.scrollTop = high_top + + result_clear_highlight: -> + @result_highlight.removeClassName('highlighted') if @result_highlight + @result_highlight = null + + results_toggle: -> + if @results_showing + this.results_hide() + else + this.results_show() + + results_show: -> + if not @is_multiple + @selected_item.addClassName('chzn-single-with-drop') + if @result_single_selected + this.result_do_highlight( @result_single_selected ) + + dd_top = if @is_multiple then @container.getHeight() else (@container.getHeight() - 1) + @dropdown.setStyle {"top": dd_top + "px", "left":0} + @results_showing = true + + @search_field.focus() + @search_field.value = @search_field.value + + this.winnow_results() + + results_hide: -> + @selected_item.removeClassName('chzn-single-with-drop') unless @is_multiple + this.result_clear_highlight() + @dropdown.setStyle({"left":"-9000px"}) + @results_showing = false + + + set_tab_index: (el) -> + if @form_field.tabIndex + ti = @form_field.tabIndex + @form_field.tabIndex = -1 + + if @is_multiple + @search_field.tabIndex = ti + else + @selected_item.tabIndex = ti + @search_field.tabIndex = -1 + + show_search_field_default: -> + if @is_multiple and @choices < 1 and not @active_field + @search_field.value = @default_text + @search_field.addClassName "default" + else + @search_field.value = "" + @search_field.removeClassName "default" + + search_results_click: (evt) -> + target = if evt.target.hasClassName("active-result") then evt.target else evt.target.up(".active-result") + if target + @result_highlight = target + this.result_select() + + search_results_mouseover: (evt) -> + target = if evt.target.hasClassName("active-result") then evt.target else evt.target.up(".active-result") + this.result_do_highlight( target ) if target + + search_results_mouseout: (evt) -> + this.result_clear_highlight() if evt.target.hasClassName('active-result') or evt.target.up('.active-result') + + + choices_click: (evt) -> + evt.preventDefault() + if( @active_field and not(evt.target.hasClassName('search-choice') or evt.target.up('.search-choice')) and not @results_showing ) + this.results_show() + + choice_build: (item) -> + choice_id = @form_field.id + "_chzn_c_" + item.array_index + @choices += 1 + @search_container.insert { before: @choice_temp.evaluate({"id":choice_id, "choice":item.text, "position":item.array_index}) } + link = $(choice_id).down('a') + link.observe "click", (evt) => this.choice_destroy_link_click(evt) + + choice_destroy_link_click: (evt) -> + evt.preventDefault() + @pending_destroy_click = true + this.choice_destroy evt.target + + choice_destroy: (link) -> + @choices -= 1 + this.show_search_field_default() + + this.results_hide() if @is_multiple and @choices > 0 and @search_field.value.length < 1 + + this.result_deselect link.readAttribute("rel") + link.up('li').remove() + + result_select: -> + if @result_highlight + high = @result_highlight + this.result_clear_highlight() + + high.addClassName("result-selected") + + if @is_multiple + this.result_deactivate high + else + @result_single_selected = high + + position = high.id.substr(high.id.lastIndexOf("_") + 1 ) + item = @results_data[position] + item.selected = true + + @form_field.options[item.options_index].selected = true + + if @is_multiple + this.choice_build item + else + @selected_item.down("span").update(item.text) + + this.results_hide() + @search_field.value = "" + + @form_field.simulate("change") if typeof Event.simulate is 'function' + this.search_field_scale() + + result_activate: (el) -> + el.addClassName("active-result").show() + + result_deactivate: (el) -> + el.removeClassName("active-result").hide() + + result_deselect: (pos) -> + result_data = @results_data[pos] + result_data.selected = false + + @form_field.options[result_data.options_index].selected = false + result = $(@form_field.id + "chzn_o_" + pos) + result.removeClassName("result-selected").addClassName("active-result").show() + + this.result_clear_highlight() + this.winnow_results() + + @form_field.simulate("change") if typeof Event.simulate is 'function' + this.search_field_scale() + + results_search: (evt) -> + if @results_showing + this.winnow_results() + else + this.results_show() + + winnow_results: -> + startTime = new Date() + this.no_results_clear() + + results = 0 + + searchText = if @search_field.value is @default_text then "" else @search_field.value.strip() + regex = new RegExp('^' + searchText.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"), 'i') + zregex = new RegExp(searchText.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, "\\$&"), 'i') + + for option in @results_data + if not option.disabled and not option.empty + if option.group + $(option.dom_id).hide() + else if not (@is_multiple and option.selected) + found = false + result_id = option.dom_id + + if regex.test option.text + found = true + results += 1 + else if option.text.indexOf(" ") >= 0 or option.text.indexOf("[") == 0 + #TODO: replace this substitution of /\[\]/ with a list of characters to skip. + parts = option.text.replace(/\[|\]/g, "").split(" ") + if parts.length + for part in parts + if regex.test part + found = true + results += 1 + + if found + if searchText.length + startpos = option.text.search zregex + text = option.text.substr(0, startpos + searchText.length) + '</em>' + option.text.substr(startpos + searchText.length) + text = text.substr(0, startpos) + '<em>' + text.substr(startpos) + else + text = option.text + + $(result_id).update text if $(result_id).innerHTML != text + + this.result_activate $(result_id) + + $(@results_data[option.group_array_index].dom_id).show() if option.group_array_index? + else + this.result_clear_highlight() if $(result_id) is @result_highlight + this.result_deactivate $(result_id) + + if results < 1 and searchText.length + this.no_results(searchText) + else + this.winnow_results_set_highlight() + + winnow_results_clear: -> + @search_field.clear() + lis = @search_results.select("li") + + for li in lis + if li.hasClassName("group-result") + li.show() + else if not @is_multiple or not li.hasClassName("result-selected") + this.result_activate li + + winnow_results_set_highlight: -> + if not @result_highlight + do_high = @search_results.down(".active-result") + if(do_high) + this.result_do_highlight do_high + + no_results: (terms) -> + @search_results.insert @no_results_temp.evaluate({"terms":terms.escapeHTML()}) + + no_results_clear: -> + nr = null + nr.remove() while nr = @search_results.down(".no-results") + + + keydown_arrow: -> + actives = @search_results.select("li.active-result") + if actives.length + if not @result_highlight + this.result_do_highlight actives.first() + else if @results_showing + sibs = @result_highlight.nextSiblings() + nexts = sibs.intersect(actives) + this.result_do_highlight nexts.first() if nexts.length + this.results_show() if not @results_showing + + keyup_arrow: -> + if not @results_showing and not @is_multiple + this.results_show() + else if @result_highlight + sibs = @result_highlight.previousSiblings() + actives = @search_results.select("li.active-result") + prevs = sibs.intersect(actives) + + if prevs.length + this.result_do_highlight prevs.first() + else + this.results_hide() if @choices > 0 + this.result_clear_highlight() + + keydown_backstroke: -> + if @pending_backstroke + this.choice_destroy @pending_backstroke.down("a") + this.clear_backstroke() + else + @pending_backstroke = @search_container.siblings("li.search-choice").last() + @pending_backstroke.addClassName("search-choice-focus") + + clear_backstroke: -> + @pending_backstroke.removeClassName("search-choice-focus") if @pending_backstroke + @pending_backstroke = null + + keyup_checker: (evt) -> + stroke = evt.which ? evt.keyCode + this.search_field_scale() + + switch stroke + when 8 + if @is_multiple and @backstroke_length < 1 and @choices > 0 + this.keydown_backstroke() + else if not @pending_backstroke + this.result_clear_highlight() + this.results_search() + when 13 + evt.preventDefault() + this.result_select() if this.results_showing + when 27 + this.results_hide() if @results_showing + when 9, 38, 40, 16 + # don't do anything on these keys + else this.results_search() + + + keydown_checker: (evt) -> + stroke = evt.which ? evt.keyCode + this.search_field_scale() + + this.clear_backstroke() if stroke != 8 and this.pending_backstroke + + switch stroke + when 8 + @backstroke_length = this.search_field.value.length + when 9 + @mouse_on_container = false + when 13 + evt.preventDefault() + when 38 + evt.preventDefault() + this.keyup_arrow() + when 40 + this.keydown_arrow() + + + search_field_scale: -> + if @is_multiple + h = 0 + w = 0 + + style_block = "position:absolute; left: -1000px; top: -1000px; display:none;" + styles = ['font-size','font-style', 'font-weight', 'font-family','line-height', 'text-transform', 'letter-spacing'] + + for style in styles + style_block += style + ":" + @search_field.getStyle(style) + ";" + + div = new Element('div', { 'style' : style_block }).update(@search_field.value) + document.body.appendChild(div) + + w = Element.measure(div, 'width') + 25 + div.remove() + + if( w > @f_width-10 ) + w = @f_width - 10 + + @search_field.setStyle({'width': w + 'px'}) + + dd_top = @container.getHeight() + @dropdown.setStyle({"top": dd_top + "px"}) + +root.Chosen = Chosen + +document.observe 'dom:loaded', (evt) -> + selects = $$(".chzn-select") + new Chosen select for select in selects + +get_side_border_padding = (elmt) -> + layout = new Element.Layout(elmt) + side_border_padding = layout.get("border-left") + layout.get("border-right") + layout.get("padding-left") + layout.get("padding-right") + +root.get_side_border_padding = get_side_border_padding + +root = exports ? this + +class SelectParser + + constructor: -> + @options_index = 0 + @parsed = [] + + add_node: (child) -> + if child.nodeName is "OPTGROUP" + this.add_group child + else + this.add_option child + + add_group: (group) -> + group_position = @parsed.length + @parsed.push + array_index: group_position + group: true + label: group.label + children: 0 + disabled: group.disabled + this.add_option( option, group_position, group.disabled ) for option in group.childNodes + + add_option: (option, group_position, group_disabled) -> + if option.nodeName is "OPTION" + if option.text != "" + if group_position? + @parsed[group_position].children += 1 + @parsed.push + array_index: @parsed.length + options_index: @options_index + value: option.value + text: option.text + selected: option.selected + disabled: if group_disabled is true then group_disabled else option.disabled + group_array_index: group_position + else + @parsed.push + array_index: @parsed.length + options_index: @options_index + empty: true + @options_index += 1 + +SelectParser.select_to_array = (select) -> + parser = new SelectParser() + parser.add_node( child ) for child in select.childNodes + parser.parsed + +root.SelectParser = SelectParser diff --git a/usr/local/www/javascript/scriptaculous/prototype.js b/usr/local/www/javascript/scriptaculous/prototype.js index bfc9d7a..474b223 100644 --- a/usr/local/www/javascript/scriptaculous/prototype.js +++ b/usr/local/www/javascript/scriptaculous/prototype.js @@ -1,5 +1,5 @@ -/* Prototype JavaScript framework, version 1.6.1 - * (c) 2005-2009 Sam Stephenson +/* Prototype JavaScript framework, version 1.7 + * (c) 2005-2010 Sam Stephenson * * Prototype is freely distributable under the terms of an MIT-style license. * For details, see the Prototype web site: http://www.prototypejs.org/ @@ -7,7 +7,8 @@ *--------------------------------------------------------------------------*/ var Prototype = { - Version: '1.6.1', + + Version: '1.7', Browser: (function(){ var ua = navigator.userAgent; @@ -17,13 +18,15 @@ var Prototype = { Opera: isOpera, WebKit: ua.indexOf('AppleWebKit/') > -1, Gecko: ua.indexOf('Gecko') > -1 && ua.indexOf('KHTML') === -1, - MobileSafari: /Apple.*Mobile.*Safari/.test(ua) + MobileSafari: /Apple.*Mobile/.test(ua) } })(), BrowserFeatures: { XPath: !!document.evaluate, + SelectorsAPI: !!document.querySelector, + ElementExtensions: (function() { var constructor = window.Element || window.HTMLElement; return !!(constructor && constructor.prototype); @@ -32,9 +35,9 @@ var Prototype = { if (typeof window.HTMLDivElement !== 'undefined') return true; - var div = document.createElement('div'); - var form = document.createElement('form'); - var isSupported = false; + var div = document.createElement('div'), + form = document.createElement('form'), + isSupported = false; if (div['__proto__'] && (div['__proto__'] !== form['__proto__'])) { isSupported = true; @@ -50,6 +53,7 @@ var Prototype = { JSONFilter: /^\/\*-secure-([\s\S]*)\*\/\s*$/, emptyFunction: function() { }, + K: function(x) { return x } }; @@ -79,6 +83,14 @@ var Try = { /* 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); @@ -99,7 +111,7 @@ var Class = (function() { parent.subclasses.push(klass); } - for (var i = 0; i < properties.length; i++) + for (var i = 0, length = properties.length; i < length; i++) klass.addMethods(properties[i]); if (!klass.prototype.initialize) @@ -110,10 +122,10 @@ var Class = (function() { } function addMethods(source) { - var ancestor = this.superclass && this.superclass.prototype; - var properties = Object.keys(source); + var ancestor = this.superclass && this.superclass.prototype, + properties = Object.keys(source); - if (!Object.keys({ toString: true }).length) { + if (IS_DONTENUM_BUGGY) { if (source.toString != Object.prototype.toString) properties.push("toString"); if (source.valueOf != Object.prototype.valueOf) @@ -123,7 +135,7 @@ var Class = (function() { for (var i = 0, length = properties.length; i < length; i++) { var property = properties[i], value = source[property]; if (ancestor && Object.isFunction(value) && - value.argumentNames().first() == "$super") { + value.argumentNames()[0] == "$super") { var method = value; value = (function(m) { return function() { return ancestor[m].apply(this, arguments); }; @@ -147,7 +159,37 @@ var Class = (function() { })(); (function() { - var _toString = Object.prototype.toString; + var _toString = Object.prototype.toString, + 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'; + + 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) @@ -166,31 +208,73 @@ var Class = (function() { } } - function toJSON(object) { - var type = typeof object; - switch (type) { - case 'undefined': - case 'function': - case 'unknown': return; - case 'boolean': return object.toString(); + function toJSON(value) { + return Str('', { '': value }, []); + } + + function Str(key, holder, stack) { + var value = holder[key], + type = typeof value; + + if (Type(value) === OBJECT_TYPE && typeof value.toJSON === 'function') { + value = value.toJSON(key); } - if (object === null) return 'null'; - if (object.toJSON) return object.toJSON(); - if (isElement(object)) return; + var _class = _toString.call(value); - var results = []; - for (var property in object) { - var value = toJSON(object[property]); - if (!isUndefined(value)) - results.push(property.toJSON() + ': ' + 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'; + } + + 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(); } + } + 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; } + } - return '{' + results.join(', ') + '}'; + function stringify(object) { + return JSON.stringify(object); } function toQueryString(object) { - //console.debug(object); return $H(object).toQueryString(); } @@ -199,9 +283,13 @@ var Class = (function() { } function keys(object) { + if (Type(object) !== OBJECT_TYPE) { throw new TypeError(); } var results = []; - for (var property in object) - results.push(property); + for (var property in object) { + if (object.hasOwnProperty(property)) { + results.push(property); + } + } return results; } @@ -221,24 +309,34 @@ var Class = (function() { } function isArray(object) { - return _toString.call(object) == "[object Array]"; + 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 typeof object === "function"; + return _toString.call(object) === FUNCTION_CLASS; } function isString(object) { - return _toString.call(object) == "[object String]"; + return _toString.call(object) === STRING_CLASS; } function isNumber(object) { - return _toString.call(object) == "[object Number]"; + return _toString.call(object) === NUMBER_CLASS; + } + + function isDate(object) { + return _toString.call(object) === DATE_CLASS; } function isUndefined(object) { @@ -248,10 +346,10 @@ var Class = (function() { extend(Object, { extend: extend, inspect: inspect, - toJSON: toJSON, + toJSON: NATIVE_JSON_STRINGIFY_SUPPORT ? stringify : toJSON, toQueryString: toQueryString, toHTML: toHTML, - keys: keys, + keys: Object.keys || keys, values: values, clone: clone, isElement: isElement, @@ -260,6 +358,7 @@ var Class = (function() { isFunction: isFunction, isString: isString, isNumber: isNumber, + isDate: isDate, isUndefined: isUndefined }); })(); @@ -312,7 +411,7 @@ Object.extend(Function.prototype, (function() { function delay(timeout) { var __method = this, args = slice.call(arguments, 1); - timeout = timeout * 1000 + timeout = timeout * 1000; return window.setTimeout(function() { return __method.apply(__method, args); }, timeout); @@ -353,14 +452,28 @@ Object.extend(Function.prototype, (function() { })()); -Date.prototype.toJSON = function() { - 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(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; @@ -419,6 +532,9 @@ Object.extend(String, { }); 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; @@ -485,8 +601,8 @@ Object.extend(String.prototype, (function() { } function extractScripts() { - var matchAll = new RegExp(Prototype.ScriptFragment, 'img'); - var matchOne = new RegExp(Prototype.ScriptFragment, 'im'); + 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]; }); @@ -511,8 +627,9 @@ Object.extend(String.prototype, (function() { return match[1].split(separator || '&').inject({ }, function(hash, pair) { if ((pair = pair.split('='))[0]) { - var key = decodeURIComponent(pair.shift()); - var value = pair.length > 1 ? pair.join('=') : pair[0]; + var key = decodeURIComponent(pair.shift()), + value = pair.length > 1 ? pair.join('=') : pair[0]; + if (value != undefined) value = decodeURIComponent(value); if (key in hash) { @@ -539,17 +656,9 @@ Object.extend(String.prototype, (function() { } function camelize() { - var parts = this.split('-'), len = parts.length; - if (len == 1) return parts[0]; - - var camelized = this.charAt(0) == '-' - ? parts[0].charAt(0).toUpperCase() + parts[0].substring(1) - : parts[0]; - - for (var i = 1; i < len; i++) - camelized += parts[i].charAt(0).toUpperCase() + parts[i].substring(1); - - return camelized; + return this.replace(/-+(.)?/g, function(match, chr) { + return chr ? chr.toUpperCase() : ''; + }); } function capitalize() { @@ -579,10 +688,6 @@ Object.extend(String.prototype, (function() { return "'" + escapedString.replace(/'/g, '\\\'') + "'"; } - function toJSON() { - return this.inspect(true); - } - function unfilterJSON(filter) { return this.replace(filter || Prototype.JSONFilter, '$1'); } @@ -590,29 +695,42 @@ Object.extend(String.prototype, (function() { function isJSON() { var str = this; if (str.blank()) return false; - str = this.replace(/\\./g, '@').replace(/"[^"\\\n\r]*"/g, ''); - return (/^[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]*$/).test(str); + 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(); + var json = this.unfilterJSON(), + cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/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) { - return this.indexOf(pattern) === 0; + return this.lastIndexOf(pattern, 0) === 0; } function endsWith(pattern) { var d = this.length - pattern.length; - return d >= 0 && this.lastIndexOf(pattern) === d; + return d >= 0 && this.indexOf(pattern, d) === d; } function empty() { @@ -632,7 +750,7 @@ Object.extend(String.prototype, (function() { sub: sub, scan: scan, truncate: truncate, - strip: String.prototype.trim ? String.prototype.trim : strip, + strip: String.prototype.trim || strip, stripTags: stripTags, stripScripts: stripScripts, extractScripts: extractScripts, @@ -649,10 +767,9 @@ Object.extend(String.prototype, (function() { underscore: underscore, dasherize: dasherize, inspect: inspect, - toJSON: toJSON, unfilterJSON: unfilterJSON, isJSON: isJSON, - evalJSON: evalJSON, + evalJSON: NATIVE_JSON_PARSE_SUPPORT ? parseJSON : evalJSON, include: include, startsWith: startsWith, endsWith: endsWith, @@ -678,8 +795,9 @@ var Template = Class.create({ var before = match[1] || ''; if (before == '\\') return match[2]; - var ctx = object, expr = match[3]; - var pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/; + var ctx = object, expr = match[3], + pattern = /^([^.[]+|\[((?:.*?[^\\])?)\])(\.|\[|$)/; + match = pattern.exec(expr); if (match == null) return before; @@ -944,6 +1062,7 @@ var Enumerable = (function() { find: detect }; })(); + function $A(iterable) { if (!iterable) return []; if ('toArray' in Object(iterable)) return iterable.toArray(); @@ -952,6 +1071,7 @@ function $A(iterable) { return results; } + function $w(string) { if (!Object.isString(string)) return []; string = string.strip(); @@ -966,9 +1086,10 @@ Array.from = $A; slice = arrayProto.slice, _each = arrayProto.forEach; // use native browser JS 1.6 implementation if available - function each(iterator) { - for (var i = 0, length = this.length; i < length; i++) - iterator(this[i]); + 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; @@ -1008,7 +1129,7 @@ Array.from = $A; } function reverse(inline) { - return (inline !== false ? this : this.toArray())._reverse(); + return (inline === false ? this.toArray() : this)._reverse(); } function uniq(sorted) { @@ -1038,15 +1159,6 @@ Array.from = $A; return '[' + this.map(Object.inspect).join(', ') + ']'; } - function toJSON() { - var results = []; - this.each(function(object) { - var value = Object.toJSON(object); - if (!Object.isUndefined(value)) results.push(value); - }); - return '[' + results.join(', ') + ']'; - } - function indexOf(item, i) { i || (i = 0); var length = this.length; @@ -1095,8 +1207,7 @@ Array.from = $A; clone: clone, toArray: clone, size: size, - inspect: inspect, - toJSON: toJSON + inspect: inspect }); var CONCAT_ARGUMENTS_BUGGY = (function() { @@ -1117,6 +1228,7 @@ var Hash = Class.create(Enumerable, (function() { this._object = Object.isHash(object) ? object.toObject() : Object.clone(object); } + function _each(iterator) { for (var key in this._object) { var value = this._object[key], pair = [key, value]; @@ -1145,6 +1257,8 @@ var Hash = Class.create(Enumerable, (function() { return Object.clone(this._object); } + + function keys() { return this.pluck('key'); } @@ -1181,8 +1295,14 @@ var Hash = Class.create(Enumerable, (function() { var key = encodeURIComponent(pair.key), values = pair.value; if (values && typeof values == 'object') { - if (Object.isArray(values)) - return results.concat(values.map(toQueryPair.curry(key))); + 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('&'); @@ -1194,10 +1314,6 @@ var Hash = Class.create(Enumerable, (function() { }).join(', ') + '}>'; } - function toJSON() { - return Object.toJSON(this.toObject()); - } - function clone() { return new Hash(this); } @@ -1217,7 +1333,7 @@ var Hash = Class.create(Enumerable, (function() { update: update, toQueryString: toQueryString, inspect: inspect, - toJSON: toJSON, + toJSON: toObject, clone: clone }; })()); @@ -1242,10 +1358,6 @@ Object.extend(Number.prototype, (function() { return '0'.times(length - string.length) + string; } - function toJSON() { - return isFinite(this) ? this.toString() : 'null'; - } - function abs() { return Math.abs(this); } @@ -1267,7 +1379,6 @@ Object.extend(Number.prototype, (function() { succ: succ, times: times, toPaddedString: toPaddedString, - toJSON: toJSON, abs: abs, round: round, ceil: ceil, @@ -1371,9 +1482,7 @@ Ajax.Base = Class.create({ this.options.method = this.options.method.toLowerCase(); - if (Object.isString(this.options.parameters)) - this.options.parameters = this.options.parameters.toQueryParams(); - else if (Object.isHash(this.options.parameters)) + if (Object.isHash(this.options.parameters)) this.options.parameters = this.options.parameters.toObject(); } }); @@ -1389,22 +1498,21 @@ Ajax.Request = Class.create(Ajax.Base, { request: function(url) { this.url = url; this.method = this.options.method; - var params = Object.clone(this.options.parameters); + var params = Object.isString(this.options.parameters) ? + this.options.parameters : + Object.toQueryString(this.options.parameters); if (!['get', 'post'].include(this.method)) { - params['_method'] = this.method; + params += (params ? '&' : '') + "_method=" + this.method; this.method = 'post'; } - this.parameters = params; - - if (params = Object.toQueryString(params)) { - if (this.method == 'get') - this.url += (this.url.include('?') ? '&' : '?') + params; - else if (/Konqueror|Safari|KHTML/.test(navigator.userAgent)) - params += '&_='; + 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); @@ -1473,11 +1581,12 @@ Ajax.Request = Class.create(Ajax.Base, { success: function() { var status = this.getStatus(); - return !status || (status >= 200 && status < 300); + 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 } }, @@ -1559,14 +1668,14 @@ Ajax.Response = Class.create({ var transport = this.transport = request.transport, readyState = this.readyState = transport.readyState; - if((readyState > 2 && !Prototype.Browser.IE) || readyState == 4) { + 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) { + if (readyState == 4) { var xml = transport.responseXML; this.responseXML = Object.isUndefined(xml) ? null : xml; this.responseJSON = this._getResponseJSON(); @@ -1706,7 +1815,6 @@ Ajax.PeriodicalUpdater = Class.create(Ajax.Base, { }); - function $(element) { if (arguments.length > 1) { for (var i = 0, elements = [], length = arguments.length; i < length; i++) @@ -1731,7 +1839,7 @@ if (Prototype.BrowserFeatures.XPath) { /*--------------------------------------------------------------------------*/ -if (!window.Node) var Node = { }; +if (!Node) var Node = { }; if (!Node.ELEMENT_NODE) { Object.extend(Node, { @@ -1751,42 +1859,61 @@ if (!Node.ELEMENT_NODE) { } + (function(global) { + function shouldUseCache(tagName, attributes) { + if (tagName === 'select') return false; + if ('type' in attributes) return false; + return true; + } - var SETATTRIBUTE_IGNORES_NAME = (function(){ - var elForm = document.createElement("form"); - var elInput = document.createElement("input"); - var root = document.documentElement; - elInput.setAttribute("name", "test"); - elForm.appendChild(elInput); - root.appendChild(elForm); - var isBuggy = elForm.elements - ? (typeof elForm.elements.test == "undefined") - : null; - root.removeChild(elForm); - elForm = elInput = null; - return isBuggy; + var HAS_EXTENDED_CREATE_ELEMENT_SYNTAX = (function(){ + try { + var el = document.createElement('<input name="x">'); + return el.tagName.toLowerCase() === 'input' && el.name === 'x'; + } + catch(err) { + return false; + } })(); var element = global.Element; + global.Element = function(tagName, attributes) { attributes = attributes || { }; tagName = tagName.toLowerCase(); var cache = Element.cache; - if (SETATTRIBUTE_IGNORES_NAME && attributes.name) { + + if (HAS_EXTENDED_CREATE_ELEMENT_SYNTAX && attributes.name) { tagName = '<' + tagName + ' name="' + attributes.name + '">'; delete attributes.name; return Element.writeAttribute(document.createElement(tagName), attributes); } + if (!cache[tagName]) cache[tagName] = Element.extend(document.createElement(tagName)); - return Element.writeAttribute(cache[tagName].cloneNode(false), attributes); + + var node = shouldUseCache(tagName, attributes) ? + cache[tagName].cloneNode(false) : document.createElement(tagName); + + return Element.writeAttribute(node, attributes); }; + Object.extend(global.Element, element || { }); if (element) global.Element.prototype = element.prototype; + })(this); -Element.cache = { }; Element.idCounter = 1; +Element.cache = { }; + +Element._purgeElement = function(element) { + var uid = element._prototypeUID; + if (uid) { + Element.stopObserving(element); + element._prototypeUID = void 0; + delete Element.Storage[uid]; + } +} Element.Methods = { visible: function(element) { @@ -1799,7 +1926,6 @@ Element.Methods = { return element; }, - hide: function(element) { element = $(element); element.style.display = 'none'; @@ -1845,6 +1971,21 @@ Element.Methods = { } })(); + var LINK_ELEMENT_INNERHTML_BUGGY = (function() { + try { + var el = document.createElement('div'); + el.innerHTML = "<link>"; + 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; @@ -1859,8 +2000,14 @@ Element.Methods = { return isBuggy; })(); + function update(element, content) { element = $(element); + var purgeElement = Element._purgeElement; + + var descendants = element.getElementsByTagName('*'), + i = descendants.length; + while (i--) purgeElement(descendants[i]); if (content && content.toElement) content = content.toElement(); @@ -1877,7 +2024,7 @@ Element.Methods = { return element; } - if (SELECT_ELEMENT_INNERHTML_BUGGY || TABLE_ELEMENT_INNERHTML_BUGGY) { + if (ANY_INNERHTML_BUGGY) { if (tagName in Element._insertionTranslations.tags) { while (element.firstChild) { element.removeChild(element.firstChild); @@ -1886,6 +2033,12 @@ Element.Methods = { .each(function(node) { element.appendChild(node) }); + } else if (LINK_ELEMENT_INNERHTML_BUGGY && Object.isString(content) && content.indexOf('<link') > -1) { + while (element.firstChild) { + element.removeChild(element.firstChild); + } + var nodes = Element._getContentFromAnonymousElement(tagName, content.stripScripts(), true); + nodes.each(function(node) { element.appendChild(node) }); } else { element.innerHTML = content.stripScripts(); @@ -1968,19 +2121,26 @@ Element.Methods = { element = $(element); var result = '<' + element.tagName.toLowerCase(); $H({'id': 'id', 'className': 'class'}).each(function(pair) { - var property = pair.first(), attribute = pair.last(); - var value = (element[property] || '').toString(); + var property = pair.first(), + attribute = pair.last(), + value = (element[property] || '').toString(); if (value) result += ' ' + attribute + '=' + value.inspect(true); }); return result + '>'; }, - recursivelyCollect: function(element, property) { + recursivelyCollect: function(element, property, maximumLength) { element = $(element); + maximumLength = maximumLength || -1; var elements = []; - while (element = element[property]) + + while (element = element[property]) { if (element.nodeType == 1) elements.push(Element.extend(element)); + if (elements.length == maximumLength) + break; + } + return elements; }, @@ -1999,13 +2159,17 @@ Element.Methods = { }, immediateDescendants: function(element) { - if (!(element = $(element).firstChild)) return []; - while (element && element.nodeType != 1) element = element.nextSibling; - if (element) return [element].concat($(element).nextSiblings()); - return []; + var results = [], child = $(element).firstChild; + while (child) { + if (child.nodeType === 1) { + results.push(Element.extend(child)); + } + child = child.nextSibling; + } + return results; }, - previousSiblings: function(element) { + previousSiblings: function(element, maximumLength) { return Element.recursivelyCollect(element, 'previousSibling'); }, @@ -2020,9 +2184,10 @@ Element.Methods = { }, match: function(element, selector) { + element = $(element); if (Object.isString(selector)) - selector = new Selector(selector); - return selector.match($(element)); + return Prototype.Selector.match(element, selector); + return selector.match(element); }, up: function(element, expression, index) { @@ -2030,7 +2195,7 @@ Element.Methods = { if (arguments.length == 1) return $(element.parentNode); var ancestors = Element.ancestors(element); return Object.isNumber(expression) ? ancestors[expression] : - Selector.findElement(ancestors, expression, index); + Prototype.Selector.find(ancestors, expression, index); }, down: function(element, expression, index) { @@ -2042,29 +2207,40 @@ Element.Methods = { previous: function(element, expression, index) { element = $(element); - if (arguments.length == 1) return $(Selector.handlers.previousElementSibling(element)); - var previousSiblings = Element.previousSiblings(element); - return Object.isNumber(expression) ? previousSiblings[expression] : - Selector.findElement(previousSiblings, expression, index); + if (Object.isNumber(expression)) index = expression, expression = false; + if (!Object.isNumber(index)) index = 0; + + if (expression) { + return Prototype.Selector.find(element.previousSiblings(), expression, index); + } else { + return element.recursivelyCollect("previousSibling", index + 1)[index]; + } }, next: function(element, expression, index) { element = $(element); - if (arguments.length == 1) return $(Selector.handlers.nextElementSibling(element)); - var nextSiblings = Element.nextSiblings(element); - return Object.isNumber(expression) ? nextSiblings[expression] : - Selector.findElement(nextSiblings, expression, index); + if (Object.isNumber(expression)) index = expression, expression = false; + if (!Object.isNumber(index)) index = 0; + + if (expression) { + return Prototype.Selector.find(element.nextSiblings(), expression, index); + } else { + var maximumLength = Object.isNumber(index) ? index + 1 : 1; + return element.recursivelyCollect("nextSibling", index + 1)[index]; + } }, select: function(element) { - var args = Array.prototype.slice.call(arguments, 1); - return Selector.findChildElements(element, args); + element = $(element); + var expressions = Array.prototype.slice.call(arguments, 1).join(', '); + return Prototype.Selector.select(expressions, element); }, adjacent: function(element) { - var args = Array.prototype.slice.call(arguments, 1); - return Selector.findChildElements(element.parentNode, args).without(element); + element = $(element); + var expressions = Array.prototype.slice.call(arguments, 1).join(', '); + return Prototype.Selector.select(expressions, element.parentNode).without(element); }, identify: function(element) { @@ -2228,28 +2404,6 @@ Element.Methods = { return element; }, - getDimensions: function(element) { - element = $(element); - var display = Element.getStyle(element, 'display'); - if (display != 'none' && display != null) // Safari bug - return {width: element.offsetWidth, height: element.offsetHeight}; - - var els = element.style; - var originalVisibility = els.visibility; - var originalPosition = els.position; - var originalDisplay = els.display; - els.visibility = 'hidden'; - if (originalPosition != 'fixed') // Switching fixed to absolute causes issues in Safari - els.position = 'absolute'; - els.display = 'block'; - var originalWidth = element.clientWidth; - var originalHeight = element.clientHeight; - els.display = originalDisplay; - els.position = originalPosition; - els.visibility = originalVisibility; - return {width: originalWidth, height: originalHeight}; - }, - makePositioned: function(element) { element = $(element); var pos = Element.getStyle(element, 'position'); @@ -2294,114 +2448,6 @@ Element.Methods = { return element; }, - cumulativeOffset: function(element) { - var valueT = 0, valueL = 0; - do { - valueT += element.offsetTop || 0; - valueL += element.offsetLeft || 0; - element = element.offsetParent; - } while (element); - return Element._returnOffset(valueL, valueT); - }, - - positionedOffset: function(element) { - var valueT = 0, valueL = 0; - do { - valueT += element.offsetTop || 0; - valueL += element.offsetLeft || 0; - element = element.offsetParent; - if (element) { - if (element.tagName.toUpperCase() == 'BODY') break; - var p = Element.getStyle(element, 'position'); - if (p !== 'static') break; - } - } while (element); - return Element._returnOffset(valueL, valueT); - }, - - absolutize: function(element) { - element = $(element); - if (Element.getStyle(element, 'position') == 'absolute') return element; - - var offsets = Element.positionedOffset(element); - var top = offsets[1]; - var left = offsets[0]; - var width = element.clientWidth; - var height = element.clientHeight; - - element._originalLeft = left - parseFloat(element.style.left || 0); - element._originalTop = top - parseFloat(element.style.top || 0); - element._originalWidth = element.style.width; - element._originalHeight = element.style.height; - - element.style.position = 'absolute'; - element.style.top = top + 'px'; - element.style.left = left + 'px'; - element.style.width = width + 'px'; - element.style.height = height + 'px'; - return element; - }, - - relativize: function(element) { - element = $(element); - if (Element.getStyle(element, 'position') == 'relative') return element; - - element.style.position = 'relative'; - var top = parseFloat(element.style.top || 0) - (element._originalTop || 0); - var left = parseFloat(element.style.left || 0) - (element._originalLeft || 0); - - element.style.top = top + 'px'; - element.style.left = left + 'px'; - element.style.height = element._originalHeight; - element.style.width = element._originalWidth; - return element; - }, - - cumulativeScrollOffset: function(element) { - var valueT = 0, valueL = 0; - do { - valueT += element.scrollTop || 0; - valueL += element.scrollLeft || 0; - element = element.parentNode; - } while (element); - return Element._returnOffset(valueL, valueT); - }, - - getOffsetParent: function(element) { - if (element.offsetParent) return $(element.offsetParent); - if (element == document.body) return $(element); - - while ((element = element.parentNode) && element != document.body) - if (Element.getStyle(element, 'position') != 'static') - return $(element); - - return $(document.body); - }, - - viewportOffset: function(forElement) { - var valueT = 0, valueL = 0; - - var element = forElement; - do { - valueT += element.offsetTop || 0; - valueL += element.offsetLeft || 0; - - if (element.offsetParent == document.body && - Element.getStyle(element, 'position') == 'absolute') break; - - } while (element = element.offsetParent); - - element = forElement; - do { - if (!Prototype.Browser.Opera || (element.tagName && (element.tagName.toUpperCase() == 'BODY'))) { - valueT -= element.scrollTop || 0; - valueL -= element.scrollLeft || 0; - } - } while (element = element.parentNode); - - return Element._returnOffset(valueL, valueT); - }, - clonePosition: function(element, source) { var options = Object.extend({ setLeft: true, @@ -2413,11 +2459,10 @@ Element.Methods = { }, arguments[2] || { }); source = $(source); - var p = Element.viewportOffset(source); + var p = Element.viewportOffset(source), delta = [0, 0], parent = null; element = $(element); - var delta = [0, 0]; - var parent = null; + if (Element.getStyle(element, 'position') == 'absolute') { parent = Element.getOffsetParent(element); delta = Element.viewportOffset(parent); @@ -2456,8 +2501,6 @@ if (Prototype.Browser.Opera) { Element.Methods.getStyle = Element.Methods.getStyle.wrap( function(proceed, element, style) { switch (style) { - case 'left': case 'top': case 'right': case 'bottom': - if (proceed(element, 'position') === 'static') return null; case 'height': case 'width': if (!Element.visible(element)) return null; @@ -2493,47 +2536,6 @@ if (Prototype.Browser.Opera) { } else if (Prototype.Browser.IE) { - Element.Methods.getOffsetParent = Element.Methods.getOffsetParent.wrap( - function(proceed, element) { - element = $(element); - try { element.offsetParent } - catch(e) { 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; - } - ); - - $w('positionedOffset viewportOffset').each(function(method) { - Element.Methods[method] = Element.Methods[method].wrap( - function(proceed, element) { - element = $(element); - try { element.offsetParent } - catch(e) { return Element._returnOffset(0,0) } - var position = element.getStyle('position'); - if (position !== 'static') return proceed(element); - var offsetParent = element.getOffsetParent(); - if (offsetParent && offsetParent.getStyle('position') === 'fixed') - offsetParent.setStyle({ zoom: 1 }); - element.setStyle({ position: 'relative' }); - var value = proceed(element); - element.setStyle({ position: position }); - return value; - } - ); - }); - - Element.Methods.cumulativeOffset = Element.Methods.cumulativeOffset.wrap( - function(proceed, element) { - try { element.offsetParent } - catch(e) { return Element._returnOffset(0,0) } - return proceed(element); - } - ); - Element.Methods.getStyle = function(element, style) { element = $(element); style = (style == 'float' || style == 'cssFloat') ? 'styleFloat' : style.camelize(); @@ -2577,10 +2579,9 @@ else if (Prototype.Browser.IE) { Element._attributeTranslations = (function(){ - var classProp = 'className'; - var forProp = 'for'; - - var el = document.createElement('div'); + var classProp = 'className', + forProp = 'for', + el = document.createElement('div'); el.setAttribute(classProp, 'x'); @@ -2623,10 +2624,9 @@ else if (Prototype.Browser.IE) { }, _getEv: (function(){ - var el = document.createElement('div'); + var el = document.createElement('div'), f; el.onclick = Prototype.emptyFunction; var value = el.getAttribute('onclick'); - var f; if (String(value).indexOf('{') > -1) { f = function(element, attribute) { @@ -2754,7 +2754,7 @@ else if (Prototype.Browser.WebKit) { (value < 0.00001) ? 0 : value; if (value == 1) - if(element.tagName.toUpperCase() == 'IMG' && element.width) { + if (element.tagName.toUpperCase() == 'IMG' && element.width) { element.width++; element.width--; } else try { var n = document.createTextNode(' '); @@ -2764,20 +2764,6 @@ else if (Prototype.Browser.WebKit) { return element; }; - - Element.Methods.cumulativeOffset = function(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 Element._returnOffset(valueL, valueT); - }; } if ('outerHTML' in document.documentElement) { @@ -2794,8 +2780,8 @@ if ('outerHTML' in document.documentElement) { var parent = element.parentNode, tagName = parent.tagName.toUpperCase(); if (Element._insertionTranslations.tags[tagName]) { - var nextSibling = element.next(); - var fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts()); + var nextSibling = element.next(), + fragments = Element._getContentFromAnonymousElement(tagName, content.stripScripts()); parent.removeChild(element); if (nextSibling) fragments.each(function(node) { parent.insertBefore(node, nextSibling) }); @@ -2816,12 +2802,27 @@ Element._returnOffset = function(l, t) { return result; }; -Element._getContentFromAnonymousElement = function(tagName, html) { - var div = new Element('div'), t = Element._insertionTranslations.tags[tagName]; - if (t) { - div.innerHTML = t[0] + html + t[1]; - t[2].times(function() { div = div.firstChild }); - } else div.innerHTML = html; +Element._getContentFromAnonymousElement = function(tagName, html, force) { + var div = new Element('div'), + t = Element._insertionTranslations.tags[tagName]; + + var workaround = false; + if (t) workaround = true; + else if (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); }; @@ -2878,7 +2879,7 @@ Object.extend(Element, Element.Methods); div = null; -})(document.createElement('div')) +})(document.createElement('div')); Element.extend = (function() { @@ -2886,8 +2887,8 @@ Element.extend = (function() { if (typeof window.Element != 'undefined') { var proto = window.Element.prototype; if (proto) { - var id = '_' + (Math.random()+'').slice(2); - var el = document.createElement(tagName); + var id = '_' + (Math.random()+'').slice(2), + el = document.createElement(tagName); proto[id] = 'x'; var isBuggy = (el[id] !== 'x'); delete proto[id]; @@ -2954,10 +2955,14 @@ Element.extend = (function() { return extend; })(); -Element.hasAttribute = function(element, attribute) { - if (element.hasAttribute) return element.hasAttribute(attribute); - return Element.Methods.Simulated.hasAttribute(element, attribute); -}; +if (document.documentElement.hasAttribute) { + Element.hasAttribute = function(element, attribute) { + return element.hasAttribute(attribute); + }; +} +else { + Element.hasAttribute = Element.Methods.Simulated.hasAttribute; +} Element.addMethods = function(methods) { var F = Prototype.BrowserFeatures, T = Element.Methods.ByTag; @@ -2969,7 +2974,8 @@ Element.addMethods = function(methods) { "FORM": Object.clone(Form.Methods), "INPUT": Object.clone(Form.Element.Methods), "SELECT": Object.clone(Form.Element.Methods), - "TEXTAREA": Object.clone(Form.Element.Methods) + "TEXTAREA": Object.clone(Form.Element.Methods), + "BUTTON": Object.clone(Form.Element.Methods) }); } @@ -3021,8 +3027,9 @@ Element.addMethods = function(methods) { klass = 'HTML' + tagName.capitalize() + 'Element'; if (window[klass]) return window[klass]; - var element = document.createElement(tagName); - var proto = element['__proto__'] || element.constructor.prototype; + var element = document.createElement(tagName), + proto = element['__proto__'] || element.constructor.prototype; + element = null; return proto; } @@ -3105,8 +3112,8 @@ Element.addMethods({ uid = 0; } else { if (typeof element._prototypeUID === "undefined") - element._prototypeUID = [Element.Storage.UID++]; - uid = element._prototypeUID[0]; + element._prototypeUID = Element.Storage.UID++; + uid = element._prototypeUID; } if (!Element.Storage[uid]) @@ -3151,770 +3158,1808 @@ Element.addMethods({ } } return Element.extend(clone); - } -}); -/* Portions of the Selector class are derived from Jack Slocum's DomQuery, - * part of YUI-Ext version 0.40, distributed under the terms of an MIT-style - * license. Please see http://www.yui-ext.com/ for more information. */ - -var Selector = Class.create({ - initialize: function(expression) { - this.expression = expression.strip(); - - if (this.shouldUseSelectorsAPI()) { - this.mode = 'selectorsAPI'; - } else if (this.shouldUseXPath()) { - this.mode = 'xpath'; - this.compileXPathMatcher(); - } else { - this.mode = "normal"; - this.compileMatcher(); - } - }, - shouldUseXPath: (function() { - - var IS_DESCENDANT_SELECTOR_BUGGY = (function(){ - var isBuggy = false; - if (document.evaluate && window.XPathResult) { - var el = document.createElement('div'); - el.innerHTML = '<ul><li></li></ul><div><ul><li></li></ul></div>'; + purge: function(element) { + if (!(element = $(element))) return; + var purgeElement = Element._purgeElement; - var xpath = ".//*[local-name()='ul' or local-name()='UL']" + - "//*[local-name()='li' or local-name()='LI']"; + purgeElement(element); - var result = document.evaluate(xpath, el, null, - XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); + var descendants = element.getElementsByTagName('*'), + i = descendants.length; - isBuggy = (result.snapshotLength !== 2); - el = null; - } - return isBuggy; - })(); + while (i--) purgeElement(descendants[i]); - return function() { - if (!Prototype.BrowserFeatures.XPath) return false; - - var e = this.expression; - - if (Prototype.Browser.WebKit && - (e.include("-of-type") || e.include(":empty"))) - return false; + return null; + } +}); - if ((/(\[[\w-]*?:|:checked)/).test(e)) - return false; +(function() { - if (IS_DESCENDANT_SELECTOR_BUGGY) return false; + function toDecimal(pctString) { + var match = pctString.match(/^(\d+)%?$/i); + if (!match) return null; + return (Number(match[1]) / 100); + } - return true; + function getPixelValue(value, property, context) { + var element = null; + if (Object.isElement(value)) { + element = value; + value = element.getStyle(property); } - })(), + if (value === null) { + return null; + } - shouldUseSelectorsAPI: function() { - if (!Prototype.BrowserFeatures.SelectorsAPI) return false; + if ((/^(?:-)?\d+(\.\d+)?(px)?$/i).test(value)) { + return window.parseFloat(value); + } - if (Selector.CASE_INSENSITIVE_CLASS_NAMES) return false; + var isPercentage = value.include('%'), isViewport = (context === document.viewport); - if (!Selector._div) Selector._div = new Element('div'); + 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; - try { - Selector._div.querySelector(this.expression); - } catch(e) { - return false; + return value; } - return true; - }, + if (element && isPercentage) { + context = context || element.parentNode; + var decimal = toDecimal(value); + var whole = null; + var position = element.getStyle('position'); - compileMatcher: function() { - var e = this.expression, ps = Selector.patterns, h = Selector.handlers, - c = Selector.criteria, le, p, m, len = ps.length, name; + var isHorizontal = property.include('left') || property.include('right') || + property.include('width'); - if (Selector._cache[e]) { - this.matcher = Selector._cache[e]; - return; - } + var isVertical = property.include('top') || property.include('bottom') || + property.include('height'); - this.matcher = ["this.matcher = function(root) {", - "var r = root, h = Selector.handlers, c = false, n;"]; - - while (e && le != e && (/\S/).test(e)) { - le = e; - for (var i = 0; i<len; i++) { - p = ps[i].re; - name = ps[i].name; - if (m = e.match(p)) { - this.matcher.push(Object.isFunction(c[name]) ? c[name](m) : - new Template(c[name]).evaluate(m)); - e = e.replace(m[0], ''); - break; + 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'); } } - } - this.matcher.push("return h.unique(n);\n}"); - eval(this.matcher.join('\n')); - Selector._cache[this.expression] = this.matcher; - }, + return (whole === null) ? 0 : whole * decimal; + } - compileXPathMatcher: function() { - var e = this.expression, ps = Selector.patterns, - x = Selector.xpath, le, m, len = ps.length, name; + return 0; + } - if (Selector._cache[e]) { - this.xpath = Selector._cache[e]; return; + function toCSSPixels(number) { + if (Object.isString(number) && number.endsWith('px')) { + return number; } + return number + 'px'; + } - this.matcher = ['.//*']; - while (e && le != e && (/\S/).test(e)) { - le = e; - for (var i = 0; i<len; i++) { - name = ps[i].name; - if (m = e.match(ps[i].re)) { - this.matcher.push(Object.isFunction(x[name]) ? x[name](m) : - new Template(x[name]).evaluate(m)); - e = e.replace(m[0], ''); - break; - } + function isDisplayed(element) { + var originalElement = element; + while (element && element.parentNode) { + var display = element.getStyle('display'); + if (display === 'none') { + return false; } + element = $(element.parentNode); } + return true; + } - this.xpath = this.matcher.join(''); - Selector._cache[this.expression] = this.xpath; - }, + var hasLayout = Prototype.K; + if ('currentStyle' in document.documentElement) { + hasLayout = function(element) { + if (!element.currentStyle.hasLayout) { + element.style.zoom = 1; + } + return element; + }; + } - findElements: function(root) { - root = root || document; - var e = this.expression, results; + function cssNameFor(key) { + if (key.include('border')) key = key + '-width'; + return key.camelize(); + } - switch (this.mode) { - case 'selectorsAPI': - if (root !== document) { - var oldId = root.id, id = $(root).identify(); - id = id.replace(/([\.:])/g, "\\$1"); - e = "#" + id + " " + e; - } + Element.Layout = Class.create(Hash, { + initialize: function($super, element, preCompute) { + $super(); + this.element = $(element); - results = $A(root.querySelectorAll(e)).map(Element.extend); - root.id = oldId; + Element.Layout.PROPERTIES.each( function(property) { + this._set(property, null); + }, this); - return results; - case 'xpath': - return document._getElementsByXPath(this.xpath, root); - default: - return this.matcher(root); - } - }, + if (preCompute) { + this._preComputing = true; + this._begin(); + Element.Layout.PROPERTIES.each( this._compute, this ); + this._end(); + this._preComputing = false; + } + }, - match: function(element) { - this.tokens = []; - - var e = this.expression, ps = Selector.patterns, as = Selector.assertions; - var le, p, m, len = ps.length, name; - - while (e && le !== e && (/\S/).test(e)) { - le = e; - for (var i = 0; i<len; i++) { - p = ps[i].re; - name = ps[i].name; - if (m = e.match(p)) { - if (as[name]) { - this.tokens.push([name, Object.clone(m)]); - e = e.replace(m[0], ''); - } else { - return this.findElements(document).include(element); - } - } + _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._prepared) return; + + var element = this.element; + if (isDisplayed(element)) { + this._prepared = true; + return; } - } - var match = true, name, matches; - for (var i = 0, token; token = this.tokens[i]; i++) { - name = token[0], matches = token[1]; - if (!Selector.assertions[name](element, matches)) { - match = false; break; + 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 = element.getStyle('position'), + width = element.getStyle('width'); + + if (width === "0px" || width === null) { + element.style.display = 'block'; + width = element.getStyle('width'); } - } - return match; - }, + var context = (position === 'fixed') ? document.viewport : + element.parentNode; - toString: function() { - return this.expression; - }, + element.setStyle({ + position: 'absolute', + visibility: 'hidden', + display: 'block' + }); - inspect: function() { - return "#<Selector:" + this.expression.inspect() + ">"; - } -}); + var positionedWidth = element.getStyle('width'); -if (Prototype.BrowserFeatures.SelectorsAPI && - document.compatMode === 'BackCompat') { - Selector.CASE_INSENSITIVE_CLASS_NAMES = (function(){ - var div = document.createElement('div'), - span = document.createElement('span'); - - div.id = "prototype_test_id"; - span.className = 'Test'; - div.appendChild(span); - var isIgnored = (div.querySelector('#prototype_test_id .test') !== null); - div = span = null; - return isIgnored; - })(); -} + var newWidth; + if (width && (positionedWidth === width)) { + newWidth = getPixelValue(element, 'width', context); + } else if (position === 'absolute' || position === 'fixed') { + newWidth = getPixelValue(element, 'width', 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'); + } -Object.extend(Selector, { - _cache: { }, - - xpath: { - descendant: "//*", - child: "/*", - adjacent: "/following-sibling::*[1]", - laterSibling: '/following-sibling::*', - tagName: function(m) { - if (m[1] == '*') return ''; - return "[local-name()='" + m[1].toLowerCase() + - "' or local-name()='" + m[1].toUpperCase() + "']"; + element.setStyle({ width: newWidth + 'px' }); + + this._prepared = true; + }, + + _end: function() { + var element = this.element; + var originalStyles = element.retrieve('prototype_original_styles'); + element.store('prototype_original_styles', null); + element.setStyle(originalStyles); + this._prepared = false; }, - className: "[contains(concat(' ', @class, ' '), ' #{1} ')]", - id: "[@id='#{1}']", - attrPresence: function(m) { - m[1] = m[1].toLowerCase(); - return new Template("[@#{1}]").evaluate(m); + + _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)); }, - attr: function(m) { - m[1] = m[1].toLowerCase(); - m[3] = m[5] || m[6]; - return new Template(Selector.xpath.operators[m[2]]).evaluate(m); + + 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; }, - pseudo: function(m) { - var h = Selector.xpath.pseudos[m[1]]; - if (!h) return ''; - if (Object.isFunction(h)) return h(m); - return new Template(Selector.xpath.pseudos[m[1]]).evaluate(m); + + toHash: function() { + var obj = this.toObject.apply(this, arguments); + return new Hash(obj); }, - operators: { - '=': "[@#{1}='#{3}']", - '!=': "[@#{1}!='#{3}']", - '^=': "[starts-with(@#{1}, '#{3}')]", - '$=': "[substring(@#{1}, (string-length(@#{1}) - string-length('#{3}') + 1))='#{3}']", - '*=': "[contains(@#{1}, '#{3}')]", - '~=': "[contains(concat(' ', @#{1}, ' '), ' #{3} ')]", - '|=': "[contains(concat('-', @#{1}, '-'), '-#{3}-')]" + + 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; }, - pseudos: { - 'first-child': '[not(preceding-sibling::*)]', - 'last-child': '[not(following-sibling::*)]', - 'only-child': '[not(preceding-sibling::* or following-sibling::*)]', - 'empty': "[count(*) = 0 and (count(text()) = 0)]", - 'checked': "[@checked]", - 'disabled': "[(@disabled) and (@type!='hidden')]", - 'enabled': "[not(@disabled) and (@type!='hidden')]", - 'not': function(m) { - var e = m[6], p = Selector.patterns, - x = Selector.xpath, le, v, len = p.length, name; - - var exclusion = []; - while (e && le != e && (/\S/).test(e)) { - le = e; - for (var i = 0; i<len; i++) { - name = p[i].name - if (m = e.match(p[i].re)) { - v = Object.isFunction(x[name]) ? x[name](m) : new Template(x[name]).evaluate(m); - exclusion.push("(" + v.substring(1, v.length - 1) + ")"); - e = e.replace(m[0], ''); - break; - } - } + + inspect: function() { + return "#<Element.Layout>"; + } + }); + + 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; } - return "[not(" + exclusion.join(" and ") + ")]"; + + 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; }, - 'nth-child': function(m) { - return Selector.xpath.pseudos.nth("(count(./preceding-sibling::*) + 1) ", m); + + '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; }, - 'nth-last-child': function(m) { - return Selector.xpath.pseudos.nth("(count(./following-sibling::*) + 1) ", m); + + 'padding-box-height': function(element) { + var height = this.get('height'), + pTop = this.get('padding-top'), + pBottom = this.get('padding-bottom'); + + return height + pTop + pBottom; }, - 'nth-of-type': function(m) { - return Selector.xpath.pseudos.nth("position() ", m); + + 'padding-box-width': function(element) { + var width = this.get('width'), + pLeft = this.get('padding-left'), + pRight = this.get('padding-right'); + + return width + pLeft + pRight; }, - 'nth-last-of-type': function(m) { - return Selector.xpath.pseudos.nth("(last() + 1 - position()) ", m); + + 'border-box-height': function(element) { + if (!this._preComputing) this._begin(); + var height = element.offsetHeight; + if (!this._preComputing) this._end(); + return height; }, - 'first-of-type': function(m) { - m[6] = "1"; return Selector.xpath.pseudos['nth-of-type'](m); + + '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; }, - 'last-of-type': function(m) { - m[6] = "1"; return Selector.xpath.pseudos['nth-last-of-type'](m); + + '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; }, - 'only-of-type': function(m) { - var p = Selector.xpath.pseudos; return p['first-of-type'](m) + p['last-of-type'](m); + + 'top': function(element) { + var offset = element.positionedOffset(); + return offset.top; }, - nth: function(fragment, m) { - var mm, formula = m[6], predicate; - if (formula == 'even') formula = '2n+0'; - if (formula == 'odd') formula = '2n+1'; - if (mm = formula.match(/^(\d+)$/)) // digit only - return '[' + fragment + "= " + mm[1] + ']'; - if (mm = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b - if (mm[1] == "-") mm[1] = -1; - var a = mm[1] ? Number(mm[1]) : 1; - var b = mm[2] ? Number(mm[2]) : 0; - predicate = "[((#{fragment} - #{b}) mod #{a} = 0) and " + - "((#{fragment} - #{b}) div #{a} >= 0)]"; - return new Template(predicate).evaluate({ - fragment: fragment, a: a, b: b }); - } + + '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'); } } - }, + }); - criteria: { - tagName: 'n = h.tagName(n, r, "#{1}", c); c = false;', - className: 'n = h.className(n, r, "#{1}", c); c = false;', - id: 'n = h.id(n, r, "#{1}", c); c = false;', - attrPresence: 'n = h.attrPresence(n, r, "#{1}", c); c = false;', - attr: function(m) { - m[3] = (m[5] || m[6]); - return new Template('n = h.attr(n, r, "#{1}", "#{3}", "#{2}", c); c = false;').evaluate(m); - }, - pseudo: function(m) { - if (m[6]) m[6] = m[6].replace(/"/g, '\\"'); - return new Template('n = h.pseudo(n, "#{1}", "#{6}", r, c); c = false;').evaluate(m); - }, - descendant: 'c = "descendant";', - child: 'c = "child";', - adjacent: 'c = "adjacent";', - laterSibling: 'c = "laterSibling";' - }, + 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(); + }, - patterns: [ - { name: 'laterSibling', re: /^\s*~\s*/ }, - { name: 'child', re: /^\s*>\s*/ }, - { name: 'adjacent', re: /^\s*\+\s*/ }, - { name: 'descendant', re: /^\s/ }, - - { name: 'tagName', re: /^\s*(\*|[\w\-]+)(\b|$)?/ }, - { name: 'id', re: /^#([\w\-\*]+)(\b|$)/ }, - { name: 'className', re: /^\.([\w\-\*]+)(\b|$)/ }, - { name: 'pseudo', re: /^:((first|last|nth|nth-last|only)(-child|-of-type)|empty|checked|(en|dis)abled|not)(\((.*?)\))?(\b|$|(?=\s|[:+~>]))/ }, - { name: 'attrPresence', re: /^\[((?:[\w-]+:)?[\w-]+)\]/ }, - { name: 'attr', re: /\[((?:[\w-]*:)?[\w-]+)\s*(?:([!^$*~|]?=)\s*((['"])([^\4]*?)\4|([^'"][^\]]*?)))?\]/ } - ], - - assertions: { - tagName: function(element, matches) { - return matches[1].toUpperCase() == element.tagName.toUpperCase(); + '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; }, - className: function(element, matches) { - return Element.hasClassName(element, matches[1]); + relativeTo: function(offset) { + return new Element.Offset( + this.left - offset.left, + this.top - offset.top + ); }, - id: function(element, matches) { - return element.id === matches[1]; + inspect: function() { + return "#<Element.Offset left: #{left} top: #{top}>".interpolate(this); }, - attrPresence: function(element, matches) { - return Element.hasAttribute(element, matches[1]); + toString: function() { + return "[#{left}, #{top}]".interpolate(this); }, - attr: function(element, matches) { - var nodeValue = Element.readAttribute(element, matches[1]); - return nodeValue && Selector.operators[matches[2]](nodeValue, matches[5] || matches[6]); + toArray: function() { + return [this.left, this.top]; } - }, + }); - handlers: { - concat: function(a, b) { - for (var i = 0, node; node = b[i]; i++) - a.push(node); - return a; - }, + function getLayout(element, preCompute) { + return new Element.Layout(element, preCompute); + } - mark: function(nodes) { - var _true = Prototype.emptyFunction; - for (var i = 0, node; node = nodes[i]; i++) - node._countedByPrototype = _true; - return nodes; - }, + function measure(element, property) { + return $(element).getLayout().get(property); + } - unmark: (function(){ + function getDimensions(element) { + element = $(element); + var display = Element.getStyle(element, 'display'); - var PROPERTIES_ATTRIBUTES_MAP = (function(){ - var el = document.createElement('div'), - isBuggy = false, - propName = '_countedByPrototype', - value = 'x' - el[propName] = value; - isBuggy = (el.getAttribute(propName) === value); - el = null; - return isBuggy; - })(); - - return PROPERTIES_ATTRIBUTES_MAP ? - function(nodes) { - for (var i = 0, node; node = nodes[i]; i++) - node.removeAttribute('_countedByPrototype'); - return nodes; - } : - function(nodes) { - for (var i = 0, node; node = nodes[i]; i++) - node._countedByPrototype = void 0; - return nodes; - } - })(), + if (display && display !== 'none') { + return { width: element.offsetWidth, height: element.offsetHeight }; + } - index: function(parentNode, reverse, ofType) { - parentNode._countedByPrototype = Prototype.emptyFunction; - if (reverse) { - for (var nodes = parentNode.childNodes, i = nodes.length - 1, j = 1; i >= 0; i--) { - var node = nodes[i]; - if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++; - } - } else { - for (var i = 0, j = 1, nodes = parentNode.childNodes; node = nodes[i]; i++) - if (node.nodeType == 1 && (!ofType || node._countedByPrototype)) node.nodeIndex = j++; - } - }, + var style = element.style; + var originalStyles = { + visibility: style.visibility, + position: style.position, + display: style.display + }; - unique: function(nodes) { - if (nodes.length == 0) return nodes; - var results = [], n; - for (var i = 0, l = nodes.length; i < l; i++) - if (typeof (n = nodes[i])._countedByPrototype == 'undefined') { - n._countedByPrototype = Prototype.emptyFunction; - results.push(Element.extend(n)); - } - return Selector.handlers.unmark(results); - }, + var newStyles = { + visibility: 'hidden', + display: 'block' + }; - descendant: function(nodes) { - var h = Selector.handlers; - for (var i = 0, results = [], node; node = nodes[i]; i++) - h.concat(results, node.getElementsByTagName('*')); - return results; - }, + if (originalStyles.position !== 'fixed') + newStyles.position = 'absolute'; - child: function(nodes) { - var h = Selector.handlers; - for (var i = 0, results = [], node; node = nodes[i]; i++) { - for (var j = 0, child; child = node.childNodes[j]; j++) - if (child.nodeType == 1 && child.tagName != '!') results.push(child); - } - return results; - }, + Element.setStyle(element, newStyles); - adjacent: function(nodes) { - for (var i = 0, results = [], node; node = nodes[i]; i++) { - var next = this.nextElementSibling(node); - if (next) results.push(next); - } - return results; - }, + var dimensions = { + width: element.offsetWidth, + height: element.offsetHeight + }; - laterSibling: function(nodes) { - var h = Selector.handlers; - for (var i = 0, results = [], node; node = nodes[i]; i++) - h.concat(results, Element.nextSiblings(node)); - return results; - }, + Element.setStyle(element, originalStyles); - nextElementSibling: function(node) { - while (node = node.nextSibling) - if (node.nodeType == 1) return node; - return null; - }, + return dimensions; + } - previousElementSibling: function(node) { - while (node = node.previousSibling) - if (node.nodeType == 1) return node; - return null; - }, + function getOffsetParent(element) { + element = $(element); - tagName: function(nodes, root, tagName, combinator) { - var uTagName = tagName.toUpperCase(); - var results = [], h = Selector.handlers; - if (nodes) { - if (combinator) { - if (combinator == "descendant") { - for (var i = 0, node; node = nodes[i]; i++) - h.concat(results, node.getElementsByTagName(tagName)); - return results; - } else nodes = this[combinator](nodes); - if (tagName == "*") return nodes; - } - for (var i = 0, node; node = nodes[i]; i++) - if (node.tagName.toUpperCase() === uTagName) results.push(node); - return results; - } else return root.getElementsByTagName(tagName); - }, + if (isDocument(element) || isDetached(element) || isBody(element) || isHtml(element)) + return $(document.body); - id: function(nodes, root, id, combinator) { - var targetNode = $(id), h = Selector.handlers; + var isInline = (Element.getStyle(element, 'display') === 'inline'); + if (!isInline && element.offsetParent) return $(element.offsetParent); - if (root == document) { - if (!targetNode) return []; - if (!nodes) return [targetNode]; - } else { - if (!root.sourceIndex || root.sourceIndex < 1) { - var nodes = root.getElementsByTagName('*'); - for (var j = 0, node; node = nodes[j]; j++) { - if (node.id === id) return [node]; - } - } + while ((element = element.parentNode) && element !== document.body) { + if (Element.getStyle(element, 'position') !== 'static') { + return isHtml(element) ? $(document.body) : $(element); } + } - if (nodes) { - if (combinator) { - if (combinator == 'child') { - for (var i = 0, node; node = nodes[i]; i++) - if (targetNode.parentNode == node) return [targetNode]; - } else if (combinator == 'descendant') { - for (var i = 0, node; node = nodes[i]; i++) - if (Element.descendantOf(targetNode, node)) return [targetNode]; - } else if (combinator == 'adjacent') { - for (var i = 0, node; node = nodes[i]; i++) - if (Selector.handlers.previousElementSibling(targetNode) == node) - return [targetNode]; - } else nodes = h[combinator](nodes); - } - for (var i = 0, node; node = nodes[i]; i++) - if (node == targetNode) return [targetNode]; - return []; - } - return (targetNode && Element.descendantOf(targetNode, root)) ? [targetNode] : []; - }, + return $(document.body); + } - className: function(nodes, root, className, combinator) { - if (nodes && combinator) nodes = this[combinator](nodes); - return Selector.handlers.byClassName(nodes, root, className); - }, - byClassName: function(nodes, root, className) { - if (!nodes) nodes = Selector.handlers.descendant([root]); - var needle = ' ' + className + ' '; - for (var i = 0, results = [], node, nodeClassName; node = nodes[i]; i++) { - nodeClassName = node.className; - if (nodeClassName.length == 0) continue; - if (nodeClassName == className || (' ' + nodeClassName + ' ').include(needle)) - results.push(node); + 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; } - return results; - }, + } while (element); - attrPresence: function(nodes, root, attr, combinator) { - if (!nodes) nodes = root.getElementsByTagName("*"); - if (nodes && combinator) nodes = this[combinator](nodes); - var results = []; - for (var i = 0, node; node = nodes[i]; i++) - if (Element.hasAttribute(node, attr)) results.push(node); - return results; - }, + valueL -= layout.get('margin-top'); + valueT -= layout.get('margin-left'); - attr: function(nodes, root, attr, value, operator, combinator) { - if (!nodes) nodes = root.getElementsByTagName("*"); - if (nodes && combinator) nodes = this[combinator](nodes); - var handler = Selector.operators[operator], results = []; - for (var i = 0, node; node = nodes[i]; i++) { - var nodeValue = Element.readAttribute(node, attr); - if (nodeValue === null) continue; - if (handler(nodeValue, value)) results.push(node); + return new Element.Offset(valueL, valueT); + } + + function cumulativeScrollOffset(element) { + var valueT = 0, valueL = 0; + do { + valueT += element.scrollTop || 0; + valueL += element.scrollLeft || 0; + element = element.parentNode; + } while (element); + return new Element.Offset(valueL, valueT); + } + + function viewportOffset(forElement) { + element = $(element); + var valueT = 0, valueL = 0, docBody = document.body; + + 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; } - return results; - }, + } while (element = element.parentNode); + return new Element.Offset(valueL, valueT); + } - pseudo: function(nodes, name, value, root, combinator) { - if (nodes && combinator) nodes = this[combinator](nodes); - if (!nodes) nodes = root.getElementsByTagName("*"); - return Selector.pseudos[name](nodes, value, root); + function absolutize(element) { + element = $(element); + + if (Element.getStyle(element, 'position') === 'absolute') { + return element; } - }, - pseudos: { - 'first-child': function(nodes, value, root) { - for (var i = 0, results = [], node; node = nodes[i]; i++) { - if (Selector.handlers.previousElementSibling(node)) continue; - results.push(node); - } - return results; - }, - 'last-child': function(nodes, value, root) { - for (var i = 0, results = [], node; node = nodes[i]; i++) { - if (Selector.handlers.nextElementSibling(node)) continue; - results.push(node); - } - return results; - }, - 'only-child': function(nodes, value, root) { - var h = Selector.handlers; - for (var i = 0, results = [], node; node = nodes[i]; i++) - if (!h.previousElementSibling(node) && !h.nextElementSibling(node)) - results.push(node); - return results; - }, - 'nth-child': function(nodes, formula, root) { - return Selector.pseudos.nth(nodes, formula, root); - }, - 'nth-last-child': function(nodes, formula, root) { - return Selector.pseudos.nth(nodes, formula, root, true); - }, - 'nth-of-type': function(nodes, formula, root) { - return Selector.pseudos.nth(nodes, formula, root, false, true); - }, - 'nth-last-of-type': function(nodes, formula, root) { - return Selector.pseudos.nth(nodes, formula, root, true, true); - }, - 'first-of-type': function(nodes, formula, root) { - return Selector.pseudos.nth(nodes, "1", root, false, true); - }, - 'last-of-type': function(nodes, formula, root) { - return Selector.pseudos.nth(nodes, "1", root, true, true); - }, - 'only-of-type': function(nodes, formula, root) { - var p = Selector.pseudos; - return p['last-of-type'](p['first-of-type'](nodes, formula, root), formula, root); - }, + var offsetParent = getOffsetParent(element); + var eOffset = element.viewportOffset(), + pOffset = offsetParent.viewportOffset(); - getIndices: function(a, b, total) { - if (a == 0) return b > 0 ? [b] : []; - return $R(1, total).inject([], function(memo, i) { - if (0 == (i - b) % a && (i - b) / a >= 0) memo.push(i); - return memo; - }); - }, + var offset = eOffset.relativeTo(pOffset); + var layout = element.getLayout(); - nth: function(nodes, formula, root, reverse, ofType) { - if (nodes.length == 0) return []; - if (formula == 'even') formula = '2n+0'; - if (formula == 'odd') formula = '2n+1'; - var h = Selector.handlers, results = [], indexed = [], m; - h.mark(nodes); - for (var i = 0, node; node = nodes[i]; i++) { - if (!node.parentNode._countedByPrototype) { - h.index(node.parentNode, reverse, ofType); - indexed.push(node.parentNode); - } - } - if (formula.match(/^\d+$/)) { // just a number - formula = Number(formula); - for (var i = 0, node; node = nodes[i]; i++) - if (node.nodeIndex == formula) results.push(node); - } else if (m = formula.match(/^(-?\d*)?n(([+-])(\d+))?/)) { // an+b - if (m[1] == "-") m[1] = -1; - var a = m[1] ? Number(m[1]) : 1; - var b = m[2] ? Number(m[2]) : 0; - var indices = Selector.pseudos.getIndices(a, b, nodes.length); - for (var i = 0, node, l = indices.length; node = nodes[i]; i++) { - for (var j = 0; j < l; j++) - if (node.nodeIndex == indices[j]) results.push(node); - } - } - h.unmark(nodes); - h.unmark(indexed); - return results; - }, + element.store('prototype_absolutize_original_styles', { + 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; + } - 'empty': function(nodes, value, root) { - for (var i = 0, results = [], node; node = nodes[i]; i++) { - if (node.tagName == '!' || node.firstChild) continue; - results.push(node); + 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; + } + + 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; } - return results; - }, + ); - 'not': function(nodes, selector, root) { - var h = Selector.handlers, selectorType, m; - var exclusions = new Selector(selector).findElements(root); - h.mark(exclusions); - for (var i = 0, results = [], node; node = nodes[i]; i++) - if (!node._countedByPrototype) results.push(node); - h.unmark(exclusions); - return results; - }, + 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); - 'enabled': function(nodes, value, root) { - for (var i = 0, results = [], node; node = nodes[i]; i++) - if (!node.disabled && (!node.type || node.type !== 'hidden')) - results.push(node); - return results; - }, + var offsetParent = element.getOffsetParent(); + if (offsetParent && offsetParent.getStyle('position') === 'fixed') + hasLayout(offsetParent); - 'disabled': function(nodes, value, root) { - for (var i = 0, results = [], node; node = nodes[i]; i++) - if (node.disabled) results.push(node); - return results; - }, + 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; - 'checked': function(nodes, value, root) { - for (var i = 0, results = [], node; node = nodes[i]; i++) - if (node.checked) results.push(node); - return results; - } - }, + element = element.offsetParent; + } while (element); - operators: { - '=': function(nv, v) { return nv == v; }, - '!=': function(nv, v) { return nv != v; }, - '^=': function(nv, v) { return nv == v || nv && nv.startsWith(v); }, - '$=': function(nv, v) { return nv == v || nv && nv.endsWith(v); }, - '*=': function(nv, v) { return nv == v || nv && nv.include(v); }, - '~=': function(nv, v) { return (' ' + nv + ' ').include(' ' + v + ' '); }, - '|=': function(nv, v) { return ('-' + (nv || "").toUpperCase() + - '-').include('-' + (v || "").toUpperCase() + '-'); } - }, + return new Element.Offset(valueL, valueT); + }; + } - split: function(expression) { - var expressions = []; - expression.scan(/(([\w#:.~>+()\s-]+|\*|\[.*?\])+)\s*(,|$)/, function(m) { - expressions.push(m[1].strip()); + + Element.addMethods({ + getLayout: getLayout, + measure: measure, + getDimensions: getDimensions, + getOffsetParent: getOffsetParent, + cumulativeOffset: cumulativeOffset, + positionedOffset: positionedOffset, + cumulativeScrollOffset: cumulativeScrollOffset, + viewportOffset: viewportOffset, + absolutize: absolutize, + relativize: relativize + }); + + 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); + } }); - return expressions; - }, + } +})(); +window.$$ = function() { + var expression = $A(arguments).join(', '); + return Prototype.Selector.select(expression, document); +}; - matchElements: function(elements, expression) { - var matches = $$(expression), h = Selector.handlers; - h.mark(matches); - for (var i = 0, results = [], element; element = elements[i]; i++) - if (element._countedByPrototype) results.push(element); - h.unmark(matches); - return results; - }, +Prototype.Selector = (function() { + + function select() { + throw new Error('Method "Prototype.Selector.select" must be defined.'); + } - findElement: function(elements, expression, index) { - if (Object.isNumber(expression)) { - index = expression; expression = false; + 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]); + } } - return Selector.matchElements(elements, expression || '*')[index || 0]; - }, + } - findChildElements: function(element, expressions) { - expressions = Selector.split(expressions.join(',')); - var results = [], h = Selector.handlers; - for (var i = 0, l = expressions.length, selector; i < l; i++) { - selector = new Selector(expressions[i].strip()); - h.concat(results, selector.findElements(element)); + function extendElements(elements) { + for (var i = 0, length = elements.length; i < length; i++) { + Element.extend(elements[i]); } - return (l > 1) ? h.unique(results) : results; + 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; +/*! + * Sizzle CSS Selector Engine - v1.0 + * Copyright 2009, The Dojo Foundation + * Released under the MIT, BSD, and GPL Licenses. + * More information: http://sizzlejs.com/ + */ +(function(){ + +var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g, + done = 0, + toString = Object.prototype.toString, + hasDuplicate = false, + baseHasDuplicate = true; + +[0, 0].sort(function(){ + baseHasDuplicate = false; + return 0; }); -if (Prototype.Browser.IE) { - Object.extend(Selector.handlers, { - concat: function(a, b) { - for (var i = 0, node; node = b[i]; i++) - if (node.tagName !== "!") a.push(node); - return a; - } - }); +var Sizzle = function(selector, context, results, seed) { + results = results || []; + var origContext = context = context || document; + + if ( context.nodeType !== 1 && context.nodeType !== 9 ) { + return []; + } + + if ( !selector || typeof selector !== "string" ) { + return results; + } + + var parts = [], m, set, checkSet, check, mode, extra, prune = true, contextXML = isXML(context), + soFar = selector; + + while ( (chunker.exec(""), m = chunker.exec(soFar)) !== null ) { + soFar = m[3]; + + parts.push( m[1] ); + + if ( m[2] ) { + extra = m[3]; + break; + } + } + + if ( parts.length > 1 && origPOS.exec( selector ) ) { + if ( parts.length === 2 && Expr.relative[ parts[0] ] ) { + set = posProcess( parts[0] + parts[1], context ); + } else { + set = Expr.relative[ parts[0] ] ? + [ context ] : + Sizzle( parts.shift(), context ); + + while ( parts.length ) { + selector = parts.shift(); + + if ( Expr.relative[ selector ] ) + selector += parts.shift(); + + set = posProcess( selector, set ); + } + } + } else { + if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML && + Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) { + var ret = Sizzle.find( parts.shift(), context, contextXML ); + context = ret.expr ? Sizzle.filter( ret.expr, ret.set )[0] : ret.set[0]; + } + + if ( context ) { + var ret = seed ? + { expr: parts.pop(), set: makeArray(seed) } : + Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML ); + set = ret.expr ? Sizzle.filter( ret.expr, ret.set ) : ret.set; + + if ( parts.length > 0 ) { + checkSet = makeArray(set); + } else { + prune = false; + } + + while ( parts.length ) { + var cur = parts.pop(), pop = cur; + + if ( !Expr.relative[ cur ] ) { + cur = ""; + } else { + pop = parts.pop(); + } + + if ( pop == null ) { + pop = context; + } + + Expr.relative[ cur ]( checkSet, pop, contextXML ); + } + } else { + checkSet = parts = []; + } + } + + if ( !checkSet ) { + checkSet = set; + } + + if ( !checkSet ) { + throw "Syntax error, unrecognized expression: " + (cur || selector); + } + + if ( toString.call(checkSet) === "[object Array]" ) { + if ( !prune ) { + results.push.apply( results, checkSet ); + } else if ( context && context.nodeType === 1 ) { + for ( var i = 0; checkSet[i] != null; i++ ) { + if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && contains(context, checkSet[i])) ) { + results.push( set[i] ); + } + } + } else { + for ( var i = 0; checkSet[i] != null; i++ ) { + if ( checkSet[i] && checkSet[i].nodeType === 1 ) { + results.push( set[i] ); + } + } + } + } else { + makeArray( checkSet, results ); + } + + if ( extra ) { + Sizzle( extra, origContext, results, seed ); + Sizzle.uniqueSort( results ); + } + + return results; +}; + +Sizzle.uniqueSort = function(results){ + if ( sortOrder ) { + hasDuplicate = baseHasDuplicate; + results.sort(sortOrder); + + if ( hasDuplicate ) { + for ( var i = 1; i < results.length; i++ ) { + if ( results[i] === results[i-1] ) { + results.splice(i--, 1); + } + } + } + } + + return results; +}; + +Sizzle.matches = function(expr, set){ + return Sizzle(expr, null, null, set); +}; + +Sizzle.find = function(expr, context, isXML){ + var set, match; + + if ( !expr ) { + return []; + } + + for ( var i = 0, l = Expr.order.length; i < l; i++ ) { + var type = Expr.order[i], match; + + if ( (match = Expr.leftMatch[ type ].exec( expr )) ) { + var left = match[1]; + match.splice(1,1); + + if ( left.substr( left.length - 1 ) !== "\\" ) { + match[1] = (match[1] || "").replace(/\\/g, ""); + set = Expr.find[ type ]( match, context, isXML ); + if ( set != null ) { + expr = expr.replace( Expr.match[ type ], "" ); + break; + } + } + } + } + + if ( !set ) { + set = context.getElementsByTagName("*"); + } + + return {set: set, expr: expr}; +}; + +Sizzle.filter = function(expr, set, inplace, not){ + var old = expr, result = [], curLoop = set, match, anyFound, + isXMLFilter = set && set[0] && isXML(set[0]); + + while ( expr && set.length ) { + for ( var type in Expr.filter ) { + if ( (match = Expr.match[ type ].exec( expr )) != null ) { + var filter = Expr.filter[ type ], found, item; + anyFound = false; + + if ( curLoop == result ) { + result = []; + } + + if ( Expr.preFilter[ type ] ) { + match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter ); + + if ( !match ) { + anyFound = found = true; + } else if ( match === true ) { + continue; + } + } + + if ( match ) { + for ( var i = 0; (item = curLoop[i]) != null; i++ ) { + if ( item ) { + found = filter( item, match, i, curLoop ); + var pass = not ^ !!found; + + if ( inplace && found != null ) { + if ( pass ) { + anyFound = true; + } else { + curLoop[i] = false; + } + } else if ( pass ) { + result.push( item ); + anyFound = true; + } + } + } + } + + if ( found !== undefined ) { + if ( !inplace ) { + curLoop = result; + } + + expr = expr.replace( Expr.match[ type ], "" ); + + if ( !anyFound ) { + return []; + } + + break; + } + } + } + + if ( expr == old ) { + if ( anyFound == null ) { + throw "Syntax error, unrecognized expression: " + expr; + } else { + break; + } + } + + old = expr; + } + + return curLoop; +}; + +var Expr = Sizzle.selectors = { + order: [ "ID", "NAME", "TAG" ], + match: { + ID: /#((?:[\w\u00c0-\uFFFF-]|\\.)+)/, + CLASS: /\.((?:[\w\u00c0-\uFFFF-]|\\.)+)/, + NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF-]|\\.)+)['"]*\]/, + ATTR: /\[\s*((?:[\w\u00c0-\uFFFF-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/, + TAG: /^((?:[\w\u00c0-\uFFFF\*-]|\\.)+)/, + CHILD: /:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/, + POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/, + PSEUDO: /:((?:[\w\u00c0-\uFFFF-]|\\.)+)(?:\((['"]*)((?:\([^\)]+\)|[^\2\(\)]*)+)\2\))?/ + }, + leftMatch: {}, + attrMap: { + "class": "className", + "for": "htmlFor" + }, + attrHandle: { + href: function(elem){ + return elem.getAttribute("href"); + } + }, + relative: { + "+": function(checkSet, part, isXML){ + var isPartStr = typeof part === "string", + isTag = isPartStr && !/\W/.test(part), + isPartStrNotTag = isPartStr && !isTag; + + if ( isTag && !isXML ) { + part = part.toUpperCase(); + } + + for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) { + if ( (elem = checkSet[i]) ) { + while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {} + + checkSet[i] = isPartStrNotTag || elem && elem.nodeName === part ? + elem || false : + elem === part; + } + } + + if ( isPartStrNotTag ) { + Sizzle.filter( part, checkSet, true ); + } + }, + ">": function(checkSet, part, isXML){ + var isPartStr = typeof part === "string"; + + if ( isPartStr && !/\W/.test(part) ) { + part = isXML ? part : part.toUpperCase(); + + for ( var i = 0, l = checkSet.length; i < l; i++ ) { + var elem = checkSet[i]; + if ( elem ) { + var parent = elem.parentNode; + checkSet[i] = parent.nodeName === part ? parent : false; + } + } + } else { + for ( var i = 0, l = checkSet.length; i < l; i++ ) { + var elem = checkSet[i]; + if ( elem ) { + checkSet[i] = isPartStr ? + elem.parentNode : + elem.parentNode === part; + } + } + + if ( isPartStr ) { + Sizzle.filter( part, checkSet, true ); + } + } + }, + "": function(checkSet, part, isXML){ + var doneName = done++, checkFn = dirCheck; + + if ( !/\W/.test(part) ) { + var nodeCheck = part = isXML ? part : part.toUpperCase(); + checkFn = dirNodeCheck; + } + + checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML); + }, + "~": function(checkSet, part, isXML){ + var doneName = done++, checkFn = dirCheck; + + if ( typeof part === "string" && !/\W/.test(part) ) { + var nodeCheck = part = isXML ? part : part.toUpperCase(); + checkFn = dirNodeCheck; + } + + checkFn("previousSibling", part, doneName, checkSet, nodeCheck, isXML); + } + }, + find: { + ID: function(match, context, isXML){ + if ( typeof context.getElementById !== "undefined" && !isXML ) { + var m = context.getElementById(match[1]); + return m ? [m] : []; + } + }, + NAME: function(match, context, isXML){ + if ( typeof context.getElementsByName !== "undefined" ) { + var ret = [], results = context.getElementsByName(match[1]); + + for ( var i = 0, l = results.length; i < l; i++ ) { + if ( results[i].getAttribute("name") === match[1] ) { + ret.push( results[i] ); + } + } + + return ret.length === 0 ? null : ret; + } + }, + TAG: function(match, context){ + return context.getElementsByTagName(match[1]); + } + }, + preFilter: { + CLASS: function(match, curLoop, inplace, result, not, isXML){ + match = " " + match[1].replace(/\\/g, "") + " "; + + if ( isXML ) { + return match; + } + + for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) { + if ( elem ) { + if ( not ^ (elem.className && (" " + elem.className + " ").indexOf(match) >= 0) ) { + if ( !inplace ) + result.push( elem ); + } else if ( inplace ) { + curLoop[i] = false; + } + } + } + + return false; + }, + ID: function(match){ + return match[1].replace(/\\/g, ""); + }, + TAG: function(match, curLoop){ + for ( var i = 0; curLoop[i] === false; i++ ){} + return curLoop[i] && isXML(curLoop[i]) ? match[1] : match[1].toUpperCase(); + }, + CHILD: function(match){ + if ( match[1] == "nth" ) { + var test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec( + match[2] == "even" && "2n" || match[2] == "odd" && "2n+1" || + !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]); + + match[2] = (test[1] + (test[2] || 1)) - 0; + match[3] = test[3] - 0; + } + + match[0] = done++; + + return match; + }, + ATTR: function(match, curLoop, inplace, result, not, isXML){ + var name = match[1].replace(/\\/g, ""); + + if ( !isXML && Expr.attrMap[name] ) { + match[1] = Expr.attrMap[name]; + } + + if ( match[2] === "~=" ) { + match[4] = " " + match[4] + " "; + } + + return match; + }, + PSEUDO: function(match, curLoop, inplace, result, not){ + if ( match[1] === "not" ) { + if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) { + match[3] = Sizzle(match[3], null, null, curLoop); + } else { + var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not); + if ( !inplace ) { + result.push.apply( result, ret ); + } + return false; + } + } else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) { + return true; + } + + return match; + }, + POS: function(match){ + match.unshift( true ); + return match; + } + }, + filters: { + enabled: function(elem){ + return elem.disabled === false && elem.type !== "hidden"; + }, + disabled: function(elem){ + return elem.disabled === true; + }, + checked: function(elem){ + return elem.checked === true; + }, + selected: function(elem){ + elem.parentNode.selectedIndex; + return elem.selected === true; + }, + parent: function(elem){ + return !!elem.firstChild; + }, + empty: function(elem){ + return !elem.firstChild; + }, + has: function(elem, i, match){ + return !!Sizzle( match[3], elem ).length; + }, + header: function(elem){ + return /h\d/i.test( elem.nodeName ); + }, + text: function(elem){ + return "text" === elem.type; + }, + radio: function(elem){ + return "radio" === elem.type; + }, + checkbox: function(elem){ + return "checkbox" === elem.type; + }, + file: function(elem){ + return "file" === elem.type; + }, + password: function(elem){ + return "password" === elem.type; + }, + submit: function(elem){ + return "submit" === elem.type; + }, + image: function(elem){ + return "image" === elem.type; + }, + reset: function(elem){ + return "reset" === elem.type; + }, + button: function(elem){ + return "button" === elem.type || elem.nodeName.toUpperCase() === "BUTTON"; + }, + input: function(elem){ + return /input|select|textarea|button/i.test(elem.nodeName); + } + }, + setFilters: { + first: function(elem, i){ + return i === 0; + }, + last: function(elem, i, match, array){ + return i === array.length - 1; + }, + even: function(elem, i){ + return i % 2 === 0; + }, + odd: function(elem, i){ + return i % 2 === 1; + }, + lt: function(elem, i, match){ + return i < match[3] - 0; + }, + gt: function(elem, i, match){ + return i > match[3] - 0; + }, + nth: function(elem, i, match){ + return match[3] - 0 == i; + }, + eq: function(elem, i, match){ + return match[3] - 0 == i; + } + }, + filter: { + PSEUDO: function(elem, match, i, array){ + var name = match[1], filter = Expr.filters[ name ]; + + if ( filter ) { + return filter( elem, i, match, array ); + } else if ( name === "contains" ) { + return (elem.textContent || elem.innerText || "").indexOf(match[3]) >= 0; + } else if ( name === "not" ) { + var not = match[3]; + + for ( var i = 0, l = not.length; i < l; i++ ) { + if ( not[i] === elem ) { + return false; + } + } + + return true; + } + }, + CHILD: function(elem, match){ + var type = match[1], node = elem; + switch (type) { + case 'only': + case 'first': + while ( (node = node.previousSibling) ) { + if ( node.nodeType === 1 ) return false; + } + if ( type == 'first') return true; + node = elem; + case 'last': + while ( (node = node.nextSibling) ) { + if ( node.nodeType === 1 ) return false; + } + return true; + case 'nth': + var first = match[2], last = match[3]; + + if ( first == 1 && last == 0 ) { + return true; + } + + var doneName = match[0], + parent = elem.parentNode; + + if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) { + var count = 0; + for ( node = parent.firstChild; node; node = node.nextSibling ) { + if ( node.nodeType === 1 ) { + node.nodeIndex = ++count; + } + } + parent.sizcache = doneName; + } + + var diff = elem.nodeIndex - last; + if ( first == 0 ) { + return diff == 0; + } else { + return ( diff % first == 0 && diff / first >= 0 ); + } + } + }, + ID: function(elem, match){ + return elem.nodeType === 1 && elem.getAttribute("id") === match; + }, + TAG: function(elem, match){ + return (match === "*" && elem.nodeType === 1) || elem.nodeName === match; + }, + CLASS: function(elem, match){ + return (" " + (elem.className || elem.getAttribute("class")) + " ") + .indexOf( match ) > -1; + }, + ATTR: function(elem, match){ + var name = match[1], + result = Expr.attrHandle[ name ] ? + Expr.attrHandle[ name ]( elem ) : + elem[ name ] != null ? + elem[ name ] : + elem.getAttribute( name ), + value = result + "", + type = match[2], + check = match[4]; + + return result == null ? + type === "!=" : + type === "=" ? + value === check : + type === "*=" ? + value.indexOf(check) >= 0 : + type === "~=" ? + (" " + value + " ").indexOf(check) >= 0 : + !check ? + value && result !== false : + type === "!=" ? + value != check : + type === "^=" ? + value.indexOf(check) === 0 : + type === "$=" ? + value.substr(value.length - check.length) === check : + type === "|=" ? + value === check || value.substr(0, check.length + 1) === check + "-" : + false; + }, + POS: function(elem, match, i, array){ + var name = match[2], filter = Expr.setFilters[ name ]; + + if ( filter ) { + return filter( elem, i, match, array ); + } + } + } +}; + +var origPOS = Expr.match.POS; + +for ( var type in Expr.match ) { + Expr.match[ type ] = new RegExp( Expr.match[ type ].source + /(?![^\[]*\])(?![^\(]*\))/.source ); + Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source ); +} + +var makeArray = function(array, results) { + array = Array.prototype.slice.call( array, 0 ); + + if ( results ) { + results.push.apply( results, array ); + return results; + } + + return array; +}; + +try { + Array.prototype.slice.call( document.documentElement.childNodes, 0 ); + +} catch(e){ + makeArray = function(array, results) { + var ret = results || []; + + if ( toString.call(array) === "[object Array]" ) { + Array.prototype.push.apply( ret, array ); + } else { + if ( typeof array.length === "number" ) { + for ( var i = 0, l = array.length; i < l; i++ ) { + ret.push( array[i] ); + } + } else { + for ( var i = 0; array[i]; i++ ) { + ret.push( array[i] ); + } + } + } + + return ret; + }; } -function $$() { - return Selector.findChildElements(document, $A(arguments)); +var sortOrder; + +if ( document.documentElement.compareDocumentPosition ) { + sortOrder = function( a, b ) { + if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) { + if ( a == b ) { + hasDuplicate = true; + } + return 0; + } + + var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1; + if ( ret === 0 ) { + hasDuplicate = true; + } + return ret; + }; +} else if ( "sourceIndex" in document.documentElement ) { + sortOrder = function( a, b ) { + if ( !a.sourceIndex || !b.sourceIndex ) { + if ( a == b ) { + hasDuplicate = true; + } + return 0; + } + + var ret = a.sourceIndex - b.sourceIndex; + if ( ret === 0 ) { + hasDuplicate = true; + } + return ret; + }; +} else if ( document.createRange ) { + sortOrder = function( a, b ) { + if ( !a.ownerDocument || !b.ownerDocument ) { + if ( a == b ) { + hasDuplicate = true; + } + return 0; + } + + var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange(); + aRange.setStart(a, 0); + aRange.setEnd(a, 0); + bRange.setStart(b, 0); + bRange.setEnd(b, 0); + var ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange); + if ( ret === 0 ) { + hasDuplicate = true; + } + return ret; + }; } +(function(){ + var form = document.createElement("div"), + id = "script" + (new Date).getTime(); + form.innerHTML = "<a name='" + id + "'/>"; + + var root = document.documentElement; + root.insertBefore( form, root.firstChild ); + + if ( !!document.getElementById( id ) ) { + Expr.find.ID = function(match, context, isXML){ + if ( typeof context.getElementById !== "undefined" && !isXML ) { + var m = context.getElementById(match[1]); + return m ? m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? [m] : undefined : []; + } + }; + + Expr.filter.ID = function(elem, match){ + var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id"); + return elem.nodeType === 1 && node && node.nodeValue === match; + }; + } + + root.removeChild( form ); + root = form = null; // release memory in IE +})(); + +(function(){ + + var div = document.createElement("div"); + div.appendChild( document.createComment("") ); + + if ( div.getElementsByTagName("*").length > 0 ) { + Expr.find.TAG = function(match, context){ + var results = context.getElementsByTagName(match[1]); + + if ( match[1] === "*" ) { + var tmp = []; + + for ( var i = 0; results[i]; i++ ) { + if ( results[i].nodeType === 1 ) { + tmp.push( results[i] ); + } + } + + results = tmp; + } + + return results; + }; + } + + div.innerHTML = "<a href='#'></a>"; + if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" && + div.firstChild.getAttribute("href") !== "#" ) { + Expr.attrHandle.href = function(elem){ + return elem.getAttribute("href", 2); + }; + } + + div = null; // release memory in IE +})(); + +if ( document.querySelectorAll ) (function(){ + var oldSizzle = Sizzle, div = document.createElement("div"); + div.innerHTML = "<p class='TEST'></p>"; + + if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) { + return; + } + + Sizzle = function(query, context, extra, seed){ + context = context || document; + + if ( !seed && context.nodeType === 9 && !isXML(context) ) { + try { + return makeArray( context.querySelectorAll(query), extra ); + } catch(e){} + } + + return oldSizzle(query, context, extra, seed); + }; + + for ( var prop in oldSizzle ) { + Sizzle[ prop ] = oldSizzle[ prop ]; + } + + div = null; // release memory in IE +})(); + +if ( document.getElementsByClassName && document.documentElement.getElementsByClassName ) (function(){ + var div = document.createElement("div"); + div.innerHTML = "<div class='test e'></div><div class='test'></div>"; + + if ( div.getElementsByClassName("e").length === 0 ) + return; + + div.lastChild.className = "e"; + + if ( div.getElementsByClassName("e").length === 1 ) + return; + + Expr.order.splice(1, 0, "CLASS"); + Expr.find.CLASS = function(match, context, isXML) { + if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) { + return context.getElementsByClassName(match[1]); + } + }; + + div = null; // release memory in IE +})(); + +function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { + var sibDir = dir == "previousSibling" && !isXML; + for ( var i = 0, l = checkSet.length; i < l; i++ ) { + var elem = checkSet[i]; + if ( elem ) { + if ( sibDir && elem.nodeType === 1 ){ + elem.sizcache = doneName; + elem.sizset = i; + } + elem = elem[dir]; + var match = false; + + while ( elem ) { + if ( elem.sizcache === doneName ) { + match = checkSet[elem.sizset]; + break; + } + + if ( elem.nodeType === 1 && !isXML ){ + elem.sizcache = doneName; + elem.sizset = i; + } + + if ( elem.nodeName === cur ) { + match = elem; + break; + } + + elem = elem[dir]; + } + + checkSet[i] = match; + } + } +} + +function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) { + var sibDir = dir == "previousSibling" && !isXML; + for ( var i = 0, l = checkSet.length; i < l; i++ ) { + var elem = checkSet[i]; + if ( elem ) { + if ( sibDir && elem.nodeType === 1 ) { + elem.sizcache = doneName; + elem.sizset = i; + } + elem = elem[dir]; + var match = false; + + while ( elem ) { + if ( elem.sizcache === doneName ) { + match = checkSet[elem.sizset]; + break; + } + + if ( elem.nodeType === 1 ) { + if ( !isXML ) { + elem.sizcache = doneName; + elem.sizset = i; + } + if ( typeof cur !== "string" ) { + if ( elem === cur ) { + match = true; + break; + } + + } else if ( Sizzle.filter( cur, [elem] ).length > 0 ) { + match = elem; + break; + } + } + + elem = elem[dir]; + } + + checkSet[i] = match; + } + } +} + +var contains = document.compareDocumentPosition ? function(a, b){ + return a.compareDocumentPosition(b) & 16; +} : function(a, b){ + return a !== b && (a.contains ? a.contains(b) : true); +}; + +var isXML = function(elem){ + return elem.nodeType === 9 && elem.documentElement.nodeName !== "HTML" || + !!elem.ownerDocument && elem.ownerDocument.documentElement.nodeName !== "HTML"; +}; + +var posProcess = function(selector, context){ + var tmpSet = [], later = "", match, + root = context.nodeType ? [context] : context; + + while ( (match = Expr.match.PSEUDO.exec( selector )) ) { + later += match[0]; + selector = selector.replace( Expr.match.PSEUDO, "" ); + } + + selector = Expr.relative[selector] ? selector + "*" : selector; + + for ( var i = 0, l = root.length; i < l; i++ ) { + Sizzle( selector, root[i], tmpSet ); + } + + return Sizzle.filter( later, tmpSet ); +}; + + +window.Sizzle = Sizzle; + +})(); + +;(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); @@ -3925,24 +4970,34 @@ var 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; + 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].push(value); + } else result[key] = value; + return result; + }; + } else { + initial = ''; + accumulator = function(result, key, value) { + return result + (result ? '&' : '') + encodeURIComponent(key) + '=' + encodeURIComponent(value); + } + } - var data = elements.inject({ }, function(result, element) { + 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)))) { - if (key in result) { - if (!Object.isArray(result[key])) result[key] = [result[key]]; - result[key].push(value); - } - else result[key] = value; + result = accumulator(result, key, value); } } return result; }); - - return options.hash ? data : Object.toQueryString(data); } }; @@ -4009,7 +5064,8 @@ Form.Methods = { focusFirstElement: function(form) { form = $(form); - form.findFirstElement().activate(); + var element = form.findFirstElement(); + if (element) element.activate(); return form; }, @@ -4116,67 +5172,77 @@ var $F = Form.Element.Methods.getValue; /*--------------------------------------------------------------------------*/ -Form.Element.Serializers = { - input: function(element, value) { +Form.Element.Serializers = (function() { + function input(element, value) { switch (element.type.toLowerCase()) { case 'checkbox': case 'radio': - return Form.Element.Serializers.inputSelector(element, value); + return inputSelector(element, value); default: - return Form.Element.Serializers.textarea(element, value); + return valueSelector(element, value); } - }, + } - inputSelector: function(element, value) { - if (Object.isUndefined(value)) return element.checked ? element.value : null; + function inputSelector(element, value) { + if (Object.isUndefined(value)) + return element.checked ? element.value : null; else element.checked = !!value; - }, + } - textarea: function(element, value) { + function valueSelector(element, value) { if (Object.isUndefined(value)) return element.value; else element.value = value; - }, + } - select: function(element, value) { + function select(element, value) { if (Object.isUndefined(value)) - return this[element.type == 'select-one' ? - 'selectOne' : 'selectMany'](element); - else { - 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; - } + 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); } + else opt.selected = value.include(currentValue); } - }, + } - selectOne: function(element) { + function selectOne(element) { var index = element.selectedIndex; - return index >= 0 ? this.optionValue(element.options[index]) : null; - }, + return index >= 0 ? optionValue(element.options[index]) : null; + } - selectMany: function(element) { + 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(this.optionValue(opt)); + if (opt.selected) values.push(optionValue(opt)); } return values; - }, + } - optionValue: function(opt) { - return Element.extend(opt).hasAttribute('value') ? opt.value : opt.text; + 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 + }; +})(); /*--------------------------------------------------------------------------*/ @@ -4287,24 +5353,53 @@ Form.EventObserver = Class.create(Abstract.EventObserver, { var MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED = 'onmouseenter' in docEl && 'onmouseleave' in docEl; + + + 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; - if (Prototype.Browser.IE) { - var buttonMap = { 0: 1, 1: 4, 2: 2 }; - _isButton = function(event, code) { - return event.button === buttonMap[code]; - }; - } else if (Prototype.Browser.WebKit) { - _isButton = function(event, code) { - switch (code) { - case 0: return event.which == 1 && !event.metaKey; - case 1: return event.which == 1 && event.metaKey; - default: return false; + + 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 = function(event, code) { - return event.which ? (event.which === code + 1) : (event.button === code); - }; + _isButton = _isButtonForDOMEvents; } function isLeftClick(event) { return _isButton(event, 0) } @@ -4334,9 +5429,14 @@ Form.EventObserver = Class.create(Abstract.EventObserver, { function findElement(event, expression) { var element = Event.element(event); + if (!expression) return element; - var elements = [element].concat(element.ancestors()); - return Selector.findElement(elements, expression, 0); + while (element) { + if (Object.isElement(element) && Prototype.Selector.match(element, expression)) { + return Element.extend(element); + } + element = element.parentNode; + } } function pointer(event) { @@ -4370,49 +5470,59 @@ Form.EventObserver = Class.create(Abstract.EventObserver, { event.stopped = true; } + Event.Methods = { - isLeftClick: isLeftClick, + isLeftClick: isLeftClick, isMiddleClick: isMiddleClick, - isRightClick: isRightClick, + isRightClick: isRightClick, - element: element, + element: element, findElement: findElement, - pointer: pointer, + 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 (Prototype.Browser.IE) { + if (window.attachEvent) { function _relatedTarget(event) { var element; switch (event.type) { - case 'mouseover': element = event.fromElement; break; - case 'mouseout': element = event.toElement; break; - default: return null; + case 'mouseover': + case 'mouseenter': + element = event.fromElement; + break; + case 'mouseout': + case 'mouseleave': + element = event.toElement; + break; + default: + return null; } return Element.extend(element); } - Object.extend(methods, { + 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 (event._extendedByPrototype) return event; + if (!isIELegacyEvent(event)) return event; + + if (event._extendedByPrototype) return event; event._extendedByPrototype = Prototype.emptyFunction; + var pointer = Event.pointer(event); Object.extend(event, { @@ -4422,12 +5532,18 @@ Form.EventObserver = Class.create(Abstract.EventObserver, { pageY: pointer.y }); - return Object.extend(event, methods); + 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); - Event.extend = Prototype.K; } function _createResponder(element, eventName, handler) { @@ -4505,12 +5621,12 @@ Form.EventObserver = Class.create(Abstract.EventObserver, { window.addEventListener('unload', Prototype.emptyFunction, false); - var _getDOMEventName = Prototype.K; + var _getDOMEventName = Prototype.K, + translations = { mouseenter: "mouseover", mouseleave: "mouseout" }; if (!MOUSEENTER_MOUSELEAVE_EVENTS_SUPPORTED) { _getDOMEventName = function(eventName) { - var translations = { mouseenter: "mouseover", mouseleave: "mouseout" }; - return eventName in translations ? translations[eventName] : eventName; + return (translations[eventName] || eventName); }; } @@ -4526,7 +5642,7 @@ Form.EventObserver = Class.create(Abstract.EventObserver, { element.addEventListener("dataavailable", responder, false); else { element.attachEvent("ondataavailable", responder); - element.attachEvent("onfilterchange", responder); + element.attachEvent("onlosecapture", responder); } } else { var actualEventName = _getDOMEventName(eventName); @@ -4544,46 +5660,44 @@ Form.EventObserver = Class.create(Abstract.EventObserver, { element = $(element); var registry = Element.retrieve(element, 'prototype_event_registry'); + if (!registry) return element; - if (Object.isUndefined(registry)) return element; - - if (eventName && !handler) { - var responders = registry.get(eventName); - - if (Object.isUndefined(responders)) return element; - - responders.each( function(r) { - Element.stopObserving(element, eventName, r.handler); - }); - return element; - } else if (!eventName) { + if (!eventName) { registry.each( function(pair) { - var eventName = pair.key, responders = pair.value; - - responders.each( function(r) { - Element.stopObserving(element, eventName, r.handler); - }); + var eventName = pair.key; + stopObserving(element, eventName); }); return element; } var responders = registry.get(eventName); + if (!responders) return element; - if (!responders) return; + if (!handler) { + responders.each(function(r) { + stopObserving(element, eventName, r.handler); + }); + return element; + } - var responder = responders.find( function(r) { return r.handler === handler; }); + var i = responders.length, responder; + while (i--) { + if (responders[i].handler === handler) { + responder = responders[i]; + break; + } + } if (!responder) return element; - var actualEventName = _getDOMEventName(eventName); - if (eventName.include(':')) { if (element.removeEventListener) element.removeEventListener("dataavailable", responder, false); else { element.detachEvent("ondataavailable", responder); - element.detachEvent("onfilterchange", responder); + element.detachEvent("onlosecapture", responder); } } else { + var actualEventName = _getDOMEventName(eventName); if (element.removeEventListener) element.removeEventListener(actualEventName, responder, false); else @@ -4607,10 +5721,10 @@ Form.EventObserver = Class.create(Abstract.EventObserver, { var event; if (document.createEvent) { event = document.createEvent('HTMLEvents'); - event.initEvent('dataavailable', true, true); + event.initEvent('dataavailable', bubble, true); } else { event = document.createEventObject(); - event.eventType = bubble ? 'ondataavailable' : 'onfilterchange'; + event.eventType = bubble ? 'ondataavailable' : 'onlosecapture'; } event.eventName = eventName; @@ -4624,13 +5738,47 @@ Form.EventObserver = Class.create(Abstract.EventObserver, { return Event.extend(event); } + 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 + stopObserving: stopObserving, + on: on }); Element.addMethods({ @@ -4638,7 +5786,9 @@ Form.EventObserver = Class.create(Abstract.EventObserver, { observe: observe, - stopObserving: stopObserving + stopObserving: stopObserving, + + on: on }); Object.extend(document, { @@ -4648,6 +5798,8 @@ Form.EventObserver = Class.create(Abstract.EventObserver, { stopObserving: stopObserving.methodize(), + on: on.methodize(), + loaded: false }); @@ -4872,4 +6024,59 @@ Element.ClassNames.prototype = { Object.extend(Element.ClassNames.prototype, Enumerable); -/*--------------------------------------------------------------------------*/
\ No newline at end of file +/*--------------------------------------------------------------------------*/ + +(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 "#<Selector: " + this.expression + ">"; + } + }); + + 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/usr/local/www/services_captiveportal.php b/usr/local/www/services_captiveportal.php index e86a2d1..2f1db79 100755 --- a/usr/local/www/services_captiveportal.php +++ b/usr/local/www/services_captiveportal.php @@ -67,7 +67,6 @@ if ($_GET['act'] == "viewhtml") { } $pconfig['cinterface'] = $config['captiveportal']['interface']; -$pconfig['maxproc'] = $config['captiveportal']['maxproc']; $pconfig['maxprocperip'] = $config['captiveportal']['maxprocperip']; $pconfig['timeout'] = $config['captiveportal']['timeout']; $pconfig['idletimeout'] = $config['captiveportal']['idletimeout']; @@ -172,18 +171,13 @@ if ($_POST) { if (($_POST['radiusacctport'] && !is_port($_POST['radiusacctport']))) { $input_errors[] = sprintf(gettext("A valid port number must be specified. [%s]"), $_POST['radiusacctport']); } - if ($_POST['maxproc'] && (!is_numeric($_POST['maxproc']) || ($_POST['maxproc'] < 4) || ($_POST['maxproc'] > 100))) { - $input_errors[] = gettext("The total maximum number of concurrent connections must be between 4 and 100."); - } - $mymaxproc = $_POST['maxproc'] ? $_POST['maxproc'] : 16; - if ($_POST['maxprocperip'] && (!is_numeric($_POST['maxprocperip']) || ($_POST['maxprocperip'] > $mymaxproc))) { + if ($_POST['maxprocperip'] && (!is_numeric($_POST['maxprocperip']) || ($_POST['maxprocperip'] < 4) || $_POST['maxprocperip'] > 100)) { $input_errors[] = gettext("The maximum number of concurrent connections per client IP address may not be larger than the global maximum."); } if (!$input_errors) { if (is_array($_POST['cinterface'])) $config['captiveportal']['interface'] = implode(",", $_POST['cinterface']); - $config['captiveportal']['maxproc'] = $_POST['maxproc']; $config['captiveportal']['maxprocperip'] = $_POST['maxprocperip'] ? $_POST['maxprocperip'] : false; $config['captiveportal']['timeout'] = $_POST['timeout']; $config['captiveportal']['idletimeout'] = $_POST['idletimeout']; @@ -256,7 +250,6 @@ function enable_change(enable_change) { radius_endis = !((!endis && document.iform.auth_method[2].checked) || enable_change); document.iform.cinterface.disabled = endis; - //document.iform.maxproc.disabled = endis; document.iform.maxprocperip.disabled = endis; document.iform.idletimeout.disabled = endis; document.iform.freelogins_count.disabled = endis; diff --git a/usr/local/www/services_captiveportal_vouchers.php b/usr/local/www/services_captiveportal_vouchers.php index f4f4b89..05bce04 100644 --- a/usr/local/www/services_captiveportal_vouchers.php +++ b/usr/local/www/services_captiveportal_vouchers.php @@ -295,7 +295,7 @@ EOF; $config['voucher']['msgexpired'] = $toreturn['voucher']['msgexpired']; if($toreturn['voucher']['msgnoaccess']) $config['voucher']['msgnoaccess'] = $toreturn['voucher']['msgnoaccess']; - $savemsg = gettext("Voucher database has been synchronized from {$url}:{$port}"); + $savemsg = sprintf(gettext("Voucher database has been synchronized from %1\$s:%2\$s"),$url,$port); write_config(); voucher_configure(true); diff --git a/usr/local/www/services_dhcp.php b/usr/local/www/services_dhcp.php index 122912d..e818738 100755 --- a/usr/local/www/services_dhcp.php +++ b/usr/local/www/services_dhcp.php @@ -156,8 +156,8 @@ if (is_array($config['dhcpd'][$if])){ $a_maps = &$config['dhcpd'][$if]['staticmap']; } -$ifcfgip = get_interface_ip($if); -$ifcfgsn = get_interface_subnet($if); +$ifcfgip = $config['interfaces'][$if]['ipaddr']; +$ifcfgsn = $config['interfaces'][$if]['subnet']; /* set the enabled flag which will tell us if DHCP relay is enabled * on any interface. We will use this to disable DHCP server since diff --git a/usr/local/www/status_services.php b/usr/local/www/status_services.php index d463f3a..2303054 100755 --- a/usr/local/www/status_services.php +++ b/usr/local/www/status_services.php @@ -63,6 +63,7 @@ if($_GET['mode'] == "restartservice" and !empty($_GET['service'])) { switch($_GET['service']) { case 'captiveportal': killbypid("{$g['varrun_path']}/lighty-CaptivePortal.pid"); + killbypid("{$g['varrun_path']}/lighty-CaptivePortal-SSL.pid"); captiveportal_init_webgui(); break; case 'ntpd': @@ -157,6 +158,7 @@ if($_GET['mode'] == "stopservice" && !empty($_GET['service'])) { switch($_GET['service']) { case 'captiveportal': killbypid("{$g['varrun_path']}/lighty-CaptivePortal.pid"); + killbypid("{$g['varrun_path']}/lighty-CaptivePortal-SSL.pid"); break; case 'ntpd': killbyname("ntpd"); diff --git a/usr/local/www/system.php b/usr/local/www/system.php index caf8e78..6be9f99 100755 --- a/usr/local/www/system.php +++ b/usr/local/www/system.php @@ -135,7 +135,7 @@ if ($_POST) { if(interface_has_gateway($_POST[$dnsgwitem])) { foreach($direct_networks_list as $direct_network) { if(ip_in_subnet($_POST[$dnsitem], $direct_network)) { - $input_errors[] = gettext("You can not assign a gateway to DNS '{$_POST[$dnsitem]}' server which is on a directly connected network."); + $input_errors[] = sprintf(gettext("You can not assign a gateway to DNS '%s' server which is on a directly connected network."),$_POST[$dnsitem]); } } } diff --git a/usr/local/www/system_advanced_admin.php b/usr/local/www/system_advanced_admin.php index d1150a2..02614e2 100644 --- a/usr/local/www/system_advanced_admin.php +++ b/usr/local/www/system_advanced_admin.php @@ -93,7 +93,7 @@ if ($_POST) { $althosts = explode(" ", $_POST['althostnames']); foreach ($althosts as $ah) if (!is_hostname($ah)) - $input_errors[] = gettext("Alternate hostname " . htmlspecialchars($ah) . " is not a valid hostname."); + $input_errors[] = sprintf(gettext("Alternate hostname %s is not a valid hostname."),htmlspecialchars($ah)); } if ($_POST['sshport']) diff --git a/usr/local/www/system_advanced_misc.php b/usr/local/www/system_advanced_misc.php index c31bc92..c418505 100644 --- a/usr/local/www/system_advanced_misc.php +++ b/usr/local/www/system_advanced_misc.php @@ -232,7 +232,7 @@ function maxmss_checked(obj) { <td width="78%" class="vtable"> <input name="proxyurl" id="proxyurl" value="<?php if ($pconfig['proxyurl'] <> "") echo $pconfig['proxyurl']; ?>" class="formfld unknown"> <br /> - <?=gettext("Proxy url for allowing {$g['product']} to use this proxy to connect outside."); ?> + <?php printf(gettext("Proxy url for allowing %s to use this proxy to connect outside."),$g['product']); ?> </td> </tr> <tr> @@ -240,7 +240,7 @@ function maxmss_checked(obj) { <td width="78%" class="vtable"> <input name="proxyport" id="proxyport" value="<?php if ($pconfig['proxyport'] <> "") echo $pconfig['proxyport']; ?>" class="formfld unknown"> <br /> - <?=gettext("Proxy url for allowing {$g['product']} to use this proxy port to connect outside. Default is 8080 for http protocol or ssl for 443."); ?> + <?php printf(gettext("Proxy url for allowing %s to use this proxy port to connect outside. Default is 8080 for http protocol or ssl for 443."),$g['product']); ?> </td> </tr> <tr> @@ -248,7 +248,7 @@ function maxmss_checked(obj) { <td width="78%" class="vtable"> <input name="proxyuser" id="proxyuser" value="<?php if ($pconfig['proxyuser'] <> "") echo $pconfig['proxyuser']; ?>" class="formfld unknown"> <br /> - <?=gettext("Proxy username for allowing {$g['product']} to use this proxy to connect outside"); ?> + <?php printf(gettext("Proxy username for allowing %s to use this proxy to connect outside"),$g['product']); ?> </td> </tr> <tr> @@ -256,7 +256,7 @@ function maxmss_checked(obj) { <td width="78%" class="vtable"> <input type="password" name="proxypass" id="proxypass" value="<?php if ($pconfig['proxypass'] <> "") echo $pconfig['proxypass']; ?>" class="formfld unknown"> <br /> - <?=gettext("Proxy password for allowing {$g['product']} to use this proxy to connect outside"); ?> + <?php printf(gettext("Proxy password for allowing %s to use this proxy to connect outside"),$g['product']); ?> </td> </tr> <tr> diff --git a/usr/local/www/system_advanced_notifications.php b/usr/local/www/system_advanced_notifications.php index 7da289a..3c997ce 100644 --- a/usr/local/www/system_advanced_notifications.php +++ b/usr/local/www/system_advanced_notifications.php @@ -202,10 +202,10 @@ include("head.inc"); <td colspan="2" valign="top" class="listtopic"><?=gettext("SMTP E-Mail"); ?></td> </tr> <tr> - <td width="22%" valign="top" class="vncell"><?=gettext("IP Address of E-Mail server"); ?></td> + <td width="22%" valign="top" class="vncell"><?=gettext("E-Mail server"); ?></td> <td width="78%" class="vtable"> <input name='smtpipaddress' value='<?php echo $pconfig['smtpipaddress']; ?>'><br/> - <?=gettext("This is the IP address of the SMTP E-Mail server that will be used to send notifications to."); ?> + <?=gettext("This is the FQDN or IP address of the SMTP E-Mail server to which notifications will be sent."); ?> </td> </tr> <tr> diff --git a/usr/local/www/system_crlmanager.php b/usr/local/www/system_crlmanager.php index 2ecc866..201d8ab 100644 --- a/usr/local/www/system_crlmanager.php +++ b/usr/local/www/system_crlmanager.php @@ -525,11 +525,11 @@ function method_change() { <td class="list"> <?php if ($cainternal == "YES"): ?> <a href="system_crlmanager.php?act=new&caref=<?php echo $ca['refid']; ?>"> - <img src="/themes/<?= $g['theme'];?>/images/icons/icon_plus.gif" title="<?=gettext("Add or Import CRL for ") . $ca['descr'];?>" alt="<?=gettext("add crl");?>" width="17" height="17" border="0" /> + <img src="/themes/<?= $g['theme'];?>/images/icons/icon_plus.gif" title="<?php printf(gettext("Add or Import CRL for %s"),$ca['descr']);?>" alt="<?=gettext("add crl");?>" width="17" height="17" border="0" /> </a> <?php else: ?> <a href="system_crlmanager.php?act=new&caref=<?php echo $ca['refid']; ?>&importonly=yes"> - <img src="/themes/<?= $g['theme'];?>/images/icons/icon_plus.gif" title="<?=gettext("Import CRL for ") . $ca['descr'];?>" alt="<?=gettext("add crl");?>" width="17" height="17" border="0" /> + <img src="/themes/<?= $g['theme'];?>/images/icons/icon_plus.gif" title="<?php printf(gettext("Import CRL for %s"),$ca['descr']);?>" alt="<?=gettext("add crl");?>" width="17" height="17" border="0" /> </a> <?php endif; ?> </td> diff --git a/usr/local/www/system_gateways_edit.php b/usr/local/www/system_gateways_edit.php index 02fac7c..f34df9f 100755 --- a/usr/local/www/system_gateways_edit.php +++ b/usr/local/www/system_gateways_edit.php @@ -122,14 +122,14 @@ if ($_POST) { $parent_sn = get_interface_subnet($_POST['interface']); $subnet = gen_subnet($parent_ip, $parent_sn) . "/" . $parent_sn; if(!ip_in_subnet($_POST['gateway'], $subnet) && !ip_in_interface_alias_subnet($_POST['interface'], $_POST['gateway'])) { - $input_errors[] = sprintf(gettext("The gateway address %s does not lie within the chosen interface's subnet '{$subnet}'."), $_POST['gateway']); + $input_errors[] = sprintf(gettext("The gateway address %1\$s does not lie within the chosen interface's subnet '%2\$s'."), $_POST['gateway'],$subnet); } } if (is_ipaddrv6($parent_ip)) { $parent_sn = get_interface_subnetv6($_POST['interface']); $subnet = gen_subnetv6($parent_ip, $parent_sn) . "/" . $parent_sn; if(!ip_in_subnet($_POST['gateway'], $subnet)) { - $input_errors[] = sprintf(gettext("The gateway address %s does not lie within the chosen interface's subnet '{$subnet}'."), $_POST['gateway']); + $input_errors[] = sprintf(gettext("The gateway address %1\$s does not lie within the chosen interface's subnet '%2\$s'."), $_POST['gateway'],$subnet); } } } diff --git a/usr/local/www/vpn_pppoe_edit.php b/usr/local/www/vpn_pppoe_edit.php index 6c94797..ea6f5f0 100755 --- a/usr/local/www/vpn_pppoe_edit.php +++ b/usr/local/www/vpn_pppoe_edit.php @@ -139,9 +139,9 @@ if ($_POST) { for($x=0; $x<4999; $x++) { if ($_POST["username{$x}"]) { if (empty($_POST["password{$x}"])) - $input_errors[] = gettext("No password specified for username ") . $_POST["username{$x}"]; + $input_errors[] = sprintf(gettext("No password specified for username %s"),$_POST["username{$x}"]); if ($_POST["ip{$x}"] <> "" && !is_ipaddr($_POST["ip{$x}"])) - $input_errors[] = gettext("Incorrect ip address specified for username ") . $_POST["username{$x}"]; + $input_errors[] = sprintf(gettext("Incorrect ip address specified for username %s"),$_POST["username{$x}"]); } } } diff --git a/usr/local/www/wizards/openvpn_wizard.inc b/usr/local/www/wizards/openvpn_wizard.inc index 51c7a63..348abbd 100644 --- a/usr/local/www/wizards/openvpn_wizard.inc +++ b/usr/local/www/wizards/openvpn_wizard.inc @@ -599,7 +599,7 @@ function step12_submitphpaction() { if (isset($pconfig['step11']['ovpnrule'])) { $rule = array(); - $rule['descr'] = gettext("OpenVPN {$server['description']} wizard"); + $rule['descr'] = sprintf(gettext("OpenVPN %s wizard"),$server['description']); /* Ensure the rule descr is not too long for pf to handle */ if (strlen($rule['descr']) > 52) $rule['descr'] = substr($rule['descr'], 0, 52); @@ -615,7 +615,7 @@ function step12_submitphpaction() { } if (isset($pconfig['step11']['ovpnallow'])) { $rule = array(); - $rule['descr'] = gettext("OpenVPN {$server['description']} wizard"); + $rule['descr'] = sprintf(gettext("OpenVPN %s wizard"),$server['description']); /* Ensure the rule descr is not too long for pf to handle */ if (strlen($rule['descr']) > 52) $rule['descr'] = substr($rule['descr'], 0, 52); diff --git a/usr/local/www/wizards/traffic_shaper_wizard.inc b/usr/local/www/wizards/traffic_shaper_wizard.inc index e6d8823..ee800ce 100644 --- a/usr/local/www/wizards/traffic_shaper_wizard.inc +++ b/usr/local/www/wizards/traffic_shaper_wizard.inc @@ -600,12 +600,15 @@ function apply_all_choosen_items() { $lanbw += $input_bw; } + $interfacelist = array(); + for ($i = 0; $i < $steps; $i++) { $altq =& new altq_root_queue(); $tmppath = array(); $altq->SetInterface($config['ezshaper']['step2']["conn{$i}interface"]); + $interfacelist[] = $config['ezshaper']['step2']["conn{$i}interface"]; $altq->SetScheduler($config['ezshaper']['step2']["conn{$i}uploadscheduler"]); $altq->SetBandwidth(floatval($config['ezshaper']['step2']["conn{$i}upload"])); $altq->SetBwscale($config['ezshaper']['step2']["conn{$i}uploadspeed"]); @@ -1329,11 +1332,17 @@ function apply_all_choosen_items() { if (!is_array($config['filter']['rule'])) $config['filter']['rule'] = array(); + if (empty($interfacelist)) + $interfacelist = ""; + else + $interfacelist = implode(",", $interfacelist); + /* Rules */ if ($penalty) { if( is_ipaddr($config['ezshaper']['step4']['address']) || is_alias($config['ezshaper']['step4']['address'])) { $rule = array(); $rule['type'] = "match"; + $rule['interface'] = $interfacelist; $rule['descr'] = gettext("Penalty Box"); $rule['source']['any'] = TRUE; $rule['defaultqueue'] = "qOthersLow"; @@ -1353,6 +1362,7 @@ function apply_all_choosen_items() { /* create VOIP rules */ $rule = array(); $rule['type'] = "match"; + $rule['interface'] = $interfacelist; $rule['descr'] = gettext("VOIP Adapter"); $rule['protocol'] = "udp"; $rule['defaultqueue'] = "qVoIP"; @@ -1367,6 +1377,7 @@ function apply_all_choosen_items() { /* create VOIP rules */ $rule = array(); $rule['type'] = "match"; + $rule['interface'] = $interfacelist; $rule['descr'] = "DiffServ/Lowdelay/Upload"; $rule['protocol'] = "udp"; $rule['source']['any'] = TRUE; @@ -1383,6 +1394,7 @@ function apply_all_choosen_items() { foreach ($voiplist[$config['ezshaper']['step3']['provider']] as $voip) { $rule = array(); $rule['type'] = "match"; + $rule['interface'] = $interfacelist; $rule['defaultqueue'] = 'qVoIP'; $rule['source']['any'] = TRUE; $rule['destination']['any'] = TRUE; @@ -1406,6 +1418,7 @@ function apply_all_choosen_items() { foreach ($p2plist[$key] as $p2pclient) { $rule = array(); $rule['type'] = "match"; + $rule['interface'] = $interfacelist; $rule['defaultqueue'] = 'qP2P'; $rule['source']['any'] = TRUE; $rule['destination']['any'] = TRUE; @@ -1428,6 +1441,7 @@ function apply_all_choosen_items() { foreach ($gamesplist[$key] as $Gameclient) { $rule = array(); $rule['type'] = "match"; + $rule['interface'] = $interfacelist; $rule['defaultqueue'] = 'qGames'; if ($Gameclient[1] == "tcp") $rule['ackqueue'] = 'qACK'; @@ -1453,6 +1467,7 @@ function apply_all_choosen_items() { foreach ($othersplist[$key] as $otherclient) { $rule = array(); $rule['type'] = "match"; + $rule['interface'] = $interfacelist; switch ($val) { case "H": $rule['defaultqueue'] = 'qOthersHigh'; /* posted value H or L */ @@ -1475,29 +1490,29 @@ function apply_all_choosen_items() { } else $loop = 1; /* It automitaclly goes to default queue */ break; - default: - $loop = 1; - } - if (!$loop) { - $rule['source']['any'] = TRUE; - $rule['destination']['any'] = TRUE; - $rule['floating'] = "yes"; - $rule['wizard'] = "yes"; - $rule['enabled'] = "on"; - $rule['descr'] = "m_Other {$otherclient[0]} outbound"; - - if($otherclient[2] or $otherclient[3]) { - $rule['destination']['port'] = $otherclient[2]."-".$otherclient[3]; - } - if($otherclient[1] != '') - $rule['protocol'] = $otherclient[1]; - - $config['filter']['rule'][] = $rule; + default: + $loop = 1; + } + if (!$loop) { + $rule['source']['any'] = TRUE; + $rule['destination']['any'] = TRUE; + $rule['floating'] = "yes"; + $rule['wizard'] = "yes"; + $rule['enabled'] = "on"; + $rule['descr'] = "m_Other {$otherclient[0]} outbound"; + + if($otherclient[2] or $otherclient[3]) { + $rule['destination']['port'] = $otherclient[2]."-".$otherclient[3]; } + if($otherclient[1] != '') + $rule['protocol'] = $otherclient[1]; + + $config['filter']['rule'][] = $rule; } } } - write_config(); + } + write_config(); } function wizard_get_bandwidthtype_scale($type = "b") { diff --git a/usr/local/www/wizards/traffic_shaper_wizard_dedicated.inc b/usr/local/www/wizards/traffic_shaper_wizard_dedicated.inc index 9707d06..bb25f79 100755 --- a/usr/local/www/wizards/traffic_shaper_wizard_dedicated.inc +++ b/usr/local/www/wizards/traffic_shaper_wizard_dedicated.inc @@ -625,13 +625,16 @@ function apply_all_choosen_items() { $altq_list_queues = array(); $steps = intval($config['ezshaper']['step1']['numberofconnections']); - + + $interfacelist = array(); + for ($i = 0; $i < $steps; $i++) { $tmppath = array(); $altq =& new altq_root_queue(); $altq->SetInterface($config['ezshaper']['step2']["conn{$i}interface"]); + $interfacelist[] = $config['ezshaper']['step2']["conn{$i}interface"]; $altq->SetScheduler($config['ezshaper']['step2']["conn{$i}uploadscheduler"]); $altq->SetBandwidth(floatval($config['ezshaper']['step2']["conn{$i}upload"])); $altq->SetBwscale($config['ezshaper']['step2']["conn{$i}uploadspeed"]); @@ -1394,175 +1397,183 @@ function apply_all_choosen_items() { if (!is_array($config['filter']['rule'])) $config['filter']['rule'] = array(); - /* Rules */ - if ($penalty) { - if( is_ipaddr($config['ezshaper']['step4']['address']) || is_alias($config['ezshaper']['step4']['address'])) { - $rule = array(); - $rule['type'] = "match"; - $rule['descr'] = gettext("Penalty Box"); - $rule['source']['any'] = TRUE; - $rule['defaultqueue'] = "qOthersLow"; - $rule['source']['address'] = $config['ezshaper']['step4']['address']; - $rule['destination']['any'] = TRUE; - $rule['floating'] = "yes"; - $rule['wizard'] = "yes"; - $rule['enabled'] = "on"; - $config['filter']['rule'][] = $rule; - - } + $interfacelist = implode(",", $interfacelist); + + /* Rules */ + if ($penalty) { + if( is_ipaddr($config['ezshaper']['step4']['address']) || is_alias($config['ezshaper']['step4']['address'])) { + $rule = array(); + $rule['type'] = "match"; + $rule['interface'] = $interfacelist; + $rule['descr'] = gettext("Penalty Box"); + $rule['source']['any'] = TRUE; + $rule['defaultqueue'] = "qOthersLow"; + $rule['source']['address'] = $config['ezshaper']['step4']['address']; + $rule['destination']['any'] = TRUE; + $rule['floating'] = "yes"; + $rule['wizard'] = "yes"; + $rule['enabled'] = "on"; + $config['filter']['rule'][] = $rule; + } + } + + /* If user specifies an IP, we don't bother with providers */ + if ($voip) { + if( is_ipaddr($config['ezshaper']['step3']['address']) || is_alias($config['ezshaper']['step3']['address'])) { + /* create VOIP rules */ + $rule = array(); + $rule['type'] = "match"; + $rule['interface'] = $interfacelist; + $rule['descr'] = gettext("VOIP Adapter"); + $rule['protocol'] = "udp"; + $rule['defaultqueue'] = "qVoIP"; + $rule['source']['address'] = $config['ezshaper']['step3']['address']; + $rule['destination']['any'] = TRUE; + $rule['floating'] = "yes"; + $rule['wizard'] = "yes"; + $rule['enabled'] = "on"; + $config['filter']['rule'][] = $rule; + + } elseif( $config['ezshaper']['step3']['provider'] == "Generic" ) { + /* create VOIP rules */ + $rule = array(); + $rule['type'] = "match"; + $rule['interface'] = $interfacelist; + $rule['descr'] = "DiffServ/Lowdelay/Upload"; + $rule['protocol'] = "udp"; + $rule['source']['any'] = TRUE; + $rule['defaultqueue'] = "qVoIP"; + $rule['destination']['any'] = TRUE; + $rule['iptos'] = "lowdelay"; + $rule['floating'] = "yes"; + $rule['wizard'] = "yes"; + $rule['enabled'] = "on"; + $config['filter']['rule'][] = $rule; + + } else { + /* loop through voiplist[] */ + foreach ($voiplist[$config['ezshaper']['step3']['provider']] as $voip) { + $rule = array(); + $rule['type'] = "match"; + $rule['interface'] = $interfacelist; + $rule['defaultqueue'] = 'qVoIP'; + $rule['source']['any'] = TRUE; + $rule['destination']['any'] = TRUE; + $rule['descr'] = "m_voip {$voip[0]} outbound"; + $rule['floating'] = "yes"; + $rule['wizard'] = "yes"; + $rule['enabled'] = "on"; + $rule['destination']['port'] = $voip[2]."-".$voip[3]; + if($voip[1] != '') + $rule['protocol'] = $voip[1]; + $config['filter']['rule'][] = $rule; } + } + } - /* If user specifies an IP, we don't bother with providers */ - if ($voip) { - if( is_ipaddr($config['ezshaper']['step3']['address']) || is_alias($config['ezshaper']['step3']['address'])) { - /* create VOIP rules */ - $rule = array(); - $rule['type'] = "match"; - $rule['descr'] = gettext("VOIP Adapter"); - $rule['protocol'] = "udp"; - $rule['defaultqueue'] = "qVoIP"; - $rule['source']['address'] = $config['ezshaper']['step3']['address']; - $rule['destination']['any'] = TRUE; - $rule['floating'] = "yes"; - $rule['wizard'] = "yes"; - $rule['enabled'] = "on"; - $config['filter']['rule'][] = $rule; - - } elseif( $config['ezshaper']['step3']['provider'] == "Generic" ) { - /* create VOIP rules */ - $rule = array(); - $rule['type'] = "match"; - $rule['descr'] = "DiffServ/Lowdelay/Upload"; - $rule['protocol'] = "udp"; + /* loop through p2plist[] */ + if ($p2p) { + foreach($config['ezshaper']['step5'] as $key => $val) { + if (!is_array($p2plist[$key])) + continue; + foreach ($p2plist[$key] as $p2pclient) { + $rule = array(); + $rule['type'] = "match"; + $rule['interface'] = $interfacelist; + $rule['defaultqueue'] = 'qP2P'; + $rule['source']['any'] = TRUE; + $rule['destination']['any'] = TRUE; + $rule['descr'] = "m_P2P {$p2pclient[0]} outbound"; + $rule['floating'] = "yes"; + $rule['wizard'] = "yes"; + $rule['destination']['port'] = $p2pclient[2]."-".$p2pclient[3]; + if($p2pclient[1] != '') + $rule['protocol'] = $p2pclient[1]; + $config['filter']['rule'][] = $rule; + } + } + } + + /* loop through gamesplist[] */ + if ($games) { + foreach($config['ezshaper']['step6'] as $key => $val) { + if (!is_array($gamesplist[$key])) + continue; + foreach ($gamesplist[$key] as $Gameclient) { + $rule = array(); + $rule['type'] = "match"; + $rule['interface'] = $interfacelist; + $rule['defaultqueue'] = 'qGames'; + if ($Gameclient[1] == "tcp") + $rule['ackqueue'] = 'qACK'; + $rule['source']['any'] = TRUE; + $rule['destination']['any'] = TRUE; + $rule['floating'] = "yes"; + $rule['wizard'] = "yes"; + $rule['enabled'] = "on"; + $rule['descr'] = "m_Game {$Gameclient[0]} outbound"; + $rule['destination']['port'] = $Gameclient[2]."-".$Gameclient[3]; + if($Gameclient[1] != '') + $rule['protocol'] = $Gameclient[1]; + $config['filter']['rule'][] = $rule; + } + } + } + + /* loop through othersplist[] */ + if ($otherpriority) { + foreach($config['ezshaper']['step7'] as $key => $val) { + if (!is_array($othersplist[$key])) + continue; + foreach ($othersplist[$key] as $otherclient) { + $rule = array(); + $rule['type'] = "match"; + $rule['interface'] = $interfacelist; + switch ($val) { + case "H": + $rule['defaultqueue'] = 'qOthersHigh'; /* posted value H or L */ + if ($otherclient[1] == "tcp") + $rule['ackqueue'] = 'qACK'; + $loop = 0; + break; + case "L": + $rule['defaultqueue'] = 'qOthersLow'; /* posted value H or L */ + if ($otherclient[1] == "tcp") + $rule['ackqueue'] = 'qACK'; + $loop = 0; + break; + case "D": + if ($p2pcatchall) { + $loop = 0; + $rule['defaultqueue'] = 'qOthersDefault'; + if ($otherclient[1] == "tcp") + $rule['ackqueue'] = 'qACK'; + } else + $loop = 1; /* It automitaclly goes to default queue */ + break; + default: + $loop = 1; + } + if (!$loop) { $rule['source']['any'] = TRUE; - $rule['defaultqueue'] = "qVoIP"; $rule['destination']['any'] = TRUE; - $rule['iptos'] = "lowdelay"; $rule['floating'] = "yes"; $rule['wizard'] = "yes"; $rule['enabled'] = "on"; - $config['filter']['rule'][] = $rule; - - } else { - /* loop through voiplist[] */ - foreach ($voiplist[$config['ezshaper']['step3']['provider']] as $voip) { - $rule = array(); - $rule['type'] = "match"; - $rule['defaultqueue'] = 'qVoIP'; - $rule['source']['any'] = TRUE; - $rule['destination']['any'] = TRUE; - $rule['descr'] = "m_voip {$voip[0]} outbound"; - $rule['floating'] = "yes"; - $rule['wizard'] = "yes"; - $rule['enabled'] = "on"; - $rule['destination']['port'] = $voip[2]."-".$voip[3]; - if($voip[1] != '') - $rule['protocol'] = $voip[1]; - $config['filter']['rule'][] = $rule; - } - } - } + $rule['descr'] = "m_Other {$otherclient[0]} outbound"; - /* loop through p2plist[] */ - if ($p2p) { - foreach($config['ezshaper']['step5'] as $key => $val) { - if (!is_array($p2plist[$key])) - continue; - foreach ($p2plist[$key] as $p2pclient) { - $rule = array(); - $rule['type'] = "match"; - $rule['defaultqueue'] = 'qP2P'; - $rule['source']['any'] = TRUE; - $rule['destination']['any'] = TRUE; - $rule['descr'] = "m_P2P {$p2pclient[0]} outbound"; - $rule['floating'] = "yes"; - $rule['wizard'] = "yes"; - $rule['destination']['port'] = $p2pclient[2]."-".$p2pclient[3]; - if($p2pclient[1] != '') - $rule['protocol'] = $p2pclient[1]; - $config['filter']['rule'][] = $rule; - } - } - } - - /* loop through gamesplist[] */ - if ($games) { - foreach($config['ezshaper']['step6'] as $key => $val) { - if (!is_array($gamesplist[$key])) - continue; - foreach ($gamesplist[$key] as $Gameclient) { - $rule = array(); - $rule['type'] = "match"; - $rule['defaultqueue'] = 'qGames'; - if ($Gameclient[1] == "tcp") - $rule['ackqueue'] = 'qACK'; - $rule['source']['any'] = TRUE; - $rule['destination']['any'] = TRUE; - $rule['floating'] = "yes"; - $rule['wizard'] = "yes"; - $rule['enabled'] = "on"; - $rule['descr'] = "m_Game {$Gameclient[0]} outbound"; - $rule['destination']['port'] = $Gameclient[2]."-".$Gameclient[3]; - if($Gameclient[1] != '') - $rule['protocol'] = $Gameclient[1]; - $config['filter']['rule'][] = $rule; - } - } - } - - /* loop through othersplist[] */ - if ($otherpriority) { - foreach($config['ezshaper']['step7'] as $key => $val) { - if (!is_array($othersplist[$key])) - continue; - foreach ($othersplist[$key] as $otherclient) { - $rule = array(); - $rule['type'] = "match"; - switch ($val) { - case "H": - $rule['defaultqueue'] = 'qOthersHigh'; /* posted value H or L */ - if ($otherclient[1] == "tcp") - $rule['ackqueue'] = 'qACK'; - $loop = 0; - break; - case "L": - $rule['defaultqueue'] = 'qOthersLow'; /* posted value H or L */ - if ($otherclient[1] == "tcp") - $rule['ackqueue'] = 'qACK'; - $loop = 0; - break; - case "D": - if ($p2pcatchall) { - $loop = 0; - $rule['defaultqueue'] = 'qOthersDefault'; - if ($otherclient[1] == "tcp") - $rule['ackqueue'] = 'qACK'; - } else - $loop = 1; /* It automitaclly goes to default queue */ - break; - default: - $loop = 1; - } - if (!$loop) { - $rule['source']['any'] = TRUE; - $rule['destination']['any'] = TRUE; - $rule['floating'] = "yes"; - $rule['wizard'] = "yes"; - $rule['enabled'] = "on"; - $rule['descr'] = "m_Other {$otherclient[0]} outbound"; - - if($otherclient[2] or $otherclient[3]) { - $rule['destination']['port'] = $otherclient[2]."-".$otherclient[3]; - } - if($otherclient[1] != '') - $rule['protocol'] = $otherclient[1]; - - $config['filter']['rule'][] = $rule; - } + if($otherclient[2] or $otherclient[3]) { + $rule['destination']['port'] = $otherclient[2]."-".$otherclient[3]; } + if($otherclient[1] != '') + $rule['protocol'] = $otherclient[1]; + + $config['filter']['rule'][] = $rule; } } - write_config(); + } + } + write_config(); } function wizard_get_bandwidthtype_scale($type = "b") { diff --git a/usr/local/www/wizards/traffic_shaper_wizard_multi_all.inc b/usr/local/www/wizards/traffic_shaper_wizard_multi_all.inc index 2ff0b84..519364f 100755 --- a/usr/local/www/wizards/traffic_shaper_wizard_multi_all.inc +++ b/usr/local/www/wizards/traffic_shaper_wizard_multi_all.inc @@ -658,13 +658,16 @@ function apply_all_choosen_items() { $altq_list_queues = array(); $steps = intval($config['ezshaper']['step1']['numberofconnections']); - + + $interfacelist = array(); + for ($i = 0; $i < $steps; $i++) { $tmppath = array(); $altq =& new altq_root_queue(); $altq->SetInterface($config['ezshaper']['step2']["conn{$i}interface"]); + $interfacelist[] = $config['ezshaper']['step2']["conn{$i}interface"]; $altq->SetScheduler($config['ezshaper']['step2']["conn{$i}uploadscheduler"]); $altq->SetBandwidth(floatval($config['ezshaper']['step2']["conn{$i}upload"])); $altq->SetBwscale($config['ezshaper']['step2']["conn{$i}uploadspeed"]); @@ -1448,23 +1451,26 @@ function apply_all_choosen_items() { if (!is_array($config['filter']['rule'])) $config['filter']['rule'] = array(); - /* Rules */ - if ($penalty) { - if( is_ipaddr($config['ezshaper']['step4']['address']) || is_alias($config['ezshaper']['step4']['address'])) { - $rule = array(); - $rule['type'] = "match"; - $rule['descr'] = gettext("Penalty Box"); - $rule['source']['any'] = TRUE; - $rule['defaultqueue'] = "qOthersLow"; - $rule['source']['address'] = $config['ezshaper']['step4']['address']; - $rule['destination']['any'] = TRUE; - $rule['floating'] = "yes"; - $rule['wizard'] = "yes"; - $rule['enabled'] = "on"; - $config['filter']['rule'][] = $rule; - - } - } + $interfacelist = implode(",", $interfacelist); + + /* Rules */ + if ($penalty) { + if( is_ipaddr($config['ezshaper']['step4']['address']) || is_alias($config['ezshaper']['step4']['address'])) { + $rule = array(); + $rule['type'] = "match"; + $rule['interface'] = $interfacelist; + $rule['descr'] = gettext("Penalty Box"); + $rule['source']['any'] = TRUE; + $rule['defaultqueue'] = "qOthersLow"; + $rule['source']['address'] = $config['ezshaper']['step4']['address']; + $rule['destination']['any'] = TRUE; + $rule['floating'] = "yes"; + $rule['wizard'] = "yes"; + $rule['enabled'] = "on"; + $config['filter']['rule'][] = $rule; + + } + } /* If user specifies an IP, we don't bother with providers */ if ($voip) { @@ -1472,6 +1478,7 @@ function apply_all_choosen_items() { /* create VOIP rules */ $rule = array(); $rule['type'] = "match"; + $rule['interface'] = $interfacelist; $rule['descr'] = gettext("VOIP Adapter"); $rule['protocol'] = "udp"; $rule['defaultqueue'] = "qVoIP"; @@ -1486,6 +1493,7 @@ function apply_all_choosen_items() { /* create VOIP rules */ $rule = array(); $rule['type'] = "match"; + $rule['interface'] = $interfacelist; $rule['descr'] = "DiffServ/Lowdelay/Upload"; $rule['protocol'] = "udp"; $rule['source']['any'] = TRUE; @@ -1502,6 +1510,7 @@ function apply_all_choosen_items() { foreach ($voiplist[$config['ezshaper']['step3']['provider']] as $voip) { $rule = array(); $rule['type'] = "match"; + $rule['interface'] = $interfacelist; $rule['defaultqueue'] = 'qVoIP'; $rule['source']['any'] = TRUE; $rule['destination']['any'] = TRUE; @@ -1525,6 +1534,7 @@ function apply_all_choosen_items() { foreach ($p2plist[$key] as $p2pclient) { $rule = array(); $rule['type'] = "match"; + $rule['interface'] = $interfacelist; $rule['defaultqueue'] = 'qP2P'; $rule['source']['any'] = TRUE; $rule['destination']['any'] = TRUE; @@ -1547,6 +1557,7 @@ function apply_all_choosen_items() { foreach ($gamesplist[$key] as $Gameclient) { $rule = array(); $rule['type'] = "match"; + $rule['interface'] = $interfacelist; $rule['defaultqueue'] = 'qGames'; if ($Gameclient[1] == "tcp") $rule['ackqueue'] = 'qACK'; @@ -1564,59 +1575,60 @@ function apply_all_choosen_items() { } } - /* loop through othersplist[] */ - if ($otherpriority) { - foreach($config['ezshaper']['step7'] as $key => $val) { - if (!is_array($othersplist[$key])) - continue; - foreach ($othersplist[$key] as $otherclient) { - $rule = array(); - $rule['type'] = "match"; - switch ($val) { - case "H": - $rule['defaultqueue'] = 'qOthersHigh'; /* posted value H or L */ - if ($otherclient[1] == "tcp") + /* loop through othersplist[] */ + if ($otherpriority) { + foreach($config['ezshaper']['step7'] as $key => $val) { + if (!is_array($othersplist[$key])) + continue; + foreach ($othersplist[$key] as $otherclient) { + $rule = array(); + $rule['type'] = "match"; + $rule['interface'] = $interfacelist; + switch ($val) { + case "H": + $rule['defaultqueue'] = 'qOthersHigh'; /* posted value H or L */ + if ($otherclient[1] == "tcp") $rule['ackqueue'] = 'qACK'; - $loop = 0; - break; - case "L": - $rule['defaultqueue'] = 'qOthersLow'; /* posted value H or L */ - if ($otherclient[1] == "tcp") - $rule['ackqueue'] = 'qACK'; - $loop = 0; - break; - case "D": - if ($p2pcatchall) { - $loop = 0; - $rule['defaultqueue'] = 'qOthersDefault'; - if ($otherclient[1] == "tcp") - $rule['ackqueue'] = 'qACK'; - } else - $loop = 1; /* It automitaclly goes to default queue */ - break; - default: - $loop = 1; - } - if (!$loop) { - $rule['source']['any'] = TRUE; - $rule['destination']['any'] = TRUE; - $rule['floating'] = "yes"; - $rule['wizard'] = "yes"; - $rule['enabled'] = "on"; - $rule['descr'] = "m_Other {$otherclient[0]} outbound"; - - if($otherclient[2] or $otherclient[3]) { - $rule['destination']['port'] = $otherclient[2]."-".$otherclient[3]; - } - if($otherclient[1] != '') - $rule['protocol'] = $otherclient[1]; - - $config['filter']['rule'][] = $rule; - } + $loop = 0; + break; + case "L": + $rule['defaultqueue'] = 'qOthersLow'; /* posted value H or L */ + if ($otherclient[1] == "tcp") + $rule['ackqueue'] = 'qACK'; + $loop = 0; + break; + case "D": + if ($p2pcatchall) { + $loop = 0; + $rule['defaultqueue'] = 'qOthersDefault'; + if ($otherclient[1] == "tcp") + $rule['ackqueue'] = 'qACK'; + } else + $loop = 1; /* It automitaclly goes to default queue */ + break; + default: + $loop = 1; + } + if (!$loop) { + $rule['source']['any'] = TRUE; + $rule['destination']['any'] = TRUE; + $rule['floating'] = "yes"; + $rule['wizard'] = "yes"; + $rule['enabled'] = "on"; + $rule['descr'] = "m_Other {$otherclient[0]} outbound"; + + if($otherclient[2] or $otherclient[3]) { + $rule['destination']['port'] = $otherclient[2]."-".$otherclient[3]; } + if($otherclient[1] != '') + $rule['protocol'] = $otherclient[1]; + + $config['filter']['rule'][] = $rule; } } - write_config(); + } + } + write_config(); } function wizard_get_bandwidthtype_scale($type = "b") { diff --git a/usr/local/www/wizards/traffic_shaper_wizard_multi_lan.inc b/usr/local/www/wizards/traffic_shaper_wizard_multi_lan.inc index 7ab8e69..9fdc3a3 100644 --- a/usr/local/www/wizards/traffic_shaper_wizard_multi_lan.inc +++ b/usr/local/www/wizards/traffic_shaper_wizard_multi_lan.inc @@ -451,6 +451,7 @@ function apply_all_choosen_items() { $altq_list_queues = array(); $steps = floatval($config['ezshaper']['step1']['numberofconnections']); + for ($i = 0; $i < $steps; $i++) { $tmppath = array(); @@ -1202,6 +1203,7 @@ function apply_all_choosen_items() { if( is_ipaddr($config['ezshaper']['step4']['address']) || is_alias($config['ezshaper']['step4']['address'])) { $rule = array(); $rule['type'] = "match"; + $rule['interface'] = "wan"; $rule['descr'] = gettext("Penalty Box"); $rule['source']['any'] = TRUE; $rule['defaultqueue'] = "qOthersLow"; @@ -1221,6 +1223,7 @@ function apply_all_choosen_items() { /* create VOIP rules */ $rule = array(); $rule['type'] = "match"; + $rule['interface'] = "wan"; $rule['descr'] = gettext("VOIP Adapter"); $rule['protocol'] = "udp"; $rule['defaultqueue'] = "qVoIP"; @@ -1235,6 +1238,7 @@ function apply_all_choosen_items() { /* create VOIP rules */ $rule = array(); $rule['type'] = "match"; + $rule['interface'] = "wan"; $rule['descr'] = "DiffServ/Lowdelay/Upload"; $rule['protocol'] = "udp"; $rule['source']['any'] = TRUE; @@ -1251,6 +1255,7 @@ function apply_all_choosen_items() { foreach ($voiplist[$config['ezshaper']['step3']['provider']] as $voip) { $rule = array(); $rule['type'] = "match"; + $rule['interface'] = "wan"; $rule['defaultqueue'] = 'qVoIP'; $rule['source']['any'] = TRUE; $rule['destination']['any'] = TRUE; @@ -1274,6 +1279,7 @@ function apply_all_choosen_items() { foreach ($p2plist[$key] as $p2pclient) { $rule = array(); $rule['type'] = "match"; + $rule['interface'] = "wan"; $rule['defaultqueue'] = 'qP2P'; $rule['source']['any'] = TRUE; $rule['destination']['any'] = TRUE; @@ -1296,6 +1302,7 @@ function apply_all_choosen_items() { foreach ($gamesplist[$key] as $Gameclient) { $rule = array(); $rule['type'] = "match"; + $rule['interface'] = "wan"; $rule['defaultqueue'] = 'qGames'; if ($Gameclient[1] == "tcp") $rule['ackqueue'] = 'qACK'; @@ -1321,6 +1328,7 @@ function apply_all_choosen_items() { foreach ($othersplist[$key] as $otherclient) { $rule = array(); $rule['type'] = "match"; + $rule['interface'] = "wan"; switch ($val) { case "H": $rule['defaultqueue'] = 'qOthersHigh'; /* posted value H or L */ |