diff --git a/GPL-LICENSE.txt b/LICENSE-GPL
similarity index 100%
rename from GPL-LICENSE.txt
rename to LICENSE-GPL
diff --git a/LICENSE-MIT b/LICENSE-MIT
new file mode 100644
index 00000000..0cfb790d
--- /dev/null
+++ b/LICENSE-MIT
@@ -0,0 +1,22 @@
+Copyright (c) 2012 Craig Michael Thompson
+
+Permission is hereby granted, free of charge, to any person
+obtaining a copy of this software and associated documentation
+files (the "Software"), to deal in the Software without
+restriction, including without limitation the rights to use,
+copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the
+Software is furnished to do so, subject to the following
+conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
+OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
diff --git a/MIT-LICENSE.txt b/MIT-LICENSE.txt
deleted file mode 100644
index b6527432..00000000
--- a/MIT-LICENSE.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-Copyright (c) 2009-2010 Craig Michael Thompson
-
- Permission is hereby granted, free of charge, to any person obtaining a copy
- of this software and associated documentation files (the "Software"), to deal
- in the Software without restriction, including without limitation the rights
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- copies of the Software, and to permit persons to whom the Software is
- furnished to do so, subject to the following conditions:
-
- The above copyright notice and this permission notice shall be included in
- all copies or substantial portions of the Software.
-
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
- THE SOFTWARE.
\ No newline at end of file
diff --git a/Makefile b/Makefile
deleted file mode 100644
index 40443d7e..00000000
--- a/Makefile
+++ /dev/null
@@ -1,90 +0,0 @@
-SRC_DIR = src
-BUILD_DIR = build
-
-PREFIX = .
-DIST_DIR = ${PREFIX}/dist
-
-PLUGINS = $(shell ls -p ${SRC_DIR} | grep / | xargs)
-PLUGINS_JS = $(if ${PLUGINS},$(shell find ${PLUGINS:%=${SRC_DIR}/%/} -name "*.js" 2> /dev/null),"")
-PLUGINS_CSS = $(if ${PLUGINS},$(shell find ${PLUGINS:%=${SRC_DIR}/%/} -name "*.css" 2> /dev/null),"")
-
-EXTRA_CSS = ${SRC_DIR}/styles.css\
- ${SRC_DIR}/extra.css
-
-JS_MODULES = ${SRC_DIR}/header.txt\
- ${SRC_DIR}/intro.js\
- ${SRC_DIR}/core.js\
- ${PLUGINS_JS}\
- ${SRC_DIR}/outro.js
-
-CSS_MODULES = ${SRC_DIR}/header.txt\
- ${SRC_DIR}/core.css\
- ${PLUGINS_CSS}\
- ${EXTRA_CSS}
-
-QTIP = ${DIST_DIR}/jquery.qtip.js
-QTIP_MIN = ${DIST_DIR}/jquery.qtip.min.js
-QTIP_CSS = ${DIST_DIR}/jquery.qtip.css
-QTIP_CSS_MIN = ${DIST_DIR}/jquery.qtip.min.css
-
-QTIP_VER = `cat version.txt`
-VER = sed s/@VERSION/${QTIP_VER}/
-
-JS_ENGINE = `which node`
-JS_LINT = ${JS_ENGINE} $(BUILD_DIR)/jslint-check.js
-JS_MINIFIER = ${JS_ENGINE} ${BUILD_DIR}/uglify.js
-CSS_MINIFIER = java -Xmx96m -jar ${BUILD_DIR}/yuicompressor.jar
-
-DATE=`git log --pretty=format:'%ad' -1`
-
-all: clean qtip lint css min
- @@printf "\n%s" ${PLUGIN_JS}
- @@printf "qTip2 built successfully!\n\n"
-
-${DIST_DIR}:
- @@mkdir -p ${DIST_DIR}
-
-qtip: ${DIST_DIR} ${JS_MODULES}
- @@mkdir -p ${DIST_DIR}
-
- @@printf "Building qTip2... Success!\n"
- @@printf "\tEnabled plugins: %s\n\n" $(if ${PLUGINS},"${PLUGINS:%/=%}", "None")
-
- @@cat ${JS_MODULES} | \
- sed 's/Date:./&'"${DATE}"'/' | \
- ${VER} > ${QTIP};
-
-css: ${DIST_DIR} ${CSS_MODULES}
- @@printf "Building CSS... "
- @@cat ${CSS_MODULES} | \
- sed 's/Date:./&'"${DATE}"'/' | \
- ${VER} > ${QTIP_CSS};
- @@printf "Success!\n"
-
-min: qtip css
- @@if test ! -z ${JS_ENGINE}; then \
- printf "Minifying JS... "; \
- head -18 ${QTIP} > ${QTIP_MIN}; \
- ${JS_MINIFIER} ${QTIP} > ${QTIP_MIN}.tmp; \
- sed '$ s#^\( \*/\)\(.\+\)#\1\n\2;#' ${QTIP_MIN}.tmp > ${QTIP_MIN}; \
- rm -rf $(QTIP_MIN).tmp; \
- printf "Success!\n"; \
- else \
- printf "You must have NodeJS installed in order to minify qTip JS.\n"; \
- fi
-
- @@printf "Minifying CSS... "
- @@${CSS_MINIFIER} ${QTIP_CSS} --type css -o ${QTIP_CSS_MIN}
- @@printf "Success!\n"
-
-lint: qtip
- @@if test ! -z ${JS_ENGINE}; then \
- printf "Checking against JSLint... "; \
- ${JS_LINT}; \
- else \
- printf "You must have NodeJS installed in order to test qTip against JSLint."; \
- fi
-
-clean:
- @@printf "Removing distribution directory: %s\n\n" ${DIST_DIR}
- @@rm -rf ${DIST_DIR}
diff --git a/README.md b/README.md
index ddf0e57e..1a309a1a 100644
--- a/README.md
+++ b/README.md
@@ -18,7 +18,6 @@ More documentation and information is available at the [official site](http://cr
2+, iOS 4+
-
## Getting qTip2
### Stable releases
@@ -29,11 +28,15 @@ you choose what [plugins](/Craga89/qTip2/tree/master/docs/plugins/) you'd like i
You can grab the latest and greatest qTip2 nightly from the [dist](/Craga89/qTip2/tree/master/dist/) directory and get started quickly. If you want more options
over what plugins are included in your build, take a look below.
+Download the [production version][min] or the [development version][max].
+
+[min]: https://raw.github.com/Craga89/qtip2/master/dist/jquery.qtip.min.js
+[max]: https://raw.github.com/Craga89/qtip2/master/dist/jquery.qtip.js
+
### Custom builds
You can also build your own qTip2 script that includes select [plugins](/Craga89/qTip2/tree/master/docs/plugins/) and [styles](/Craga89/qTip2/tree/master/docs/style.md) to reduce the overall file size and remove features
you don't plan on using. Find more information about this [here](/Craga89/qTip2/tree/master/docs/build.md)
-
## Installation
Now you have the jQuery library and qTip2 files, it's time to include them within your HTML. I **highly recommend** that all JavaScript includes be placed just before the end *</body>*
tag as this ensures the DOM is loaded before manipulation of it occurs. This is not a requirement, simply an insiders tip!
@@ -58,10 +61,15 @@ tag as this ensures the DOM is loaded before manipulation of it occurs. This is
**Note:** Make sure to include either the non-minified *or* the un-minified script, **not both!**
**Note:** Notice *the jQuery library is included ***before** qTip2*. This is absolutely essential for correct functioning of the plugin!
+## Release History
+_(Nothing yet)_
## Questions or problems?
If you have any questions, please feel free to post on the [support forums](http://craigsworks.com/projects/forums), but before you do make sure to
check out the [thorough documentation](/Craga89/qTip2/tree/master/docs/) both here in the repo and on the [official site](http://craigsworks.com/projects/qtip2).
+## License
+Copyright (c) 2012 Craig Michael Thompson. Licensed under the MIT, GPL licenses. *See [here](http://jquery.org/license/) for more details.*
+
## Special thanks
-Big shout-out to the jQuery team for providing the directory structure and base files for the git repo, as well as the base-files for the new NodeJS build system!
\ No newline at end of file
+Big shout-out to ["Cowboy" Ben Alman](https://github.com/cowboy/) for providing the [grunt](https://github.com/cowboy/grunt) build system used by qTip2
\ No newline at end of file
diff --git a/build/js.jar b/build/js.jar
deleted file mode 100644
index a76cc7c6..00000000
Binary files a/build/js.jar and /dev/null differ
diff --git a/build/jslint-check.js b/build/jslint-check.js
deleted file mode 100755
index 6863bbff..00000000
--- a/build/jslint-check.js
+++ /dev/null
@@ -1,34 +0,0 @@
-var JSLINT = require("./lib/jslint").JSLINT,
- src = require("fs").readFileSync("dist/jquery.qtip.js", "utf8");
-
-JSLINT(src, { evil: true, forin: true, maxerr: 100 });
-
-// All of the following are known issues that we think are 'ok'
-// (in contradiction with JSLint) more information here:
-// http://docs.jquery.com/JQuery_Core_Style_Guidelines
-var ok = {
- "Expected an identifier and instead saw 'undefined' (a reserved word).": true,
- "Expected a conditional expression and instead saw an assignment.": true,
- "Expected an identifier and instead saw 'default' (a reserved word).": true,
- "Insecure '.'.": true,
- "Insecure '^'.": true,
- 'Missing "use strict" statement.': true
-};
-
-var e = JSLINT.errors, found = 0, w;
-
-for ( var i = 0; i < e.length; i++ ) {
- w = e[i];
-
- if (w && w.reason && w.evidence && !ok[ w.reason ] ) {
- console.log( "\n" + w.evidence + "\n" );
- console.log( " Problem at line " + w.line + " character " + w.character + ": " + w.reason );
- }
-}
-
-if ( found > 0 ) {
- console.log( "\n" + found + " Error(s) found.\n" );
-
-} else {
- console.log( "Success!\n" );
-}
diff --git a/build/lib/consolidator.js b/build/lib/consolidator.js
deleted file mode 100644
index 623c9106..00000000
--- a/build/lib/consolidator.js
+++ /dev/null
@@ -1,2598 +0,0 @@
-/**
- * @preserve Copyright 2012 Robert Gust-Bardon .
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- *
- * * Redistributions of source code must retain the above
- * copyright notice, this list of conditions and the following
- * disclaimer.
- *
- * * Redistributions in binary form must reproduce the above
- * copyright notice, this list of conditions and the following
- * disclaimer in the documentation and/or other materials
- * provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS" AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
- * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
- * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
- * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
- * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- * SUCH DAMAGE.
- */
-
-/**
- * @fileoverview Enhances UglifyJS with consolidation of null, Boolean, and String values.
- *
Also known as aliasing, this feature has been deprecated in the Closure Compiler since its
- * initial release, where it is unavailable from the CLI. The Closure Compiler allows one to log and
- * influence this process. In contrast, this implementation does not introduce
- * any variable declarations in global code and derives String values from
- * identifier names used as property accessors.
- *
Consolidating literals may worsen the data compression ratio when an encoding
- * transformation is applied. For instance, jQuery 1.7.1 takes 248235 bytes.
- * Building it with
- * UglifyJS v1.2.5 results in 93647 bytes (37.73% of the original) which are
- * then compressed to 33154 bytes (13.36% of the original) using gzip(1). Building it with the same
- * version of UglifyJS 1.2.5 patched with the implementation of consolidation
- * results in 80784 bytes (a decrease of 12863 bytes, i.e. 13.74%, in comparison
- * to the aforementioned 93647 bytes) which are then compressed to 34013 bytes
- * (an increase of 859 bytes, i.e. 2.59%, in comparison to the aforementioned
- * 33154 bytes).
- */
-
-/*global console:false, exports:true, module:false, require:false */
-/*jshint sub:true */
-/**
- * Consolidates null, Boolean, and String values found inside an AST.
- * @param {!TSyntacticCodeUnit} oAbstractSyntaxTree An array-like object
- * representing an AST.
- * @return {!TSyntacticCodeUnit} An array-like object representing an AST with its null, Boolean, and
- * String values consolidated.
- */
-// TODO(user) Consolidation of mathematical values found in numeric literals.
-// TODO(user) Unconsolidation.
-// TODO(user) Consolidation of ECMA-262 6th Edition programs.
-// TODO(user) Rewrite in ECMA-262 6th Edition.
-exports['ast_consolidate'] = function(oAbstractSyntaxTree) {
- 'use strict';
- /*jshint bitwise:true, curly:true, eqeqeq:true, forin:true, immed:true,
- latedef:true, newcap:true, noarge:true, noempty:true, nonew:true,
- onevar:true, plusplus:true, regexp:true, undef:true, strict:true,
- sub:false, trailing:true */
-
- var _,
- /**
- * A record consisting of data about one or more source elements.
- * @constructor
- * @nosideeffects
- */
- TSourceElementsData = function() {
- /**
- * The category of the elements.
- * @type {number}
- * @see ESourceElementCategories
- */
- this.nCategory = ESourceElementCategories.N_OTHER;
- /**
- * The number of occurrences (within the elements) of each primitive
- * value that could be consolidated.
- * @type {!Array.>}
- */
- this.aCount = [];
- this.aCount[EPrimaryExpressionCategories.N_IDENTIFIER_NAMES] = {};
- this.aCount[EPrimaryExpressionCategories.N_STRING_LITERALS] = {};
- this.aCount[EPrimaryExpressionCategories.N_NULL_AND_BOOLEAN_LITERALS] =
- {};
- /**
- * Identifier names found within the elements.
- * @type {!Array.}
- */
- this.aIdentifiers = [];
- /**
- * Prefixed representation Strings of each primitive value that could be
- * consolidated within the elements.
- * @type {!Array.}
- */
- this.aPrimitiveValues = [];
- },
- /**
- * A record consisting of data about a primitive value that could be
- * consolidated.
- * @constructor
- * @nosideeffects
- */
- TPrimitiveValue = function() {
- /**
- * The difference in the number of terminal symbols between the original
- * source text and the one with the primitive value consolidated. If the
- * difference is positive, the primitive value is considered worthwhile.
- * @type {number}
- */
- this.nSaving = 0;
- /**
- * An identifier name of the variable that will be declared and assigned
- * the primitive value if the primitive value is consolidated.
- * @type {string}
- */
- this.sName = '';
- },
- /**
- * A record consisting of data on what to consolidate within the range of
- * source elements that is currently being considered.
- * @constructor
- * @nosideeffects
- */
- TSolution = function() {
- /**
- * An object whose keys are prefixed representation Strings of each
- * primitive value that could be consolidated within the elements and
- * whose values are corresponding data about those primitive values.
- * @type {!Object.}
- * @see TPrimitiveValue
- */
- this.oPrimitiveValues = {};
- /**
- * The difference in the number of terminal symbols between the original
- * source text and the one with all the worthwhile primitive values
- * consolidated.
- * @type {number}
- * @see TPrimitiveValue#nSaving
- */
- this.nSavings = 0;
- },
- /**
- * The processor of ASTs found
- * in UglifyJS.
- * @namespace
- * @type {!TProcessor}
- */
- oProcessor = (/** @type {!TProcessor} */ require('./process')),
- /**
- * A record consisting of a number of constants that represent the
- * difference in the number of terminal symbols between a source text with
- * a modified syntactic code unit and the original one.
- * @namespace
- * @type {!Object.}
- */
- oWeights = {
- /**
- * The difference in the number of punctuators required by the bracket
- * notation and the dot notation.
- *
'[]'.length - '.'.length
- * @const
- * @type {number}
- */
- N_PROPERTY_ACCESSOR: 1,
- /**
- * The number of punctuators required by a variable declaration with an
- * initialiser.
- *
':'.length + ';'.length
- * @const
- * @type {number}
- */
- N_VARIABLE_DECLARATION: 2,
- /**
- * The number of terminal symbols required to introduce a variable
- * statement (excluding its variable declaration list).
- *
'var '.length
- * @const
- * @type {number}
- */
- N_VARIABLE_STATEMENT_AFFIXATION: 4,
- /**
- * The number of terminal symbols needed to enclose source elements
- * within a function call with no argument values to a function with an
- * empty parameter list.
- *
'(function(){}());'.length
- * @const
- * @type {number}
- */
- N_CLOSURE: 17
- },
- /**
- * Categories of primary expressions from which primitive values that
- * could be consolidated are derivable.
- * @namespace
- * @enum {number}
- */
- EPrimaryExpressionCategories = {
- /**
- * Identifier names used as property accessors.
- * @type {number}
- */
- N_IDENTIFIER_NAMES: 0,
- /**
- * String literals.
- * @type {number}
- */
- N_STRING_LITERALS: 1,
- /**
- * Null and Boolean literals.
- * @type {number}
- */
- N_NULL_AND_BOOLEAN_LITERALS: 2
- },
- /**
- * Prefixes of primitive values that could be consolidated.
- * The String values of the prefixes must have same number of characters.
- * The prefixes must not be used in any properties defined in any version
- * of ECMA-262.
- * @namespace
- * @enum {string}
- */
- EValuePrefixes = {
- /**
- * Identifies String values.
- * @type {string}
- */
- S_STRING: '#S',
- /**
- * Identifies null and Boolean values.
- * @type {string}
- */
- S_SYMBOLIC: '#O'
- },
- /**
- * Categories of source elements in terms of their appropriateness of
- * having their primitive values consolidated.
- * @namespace
- * @enum {number}
- */
- ESourceElementCategories = {
- /**
- * Identifies a source element that includes the {@code with} statement.
- * @type {number}
- */
- N_WITH: 0,
- /**
- * Identifies a source element that includes the {@code eval} identifier name.
- * @type {number}
- */
- N_EVAL: 1,
- /**
- * Identifies a source element that must be excluded from the process
- * unless its whole scope is examined.
- * @type {number}
- */
- N_EXCLUDABLE: 2,
- /**
- * Identifies source elements not posing any problems.
- * @type {number}
- */
- N_OTHER: 3
- },
- /**
- * The list of literals (other than the String ones) whose primitive
- * values can be consolidated.
- * @const
- * @type {!Array.}
- */
- A_OTHER_SUBSTITUTABLE_LITERALS = [
- 'null', // The null literal.
- 'false', // The Boolean literal {@code false}.
- 'true' // The Boolean literal {@code true}.
- ];
-
- (/**
- * Consolidates all worthwhile primitive values in a syntactic code unit.
- * @param {!TSyntacticCodeUnit} oSyntacticCodeUnit An array-like object
- * representing the branch of the abstract syntax tree representing the
- * syntactic code unit along with its scope.
- * @see TPrimitiveValue#nSaving
- */
- function fExamineSyntacticCodeUnit(oSyntacticCodeUnit) {
- var _,
- /**
- * Indicates whether the syntactic code unit represents global code.
- * @type {boolean}
- */
- bIsGlobal = 'toplevel' === oSyntacticCodeUnit[0],
- /**
- * Indicates whether the whole scope is being examined.
- * @type {boolean}
- */
- bIsWhollyExaminable = !bIsGlobal,
- /**
- * An array-like object representing source elements that constitute a
- * syntactic code unit.
- * @type {!TSyntacticCodeUnit}
- */
- oSourceElements,
- /**
- * A record consisting of data about the source element that is
- * currently being examined.
- * @type {!TSourceElementsData}
- */
- oSourceElementData,
- /**
- * The scope of the syntactic code unit.
- * @type {!TScope}
- */
- oScope,
- /**
- * An instance of an object that allows the traversal of an AST.
- * @type {!TWalker}
- */
- oWalker,
- /**
- * An object encompassing collections of functions used during the
- * traversal of an AST.
- * @namespace
- * @type {!Object.>}
- */
- oWalkers = {
- /**
- * A collection of functions used during the surveyance of source
- * elements.
- * @namespace
- * @type {!Object.}
- */
- oSurveySourceElement: {
- /**#nocode+*/ // JsDoc Toolkit 2.4.0 hides some of the keys.
- /**
- * Classifies the source element as excludable if it does not
- * contain a {@code with} statement or the {@code eval} identifier
- * name. Adds the identifier of the function and its formal
- * parameters to the list of identifier names found.
- * @param {string} sIdentifier The identifier of the function.
- * @param {!Array.} aFormalParameterList Formal parameters.
- * @param {!TSyntacticCodeUnit} oFunctionBody Function code.
- */
- 'defun': function(
- sIdentifier,
- aFormalParameterList,
- oFunctionBody) {
- fClassifyAsExcludable();
- fAddIdentifier(sIdentifier);
- aFormalParameterList.forEach(fAddIdentifier);
- },
- /**
- * Increments the count of the number of occurrences of the String
- * value that is equivalent to the sequence of terminal symbols
- * that constitute the encountered identifier name.
- * @param {!TSyntacticCodeUnit} oExpression The nonterminal
- * MemberExpression.
- * @param {string} sIdentifierName The identifier name used as the
- * property accessor.
- * @return {!Array} The encountered branch of an AST with its nonterminal
- * MemberExpression traversed.
- */
- 'dot': function(oExpression, sIdentifierName) {
- fCountPrimaryExpression(
- EPrimaryExpressionCategories.N_IDENTIFIER_NAMES,
- EValuePrefixes.S_STRING + sIdentifierName);
- return ['dot', oWalker.walk(oExpression), sIdentifierName];
- },
- /**
- * Adds the optional identifier of the function and its formal
- * parameters to the list of identifier names found.
- * @param {?string} sIdentifier The optional identifier of the
- * function.
- * @param {!Array.} aFormalParameterList Formal parameters.
- * @param {!TSyntacticCodeUnit} oFunctionBody Function code.
- */
- 'function': function(
- sIdentifier,
- aFormalParameterList,
- oFunctionBody) {
- if ('string' === typeof sIdentifier) {
- fAddIdentifier(sIdentifier);
- }
- aFormalParameterList.forEach(fAddIdentifier);
- },
- /**
- * Either increments the count of the number of occurrences of the
- * encountered null or Boolean value or classifies a source element
- * as containing the {@code eval} identifier name.
- * @param {string} sIdentifier The identifier encountered.
- */
- 'name': function(sIdentifier) {
- if (-1 !== A_OTHER_SUBSTITUTABLE_LITERALS.indexOf(sIdentifier)) {
- fCountPrimaryExpression(
- EPrimaryExpressionCategories.N_NULL_AND_BOOLEAN_LITERALS,
- EValuePrefixes.S_SYMBOLIC + sIdentifier);
- } else {
- if ('eval' === sIdentifier) {
- oSourceElementData.nCategory =
- ESourceElementCategories.N_EVAL;
- }
- fAddIdentifier(sIdentifier);
- }
- },
- /**
- * Classifies the source element as excludable if it does not
- * contain a {@code with} statement or the {@code eval} identifier
- * name.
- * @param {TSyntacticCodeUnit} oExpression The expression whose
- * value is to be returned.
- */
- 'return': function(oExpression) {
- fClassifyAsExcludable();
- },
- /**
- * Increments the count of the number of occurrences of the
- * encountered String value.
- * @param {string} sStringValue The String value of the string
- * literal encountered.
- */
- 'string': function(sStringValue) {
- if (sStringValue.length > 0) {
- fCountPrimaryExpression(
- EPrimaryExpressionCategories.N_STRING_LITERALS,
- EValuePrefixes.S_STRING + sStringValue);
- }
- },
- /**
- * Adds the identifier reserved for an exception to the list of
- * identifier names found.
- * @param {!TSyntacticCodeUnit} oTry A block of code in which an
- * exception can occur.
- * @param {Array} aCatch The identifier reserved for an exception
- * and a block of code to handle the exception.
- * @param {TSyntacticCodeUnit} oFinally An optional block of code
- * to be evaluated regardless of whether an exception occurs.
- */
- 'try': function(oTry, aCatch, oFinally) {
- if (Array.isArray(aCatch)) {
- fAddIdentifier(aCatch[0]);
- }
- },
- /**
- * Classifies the source element as excludable if it does not
- * contain a {@code with} statement or the {@code eval} identifier
- * name. Adds the identifier of each declared variable to the list
- * of identifier names found.
- * @param {!Array.} aVariableDeclarationList Variable
- * declarations.
- */
- 'var': function(aVariableDeclarationList) {
- fClassifyAsExcludable();
- aVariableDeclarationList.forEach(fAddVariable);
- },
- /**
- * Classifies a source element as containing the {@code with}
- * statement.
- * @param {!TSyntacticCodeUnit} oExpression An expression whose
- * value is to be converted to a value of type Object and
- * become the binding object of a new object environment
- * record of a new lexical environment in which the statement
- * is to be executed.
- * @param {!TSyntacticCodeUnit} oStatement The statement to be
- * executed in the augmented lexical environment.
- * @return {!Array} An empty array to stop the traversal.
- */
- 'with': function(oExpression, oStatement) {
- oSourceElementData.nCategory = ESourceElementCategories.N_WITH;
- return [];
- }
- /**#nocode-*/ // JsDoc Toolkit 2.4.0 hides some of the keys.
- },
- /**
- * A collection of functions used while looking for nested functions.
- * @namespace
- * @type {!Object.}
- */
- oExamineFunctions: {
- /**#nocode+*/ // JsDoc Toolkit 2.4.0 hides some of the keys.
- /**
- * Orders an examination of a nested function declaration.
- * @this {!TSyntacticCodeUnit} An array-like object representing
- * the branch of an AST representing the syntactic code unit along with
- * its scope.
- * @return {!Array} An empty array to stop the traversal.
- */
- 'defun': function() {
- fExamineSyntacticCodeUnit(this);
- return [];
- },
- /**
- * Orders an examination of a nested function expression.
- * @this {!TSyntacticCodeUnit} An array-like object representing
- * the branch of an AST representing the syntactic code unit along with
- * its scope.
- * @return {!Array} An empty array to stop the traversal.
- */
- 'function': function() {
- fExamineSyntacticCodeUnit(this);
- return [];
- }
- /**#nocode-*/ // JsDoc Toolkit 2.4.0 hides some of the keys.
- }
- },
- /**
- * Records containing data about source elements.
- * @type {Array.}
- */
- aSourceElementsData = [],
- /**
- * The index (in the source text order) of the source element
- * immediately following a Directive Prologue.
- * @type {number}
- */
- nAfterDirectivePrologue = 0,
- /**
- * The index (in the source text order) of the source element that is
- * currently being considered.
- * @type {number}
- */
- nPosition,
- /**
- * The index (in the source text order) of the source element that is
- * the last element of the range of source elements that is currently
- * being considered.
- * @type {(undefined|number)}
- */
- nTo,
- /**
- * Initiates the traversal of a source element.
- * @param {!TWalker} oWalker An instance of an object that allows the
- * traversal of an abstract syntax tree.
- * @param {!TSyntacticCodeUnit} oSourceElement A source element from
- * which the traversal should commence.
- * @return {function(): !TSyntacticCodeUnit} A function that is able to
- * initiate the traversal from a given source element.
- */
- cContext = function(oWalker, oSourceElement) {
- /**
- * @return {!TSyntacticCodeUnit} A function that is able to
- * initiate the traversal from a given source element.
- */
- var fLambda = function() {
- return oWalker.walk(oSourceElement);
- };
-
- return fLambda;
- },
- /**
- * Classifies the source element as excludable if it does not
- * contain a {@code with} statement or the {@code eval} identifier
- * name.
- */
- fClassifyAsExcludable = function() {
- if (oSourceElementData.nCategory ===
- ESourceElementCategories.N_OTHER) {
- oSourceElementData.nCategory =
- ESourceElementCategories.N_EXCLUDABLE;
- }
- },
- /**
- * Adds an identifier to the list of identifier names found.
- * @param {string} sIdentifier The identifier to be added.
- */
- fAddIdentifier = function(sIdentifier) {
- if (-1 === oSourceElementData.aIdentifiers.indexOf(sIdentifier)) {
- oSourceElementData.aIdentifiers.push(sIdentifier);
- }
- },
- /**
- * Adds the identifier of a variable to the list of identifier names
- * found.
- * @param {!Array} aVariableDeclaration A variable declaration.
- */
- fAddVariable = function(aVariableDeclaration) {
- fAddIdentifier(/** @type {string} */ aVariableDeclaration[0]);
- },
- /**
- * Increments the count of the number of occurrences of the prefixed
- * String representation attributed to the primary expression.
- * @param {number} nCategory The category of the primary expression.
- * @param {string} sName The prefixed String representation attributed
- * to the primary expression.
- */
- fCountPrimaryExpression = function(nCategory, sName) {
- if (!oSourceElementData.aCount[nCategory].hasOwnProperty(sName)) {
- oSourceElementData.aCount[nCategory][sName] = 0;
- if (-1 === oSourceElementData.aPrimitiveValues.indexOf(sName)) {
- oSourceElementData.aPrimitiveValues.push(sName);
- }
- }
- oSourceElementData.aCount[nCategory][sName] += 1;
- },
- /**
- * Consolidates all worthwhile primitive values in a range of source
- * elements.
- * @param {number} nFrom The index (in the source text order) of the
- * source element that is the first element of the range.
- * @param {number} nTo The index (in the source text order) of the
- * source element that is the last element of the range.
- * @param {boolean} bEnclose Indicates whether the range should be
- * enclosed within a function call with no argument values to a
- * function with an empty parameter list if any primitive values
- * are consolidated.
- * @see TPrimitiveValue#nSaving
- */
- fExamineSourceElements = function(nFrom, nTo, bEnclose) {
- var _,
- /**
- * The index of the last mangled name.
- * @type {number}
- */
- nIndex = oScope.cname,
- /**
- * The index of the source element that is currently being
- * considered.
- * @type {number}
- */
- nPosition,
- /**
- * A collection of functions used during the consolidation of
- * primitive values and identifier names used as property
- * accessors.
- * @namespace
- * @type {!Object.}
- */
- oWalkersTransformers = {
- /**
- * If the String value that is equivalent to the sequence of
- * terminal symbols that constitute the encountered identifier
- * name is worthwhile, a syntactic conversion from the dot
- * notation to the bracket notation ensues with that sequence
- * being substituted by an identifier name to which the value
- * is assigned.
- * Applies to property accessors that use the dot notation.
- * @param {!TSyntacticCodeUnit} oExpression The nonterminal
- * MemberExpression.
- * @param {string} sIdentifierName The identifier name used as
- * the property accessor.
- * @return {!Array} A syntactic code unit that is equivalent to
- * the one encountered.
- * @see TPrimitiveValue#nSaving
- */
- 'dot': function(oExpression, sIdentifierName) {
- /**
- * The prefixed String value that is equivalent to the
- * sequence of terminal symbols that constitute the
- * encountered identifier name.
- * @type {string}
- */
- var sPrefixed = EValuePrefixes.S_STRING + sIdentifierName;
-
- return oSolutionBest.oPrimitiveValues.hasOwnProperty(
- sPrefixed) &&
- oSolutionBest.oPrimitiveValues[sPrefixed].nSaving > 0 ?
- ['sub',
- oWalker.walk(oExpression),
- ['name',
- oSolutionBest.oPrimitiveValues[sPrefixed].sName]] :
- ['dot', oWalker.walk(oExpression), sIdentifierName];
- },
- /**
- * If the encountered identifier is a null or Boolean literal
- * and its value is worthwhile, the identifier is substituted
- * by an identifier name to which that value is assigned.
- * Applies to identifier names.
- * @param {string} sIdentifier The identifier encountered.
- * @return {!Array} A syntactic code unit that is equivalent to
- * the one encountered.
- * @see TPrimitiveValue#nSaving
- */
- 'name': function(sIdentifier) {
- /**
- * The prefixed representation String of the identifier.
- * @type {string}
- */
- var sPrefixed = EValuePrefixes.S_SYMBOLIC + sIdentifier;
-
- return [
- 'name',
- oSolutionBest.oPrimitiveValues.hasOwnProperty(sPrefixed) &&
- oSolutionBest.oPrimitiveValues[sPrefixed].nSaving > 0 ?
- oSolutionBest.oPrimitiveValues[sPrefixed].sName :
- sIdentifier
- ];
- },
- /**
- * If the encountered String value is worthwhile, it is
- * substituted by an identifier name to which that value is
- * assigned.
- * Applies to String values.
- * @param {string} sStringValue The String value of the string
- * literal encountered.
- * @return {!Array} A syntactic code unit that is equivalent to
- * the one encountered.
- * @see TPrimitiveValue#nSaving
- */
- 'string': function(sStringValue) {
- /**
- * The prefixed representation String of the primitive value
- * of the literal.
- * @type {string}
- */
- var sPrefixed =
- EValuePrefixes.S_STRING + sStringValue;
-
- return oSolutionBest.oPrimitiveValues.hasOwnProperty(
- sPrefixed) &&
- oSolutionBest.oPrimitiveValues[sPrefixed].nSaving > 0 ?
- ['name',
- oSolutionBest.oPrimitiveValues[sPrefixed].sName] :
- ['string', sStringValue];
- }
- },
- /**
- * Such data on what to consolidate within the range of source
- * elements that is currently being considered that lead to the
- * greatest known reduction of the number of the terminal symbols
- * in comparison to the original source text.
- * @type {!TSolution}
- */
- oSolutionBest = new TSolution(),
- /**
- * Data representing an ongoing attempt to find a better
- * reduction of the number of the terminal symbols in comparison
- * to the original source text than the best one that is
- * currently known.
- * @type {!TSolution}
- * @see oSolutionBest
- */
- oSolutionCandidate = new TSolution(),
- /**
- * A record consisting of data about the range of source elements
- * that is currently being examined.
- * @type {!TSourceElementsData}
- */
- oSourceElementsData = new TSourceElementsData(),
- /**
- * Variable declarations for each primitive value that is to be
- * consolidated within the elements.
- * @type {!Array.}
- */
- aVariableDeclarations = [],
- /**
- * Augments a list with a prefixed representation String.
- * @param {!Array.} aList A list that is to be augmented.
- * @return {function(string)} A function that augments a list
- * with a prefixed representation String.
- */
- cAugmentList = function(aList) {
- /**
- * @param {string} sPrefixed Prefixed representation String of
- * a primitive value that could be consolidated within the
- * elements.
- */
- var fLambda = function(sPrefixed) {
- if (-1 === aList.indexOf(sPrefixed)) {
- aList.push(sPrefixed);
- }
- };
-
- return fLambda;
- },
- /**
- * Adds the number of occurrences of a primitive value of a given
- * category that could be consolidated in the source element with
- * a given index to the count of occurrences of that primitive
- * value within the range of source elements that is currently
- * being considered.
- * @param {number} nPosition The index (in the source text order)
- * of a source element.
- * @param {number} nCategory The category of the primary
- * expression from which the primitive value is derived.
- * @return {function(string)} A function that performs the
- * addition.
- * @see cAddOccurrencesInCategory
- */
- cAddOccurrences = function(nPosition, nCategory) {
- /**
- * @param {string} sPrefixed The prefixed representation String
- * of a primitive value.
- */
- var fLambda = function(sPrefixed) {
- if (!oSourceElementsData.aCount[nCategory].hasOwnProperty(
- sPrefixed)) {
- oSourceElementsData.aCount[nCategory][sPrefixed] = 0;
- }
- oSourceElementsData.aCount[nCategory][sPrefixed] +=
- aSourceElementsData[nPosition].aCount[nCategory][
- sPrefixed];
- };
-
- return fLambda;
- },
- /**
- * Adds the number of occurrences of each primitive value of a
- * given category that could be consolidated in the source
- * element with a given index to the count of occurrences of that
- * primitive values within the range of source elements that is
- * currently being considered.
- * @param {number} nPosition The index (in the source text order)
- * of a source element.
- * @return {function(number)} A function that performs the
- * addition.
- * @see fAddOccurrences
- */
- cAddOccurrencesInCategory = function(nPosition) {
- /**
- * @param {number} nCategory The category of the primary
- * expression from which the primitive value is derived.
- */
- var fLambda = function(nCategory) {
- Object.keys(
- aSourceElementsData[nPosition].aCount[nCategory]
- ).forEach(cAddOccurrences(nPosition, nCategory));
- };
-
- return fLambda;
- },
- /**
- * Adds the number of occurrences of each primitive value that
- * could be consolidated in the source element with a given index
- * to the count of occurrences of that primitive values within
- * the range of source elements that is currently being
- * considered.
- * @param {number} nPosition The index (in the source text order)
- * of a source element.
- */
- fAddOccurrences = function(nPosition) {
- Object.keys(aSourceElementsData[nPosition].aCount).forEach(
- cAddOccurrencesInCategory(nPosition));
- },
- /**
- * Creates a variable declaration for a primitive value if that
- * primitive value is to be consolidated within the elements.
- * @param {string} sPrefixed Prefixed representation String of a
- * primitive value that could be consolidated within the
- * elements.
- * @see aVariableDeclarations
- */
- cAugmentVariableDeclarations = function(sPrefixed) {
- if (oSolutionBest.oPrimitiveValues[sPrefixed].nSaving > 0) {
- aVariableDeclarations.push([
- oSolutionBest.oPrimitiveValues[sPrefixed].sName,
- [0 === sPrefixed.indexOf(EValuePrefixes.S_SYMBOLIC) ?
- 'name' : 'string',
- sPrefixed.substring(EValuePrefixes.S_SYMBOLIC.length)]
- ]);
- }
- },
- /**
- * Sorts primitive values with regard to the difference in the
- * number of terminal symbols between the original source text
- * and the one with those primitive values consolidated.
- * @param {string} sPrefixed0 The prefixed representation String
- * of the first of the two primitive values that are being
- * compared.
- * @param {string} sPrefixed1 The prefixed representation String
- * of the second of the two primitive values that are being
- * compared.
- * @return {number}
- *
- *
-1
- *
if the first primitive value must be placed before
- * the other one,
- *
0
- *
if the first primitive value may be placed before
- * the other one,
- *
1
- *
if the first primitive value must not be placed
- * before the other one.
');
- detail('Unused', f.unused);
- detail('Closure', f.closure);
- detail('Variable', f['var']);
- detail('Exception', f.exception);
- detail('Outer', f.outer);
- detail('Global', f.global);
- detail('Label', f.label);
- }
-
- if (data.member) {
- a = to_array(data.member);
- if (a.length) {
- a = a.sort();
- m = '
/*members ';
- l = 10;
- for (i = 0; i < a.length; i += 1) {
- k = a[i];
- n = k.name();
- if (l + n.length > 72) {
- o.push(m + ' ');
- m = ' ';
- l = 1;
- }
- l += n.length + 2;
- if (data.member[k] === 1) {
- n = '' + n + '';
- }
- if (i < a.length - 1) {
- n += ', ';
- }
- m += n;
- }
- o.push(m + ' */
');
- }
- o.push('
');
- }
- }
- return o.join('');
- };
- itself.jslint = itself;
-
- itself.edition = '2010-02-20';
-
- if (typeof exports !== "undefined") {
- exports.JSLINT = itself;
- }
-
- return itself;
-
-}());
diff --git a/build/lib/parse-js.js b/build/lib/parse-js.js
deleted file mode 100644
index d966eb06..00000000
--- a/build/lib/parse-js.js
+++ /dev/null
@@ -1,1355 +0,0 @@
-/***********************************************************************
-
- A JavaScript tokenizer / parser / beautifier / compressor.
-
- This version is suitable for Node.js. With minimal changes (the
- exports stuff) it should work on any JS platform.
-
- This file contains the tokenizer/parser. It is a port to JavaScript
- of parse-js [1], a JavaScript parser library written in Common Lisp
- by Marijn Haverbeke. Thank you Marijn!
-
- [1] http://marijn.haverbeke.nl/parse-js/
-
- Exported functions:
-
- - tokenizer(code) -- returns a function. Call the returned
- function to fetch the next token.
-
- - parse(code) -- returns an AST of the given JavaScript code.
-
- -------------------------------- (C) ---------------------------------
-
- Author: Mihai Bazon
-
- http://mihai.bazon.net/blog
-
- Distributed under the BSD license:
-
- Copyright 2010 (c) Mihai Bazon
- Based on parse-js (http://marijn.haverbeke.nl/parse-js/).
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions
- are met:
-
- * Redistributions of source code must retain the above
- copyright notice, this list of conditions and the following
- disclaimer.
-
- * Redistributions in binary form must reproduce the above
- copyright notice, this list of conditions and the following
- disclaimer in the documentation and/or other materials
- provided with the distribution.
-
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
- EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
- OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
- TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
- THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- SUCH DAMAGE.
-
- ***********************************************************************/
-
-/* -----[ Tokenizer (constants) ]----- */
-
-var KEYWORDS = array_to_hash([
- "break",
- "case",
- "catch",
- "const",
- "continue",
- "debugger",
- "default",
- "delete",
- "do",
- "else",
- "finally",
- "for",
- "function",
- "if",
- "in",
- "instanceof",
- "new",
- "return",
- "switch",
- "throw",
- "try",
- "typeof",
- "var",
- "void",
- "while",
- "with"
-]);
-
-var RESERVED_WORDS = array_to_hash([
- "abstract",
- "boolean",
- "byte",
- "char",
- "class",
- "double",
- "enum",
- "export",
- "extends",
- "final",
- "float",
- "goto",
- "implements",
- "import",
- "int",
- "interface",
- "long",
- "native",
- "package",
- "private",
- "protected",
- "public",
- "short",
- "static",
- "super",
- "synchronized",
- "throws",
- "transient",
- "volatile"
-]);
-
-var KEYWORDS_BEFORE_EXPRESSION = array_to_hash([
- "return",
- "new",
- "delete",
- "throw",
- "else",
- "case"
-]);
-
-var KEYWORDS_ATOM = array_to_hash([
- "false",
- "null",
- "true",
- "undefined"
-]);
-
-var OPERATOR_CHARS = array_to_hash(characters("+-*&%=<>!?|~^"));
-
-var RE_HEX_NUMBER = /^0x[0-9a-f]+$/i;
-var RE_OCT_NUMBER = /^0[0-7]+$/;
-var RE_DEC_NUMBER = /^\d*\.?\d*(?:e[+-]?\d*(?:\d\.?|\.?\d)\d*)?$/i;
-
-var OPERATORS = array_to_hash([
- "in",
- "instanceof",
- "typeof",
- "new",
- "void",
- "delete",
- "++",
- "--",
- "+",
- "-",
- "!",
- "~",
- "&",
- "|",
- "^",
- "*",
- "/",
- "%",
- ">>",
- "<<",
- ">>>",
- "<",
- ">",
- "<=",
- ">=",
- "==",
- "===",
- "!=",
- "!==",
- "?",
- "=",
- "+=",
- "-=",
- "/=",
- "*=",
- "%=",
- ">>=",
- "<<=",
- ">>>=",
- "|=",
- "^=",
- "&=",
- "&&",
- "||"
-]);
-
-var WHITESPACE_CHARS = array_to_hash(characters(" \u00a0\n\r\t\f\u000b\u200b\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000"));
-
-var PUNC_BEFORE_EXPRESSION = array_to_hash(characters("[{(,.;:"));
-
-var PUNC_CHARS = array_to_hash(characters("[]{}(),;:"));
-
-var REGEXP_MODIFIERS = array_to_hash(characters("gmsiy"));
-
-/* -----[ Tokenizer ]----- */
-
-// regexps adapted from http://xregexp.com/plugins/#unicode
-var UNICODE = {
- letter: new RegExp("[\\u0041-\\u005A\\u0061-\\u007A\\u00AA\\u00B5\\u00BA\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02C1\\u02C6-\\u02D1\\u02E0-\\u02E4\\u02EC\\u02EE\\u0370-\\u0374\\u0376\\u0377\\u037A-\\u037D\\u0386\\u0388-\\u038A\\u038C\\u038E-\\u03A1\\u03A3-\\u03F5\\u03F7-\\u0481\\u048A-\\u0523\\u0531-\\u0556\\u0559\\u0561-\\u0587\\u05D0-\\u05EA\\u05F0-\\u05F2\\u0621-\\u064A\\u066E\\u066F\\u0671-\\u06D3\\u06D5\\u06E5\\u06E6\\u06EE\\u06EF\\u06FA-\\u06FC\\u06FF\\u0710\\u0712-\\u072F\\u074D-\\u07A5\\u07B1\\u07CA-\\u07EA\\u07F4\\u07F5\\u07FA\\u0904-\\u0939\\u093D\\u0950\\u0958-\\u0961\\u0971\\u0972\\u097B-\\u097F\\u0985-\\u098C\\u098F\\u0990\\u0993-\\u09A8\\u09AA-\\u09B0\\u09B2\\u09B6-\\u09B9\\u09BD\\u09CE\\u09DC\\u09DD\\u09DF-\\u09E1\\u09F0\\u09F1\\u0A05-\\u0A0A\\u0A0F\\u0A10\\u0A13-\\u0A28\\u0A2A-\\u0A30\\u0A32\\u0A33\\u0A35\\u0A36\\u0A38\\u0A39\\u0A59-\\u0A5C\\u0A5E\\u0A72-\\u0A74\\u0A85-\\u0A8D\\u0A8F-\\u0A91\\u0A93-\\u0AA8\\u0AAA-\\u0AB0\\u0AB2\\u0AB3\\u0AB5-\\u0AB9\\u0ABD\\u0AD0\\u0AE0\\u0AE1\\u0B05-\\u0B0C\\u0B0F\\u0B10\\u0B13-\\u0B28\\u0B2A-\\u0B30\\u0B32\\u0B33\\u0B35-\\u0B39\\u0B3D\\u0B5C\\u0B5D\\u0B5F-\\u0B61\\u0B71\\u0B83\\u0B85-\\u0B8A\\u0B8E-\\u0B90\\u0B92-\\u0B95\\u0B99\\u0B9A\\u0B9C\\u0B9E\\u0B9F\\u0BA3\\u0BA4\\u0BA8-\\u0BAA\\u0BAE-\\u0BB9\\u0BD0\\u0C05-\\u0C0C\\u0C0E-\\u0C10\\u0C12-\\u0C28\\u0C2A-\\u0C33\\u0C35-\\u0C39\\u0C3D\\u0C58\\u0C59\\u0C60\\u0C61\\u0C85-\\u0C8C\\u0C8E-\\u0C90\\u0C92-\\u0CA8\\u0CAA-\\u0CB3\\u0CB5-\\u0CB9\\u0CBD\\u0CDE\\u0CE0\\u0CE1\\u0D05-\\u0D0C\\u0D0E-\\u0D10\\u0D12-\\u0D28\\u0D2A-\\u0D39\\u0D3D\\u0D60\\u0D61\\u0D7A-\\u0D7F\\u0D85-\\u0D96\\u0D9A-\\u0DB1\\u0DB3-\\u0DBB\\u0DBD\\u0DC0-\\u0DC6\\u0E01-\\u0E30\\u0E32\\u0E33\\u0E40-\\u0E46\\u0E81\\u0E82\\u0E84\\u0E87\\u0E88\\u0E8A\\u0E8D\\u0E94-\\u0E97\\u0E99-\\u0E9F\\u0EA1-\\u0EA3\\u0EA5\\u0EA7\\u0EAA\\u0EAB\\u0EAD-\\u0EB0\\u0EB2\\u0EB3\\u0EBD\\u0EC0-\\u0EC4\\u0EC6\\u0EDC\\u0EDD\\u0F00\\u0F40-\\u0F47\\u0F49-\\u0F6C\\u0F88-\\u0F8B\\u1000-\\u102A\\u103F\\u1050-\\u1055\\u105A-\\u105D\\u1061\\u1065\\u1066\\u106E-\\u1070\\u1075-\\u1081\\u108E\\u10A0-\\u10C5\\u10D0-\\u10FA\\u10FC\\u1100-\\u1159\\u115F-\\u11A2\\u11A8-\\u11F9\\u1200-\\u1248\\u124A-\\u124D\\u1250-\\u1256\\u1258\\u125A-\\u125D\\u1260-\\u1288\\u128A-\\u128D\\u1290-\\u12B0\\u12B2-\\u12B5\\u12B8-\\u12BE\\u12C0\\u12C2-\\u12C5\\u12C8-\\u12D6\\u12D8-\\u1310\\u1312-\\u1315\\u1318-\\u135A\\u1380-\\u138F\\u13A0-\\u13F4\\u1401-\\u166C\\u166F-\\u1676\\u1681-\\u169A\\u16A0-\\u16EA\\u1700-\\u170C\\u170E-\\u1711\\u1720-\\u1731\\u1740-\\u1751\\u1760-\\u176C\\u176E-\\u1770\\u1780-\\u17B3\\u17D7\\u17DC\\u1820-\\u1877\\u1880-\\u18A8\\u18AA\\u1900-\\u191C\\u1950-\\u196D\\u1970-\\u1974\\u1980-\\u19A9\\u19C1-\\u19C7\\u1A00-\\u1A16\\u1B05-\\u1B33\\u1B45-\\u1B4B\\u1B83-\\u1BA0\\u1BAE\\u1BAF\\u1C00-\\u1C23\\u1C4D-\\u1C4F\\u1C5A-\\u1C7D\\u1D00-\\u1DBF\\u1E00-\\u1F15\\u1F18-\\u1F1D\\u1F20-\\u1F45\\u1F48-\\u1F4D\\u1F50-\\u1F57\\u1F59\\u1F5B\\u1F5D\\u1F5F-\\u1F7D\\u1F80-\\u1FB4\\u1FB6-\\u1FBC\\u1FBE\\u1FC2-\\u1FC4\\u1FC6-\\u1FCC\\u1FD0-\\u1FD3\\u1FD6-\\u1FDB\\u1FE0-\\u1FEC\\u1FF2-\\u1FF4\\u1FF6-\\u1FFC\\u2071\\u207F\\u2090-\\u2094\\u2102\\u2107\\u210A-\\u2113\\u2115\\u2119-\\u211D\\u2124\\u2126\\u2128\\u212A-\\u212D\\u212F-\\u2139\\u213C-\\u213F\\u2145-\\u2149\\u214E\\u2183\\u2184\\u2C00-\\u2C2E\\u2C30-\\u2C5E\\u2C60-\\u2C6F\\u2C71-\\u2C7D\\u2C80-\\u2CE4\\u2D00-\\u2D25\\u2D30-\\u2D65\\u2D6F\\u2D80-\\u2D96\\u2DA0-\\u2DA6\\u2DA8-\\u2DAE\\u2DB0-\\u2DB6\\u2DB8-\\u2DBE\\u2DC0-\\u2DC6\\u2DC8-\\u2DCE\\u2DD0-\\u2DD6\\u2DD8-\\u2DDE\\u2E2F\\u3005\\u3006\\u3031-\\u3035\\u303B\\u303C\\u3041-\\u3096\\u309D-\\u309F\\u30A1-\\u30FA\\u30FC-\\u30FF\\u3105-\\u312D\\u3131-\\u318E\\u31A0-\\u31B7\\u31F0-\\u31FF\\u3400\\u4DB5\\u4E00\\u9FC3\\uA000-\\uA48C\\uA500-\\uA60C\\uA610-\\uA61F\\uA62A\\uA62B\\uA640-\\uA65F\\uA662-\\uA66E\\uA67F-\\uA697\\uA717-\\uA71F\\uA722-\\uA788\\uA78B\\uA78C\\uA7FB-\\uA801\\uA803-\\uA805\\uA807-\\uA80A\\uA80C-\\uA822\\uA840-\\uA873\\uA882-\\uA8B3\\uA90A-\\uA925\\uA930-\\uA946\\uAA00-\\uAA28\\uAA40-\\uAA42\\uAA44-\\uAA4B\\uAC00\\uD7A3\\uF900-\\uFA2D\\uFA30-\\uFA6A\\uFA70-\\uFAD9\\uFB00-\\uFB06\\uFB13-\\uFB17\\uFB1D\\uFB1F-\\uFB28\\uFB2A-\\uFB36\\uFB38-\\uFB3C\\uFB3E\\uFB40\\uFB41\\uFB43\\uFB44\\uFB46-\\uFBB1\\uFBD3-\\uFD3D\\uFD50-\\uFD8F\\uFD92-\\uFDC7\\uFDF0-\\uFDFB\\uFE70-\\uFE74\\uFE76-\\uFEFC\\uFF21-\\uFF3A\\uFF41-\\uFF5A\\uFF66-\\uFFBE\\uFFC2-\\uFFC7\\uFFCA-\\uFFCF\\uFFD2-\\uFFD7\\uFFDA-\\uFFDC]"),
- non_spacing_mark: new RegExp("[\\u0300-\\u036F\\u0483-\\u0487\\u0591-\\u05BD\\u05BF\\u05C1\\u05C2\\u05C4\\u05C5\\u05C7\\u0610-\\u061A\\u064B-\\u065E\\u0670\\u06D6-\\u06DC\\u06DF-\\u06E4\\u06E7\\u06E8\\u06EA-\\u06ED\\u0711\\u0730-\\u074A\\u07A6-\\u07B0\\u07EB-\\u07F3\\u0816-\\u0819\\u081B-\\u0823\\u0825-\\u0827\\u0829-\\u082D\\u0900-\\u0902\\u093C\\u0941-\\u0948\\u094D\\u0951-\\u0955\\u0962\\u0963\\u0981\\u09BC\\u09C1-\\u09C4\\u09CD\\u09E2\\u09E3\\u0A01\\u0A02\\u0A3C\\u0A41\\u0A42\\u0A47\\u0A48\\u0A4B-\\u0A4D\\u0A51\\u0A70\\u0A71\\u0A75\\u0A81\\u0A82\\u0ABC\\u0AC1-\\u0AC5\\u0AC7\\u0AC8\\u0ACD\\u0AE2\\u0AE3\\u0B01\\u0B3C\\u0B3F\\u0B41-\\u0B44\\u0B4D\\u0B56\\u0B62\\u0B63\\u0B82\\u0BC0\\u0BCD\\u0C3E-\\u0C40\\u0C46-\\u0C48\\u0C4A-\\u0C4D\\u0C55\\u0C56\\u0C62\\u0C63\\u0CBC\\u0CBF\\u0CC6\\u0CCC\\u0CCD\\u0CE2\\u0CE3\\u0D41-\\u0D44\\u0D4D\\u0D62\\u0D63\\u0DCA\\u0DD2-\\u0DD4\\u0DD6\\u0E31\\u0E34-\\u0E3A\\u0E47-\\u0E4E\\u0EB1\\u0EB4-\\u0EB9\\u0EBB\\u0EBC\\u0EC8-\\u0ECD\\u0F18\\u0F19\\u0F35\\u0F37\\u0F39\\u0F71-\\u0F7E\\u0F80-\\u0F84\\u0F86\\u0F87\\u0F90-\\u0F97\\u0F99-\\u0FBC\\u0FC6\\u102D-\\u1030\\u1032-\\u1037\\u1039\\u103A\\u103D\\u103E\\u1058\\u1059\\u105E-\\u1060\\u1071-\\u1074\\u1082\\u1085\\u1086\\u108D\\u109D\\u135F\\u1712-\\u1714\\u1732-\\u1734\\u1752\\u1753\\u1772\\u1773\\u17B7-\\u17BD\\u17C6\\u17C9-\\u17D3\\u17DD\\u180B-\\u180D\\u18A9\\u1920-\\u1922\\u1927\\u1928\\u1932\\u1939-\\u193B\\u1A17\\u1A18\\u1A56\\u1A58-\\u1A5E\\u1A60\\u1A62\\u1A65-\\u1A6C\\u1A73-\\u1A7C\\u1A7F\\u1B00-\\u1B03\\u1B34\\u1B36-\\u1B3A\\u1B3C\\u1B42\\u1B6B-\\u1B73\\u1B80\\u1B81\\u1BA2-\\u1BA5\\u1BA8\\u1BA9\\u1C2C-\\u1C33\\u1C36\\u1C37\\u1CD0-\\u1CD2\\u1CD4-\\u1CE0\\u1CE2-\\u1CE8\\u1CED\\u1DC0-\\u1DE6\\u1DFD-\\u1DFF\\u20D0-\\u20DC\\u20E1\\u20E5-\\u20F0\\u2CEF-\\u2CF1\\u2DE0-\\u2DFF\\u302A-\\u302F\\u3099\\u309A\\uA66F\\uA67C\\uA67D\\uA6F0\\uA6F1\\uA802\\uA806\\uA80B\\uA825\\uA826\\uA8C4\\uA8E0-\\uA8F1\\uA926-\\uA92D\\uA947-\\uA951\\uA980-\\uA982\\uA9B3\\uA9B6-\\uA9B9\\uA9BC\\uAA29-\\uAA2E\\uAA31\\uAA32\\uAA35\\uAA36\\uAA43\\uAA4C\\uAAB0\\uAAB2-\\uAAB4\\uAAB7\\uAAB8\\uAABE\\uAABF\\uAAC1\\uABE5\\uABE8\\uABED\\uFB1E\\uFE00-\\uFE0F\\uFE20-\\uFE26]"),
- space_combining_mark: new RegExp("[\\u0903\\u093E-\\u0940\\u0949-\\u094C\\u094E\\u0982\\u0983\\u09BE-\\u09C0\\u09C7\\u09C8\\u09CB\\u09CC\\u09D7\\u0A03\\u0A3E-\\u0A40\\u0A83\\u0ABE-\\u0AC0\\u0AC9\\u0ACB\\u0ACC\\u0B02\\u0B03\\u0B3E\\u0B40\\u0B47\\u0B48\\u0B4B\\u0B4C\\u0B57\\u0BBE\\u0BBF\\u0BC1\\u0BC2\\u0BC6-\\u0BC8\\u0BCA-\\u0BCC\\u0BD7\\u0C01-\\u0C03\\u0C41-\\u0C44\\u0C82\\u0C83\\u0CBE\\u0CC0-\\u0CC4\\u0CC7\\u0CC8\\u0CCA\\u0CCB\\u0CD5\\u0CD6\\u0D02\\u0D03\\u0D3E-\\u0D40\\u0D46-\\u0D48\\u0D4A-\\u0D4C\\u0D57\\u0D82\\u0D83\\u0DCF-\\u0DD1\\u0DD8-\\u0DDF\\u0DF2\\u0DF3\\u0F3E\\u0F3F\\u0F7F\\u102B\\u102C\\u1031\\u1038\\u103B\\u103C\\u1056\\u1057\\u1062-\\u1064\\u1067-\\u106D\\u1083\\u1084\\u1087-\\u108C\\u108F\\u109A-\\u109C\\u17B6\\u17BE-\\u17C5\\u17C7\\u17C8\\u1923-\\u1926\\u1929-\\u192B\\u1930\\u1931\\u1933-\\u1938\\u19B0-\\u19C0\\u19C8\\u19C9\\u1A19-\\u1A1B\\u1A55\\u1A57\\u1A61\\u1A63\\u1A64\\u1A6D-\\u1A72\\u1B04\\u1B35\\u1B3B\\u1B3D-\\u1B41\\u1B43\\u1B44\\u1B82\\u1BA1\\u1BA6\\u1BA7\\u1BAA\\u1C24-\\u1C2B\\u1C34\\u1C35\\u1CE1\\u1CF2\\uA823\\uA824\\uA827\\uA880\\uA881\\uA8B4-\\uA8C3\\uA952\\uA953\\uA983\\uA9B4\\uA9B5\\uA9BA\\uA9BB\\uA9BD-\\uA9C0\\uAA2F\\uAA30\\uAA33\\uAA34\\uAA4D\\uAA7B\\uABE3\\uABE4\\uABE6\\uABE7\\uABE9\\uABEA\\uABEC]"),
- connector_punctuation: new RegExp("[\\u005F\\u203F\\u2040\\u2054\\uFE33\\uFE34\\uFE4D-\\uFE4F\\uFF3F]")
-};
-
-function is_letter(ch) {
- return UNICODE.letter.test(ch);
-};
-
-function is_digit(ch) {
- ch = ch.charCodeAt(0);
- return ch >= 48 && ch <= 57; //XXX: find out if "UnicodeDigit" means something else than 0..9
-};
-
-function is_alphanumeric_char(ch) {
- return is_digit(ch) || is_letter(ch);
-};
-
-function is_unicode_combining_mark(ch) {
- return UNICODE.non_spacing_mark.test(ch) || UNICODE.space_combining_mark.test(ch);
-};
-
-function is_unicode_connector_punctuation(ch) {
- return UNICODE.connector_punctuation.test(ch);
-};
-
-function is_identifier_start(ch) {
- return ch == "$" || ch == "_" || is_letter(ch);
-};
-
-function is_identifier_char(ch) {
- return is_identifier_start(ch)
- || is_unicode_combining_mark(ch)
- || is_digit(ch)
- || is_unicode_connector_punctuation(ch)
- || ch == "\u200c" // zero-width non-joiner
- || ch == "\u200d" // zero-width joiner (in my ECMA-262 PDF, this is also 200c)
- ;
-};
-
-function parse_js_number(num) {
- if (RE_HEX_NUMBER.test(num)) {
- return parseInt(num.substr(2), 16);
- } else if (RE_OCT_NUMBER.test(num)) {
- return parseInt(num.substr(1), 8);
- } else if (RE_DEC_NUMBER.test(num)) {
- return parseFloat(num);
- }
-};
-
-function JS_Parse_Error(message, line, col, pos) {
- this.message = message;
- this.line = line + 1;
- this.col = col + 1;
- this.pos = pos + 1;
- this.stack = new Error().stack;
-};
-
-JS_Parse_Error.prototype.toString = function() {
- return this.message + " (line: " + this.line + ", col: " + this.col + ", pos: " + this.pos + ")" + "\n\n" + this.stack;
-};
-
-function js_error(message, line, col, pos) {
- throw new JS_Parse_Error(message, line, col, pos);
-};
-
-function is_token(token, type, val) {
- return token.type == type && (val == null || token.value == val);
-};
-
-var EX_EOF = {};
-
-function tokenizer($TEXT) {
-
- var S = {
- text : $TEXT.replace(/\r\n?|[\n\u2028\u2029]/g, "\n").replace(/^\uFEFF/, ''),
- pos : 0,
- tokpos : 0,
- line : 0,
- tokline : 0,
- col : 0,
- tokcol : 0,
- newline_before : false,
- regex_allowed : false,
- comments_before : []
- };
-
- function peek() { return S.text.charAt(S.pos); };
-
- function next(signal_eof, in_string) {
- var ch = S.text.charAt(S.pos++);
- if (signal_eof && !ch)
- throw EX_EOF;
- if (ch == "\n") {
- S.newline_before = S.newline_before || !in_string;
- ++S.line;
- S.col = 0;
- } else {
- ++S.col;
- }
- return ch;
- };
-
- function eof() {
- return !S.peek();
- };
-
- function find(what, signal_eof) {
- var pos = S.text.indexOf(what, S.pos);
- if (signal_eof && pos == -1) throw EX_EOF;
- return pos;
- };
-
- function start_token() {
- S.tokline = S.line;
- S.tokcol = S.col;
- S.tokpos = S.pos;
- };
-
- function token(type, value, is_comment) {
- S.regex_allowed = ((type == "operator" && !HOP(UNARY_POSTFIX, value)) ||
- (type == "keyword" && HOP(KEYWORDS_BEFORE_EXPRESSION, value)) ||
- (type == "punc" && HOP(PUNC_BEFORE_EXPRESSION, value)));
- var ret = {
- type : type,
- value : value,
- line : S.tokline,
- col : S.tokcol,
- pos : S.tokpos,
- endpos : S.pos,
- nlb : S.newline_before
- };
- if (!is_comment) {
- ret.comments_before = S.comments_before;
- S.comments_before = [];
- }
- S.newline_before = false;
- return ret;
- };
-
- function skip_whitespace() {
- while (HOP(WHITESPACE_CHARS, peek()))
- next();
- };
-
- function read_while(pred) {
- var ret = "", ch = peek(), i = 0;
- while (ch && pred(ch, i++)) {
- ret += next();
- ch = peek();
- }
- return ret;
- };
-
- function parse_error(err) {
- js_error(err, S.tokline, S.tokcol, S.tokpos);
- };
-
- function read_num(prefix) {
- var has_e = false, after_e = false, has_x = false, has_dot = prefix == ".";
- var num = read_while(function(ch, i){
- if (ch == "x" || ch == "X") {
- if (has_x) return false;
- return has_x = true;
- }
- if (!has_x && (ch == "E" || ch == "e")) {
- if (has_e) return false;
- return has_e = after_e = true;
- }
- if (ch == "-") {
- if (after_e || (i == 0 && !prefix)) return true;
- return false;
- }
- if (ch == "+") return after_e;
- after_e = false;
- if (ch == ".") {
- if (!has_dot && !has_x)
- return has_dot = true;
- return false;
- }
- return is_alphanumeric_char(ch);
- });
- if (prefix)
- num = prefix + num;
- var valid = parse_js_number(num);
- if (!isNaN(valid)) {
- return token("num", valid);
- } else {
- parse_error("Invalid syntax: " + num);
- }
- };
-
- function read_escaped_char(in_string) {
- var ch = next(true, in_string);
- switch (ch) {
- case "n" : return "\n";
- case "r" : return "\r";
- case "t" : return "\t";
- case "b" : return "\b";
- case "v" : return "\u000b";
- case "f" : return "\f";
- case "0" : return "\0";
- case "x" : return String.fromCharCode(hex_bytes(2));
- case "u" : return String.fromCharCode(hex_bytes(4));
- case "\n": return "";
- default : return ch;
- }
- };
-
- function hex_bytes(n) {
- var num = 0;
- for (; n > 0; --n) {
- var digit = parseInt(next(true), 16);
- if (isNaN(digit))
- parse_error("Invalid hex-character pattern in string");
- num = (num << 4) | digit;
- }
- return num;
- };
-
- function read_string() {
- return with_eof_error("Unterminated string constant", function(){
- var quote = next(), ret = "";
- for (;;) {
- var ch = next(true);
- if (ch == "\\") {
- // read OctalEscapeSequence (XXX: deprecated if "strict mode")
- // https://github.com/mishoo/UglifyJS/issues/178
- var octal_len = 0, first = null;
- ch = read_while(function(ch){
- if (ch >= "0" && ch <= "7") {
- if (!first) {
- first = ch;
- return ++octal_len;
- }
- else if (first <= "3" && octal_len <= 2) return ++octal_len;
- else if (first >= "4" && octal_len <= 1) return ++octal_len;
- }
- return false;
- });
- if (octal_len > 0) ch = String.fromCharCode(parseInt(ch, 8));
- else ch = read_escaped_char(true);
- }
- else if (ch == quote) break;
- ret += ch;
- }
- return token("string", ret);
- });
- };
-
- function read_line_comment() {
- next();
- var i = find("\n"), ret;
- if (i == -1) {
- ret = S.text.substr(S.pos);
- S.pos = S.text.length;
- } else {
- ret = S.text.substring(S.pos, i);
- S.pos = i;
- }
- return token("comment1", ret, true);
- };
-
- function read_multiline_comment() {
- next();
- return with_eof_error("Unterminated multiline comment", function(){
- var i = find("*/", true),
- text = S.text.substring(S.pos, i);
- S.pos = i + 2;
- S.line += text.split("\n").length - 1;
- S.newline_before = text.indexOf("\n") >= 0;
-
- // https://github.com/mishoo/UglifyJS/issues/#issue/100
- if (/^@cc_on/i.test(text)) {
- warn("WARNING: at line " + S.line);
- warn("*** Found \"conditional comment\": " + text);
- warn("*** UglifyJS DISCARDS ALL COMMENTS. This means your code might no longer work properly in Internet Explorer.");
- }
-
- return token("comment2", text, true);
- });
- };
-
- function read_name() {
- var backslash = false, name = "", ch, escaped = false, hex;
- while ((ch = peek()) != null) {
- if (!backslash) {
- if (ch == "\\") escaped = backslash = true, next();
- else if (is_identifier_char(ch)) name += next();
- else break;
- }
- else {
- if (ch != "u") parse_error("Expecting UnicodeEscapeSequence -- uXXXX");
- ch = read_escaped_char();
- if (!is_identifier_char(ch)) parse_error("Unicode char: " + ch.charCodeAt(0) + " is not valid in identifier");
- name += ch;
- backslash = false;
- }
- }
- if (HOP(KEYWORDS, name) && escaped) {
- hex = name.charCodeAt(0).toString(16).toUpperCase();
- name = "\\u" + "0000".substr(hex.length) + hex + name.slice(1);
- }
- return name;
- };
-
- function read_regexp(regexp) {
- return with_eof_error("Unterminated regular expression", function(){
- var prev_backslash = false, ch, in_class = false;
- while ((ch = next(true))) if (prev_backslash) {
- regexp += "\\" + ch;
- prev_backslash = false;
- } else if (ch == "[") {
- in_class = true;
- regexp += ch;
- } else if (ch == "]" && in_class) {
- in_class = false;
- regexp += ch;
- } else if (ch == "/" && !in_class) {
- break;
- } else if (ch == "\\") {
- prev_backslash = true;
- } else {
- regexp += ch;
- }
- var mods = read_name();
- return token("regexp", [ regexp, mods ]);
- });
- };
-
- function read_operator(prefix) {
- function grow(op) {
- if (!peek()) return op;
- var bigger = op + peek();
- if (HOP(OPERATORS, bigger)) {
- next();
- return grow(bigger);
- } else {
- return op;
- }
- };
- return token("operator", grow(prefix || next()));
- };
-
- function handle_slash() {
- next();
- var regex_allowed = S.regex_allowed;
- switch (peek()) {
- case "/":
- S.comments_before.push(read_line_comment());
- S.regex_allowed = regex_allowed;
- return next_token();
- case "*":
- S.comments_before.push(read_multiline_comment());
- S.regex_allowed = regex_allowed;
- return next_token();
- }
- return S.regex_allowed ? read_regexp("") : read_operator("/");
- };
-
- function handle_dot() {
- next();
- return is_digit(peek())
- ? read_num(".")
- : token("punc", ".");
- };
-
- function read_word() {
- var word = read_name();
- return !HOP(KEYWORDS, word)
- ? token("name", word)
- : HOP(OPERATORS, word)
- ? token("operator", word)
- : HOP(KEYWORDS_ATOM, word)
- ? token("atom", word)
- : token("keyword", word);
- };
-
- function with_eof_error(eof_error, cont) {
- try {
- return cont();
- } catch(ex) {
- if (ex === EX_EOF) parse_error(eof_error);
- else throw ex;
- }
- };
-
- function next_token(force_regexp) {
- if (force_regexp != null)
- return read_regexp(force_regexp);
- skip_whitespace();
- start_token();
- var ch = peek();
- if (!ch) return token("eof");
- if (is_digit(ch)) return read_num();
- if (ch == '"' || ch == "'") return read_string();
- if (HOP(PUNC_CHARS, ch)) return token("punc", next());
- if (ch == ".") return handle_dot();
- if (ch == "/") return handle_slash();
- if (HOP(OPERATOR_CHARS, ch)) return read_operator();
- if (ch == "\\" || is_identifier_start(ch)) return read_word();
- parse_error("Unexpected character '" + ch + "'");
- };
-
- next_token.context = function(nc) {
- if (nc) S = nc;
- return S;
- };
-
- return next_token;
-
-};
-
-/* -----[ Parser (constants) ]----- */
-
-var UNARY_PREFIX = array_to_hash([
- "typeof",
- "void",
- "delete",
- "--",
- "++",
- "!",
- "~",
- "-",
- "+"
-]);
-
-var UNARY_POSTFIX = array_to_hash([ "--", "++" ]);
-
-var ASSIGNMENT = (function(a, ret, i){
- while (i < a.length) {
- ret[a[i]] = a[i].substr(0, a[i].length - 1);
- i++;
- }
- return ret;
-})(
- ["+=", "-=", "/=", "*=", "%=", ">>=", "<<=", ">>>=", "|=", "^=", "&="],
- { "=": true },
- 0
-);
-
-var PRECEDENCE = (function(a, ret){
- for (var i = 0, n = 1; i < a.length; ++i, ++n) {
- var b = a[i];
- for (var j = 0; j < b.length; ++j) {
- ret[b[j]] = n;
- }
- }
- return ret;
-})(
- [
- ["||"],
- ["&&"],
- ["|"],
- ["^"],
- ["&"],
- ["==", "===", "!=", "!=="],
- ["<", ">", "<=", ">=", "in", "instanceof"],
- [">>", "<<", ">>>"],
- ["+", "-"],
- ["*", "/", "%"]
- ],
- {}
-);
-
-var STATEMENTS_WITH_LABELS = array_to_hash([ "for", "do", "while", "switch" ]);
-
-var ATOMIC_START_TOKEN = array_to_hash([ "atom", "num", "string", "regexp", "name" ]);
-
-/* -----[ Parser ]----- */
-
-function NodeWithToken(str, start, end) {
- this.name = str;
- this.start = start;
- this.end = end;
-};
-
-NodeWithToken.prototype.toString = function() { return this.name; };
-
-function parse($TEXT, exigent_mode, embed_tokens) {
-
- var S = {
- input : typeof $TEXT == "string" ? tokenizer($TEXT, true) : $TEXT,
- token : null,
- prev : null,
- peeked : null,
- in_function : 0,
- in_directives : true,
- in_loop : 0,
- labels : []
- };
-
- S.token = next();
-
- function is(type, value) {
- return is_token(S.token, type, value);
- };
-
- function peek() { return S.peeked || (S.peeked = S.input()); };
-
- function next() {
- S.prev = S.token;
- if (S.peeked) {
- S.token = S.peeked;
- S.peeked = null;
- } else {
- S.token = S.input();
- }
- S.in_directives = S.in_directives && (
- S.token.type == "string" || is("punc", ";")
- );
- return S.token;
- };
-
- function prev() {
- return S.prev;
- };
-
- function croak(msg, line, col, pos) {
- var ctx = S.input.context();
- js_error(msg,
- line != null ? line : ctx.tokline,
- col != null ? col : ctx.tokcol,
- pos != null ? pos : ctx.tokpos);
- };
-
- function token_error(token, msg) {
- croak(msg, token.line, token.col);
- };
-
- function unexpected(token) {
- if (token == null)
- token = S.token;
- token_error(token, "Unexpected token: " + token.type + " (" + token.value + ")");
- };
-
- function expect_token(type, val) {
- if (is(type, val)) {
- return next();
- }
- token_error(S.token, "Unexpected token " + S.token.type + ", expected " + type);
- };
-
- function expect(punc) { return expect_token("punc", punc); };
-
- function can_insert_semicolon() {
- return !exigent_mode && (
- S.token.nlb || is("eof") || is("punc", "}")
- );
- };
-
- function semicolon() {
- if (is("punc", ";")) next();
- else if (!can_insert_semicolon()) unexpected();
- };
-
- function as() {
- return slice(arguments);
- };
-
- function parenthesised() {
- expect("(");
- var ex = expression();
- expect(")");
- return ex;
- };
-
- function add_tokens(str, start, end) {
- return str instanceof NodeWithToken ? str : new NodeWithToken(str, start, end);
- };
-
- function maybe_embed_tokens(parser) {
- if (embed_tokens) return function() {
- var start = S.token;
- var ast = parser.apply(this, arguments);
- ast[0] = add_tokens(ast[0], start, prev());
- return ast;
- };
- else return parser;
- };
-
- var statement = maybe_embed_tokens(function() {
- if (is("operator", "/") || is("operator", "/=")) {
- S.peeked = null;
- S.token = S.input(S.token.value.substr(1)); // force regexp
- }
- switch (S.token.type) {
- case "string":
- var dir = S.in_directives, stat = simple_statement();
- if (dir && stat[1][0] == "string" && !is("punc", ","))
- return as("directive", stat[1][1]);
- return stat;
- case "num":
- case "regexp":
- case "operator":
- case "atom":
- return simple_statement();
-
- case "name":
- return is_token(peek(), "punc", ":")
- ? labeled_statement(prog1(S.token.value, next, next))
- : simple_statement();
-
- case "punc":
- switch (S.token.value) {
- case "{":
- return as("block", block_());
- case "[":
- case "(":
- return simple_statement();
- case ";":
- next();
- return as("block");
- default:
- unexpected();
- }
-
- case "keyword":
- switch (prog1(S.token.value, next)) {
- case "break":
- return break_cont("break");
-
- case "continue":
- return break_cont("continue");
-
- case "debugger":
- semicolon();
- return as("debugger");
-
- case "do":
- return (function(body){
- expect_token("keyword", "while");
- return as("do", prog1(parenthesised, semicolon), body);
- })(in_loop(statement));
-
- case "for":
- return for_();
-
- case "function":
- return function_(true);
-
- case "if":
- return if_();
-
- case "return":
- if (S.in_function == 0)
- croak("'return' outside of function");
- return as("return",
- is("punc", ";")
- ? (next(), null)
- : can_insert_semicolon()
- ? null
- : prog1(expression, semicolon));
-
- case "switch":
- return as("switch", parenthesised(), switch_block_());
-
- case "throw":
- if (S.token.nlb)
- croak("Illegal newline after 'throw'");
- return as("throw", prog1(expression, semicolon));
-
- case "try":
- return try_();
-
- case "var":
- return prog1(var_, semicolon);
-
- case "const":
- return prog1(const_, semicolon);
-
- case "while":
- return as("while", parenthesised(), in_loop(statement));
-
- case "with":
- return as("with", parenthesised(), statement());
-
- default:
- unexpected();
- }
- }
- });
-
- function labeled_statement(label) {
- S.labels.push(label);
- var start = S.token, stat = statement();
- if (exigent_mode && !HOP(STATEMENTS_WITH_LABELS, stat[0]))
- unexpected(start);
- S.labels.pop();
- return as("label", label, stat);
- };
-
- function simple_statement() {
- return as("stat", prog1(expression, semicolon));
- };
-
- function break_cont(type) {
- var name;
- if (!can_insert_semicolon()) {
- name = is("name") ? S.token.value : null;
- }
- if (name != null) {
- next();
- if (!member(name, S.labels))
- croak("Label " + name + " without matching loop or statement");
- }
- else if (S.in_loop == 0)
- croak(type + " not inside a loop or switch");
- semicolon();
- return as(type, name);
- };
-
- function for_() {
- expect("(");
- var init = null;
- if (!is("punc", ";")) {
- init = is("keyword", "var")
- ? (next(), var_(true))
- : expression(true, true);
- if (is("operator", "in")) {
- if (init[0] == "var" && init[1].length > 1)
- croak("Only one variable declaration allowed in for..in loop");
- return for_in(init);
- }
- }
- return regular_for(init);
- };
-
- function regular_for(init) {
- expect(";");
- var test = is("punc", ";") ? null : expression();
- expect(";");
- var step = is("punc", ")") ? null : expression();
- expect(")");
- return as("for", init, test, step, in_loop(statement));
- };
-
- function for_in(init) {
- var lhs = init[0] == "var" ? as("name", init[1][0]) : init;
- next();
- var obj = expression();
- expect(")");
- return as("for-in", init, lhs, obj, in_loop(statement));
- };
-
- var function_ = function(in_statement) {
- var name = is("name") ? prog1(S.token.value, next) : null;
- if (in_statement && !name)
- unexpected();
- expect("(");
- return as(in_statement ? "defun" : "function",
- name,
- // arguments
- (function(first, a){
- while (!is("punc", ")")) {
- if (first) first = false; else expect(",");
- if (!is("name")) unexpected();
- a.push(S.token.value);
- next();
- }
- next();
- return a;
- })(true, []),
- // body
- (function(){
- ++S.in_function;
- var loop = S.in_loop;
- S.in_directives = true;
- S.in_loop = 0;
- var a = block_();
- --S.in_function;
- S.in_loop = loop;
- return a;
- })());
- };
-
- function if_() {
- var cond = parenthesised(), body = statement(), belse;
- if (is("keyword", "else")) {
- next();
- belse = statement();
- }
- return as("if", cond, body, belse);
- };
-
- function block_() {
- expect("{");
- var a = [];
- while (!is("punc", "}")) {
- if (is("eof")) unexpected();
- a.push(statement());
- }
- next();
- return a;
- };
-
- var switch_block_ = curry(in_loop, function(){
- expect("{");
- var a = [], cur = null;
- while (!is("punc", "}")) {
- if (is("eof")) unexpected();
- if (is("keyword", "case")) {
- next();
- cur = [];
- a.push([ expression(), cur ]);
- expect(":");
- }
- else if (is("keyword", "default")) {
- next();
- expect(":");
- cur = [];
- a.push([ null, cur ]);
- }
- else {
- if (!cur) unexpected();
- cur.push(statement());
- }
- }
- next();
- return a;
- });
-
- function try_() {
- var body = block_(), bcatch, bfinally;
- if (is("keyword", "catch")) {
- next();
- expect("(");
- if (!is("name"))
- croak("Name expected");
- var name = S.token.value;
- next();
- expect(")");
- bcatch = [ name, block_() ];
- }
- if (is("keyword", "finally")) {
- next();
- bfinally = block_();
- }
- if (!bcatch && !bfinally)
- croak("Missing catch/finally blocks");
- return as("try", body, bcatch, bfinally);
- };
-
- function vardefs(no_in) {
- var a = [];
- for (;;) {
- if (!is("name"))
- unexpected();
- var name = S.token.value;
- next();
- if (is("operator", "=")) {
- next();
- a.push([ name, expression(false, no_in) ]);
- } else {
- a.push([ name ]);
- }
- if (!is("punc", ","))
- break;
- next();
- }
- return a;
- };
-
- function var_(no_in) {
- return as("var", vardefs(no_in));
- };
-
- function const_() {
- return as("const", vardefs());
- };
-
- function new_() {
- var newexp = expr_atom(false), args;
- if (is("punc", "(")) {
- next();
- args = expr_list(")");
- } else {
- args = [];
- }
- return subscripts(as("new", newexp, args), true);
- };
-
- var expr_atom = maybe_embed_tokens(function(allow_calls) {
- if (is("operator", "new")) {
- next();
- return new_();
- }
- if (is("punc")) {
- switch (S.token.value) {
- case "(":
- next();
- return subscripts(prog1(expression, curry(expect, ")")), allow_calls);
- case "[":
- next();
- return subscripts(array_(), allow_calls);
- case "{":
- next();
- return subscripts(object_(), allow_calls);
- }
- unexpected();
- }
- if (is("keyword", "function")) {
- next();
- return subscripts(function_(false), allow_calls);
- }
- if (HOP(ATOMIC_START_TOKEN, S.token.type)) {
- var atom = S.token.type == "regexp"
- ? as("regexp", S.token.value[0], S.token.value[1])
- : as(S.token.type, S.token.value);
- return subscripts(prog1(atom, next), allow_calls);
- }
- unexpected();
- });
-
- function expr_list(closing, allow_trailing_comma, allow_empty) {
- var first = true, a = [];
- while (!is("punc", closing)) {
- if (first) first = false; else expect(",");
- if (allow_trailing_comma && is("punc", closing)) break;
- if (is("punc", ",") && allow_empty) {
- a.push([ "atom", "undefined" ]);
- } else {
- a.push(expression(false));
- }
- }
- next();
- return a;
- };
-
- function array_() {
- return as("array", expr_list("]", !exigent_mode, true));
- };
-
- function object_() {
- var first = true, a = [];
- while (!is("punc", "}")) {
- if (first) first = false; else expect(",");
- if (!exigent_mode && is("punc", "}"))
- // allow trailing comma
- break;
- var type = S.token.type;
- var name = as_property_name();
- if (type == "name" && (name == "get" || name == "set") && !is("punc", ":")) {
- a.push([ as_name(), function_(false), name ]);
- } else {
- expect(":");
- a.push([ name, expression(false) ]);
- }
- }
- next();
- return as("object", a);
- };
-
- function as_property_name() {
- switch (S.token.type) {
- case "num":
- case "string":
- return prog1(S.token.value, next);
- }
- return as_name();
- };
-
- function as_name() {
- switch (S.token.type) {
- case "name":
- case "operator":
- case "keyword":
- case "atom":
- return prog1(S.token.value, next);
- default:
- unexpected();
- }
- };
-
- function subscripts(expr, allow_calls) {
- if (is("punc", ".")) {
- next();
- return subscripts(as("dot", expr, as_name()), allow_calls);
- }
- if (is("punc", "[")) {
- next();
- return subscripts(as("sub", expr, prog1(expression, curry(expect, "]"))), allow_calls);
- }
- if (allow_calls && is("punc", "(")) {
- next();
- return subscripts(as("call", expr, expr_list(")")), true);
- }
- return expr;
- };
-
- function maybe_unary(allow_calls) {
- if (is("operator") && HOP(UNARY_PREFIX, S.token.value)) {
- return make_unary("unary-prefix",
- prog1(S.token.value, next),
- maybe_unary(allow_calls));
- }
- var val = expr_atom(allow_calls);
- while (is("operator") && HOP(UNARY_POSTFIX, S.token.value) && !S.token.nlb) {
- val = make_unary("unary-postfix", S.token.value, val);
- next();
- }
- return val;
- };
-
- function make_unary(tag, op, expr) {
- if ((op == "++" || op == "--") && !is_assignable(expr))
- croak("Invalid use of " + op + " operator");
- return as(tag, op, expr);
- };
-
- function expr_op(left, min_prec, no_in) {
- var op = is("operator") ? S.token.value : null;
- if (op && op == "in" && no_in) op = null;
- var prec = op != null ? PRECEDENCE[op] : null;
- if (prec != null && prec > min_prec) {
- next();
- var right = expr_op(maybe_unary(true), prec, no_in);
- return expr_op(as("binary", op, left, right), min_prec, no_in);
- }
- return left;
- };
-
- function expr_ops(no_in) {
- return expr_op(maybe_unary(true), 0, no_in);
- };
-
- function maybe_conditional(no_in) {
- var expr = expr_ops(no_in);
- if (is("operator", "?")) {
- next();
- var yes = expression(false);
- expect(":");
- return as("conditional", expr, yes, expression(false, no_in));
- }
- return expr;
- };
-
- function is_assignable(expr) {
- if (!exigent_mode) return true;
- switch (expr[0]+"") {
- case "dot":
- case "sub":
- case "new":
- case "call":
- return true;
- case "name":
- return expr[1] != "this";
- }
- };
-
- function maybe_assign(no_in) {
- var left = maybe_conditional(no_in), val = S.token.value;
- if (is("operator") && HOP(ASSIGNMENT, val)) {
- if (is_assignable(left)) {
- next();
- return as("assign", ASSIGNMENT[val], left, maybe_assign(no_in));
- }
- croak("Invalid assignment");
- }
- return left;
- };
-
- var expression = maybe_embed_tokens(function(commas, no_in) {
- if (arguments.length == 0)
- commas = true;
- var expr = maybe_assign(no_in);
- if (commas && is("punc", ",")) {
- next();
- return as("seq", expr, expression(true, no_in));
- }
- return expr;
- });
-
- function in_loop(cont) {
- try {
- ++S.in_loop;
- return cont();
- } finally {
- --S.in_loop;
- }
- };
-
- return as("toplevel", (function(a){
- while (!is("eof"))
- a.push(statement());
- return a;
- })([]));
-
-};
-
-/* -----[ Utilities ]----- */
-
-function curry(f) {
- var args = slice(arguments, 1);
- return function() { return f.apply(this, args.concat(slice(arguments))); };
-};
-
-function prog1(ret) {
- if (ret instanceof Function)
- ret = ret();
- for (var i = 1, n = arguments.length; --n > 0; ++i)
- arguments[i]();
- return ret;
-};
-
-function array_to_hash(a) {
- var ret = {};
- for (var i = 0; i < a.length; ++i)
- ret[a[i]] = true;
- return ret;
-};
-
-function slice(a, start) {
- return Array.prototype.slice.call(a, start || 0);
-};
-
-function characters(str) {
- return str.split("");
-};
-
-function member(name, array) {
- for (var i = array.length; --i >= 0;)
- if (array[i] == name)
- return true;
- return false;
-};
-
-function HOP(obj, prop) {
- return Object.prototype.hasOwnProperty.call(obj, prop);
-};
-
-var warn = function() {};
-
-/* -----[ Exports ]----- */
-
-exports.tokenizer = tokenizer;
-exports.parse = parse;
-exports.slice = slice;
-exports.curry = curry;
-exports.member = member;
-exports.array_to_hash = array_to_hash;
-exports.PRECEDENCE = PRECEDENCE;
-exports.KEYWORDS_ATOM = KEYWORDS_ATOM;
-exports.RESERVED_WORDS = RESERVED_WORDS;
-exports.KEYWORDS = KEYWORDS;
-exports.ATOMIC_START_TOKEN = ATOMIC_START_TOKEN;
-exports.OPERATORS = OPERATORS;
-exports.is_alphanumeric_char = is_alphanumeric_char;
-exports.set_logger = function(logger) {
- warn = logger;
-};
\ No newline at end of file
diff --git a/build/lib/process.js b/build/lib/process.js
deleted file mode 100644
index 8c8963d1..00000000
--- a/build/lib/process.js
+++ /dev/null
@@ -1,2022 +0,0 @@
-/***********************************************************************
-
- A JavaScript tokenizer / parser / beautifier / compressor.
-
- This version is suitable for Node.js. With minimal changes (the
- exports stuff) it should work on any JS platform.
-
- This file implements some AST processors. They work on data built
- by parse-js.
-
- Exported functions:
-
- - ast_mangle(ast, options) -- mangles the variable/function names
- in the AST. Returns an AST.
-
- - ast_squeeze(ast) -- employs various optimizations to make the
- final generated code even smaller. Returns an AST.
-
- - gen_code(ast, options) -- generates JS code from the AST. Pass
- true (or an object, see the code for some options) as second
- argument to get "pretty" (indented) code.
-
- -------------------------------- (C) ---------------------------------
-
- Author: Mihai Bazon
-
- http://mihai.bazon.net/blog
-
- Distributed under the BSD license:
-
- Copyright 2010 (c) Mihai Bazon
-
- Redistribution and use in source and binary forms, with or without
- modification, are permitted provided that the following conditions
- are met:
-
- * Redistributions of source code must retain the above
- copyright notice, this list of conditions and the following
- disclaimer.
-
- * Redistributions in binary form must reproduce the above
- copyright notice, this list of conditions and the following
- disclaimer in the documentation and/or other materials
- provided with the distribution.
-
- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER “AS IS” AND ANY
- EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE
- LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
- OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
- TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
- THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
- SUCH DAMAGE.
-
- ***********************************************************************/
-
-var jsp = require("./parse-js"),
- slice = jsp.slice,
- member = jsp.member,
- PRECEDENCE = jsp.PRECEDENCE,
- OPERATORS = jsp.OPERATORS;
-
-/* -----[ helper for AST traversal ]----- */
-
-function ast_walker() {
- function _vardefs(defs) {
- return [ this[0], MAP(defs, function(def){
- var a = [ def[0] ];
- if (def.length > 1)
- a[1] = walk(def[1]);
- return a;
- }) ];
- };
- function _block(statements) {
- var out = [ this[0] ];
- if (statements != null)
- out.push(MAP(statements, walk));
- return out;
- };
- var walkers = {
- "string": function(str) {
- return [ this[0], str ];
- },
- "num": function(num) {
- return [ this[0], num ];
- },
- "name": function(name) {
- return [ this[0], name ];
- },
- "toplevel": function(statements) {
- return [ this[0], MAP(statements, walk) ];
- },
- "block": _block,
- "splice": _block,
- "var": _vardefs,
- "const": _vardefs,
- "try": function(t, c, f) {
- return [
- this[0],
- MAP(t, walk),
- c != null ? [ c[0], MAP(c[1], walk) ] : null,
- f != null ? MAP(f, walk) : null
- ];
- },
- "throw": function(expr) {
- return [ this[0], walk(expr) ];
- },
- "new": function(ctor, args) {
- return [ this[0], walk(ctor), MAP(args, walk) ];
- },
- "switch": function(expr, body) {
- return [ this[0], walk(expr), MAP(body, function(branch){
- return [ branch[0] ? walk(branch[0]) : null,
- MAP(branch[1], walk) ];
- }) ];
- },
- "break": function(label) {
- return [ this[0], label ];
- },
- "continue": function(label) {
- return [ this[0], label ];
- },
- "conditional": function(cond, t, e) {
- return [ this[0], walk(cond), walk(t), walk(e) ];
- },
- "assign": function(op, lvalue, rvalue) {
- return [ this[0], op, walk(lvalue), walk(rvalue) ];
- },
- "dot": function(expr) {
- return [ this[0], walk(expr) ].concat(slice(arguments, 1));
- },
- "call": function(expr, args) {
- return [ this[0], walk(expr), MAP(args, walk) ];
- },
- "function": function(name, args, body) {
- return [ this[0], name, args.slice(), MAP(body, walk) ];
- },
- "debugger": function() {
- return [ this[0] ];
- },
- "defun": function(name, args, body) {
- return [ this[0], name, args.slice(), MAP(body, walk) ];
- },
- "if": function(conditional, t, e) {
- return [ this[0], walk(conditional), walk(t), walk(e) ];
- },
- "for": function(init, cond, step, block) {
- return [ this[0], walk(init), walk(cond), walk(step), walk(block) ];
- },
- "for-in": function(vvar, key, hash, block) {
- return [ this[0], walk(vvar), walk(key), walk(hash), walk(block) ];
- },
- "while": function(cond, block) {
- return [ this[0], walk(cond), walk(block) ];
- },
- "do": function(cond, block) {
- return [ this[0], walk(cond), walk(block) ];
- },
- "return": function(expr) {
- return [ this[0], walk(expr) ];
- },
- "binary": function(op, left, right) {
- return [ this[0], op, walk(left), walk(right) ];
- },
- "unary-prefix": function(op, expr) {
- return [ this[0], op, walk(expr) ];
- },
- "unary-postfix": function(op, expr) {
- return [ this[0], op, walk(expr) ];
- },
- "sub": function(expr, subscript) {
- return [ this[0], walk(expr), walk(subscript) ];
- },
- "object": function(props) {
- return [ this[0], MAP(props, function(p){
- return p.length == 2
- ? [ p[0], walk(p[1]) ]
- : [ p[0], walk(p[1]), p[2] ]; // get/set-ter
- }) ];
- },
- "regexp": function(rx, mods) {
- return [ this[0], rx, mods ];
- },
- "array": function(elements) {
- return [ this[0], MAP(elements, walk) ];
- },
- "stat": function(stat) {
- return [ this[0], walk(stat) ];
- },
- "seq": function() {
- return [ this[0] ].concat(MAP(slice(arguments), walk));
- },
- "label": function(name, block) {
- return [ this[0], name, walk(block) ];
- },
- "with": function(expr, block) {
- return [ this[0], walk(expr), walk(block) ];
- },
- "atom": function(name) {
- return [ this[0], name ];
- },
- "directive": function(dir) {
- return [ this[0], dir ];
- }
- };
-
- var user = {};
- var stack = [];
- function walk(ast) {
- if (ast == null)
- return null;
- try {
- stack.push(ast);
- var type = ast[0];
- var gen = user[type];
- if (gen) {
- var ret = gen.apply(ast, ast.slice(1));
- if (ret != null)
- return ret;
- }
- gen = walkers[type];
- return gen.apply(ast, ast.slice(1));
- } finally {
- stack.pop();
- }
- };
-
- function dive(ast) {
- if (ast == null)
- return null;
- try {
- stack.push(ast);
- return walkers[ast[0]].apply(ast, ast.slice(1));
- } finally {
- stack.pop();
- }
- };
-
- function with_walkers(walkers, cont){
- var save = {}, i;
- for (i in walkers) if (HOP(walkers, i)) {
- save[i] = user[i];
- user[i] = walkers[i];
- }
- var ret = cont();
- for (i in save) if (HOP(save, i)) {
- if (!save[i]) delete user[i];
- else user[i] = save[i];
- }
- return ret;
- };
-
- return {
- walk: walk,
- dive: dive,
- with_walkers: with_walkers,
- parent: function() {
- return stack[stack.length - 2]; // last one is current node
- },
- stack: function() {
- return stack;
- }
- };
-};
-
-/* -----[ Scope and mangling ]----- */
-
-function Scope(parent) {
- this.names = {}; // names defined in this scope
- this.mangled = {}; // mangled names (orig.name => mangled)
- this.rev_mangled = {}; // reverse lookup (mangled => orig.name)
- this.cname = -1; // current mangled name
- this.refs = {}; // names referenced from this scope
- this.uses_with = false; // will become TRUE if with() is detected in this or any subscopes
- this.uses_eval = false; // will become TRUE if eval() is detected in this or any subscopes
- this.parent = parent; // parent scope
- this.children = []; // sub-scopes
- if (parent) {
- this.level = parent.level + 1;
- parent.children.push(this);
- } else {
- this.level = 0;
- }
-};
-
-var base54 = (function(){
- var DIGITS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_0123456789";
- return function(num) {
- var ret = "", base = 54;
- do {
- ret += DIGITS.charAt(num % base);
- num = Math.floor(num / base);
- base = 64;
- } while (num > 0);
- return ret;
- };
-})();
-
-Scope.prototype = {
- has: function(name) {
- for (var s = this; s; s = s.parent)
- if (HOP(s.names, name))
- return s;
- },
- has_mangled: function(mname) {
- for (var s = this; s; s = s.parent)
- if (HOP(s.rev_mangled, mname))
- return s;
- },
- toJSON: function() {
- return {
- names: this.names,
- uses_eval: this.uses_eval,
- uses_with: this.uses_with
- };
- },
-
- next_mangled: function() {
- // we must be careful that the new mangled name:
- //
- // 1. doesn't shadow a mangled name from a parent
- // scope, unless we don't reference the original
- // name from this scope OR from any sub-scopes!
- // This will get slow.
- //
- // 2. doesn't shadow an original name from a parent
- // scope, in the event that the name is not mangled
- // in the parent scope and we reference that name
- // here OR IN ANY SUBSCOPES!
- //
- // 3. doesn't shadow a name that is referenced but not
- // defined (possibly global defined elsewhere).
- for (;;) {
- var m = base54(++this.cname), prior;
-
- // case 1.
- prior = this.has_mangled(m);
- if (prior && this.refs[prior.rev_mangled[m]] === prior)
- continue;
-
- // case 2.
- prior = this.has(m);
- if (prior && prior !== this && this.refs[m] === prior && !prior.has_mangled(m))
- continue;
-
- // case 3.
- if (HOP(this.refs, m) && this.refs[m] == null)
- continue;
-
- // I got "do" once. :-/
- if (!is_identifier(m))
- continue;
-
- return m;
- }
- },
- set_mangle: function(name, m) {
- this.rev_mangled[m] = name;
- return this.mangled[name] = m;
- },
- get_mangled: function(name, newMangle) {
- if (this.uses_eval || this.uses_with) return name; // no mangle if eval or with is in use
- var s = this.has(name);
- if (!s) return name; // not in visible scope, no mangle
- if (HOP(s.mangled, name)) return s.mangled[name]; // already mangled in this scope
- if (!newMangle) return name; // not found and no mangling requested
- return s.set_mangle(name, s.next_mangled());
- },
- references: function(name) {
- return name && !this.parent || this.uses_with || this.uses_eval || this.refs[name];
- },
- define: function(name, type) {
- if (name != null) {
- if (type == "var" || !HOP(this.names, name))
- this.names[name] = type || "var";
- return name;
- }
- }
-};
-
-function ast_add_scope(ast) {
-
- var current_scope = null;
- var w = ast_walker(), walk = w.walk;
- var having_eval = [];
-
- function with_new_scope(cont) {
- current_scope = new Scope(current_scope);
- current_scope.labels = new Scope();
- var ret = current_scope.body = cont();
- ret.scope = current_scope;
- current_scope = current_scope.parent;
- return ret;
- };
-
- function define(name, type) {
- return current_scope.define(name, type);
- };
-
- function reference(name) {
- current_scope.refs[name] = true;
- };
-
- function _lambda(name, args, body) {
- var is_defun = this[0] == "defun";
- return [ this[0], is_defun ? define(name, "defun") : name, args, with_new_scope(function(){
- if (!is_defun) define(name, "lambda");
- MAP(args, function(name){ define(name, "arg") });
- return MAP(body, walk);
- })];
- };
-
- function _vardefs(type) {
- return function(defs) {
- MAP(defs, function(d){
- define(d[0], type);
- if (d[1]) reference(d[0]);
- });
- };
- };
-
- function _breacont(label) {
- if (label)
- current_scope.labels.refs[label] = true;
- };
-
- return with_new_scope(function(){
- // process AST
- var ret = w.with_walkers({
- "function": _lambda,
- "defun": _lambda,
- "label": function(name, stat) { current_scope.labels.define(name) },
- "break": _breacont,
- "continue": _breacont,
- "with": function(expr, block) {
- for (var s = current_scope; s; s = s.parent)
- s.uses_with = true;
- },
- "var": _vardefs("var"),
- "const": _vardefs("const"),
- "try": function(t, c, f) {
- if (c != null) return [
- this[0],
- MAP(t, walk),
- [ define(c[0], "catch"), MAP(c[1], walk) ],
- f != null ? MAP(f, walk) : null
- ];
- },
- "name": function(name) {
- if (name == "eval")
- having_eval.push(current_scope);
- reference(name);
- }
- }, function(){
- return walk(ast);
- });
-
- // the reason why we need an additional pass here is
- // that names can be used prior to their definition.
-
- // scopes where eval was detected and their parents
- // are marked with uses_eval, unless they define the
- // "eval" name.
- MAP(having_eval, function(scope){
- if (!scope.has("eval")) while (scope) {
- scope.uses_eval = true;
- scope = scope.parent;
- }
- });
-
- // for referenced names it might be useful to know
- // their origin scope. current_scope here is the
- // toplevel one.
- function fixrefs(scope, i) {
- // do children first; order shouldn't matter
- for (i = scope.children.length; --i >= 0;)
- fixrefs(scope.children[i]);
- for (i in scope.refs) if (HOP(scope.refs, i)) {
- // find origin scope and propagate the reference to origin
- for (var origin = scope.has(i), s = scope; s; s = s.parent) {
- s.refs[i] = origin;
- if (s === origin) break;
- }
- }
- };
- fixrefs(current_scope);
-
- return ret;
- });
-
-};
-
-/* -----[ mangle names ]----- */
-
-function ast_mangle(ast, options) {
- var w = ast_walker(), walk = w.walk, scope;
- options = options || {};
-
- function get_mangled(name, newMangle) {
- if (!options.toplevel && !scope.parent) return name; // don't mangle toplevel
- if (options.except && member(name, options.except))
- return name;
- return scope.get_mangled(name, newMangle);
- };
-
- function get_define(name) {
- if (options.defines) {
- // we always lookup a defined symbol for the current scope FIRST, so declared
- // vars trump a DEFINE symbol, but if no such var is found, then match a DEFINE value
- if (!scope.has(name)) {
- if (HOP(options.defines, name)) {
- return options.defines[name];
- }
- }
- return null;
- }
- };
-
- function _lambda(name, args, body) {
- if (!options.no_functions) {
- var is_defun = this[0] == "defun", extra;
- if (name) {
- if (is_defun) name = get_mangled(name);
- else if (body.scope.references(name)) {
- extra = {};
- if (!(scope.uses_eval || scope.uses_with))
- name = extra[name] = scope.next_mangled();
- else
- extra[name] = name;
- }
- else name = null;
- }
- }
- body = with_scope(body.scope, function(){
- args = MAP(args, function(name){ return get_mangled(name) });
- return MAP(body, walk);
- }, extra);
- return [ this[0], name, args, body ];
- };
-
- function with_scope(s, cont, extra) {
- var _scope = scope;
- scope = s;
- if (extra) for (var i in extra) if (HOP(extra, i)) {
- s.set_mangle(i, extra[i]);
- }
- for (var i in s.names) if (HOP(s.names, i)) {
- get_mangled(i, true);
- }
- var ret = cont();
- ret.scope = s;
- scope = _scope;
- return ret;
- };
-
- function _vardefs(defs) {
- return [ this[0], MAP(defs, function(d){
- return [ get_mangled(d[0]), walk(d[1]) ];
- }) ];
- };
-
- function _breacont(label) {
- if (label) return [ this[0], scope.labels.get_mangled(label) ];
- };
-
- return w.with_walkers({
- "function": _lambda,
- "defun": function() {
- // move function declarations to the top when
- // they are not in some block.
- var ast = _lambda.apply(this, arguments);
- switch (w.parent()[0]) {
- case "toplevel":
- case "function":
- case "defun":
- return MAP.at_top(ast);
- }
- return ast;
- },
- "label": function(label, stat) {
- if (scope.labels.refs[label]) return [
- this[0],
- scope.labels.get_mangled(label, true),
- walk(stat)
- ];
- return walk(stat);
- },
- "break": _breacont,
- "continue": _breacont,
- "var": _vardefs,
- "const": _vardefs,
- "name": function(name) {
- return get_define(name) || [ this[0], get_mangled(name) ];
- },
- "try": function(t, c, f) {
- return [ this[0],
- MAP(t, walk),
- c != null ? [ get_mangled(c[0]), MAP(c[1], walk) ] : null,
- f != null ? MAP(f, walk) : null ];
- },
- "toplevel": function(body) {
- var self = this;
- return with_scope(self.scope, function(){
- return [ self[0], MAP(body, walk) ];
- });
- },
- "directive": function() {
- return MAP.at_top(this);
- }
- }, function() {
- return walk(ast_add_scope(ast));
- });
-};
-
-/* -----[
- - compress foo["bar"] into foo.bar,
- - remove block brackets {} where possible
- - join consecutive var declarations
- - various optimizations for IFs:
- - if (cond) foo(); else bar(); ==> cond?foo():bar();
- - if (cond) foo(); ==> cond&&foo();
- - if (foo) return bar(); else return baz(); ==> return foo?bar():baz(); // also for throw
- - if (foo) return bar(); else something(); ==> {if(foo)return bar();something()}
- ]----- */
-
-var warn = function(){};
-
-function best_of(ast1, ast2) {
- return gen_code(ast1).length > gen_code(ast2[0] == "stat" ? ast2[1] : ast2).length ? ast2 : ast1;
-};
-
-function last_stat(b) {
- if (b[0] == "block" && b[1] && b[1].length > 0)
- return b[1][b[1].length - 1];
- return b;
-}
-
-function aborts(t) {
- if (t) switch (last_stat(t)[0]) {
- case "return":
- case "break":
- case "continue":
- case "throw":
- return true;
- }
-};
-
-function boolean_expr(expr) {
- return ( (expr[0] == "unary-prefix"
- && member(expr[1], [ "!", "delete" ])) ||
-
- (expr[0] == "binary"
- && member(expr[1], [ "in", "instanceof", "==", "!=", "===", "!==", "<", "<=", ">=", ">" ])) ||
-
- (expr[0] == "binary"
- && member(expr[1], [ "&&", "||" ])
- && boolean_expr(expr[2])
- && boolean_expr(expr[3])) ||
-
- (expr[0] == "conditional"
- && boolean_expr(expr[2])
- && boolean_expr(expr[3])) ||
-
- (expr[0] == "assign"
- && expr[1] === true
- && boolean_expr(expr[3])) ||
-
- (expr[0] == "seq"
- && boolean_expr(expr[expr.length - 1]))
- );
-};
-
-function empty(b) {
- return !b || (b[0] == "block" && (!b[1] || b[1].length == 0));
-};
-
-function is_string(node) {
- return (node[0] == "string" ||
- node[0] == "unary-prefix" && node[1] == "typeof" ||
- node[0] == "binary" && node[1] == "+" &&
- (is_string(node[2]) || is_string(node[3])));
-};
-
-var when_constant = (function(){
-
- var $NOT_CONSTANT = {};
-
- // this can only evaluate constant expressions. If it finds anything
- // not constant, it throws $NOT_CONSTANT.
- function evaluate(expr) {
- switch (expr[0]) {
- case "string":
- case "num":
- return expr[1];
- case "name":
- case "atom":
- switch (expr[1]) {
- case "true": return true;
- case "false": return false;
- case "null": return null;
- }
- break;
- case "unary-prefix":
- switch (expr[1]) {
- case "!": return !evaluate(expr[2]);
- case "typeof": return typeof evaluate(expr[2]);
- case "~": return ~evaluate(expr[2]);
- case "-": return -evaluate(expr[2]);
- case "+": return +evaluate(expr[2]);
- }
- break;
- case "binary":
- var left = expr[2], right = expr[3];
- switch (expr[1]) {
- case "&&" : return evaluate(left) && evaluate(right);
- case "||" : return evaluate(left) || evaluate(right);
- case "|" : return evaluate(left) | evaluate(right);
- case "&" : return evaluate(left) & evaluate(right);
- case "^" : return evaluate(left) ^ evaluate(right);
- case "+" : return evaluate(left) + evaluate(right);
- case "*" : return evaluate(left) * evaluate(right);
- case "/" : return evaluate(left) / evaluate(right);
- case "%" : return evaluate(left) % evaluate(right);
- case "-" : return evaluate(left) - evaluate(right);
- case "<<" : return evaluate(left) << evaluate(right);
- case ">>" : return evaluate(left) >> evaluate(right);
- case ">>>" : return evaluate(left) >>> evaluate(right);
- case "==" : return evaluate(left) == evaluate(right);
- case "===" : return evaluate(left) === evaluate(right);
- case "!=" : return evaluate(left) != evaluate(right);
- case "!==" : return evaluate(left) !== evaluate(right);
- case "<" : return evaluate(left) < evaluate(right);
- case "<=" : return evaluate(left) <= evaluate(right);
- case ">" : return evaluate(left) > evaluate(right);
- case ">=" : return evaluate(left) >= evaluate(right);
- case "in" : return evaluate(left) in evaluate(right);
- case "instanceof" : return evaluate(left) instanceof evaluate(right);
- }
- }
- throw $NOT_CONSTANT;
- };
-
- return function(expr, yes, no) {
- try {
- var val = evaluate(expr), ast;
- switch (typeof val) {
- case "string": ast = [ "string", val ]; break;
- case "number": ast = [ "num", val ]; break;
- case "boolean": ast = [ "name", String(val) ]; break;
- default:
- if (val === null) { ast = [ "atom", "null" ]; break; }
- throw new Error("Can't handle constant of type: " + (typeof val));
- }
- return yes.call(expr, ast, val);
- } catch(ex) {
- if (ex === $NOT_CONSTANT) {
- if (expr[0] == "binary"
- && (expr[1] == "===" || expr[1] == "!==")
- && ((is_string(expr[2]) && is_string(expr[3]))
- || (boolean_expr(expr[2]) && boolean_expr(expr[3])))) {
- expr[1] = expr[1].substr(0, 2);
- }
- else if (no && expr[0] == "binary"
- && (expr[1] == "||" || expr[1] == "&&")) {
- // the whole expression is not constant but the lval may be...
- try {
- var lval = evaluate(expr[2]);
- expr = ((expr[1] == "&&" && (lval ? expr[3] : lval)) ||
- (expr[1] == "||" && (lval ? lval : expr[3])) ||
- expr);
- } catch(ex2) {
- // IGNORE... lval is not constant
- }
- }
- return no ? no.call(expr, expr) : null;
- }
- else throw ex;
- }
- };
-
-})();
-
-function warn_unreachable(ast) {
- if (!empty(ast))
- warn("Dropping unreachable code: " + gen_code(ast, true));
-};
-
-function prepare_ifs(ast) {
- var w = ast_walker(), walk = w.walk;
- // In this first pass, we rewrite ifs which abort with no else with an
- // if-else. For example:
- //
- // if (x) {
- // blah();
- // return y;
- // }
- // foobar();
- //
- // is rewritten into:
- //
- // if (x) {
- // blah();
- // return y;
- // } else {
- // foobar();
- // }
- function redo_if(statements) {
- statements = MAP(statements, walk);
-
- for (var i = 0; i < statements.length; ++i) {
- var fi = statements[i];
- if (fi[0] != "if") continue;
-
- if (fi[3] && walk(fi[3])) continue;
-
- var t = walk(fi[2]);
- if (!aborts(t)) continue;
-
- var conditional = walk(fi[1]);
-
- var e_body = redo_if(statements.slice(i + 1));
- var e = e_body.length == 1 ? e_body[0] : [ "block", e_body ];
-
- return statements.slice(0, i).concat([ [
- fi[0], // "if"
- conditional, // conditional
- t, // then
- e // else
- ] ]);
- }
-
- return statements;
- };
-
- function redo_if_lambda(name, args, body) {
- body = redo_if(body);
- return [ this[0], name, args, body ];
- };
-
- function redo_if_block(statements) {
- return [ this[0], statements != null ? redo_if(statements) : null ];
- };
-
- return w.with_walkers({
- "defun": redo_if_lambda,
- "function": redo_if_lambda,
- "block": redo_if_block,
- "splice": redo_if_block,
- "toplevel": function(statements) {
- return [ this[0], redo_if(statements) ];
- },
- "try": function(t, c, f) {
- return [
- this[0],
- redo_if(t),
- c != null ? [ c[0], redo_if(c[1]) ] : null,
- f != null ? redo_if(f) : null
- ];
- }
- }, function() {
- return walk(ast);
- });
-};
-
-function for_side_effects(ast, handler) {
- var w = ast_walker(), walk = w.walk;
- var $stop = {}, $restart = {};
- function stop() { throw $stop };
- function restart() { throw $restart };
- function found(){ return handler.call(this, this, w, stop, restart) };
- function unary(op) {
- if (op == "++" || op == "--")
- return found.apply(this, arguments);
- };
- return w.with_walkers({
- "try": found,
- "throw": found,
- "return": found,
- "new": found,
- "switch": found,
- "break": found,
- "continue": found,
- "assign": found,
- "call": found,
- "if": found,
- "for": found,
- "for-in": found,
- "while": found,
- "do": found,
- "return": found,
- "unary-prefix": unary,
- "unary-postfix": unary,
- "defun": found
- }, function(){
- while (true) try {
- walk(ast);
- break;
- } catch(ex) {
- if (ex === $stop) break;
- if (ex === $restart) continue;
- throw ex;
- }
- });
-};
-
-function ast_lift_variables(ast) {
- var w = ast_walker(), walk = w.walk, scope;
- function do_body(body, env) {
- var _scope = scope;
- scope = env;
- body = MAP(body, walk);
- var hash = {}, names = MAP(env.names, function(type, name){
- if (type != "var") return MAP.skip;
- if (!env.references(name)) return MAP.skip;
- hash[name] = true;
- return [ name ];
- });
- if (names.length > 0) {
- // looking for assignments to any of these variables.
- // we can save considerable space by moving the definitions
- // in the var declaration.
- for_side_effects([ "block", body ], function(ast, walker, stop, restart) {
- if (ast[0] == "assign"
- && ast[1] === true
- && ast[2][0] == "name"
- && HOP(hash, ast[2][1])) {
- // insert the definition into the var declaration
- for (var i = names.length; --i >= 0;) {
- if (names[i][0] == ast[2][1]) {
- if (names[i][1]) // this name already defined, we must stop
- stop();
- names[i][1] = ast[3]; // definition
- names.push(names.splice(i, 1)[0]);
- break;
- }
- }
- // remove this assignment from the AST.
- var p = walker.parent();
- if (p[0] == "seq") {
- var a = p[2];
- a.unshift(0, p.length);
- p.splice.apply(p, a);
- }
- else if (p[0] == "stat") {
- p.splice(0, p.length, "block"); // empty statement
- }
- else {
- stop();
- }
- restart();
- }
- stop();
- });
- body.unshift([ "var", names ]);
- }
- scope = _scope;
- return body;
- };
- function _vardefs(defs) {
- var ret = null;
- for (var i = defs.length; --i >= 0;) {
- var d = defs[i];
- if (!d[1]) continue;
- d = [ "assign", true, [ "name", d[0] ], d[1] ];
- if (ret == null) ret = d;
- else ret = [ "seq", d, ret ];
- }
- if (ret == null) {
- if (w.parent()[0] == "for-in")
- return [ "name", defs[0][0] ];
- return MAP.skip;
- }
- return [ "stat", ret ];
- };
- function _toplevel(body) {
- return [ this[0], do_body(body, this.scope) ];
- };
- return w.with_walkers({
- "function": function(name, args, body){
- for (var i = args.length; --i >= 0 && !body.scope.references(args[i]);)
- args.pop();
- if (!body.scope.references(name)) name = null;
- return [ this[0], name, args, do_body(body, body.scope) ];
- },
- "defun": function(name, args, body){
- if (!scope.references(name)) return MAP.skip;
- for (var i = args.length; --i >= 0 && !body.scope.references(args[i]);)
- args.pop();
- return [ this[0], name, args, do_body(body, body.scope) ];
- },
- "var": _vardefs,
- "toplevel": _toplevel
- }, function(){
- return walk(ast_add_scope(ast));
- });
-};
-
-function ast_squeeze(ast, options) {
- options = defaults(options, {
- make_seqs : true,
- dead_code : true,
- no_warnings : false,
- keep_comps : true
- });
-
- var w = ast_walker(), walk = w.walk;
-
- function negate(c) {
- var not_c = [ "unary-prefix", "!", c ];
- switch (c[0]) {
- case "unary-prefix":
- return c[1] == "!" && boolean_expr(c[2]) ? c[2] : not_c;
- case "seq":
- c = slice(c);
- c[c.length - 1] = negate(c[c.length - 1]);
- return c;
- case "conditional":
- return best_of(not_c, [ "conditional", c[1], negate(c[2]), negate(c[3]) ]);
- case "binary":
- var op = c[1], left = c[2], right = c[3];
- if (!options.keep_comps) switch (op) {
- case "<=" : return [ "binary", ">", left, right ];
- case "<" : return [ "binary", ">=", left, right ];
- case ">=" : return [ "binary", "<", left, right ];
- case ">" : return [ "binary", "<=", left, right ];
- }
- switch (op) {
- case "==" : return [ "binary", "!=", left, right ];
- case "!=" : return [ "binary", "==", left, right ];
- case "===" : return [ "binary", "!==", left, right ];
- case "!==" : return [ "binary", "===", left, right ];
- case "&&" : return best_of(not_c, [ "binary", "||", negate(left), negate(right) ]);
- case "||" : return best_of(not_c, [ "binary", "&&", negate(left), negate(right) ]);
- }
- break;
- }
- return not_c;
- };
-
- function make_conditional(c, t, e) {
- var make_real_conditional = function() {
- if (c[0] == "unary-prefix" && c[1] == "!") {
- return e ? [ "conditional", c[2], e, t ] : [ "binary", "||", c[2], t ];
- } else {
- return e ? best_of(
- [ "conditional", c, t, e ],
- [ "conditional", negate(c), e, t ]
- ) : [ "binary", "&&", c, t ];
- }
- };
- // shortcut the conditional if the expression has a constant value
- return when_constant(c, function(ast, val){
- warn_unreachable(val ? e : t);
- return (val ? t : e);
- }, make_real_conditional);
- };
-
- function rmblock(block) {
- if (block != null && block[0] == "block" && block[1]) {
- if (block[1].length == 1)
- block = block[1][0];
- else if (block[1].length == 0)
- block = [ "block" ];
- }
- return block;
- };
-
- function _lambda(name, args, body) {
- return [ this[0], name, args, tighten(body, "lambda") ];
- };
-
- // this function does a few things:
- // 1. discard useless blocks
- // 2. join consecutive var declarations
- // 3. remove obviously dead code
- // 4. transform consecutive statements using the comma operator
- // 5. if block_type == "lambda" and it detects constructs like if(foo) return ... - rewrite like if (!foo) { ... }
- function tighten(statements, block_type) {
- statements = MAP(statements, walk);
-
- statements = statements.reduce(function(a, stat){
- if (stat[0] == "block") {
- if (stat[1]) {
- a.push.apply(a, stat[1]);
- }
- } else {
- a.push(stat);
- }
- return a;
- }, []);
-
- statements = (function(a, prev){
- statements.forEach(function(cur){
- if (prev && ((cur[0] == "var" && prev[0] == "var") ||
- (cur[0] == "const" && prev[0] == "const"))) {
- prev[1] = prev[1].concat(cur[1]);
- } else {
- a.push(cur);
- prev = cur;
- }
- });
- return a;
- })([]);
-
- if (options.dead_code) statements = (function(a, has_quit){
- statements.forEach(function(st){
- if (has_quit) {
- if (st[0] == "function" || st[0] == "defun") {
- a.push(st);
- }
- else if (st[0] == "var" || st[0] == "const") {
- if (!options.no_warnings)
- warn("Variables declared in unreachable code");
- st[1] = MAP(st[1], function(def){
- if (def[1] && !options.no_warnings)
- warn_unreachable([ "assign", true, [ "name", def[0] ], def[1] ]);
- return [ def[0] ];
- });
- a.push(st);
- }
- else if (!options.no_warnings)
- warn_unreachable(st);
- }
- else {
- a.push(st);
- if (member(st[0], [ "return", "throw", "break", "continue" ]))
- has_quit = true;
- }
- });
- return a;
- })([]);
-
- if (options.make_seqs) statements = (function(a, prev) {
- statements.forEach(function(cur){
- if (prev && prev[0] == "stat" && cur[0] == "stat") {
- prev[1] = [ "seq", prev[1], cur[1] ];
- } else {
- a.push(cur);
- prev = cur;
- }
- });
- if (a.length >= 2
- && a[a.length-2][0] == "stat"
- && (a[a.length-1][0] == "return" || a[a.length-1][0] == "throw")
- && a[a.length-1][1])
- {
- a.splice(a.length - 2, 2,
- [ a[a.length-1][0],
- [ "seq", a[a.length-2][1], a[a.length-1][1] ]]);
- }
- return a;
- })([]);
-
- // this increases jQuery by 1K. Probably not such a good idea after all..
- // part of this is done in prepare_ifs anyway.
- // if (block_type == "lambda") statements = (function(i, a, stat){
- // while (i < statements.length) {
- // stat = statements[i++];
- // if (stat[0] == "if" && !stat[3]) {
- // if (stat[2][0] == "return" && stat[2][1] == null) {
- // a.push(make_if(negate(stat[1]), [ "block", statements.slice(i) ]));
- // break;
- // }
- // var last = last_stat(stat[2]);
- // if (last[0] == "return" && last[1] == null) {
- // a.push(make_if(stat[1], [ "block", stat[2][1].slice(0, -1) ], [ "block", statements.slice(i) ]));
- // break;
- // }
- // }
- // a.push(stat);
- // }
- // return a;
- // })(0, []);
-
- return statements;
- };
-
- function make_if(c, t, e) {
- return when_constant(c, function(ast, val){
- if (val) {
- t = walk(t);
- warn_unreachable(e);
- return t || [ "block" ];
- } else {
- e = walk(e);
- warn_unreachable(t);
- return e || [ "block" ];
- }
- }, function() {
- return make_real_if(c, t, e);
- });
- };
-
- function abort_else(c, t, e) {
- var ret = [ [ "if", negate(c), e ] ];
- if (t[0] == "block") {
- if (t[1]) ret = ret.concat(t[1]);
- } else {
- ret.push(t);
- }
- return walk([ "block", ret ]);
- };
-
- function make_real_if(c, t, e) {
- c = walk(c);
- t = walk(t);
- e = walk(e);
-
- if (empty(t)) {
- c = negate(c);
- t = e;
- e = null;
- } else if (empty(e)) {
- e = null;
- } else {
- // if we have both else and then, maybe it makes sense to switch them?
- (function(){
- var a = gen_code(c);
- var n = negate(c);
- var b = gen_code(n);
- if (b.length < a.length) {
- var tmp = t;
- t = e;
- e = tmp;
- c = n;
- }
- })();
- }
- if (empty(e) && empty(t))
- return [ "stat", c ];
- var ret = [ "if", c, t, e ];
- if (t[0] == "if" && empty(t[3]) && empty(e)) {
- ret = best_of(ret, walk([ "if", [ "binary", "&&", c, t[1] ], t[2] ]));
- }
- else if (t[0] == "stat") {
- if (e) {
- if (e[0] == "stat")
- ret = best_of(ret, [ "stat", make_conditional(c, t[1], e[1]) ]);
- else if (aborts(e))
- ret = abort_else(c, t, e);
- }
- else {
- ret = best_of(ret, [ "stat", make_conditional(c, t[1]) ]);
- }
- }
- else if (e && t[0] == e[0] && (t[0] == "return" || t[0] == "throw") && t[1] && e[1]) {
- ret = best_of(ret, [ t[0], make_conditional(c, t[1], e[1] ) ]);
- }
- else if (e && aborts(t)) {
- ret = [ [ "if", c, t ] ];
- if (e[0] == "block") {
- if (e[1]) ret = ret.concat(e[1]);
- }
- else {
- ret.push(e);
- }
- ret = walk([ "block", ret ]);
- }
- else if (t && aborts(e)) {
- ret = abort_else(c, t, e);
- }
- return ret;
- };
-
- function _do_while(cond, body) {
- return when_constant(cond, function(cond, val){
- if (!val) {
- warn_unreachable(body);
- return [ "block" ];
- } else {
- return [ "for", null, null, null, walk(body) ];
- }
- });
- };
-
- return w.with_walkers({
- "sub": function(expr, subscript) {
- if (subscript[0] == "string") {
- var name = subscript[1];
- if (is_identifier(name))
- return [ "dot", walk(expr), name ];
- else if (/^[1-9][0-9]*$/.test(name) || name === "0")
- return [ "sub", walk(expr), [ "num", parseInt(name, 10) ] ];
- }
- },
- "if": make_if,
- "toplevel": function(body) {
- return [ "toplevel", tighten(body) ];
- },
- "switch": function(expr, body) {
- var last = body.length - 1;
- return [ "switch", walk(expr), MAP(body, function(branch, i){
- var block = tighten(branch[1]);
- if (i == last && block.length > 0) {
- var node = block[block.length - 1];
- if (node[0] == "break" && !node[1])
- block.pop();
- }
- return [ branch[0] ? walk(branch[0]) : null, block ];
- }) ];
- },
- "function": _lambda,
- "defun": _lambda,
- "block": function(body) {
- if (body) return rmblock([ "block", tighten(body) ]);
- },
- "binary": function(op, left, right) {
- return when_constant([ "binary", op, walk(left), walk(right) ], function yes(c){
- return best_of(walk(c), this);
- }, function no() {
- return function(){
- if(op != "==" && op != "!=") return;
- var l = walk(left), r = walk(right);
- if(l && l[0] == "unary-prefix" && l[1] == "!" && l[2][0] == "num")
- left = ['num', +!l[2][1]];
- else if (r && r[0] == "unary-prefix" && r[1] == "!" && r[2][0] == "num")
- right = ['num', +!r[2][1]];
- return ["binary", op, left, right];
- }() || this;
- });
- },
- "conditional": function(c, t, e) {
- return make_conditional(walk(c), walk(t), walk(e));
- },
- "try": function(t, c, f) {
- return [
- "try",
- tighten(t),
- c != null ? [ c[0], tighten(c[1]) ] : null,
- f != null ? tighten(f) : null
- ];
- },
- "unary-prefix": function(op, expr) {
- expr = walk(expr);
- var ret = [ "unary-prefix", op, expr ];
- if (op == "!")
- ret = best_of(ret, negate(expr));
- return when_constant(ret, function(ast, val){
- return walk(ast); // it's either true or false, so minifies to !0 or !1
- }, function() { return ret });
- },
- "name": function(name) {
- switch (name) {
- case "true": return [ "unary-prefix", "!", [ "num", 0 ]];
- case "false": return [ "unary-prefix", "!", [ "num", 1 ]];
- }
- },
- "while": _do_while,
- "assign": function(op, lvalue, rvalue) {
- lvalue = walk(lvalue);
- rvalue = walk(rvalue);
- var okOps = [ '+', '-', '/', '*', '%', '>>', '<<', '>>>', '|', '^', '&' ];
- if (op === true && lvalue[0] === "name" && rvalue[0] === "binary" &&
- ~okOps.indexOf(rvalue[1]) && rvalue[2][0] === "name" &&
- rvalue[2][1] === lvalue[1]) {
- return [ this[0], rvalue[1], lvalue, rvalue[3] ]
- }
- return [ this[0], op, lvalue, rvalue ];
- }
- }, function() {
- for (var i = 0; i < 2; ++i) {
- ast = prepare_ifs(ast);
- ast = walk(ast);
- }
- return ast;
- });
-};
-
-/* -----[ re-generate code from the AST ]----- */
-
-var DOT_CALL_NO_PARENS = jsp.array_to_hash([
- "name",
- "array",
- "object",
- "string",
- "dot",
- "sub",
- "call",
- "regexp",
- "defun"
-]);
-
-function make_string(str, ascii_only) {
- var dq = 0, sq = 0;
- str = str.replace(/[\\\b\f\n\r\t\x22\x27\u2028\u2029\0]/g, function(s){
- switch (s) {
- case "\\": return "\\\\";
- case "\b": return "\\b";
- case "\f": return "\\f";
- case "\n": return "\\n";
- case "\r": return "\\r";
- case "\u2028": return "\\u2028";
- case "\u2029": return "\\u2029";
- case '"': ++dq; return '"';
- case "'": ++sq; return "'";
- case "\0": return "\\0";
- }
- return s;
- });
- if (ascii_only) str = to_ascii(str);
- if (dq > sq) return "'" + str.replace(/\x27/g, "\\'") + "'";
- else return '"' + str.replace(/\x22/g, '\\"') + '"';
-};
-
-function to_ascii(str) {
- return str.replace(/[\u0080-\uffff]/g, function(ch) {
- var code = ch.charCodeAt(0).toString(16);
- while (code.length < 4) code = "0" + code;
- return "\\u" + code;
- });
-};
-
-var SPLICE_NEEDS_BRACKETS = jsp.array_to_hash([ "if", "while", "do", "for", "for-in", "with" ]);
-
-function gen_code(ast, options) {
- options = defaults(options, {
- indent_start : 0,
- indent_level : 4,
- quote_keys : false,
- space_colon : false,
- beautify : false,
- ascii_only : false,
- inline_script: false
- });
- var beautify = !!options.beautify;
- var indentation = 0,
- newline = beautify ? "\n" : "",
- space = beautify ? " " : "";
-
- function encode_string(str) {
- var ret = make_string(str, options.ascii_only);
- if (options.inline_script)
- ret = ret.replace(/<\x2fscript([>\/\t\n\f\r ])/gi, "<\\/script$1");
- return ret;
- };
-
- function make_name(name) {
- name = name.toString();
- if (options.ascii_only)
- name = to_ascii(name);
- return name;
- };
-
- function indent(line) {
- if (line == null)
- line = "";
- if (beautify)
- line = repeat_string(" ", options.indent_start + indentation * options.indent_level) + line;
- return line;
- };
-
- function with_indent(cont, incr) {
- if (incr == null) incr = 1;
- indentation += incr;
- try { return cont.apply(null, slice(arguments, 1)); }
- finally { indentation -= incr; }
- };
-
- function add_spaces(a) {
- if (beautify)
- return a.join(" ");
- var b = [];
- for (var i = 0; i < a.length; ++i) {
- var next = a[i + 1];
- b.push(a[i]);
- if (next &&
- ((/[a-z0-9_\x24]$/i.test(a[i].toString()) && /^[a-z0-9_\x24]/i.test(next.toString())) ||
- (/[\+\-]$/.test(a[i].toString()) && /^[\+\-]/.test(next.toString())))) {
- b.push(" ");
- }
- }
- return b.join("");
- };
-
- function add_commas(a) {
- return a.join("," + space);
- };
-
- function parenthesize(expr) {
- var gen = make(expr);
- for (var i = 1; i < arguments.length; ++i) {
- var el = arguments[i];
- if ((el instanceof Function && el(expr)) || expr[0] == el)
- return "(" + gen + ")";
- }
- return gen;
- };
-
- function best_of(a) {
- if (a.length == 1) {
- return a[0];
- }
- if (a.length == 2) {
- var b = a[1];
- a = a[0];
- return a.length <= b.length ? a : b;
- }
- return best_of([ a[0], best_of(a.slice(1)) ]);
- };
-
- function needs_parens(expr) {
- if (expr[0] == "function" || expr[0] == "object") {
- // dot/call on a literal function requires the
- // function literal itself to be parenthesized
- // only if it's the first "thing" in a
- // statement. This means that the parent is
- // "stat", but it could also be a "seq" and
- // we're the first in this "seq" and the
- // parent is "stat", and so on. Messy stuff,
- // but it worths the trouble.
- var a = slice(w.stack()), self = a.pop(), p = a.pop();
- while (p) {
- if (p[0] == "stat") return true;
- if (((p[0] == "seq" || p[0] == "call" || p[0] == "dot" || p[0] == "sub" || p[0] == "conditional") && p[1] === self) ||
- ((p[0] == "binary" || p[0] == "assign" || p[0] == "unary-postfix") && p[2] === self)) {
- self = p;
- p = a.pop();
- } else {
- return false;
- }
- }
- }
- return !HOP(DOT_CALL_NO_PARENS, expr[0]);
- };
-
- function make_num(num) {
- var str = num.toString(10), a = [ str.replace(/^0\./, ".") ], m;
- if (Math.floor(num) === num) {
- if (num >= 0) {
- a.push("0x" + num.toString(16).toLowerCase(), // probably pointless
- "0" + num.toString(8)); // same.
- } else {
- a.push("-0x" + (-num).toString(16).toLowerCase(), // probably pointless
- "-0" + (-num).toString(8)); // same.
- }
- if ((m = /^(.*?)(0+)$/.exec(num))) {
- a.push(m[1] + "e" + m[2].length);
- }
- } else if ((m = /^0?\.(0+)(.*)$/.exec(num))) {
- a.push(m[2] + "e-" + (m[1].length + m[2].length),
- str.substr(str.indexOf(".")));
- }
- return best_of(a);
- };
-
- var w = ast_walker();
- var make = w.walk;
- return w.with_walkers({
- "string": encode_string,
- "num": make_num,
- "name": make_name,
- "debugger": function(){ return "debugger" },
- "toplevel": function(statements) {
- return make_block_statements(statements)
- .join(newline + newline);
- },
- "splice": function(statements) {
- var parent = w.parent();
- if (HOP(SPLICE_NEEDS_BRACKETS, parent)) {
- // we need block brackets in this case
- return make_block.apply(this, arguments);
- } else {
- return MAP(make_block_statements(statements, true),
- function(line, i) {
- // the first line is already indented
- return i > 0 ? indent(line) : line;
- }).join(newline);
- }
- },
- "block": make_block,
- "var": function(defs) {
- return "var " + add_commas(MAP(defs, make_1vardef)) + ";";
- },
- "const": function(defs) {
- return "const " + add_commas(MAP(defs, make_1vardef)) + ";";
- },
- "try": function(tr, ca, fi) {
- var out = [ "try", make_block(tr) ];
- if (ca) out.push("catch", "(" + ca[0] + ")", make_block(ca[1]));
- if (fi) out.push("finally", make_block(fi));
- return add_spaces(out);
- },
- "throw": function(expr) {
- return add_spaces([ "throw", make(expr) ]) + ";";
- },
- "new": function(ctor, args) {
- args = args.length > 0 ? "(" + add_commas(MAP(args, function(expr){
- return parenthesize(expr, "seq");
- })) + ")" : "";
- return add_spaces([ "new", parenthesize(ctor, "seq", "binary", "conditional", "assign", function(expr){
- var w = ast_walker(), has_call = {};
- try {
- w.with_walkers({
- "call": function() { throw has_call },
- "function": function() { return this }
- }, function(){
- w.walk(expr);
- });
- } catch(ex) {
- if (ex === has_call)
- return true;
- throw ex;
- }
- }) + args ]);
- },
- "switch": function(expr, body) {
- return add_spaces([ "switch", "(" + make(expr) + ")", make_switch_block(body) ]);
- },
- "break": function(label) {
- var out = "break";
- if (label != null)
- out += " " + make_name(label);
- return out + ";";
- },
- "continue": function(label) {
- var out = "continue";
- if (label != null)
- out += " " + make_name(label);
- return out + ";";
- },
- "conditional": function(co, th, el) {
- return add_spaces([ parenthesize(co, "assign", "seq", "conditional"), "?",
- parenthesize(th, "seq"), ":",
- parenthesize(el, "seq") ]);
- },
- "assign": function(op, lvalue, rvalue) {
- if (op && op !== true) op += "=";
- else op = "=";
- return add_spaces([ make(lvalue), op, parenthesize(rvalue, "seq") ]);
- },
- "dot": function(expr) {
- var out = make(expr), i = 1;
- if (expr[0] == "num") {
- if (!/\./.test(expr[1]))
- out += ".";
- } else if (expr[0] != "function" && needs_parens(expr))
- out = "(" + out + ")";
- while (i < arguments.length)
- out += "." + make_name(arguments[i++]);
- return out;
- },
- "call": function(func, args) {
- var f = make(func);
- if (f.charAt(0) != "(" && needs_parens(func))
- f = "(" + f + ")";
- return f + "(" + add_commas(MAP(args, function(expr){
- return parenthesize(expr, "seq");
- })) + ")";
- },
- "function": make_function,
- "defun": make_function,
- "if": function(co, th, el) {
- var out = [ "if", "(" + make(co) + ")", el ? make_then(th) : make(th) ];
- if (el) {
- out.push("else", make(el));
- }
- return add_spaces(out);
- },
- "for": function(init, cond, step, block) {
- var out = [ "for" ];
- init = (init != null ? make(init) : "").replace(/;*\s*$/, ";" + space);
- cond = (cond != null ? make(cond) : "").replace(/;*\s*$/, ";" + space);
- step = (step != null ? make(step) : "").replace(/;*\s*$/, "");
- var args = init + cond + step;
- if (args == "; ; ") args = ";;";
- out.push("(" + args + ")", make(block));
- return add_spaces(out);
- },
- "for-in": function(vvar, key, hash, block) {
- return add_spaces([ "for", "(" +
- (vvar ? make(vvar).replace(/;+$/, "") : make(key)),
- "in",
- make(hash) + ")", make(block) ]);
- },
- "while": function(condition, block) {
- return add_spaces([ "while", "(" + make(condition) + ")", make(block) ]);
- },
- "do": function(condition, block) {
- return add_spaces([ "do", make(block), "while", "(" + make(condition) + ")" ]) + ";";
- },
- "return": function(expr) {
- var out = [ "return" ];
- if (expr != null) out.push(make(expr));
- return add_spaces(out) + ";";
- },
- "binary": function(operator, lvalue, rvalue) {
- var left = make(lvalue), right = make(rvalue);
- // XXX: I'm pretty sure other cases will bite here.
- // we need to be smarter.
- // adding parens all the time is the safest bet.
- if (member(lvalue[0], [ "assign", "conditional", "seq" ]) ||
- lvalue[0] == "binary" && PRECEDENCE[operator] > PRECEDENCE[lvalue[1]] ||
- lvalue[0] == "function" && needs_parens(this)) {
- left = "(" + left + ")";
- }
- if (member(rvalue[0], [ "assign", "conditional", "seq" ]) ||
- rvalue[0] == "binary" && PRECEDENCE[operator] >= PRECEDENCE[rvalue[1]] &&
- !(rvalue[1] == operator && member(operator, [ "&&", "||", "*" ]))) {
- right = "(" + right + ")";
- }
- else if (!beautify && options.inline_script && (operator == "<" || operator == "<<")
- && rvalue[0] == "regexp" && /^script/i.test(rvalue[1])) {
- right = " " + right;
- }
- return add_spaces([ left, operator, right ]);
- },
- "unary-prefix": function(operator, expr) {
- var val = make(expr);
- if (!(expr[0] == "num" || (expr[0] == "unary-prefix" && !HOP(OPERATORS, operator + expr[1])) || !needs_parens(expr)))
- val = "(" + val + ")";
- return operator + (jsp.is_alphanumeric_char(operator.charAt(0)) ? " " : "") + val;
- },
- "unary-postfix": function(operator, expr) {
- var val = make(expr);
- if (!(expr[0] == "num" || (expr[0] == "unary-postfix" && !HOP(OPERATORS, operator + expr[1])) || !needs_parens(expr)))
- val = "(" + val + ")";
- return val + operator;
- },
- "sub": function(expr, subscript) {
- var hash = make(expr);
- if (needs_parens(expr))
- hash = "(" + hash + ")";
- return hash + "[" + make(subscript) + "]";
- },
- "object": function(props) {
- var obj_needs_parens = needs_parens(this);
- if (props.length == 0)
- return obj_needs_parens ? "({})" : "{}";
- var out = "{" + newline + with_indent(function(){
- return MAP(props, function(p){
- if (p.length == 3) {
- // getter/setter. The name is in p[0], the arg.list in p[1][2], the
- // body in p[1][3] and type ("get" / "set") in p[2].
- return indent(make_function(p[0], p[1][2], p[1][3], p[2], true));
- }
- var key = p[0], val = parenthesize(p[1], "seq");
- if (options.quote_keys) {
- key = encode_string(key);
- } else if ((typeof key == "number" || !beautify && +key + "" == key)
- && parseFloat(key) >= 0) {
- key = make_num(+key);
- } else if (!is_identifier(key)) {
- key = encode_string(key);
- }
- return indent(add_spaces(beautify && options.space_colon
- ? [ key, ":", val ]
- : [ key + ":", val ]));
- }).join("," + newline);
- }) + newline + indent("}");
- return obj_needs_parens ? "(" + out + ")" : out;
- },
- "regexp": function(rx, mods) {
- return "/" + rx + "/" + mods;
- },
- "array": function(elements) {
- if (elements.length == 0) return "[]";
- return add_spaces([ "[", add_commas(MAP(elements, function(el, i){
- if (!beautify && el[0] == "atom" && el[1] == "undefined") return i === elements.length - 1 ? "," : "";
- return parenthesize(el, "seq");
- })), "]" ]);
- },
- "stat": function(stmt) {
- return make(stmt).replace(/;*\s*$/, ";");
- },
- "seq": function() {
- return add_commas(MAP(slice(arguments), make));
- },
- "label": function(name, block) {
- return add_spaces([ make_name(name), ":", make(block) ]);
- },
- "with": function(expr, block) {
- return add_spaces([ "with", "(" + make(expr) + ")", make(block) ]);
- },
- "atom": function(name) {
- return make_name(name);
- },
- "directive": function(dir) {
- return make_string(dir) + ";";
- }
- }, function(){ return make(ast) });
-
- // The squeezer replaces "block"-s that contain only a single
- // statement with the statement itself; technically, the AST
- // is correct, but this can create problems when we output an
- // IF having an ELSE clause where the THEN clause ends in an
- // IF *without* an ELSE block (then the outer ELSE would refer
- // to the inner IF). This function checks for this case and
- // adds the block brackets if needed.
- function make_then(th) {
- if (th == null) return ";";
- if (th[0] == "do") {
- // https://github.com/mishoo/UglifyJS/issues/#issue/57
- // IE croaks with "syntax error" on code like this:
- // if (foo) do ... while(cond); else ...
- // we need block brackets around do/while
- return make_block([ th ]);
- }
- var b = th;
- while (true) {
- var type = b[0];
- if (type == "if") {
- if (!b[3])
- // no else, we must add the block
- return make([ "block", [ th ]]);
- b = b[3];
- }
- else if (type == "while" || type == "do") b = b[2];
- else if (type == "for" || type == "for-in") b = b[4];
- else break;
- }
- return make(th);
- };
-
- function make_function(name, args, body, keyword, no_parens) {
- var out = keyword || "function";
- if (name) {
- out += " " + make_name(name);
- }
- out += "(" + add_commas(MAP(args, make_name)) + ")";
- out = add_spaces([ out, make_block(body) ]);
- return (!no_parens && needs_parens(this)) ? "(" + out + ")" : out;
- };
-
- function must_has_semicolon(node) {
- switch (node[0]) {
- case "with":
- case "while":
- return empty(node[2]); // `with' or `while' with empty body?
- case "for":
- case "for-in":
- return empty(node[4]); // `for' with empty body?
- case "if":
- if (empty(node[2]) && !node[3]) return true; // `if' with empty `then' and no `else'
- if (node[3]) {
- if (empty(node[3])) return true; // `else' present but empty
- return must_has_semicolon(node[3]); // dive into the `else' branch
- }
- return must_has_semicolon(node[2]); // dive into the `then' branch
- case "directive":
- return true;
- }
- };
-
- function make_block_statements(statements, noindent) {
- for (var a = [], last = statements.length - 1, i = 0; i <= last; ++i) {
- var stat = statements[i];
- var code = make(stat);
- if (code != ";") {
- if (!beautify && i == last && !must_has_semicolon(stat)) {
- code = code.replace(/;+\s*$/, "");
- }
- a.push(code);
- }
- }
- return noindent ? a : MAP(a, indent);
- };
-
- function make_switch_block(body) {
- var n = body.length;
- if (n == 0) return "{}";
- return "{" + newline + MAP(body, function(branch, i){
- var has_body = branch[1].length > 0, code = with_indent(function(){
- return indent(branch[0]
- ? add_spaces([ "case", make(branch[0]) + ":" ])
- : "default:");
- }, 0.5) + (has_body ? newline + with_indent(function(){
- return make_block_statements(branch[1]).join(newline);
- }) : "");
- if (!beautify && has_body && i < n - 1)
- code += ";";
- return code;
- }).join(newline) + newline + indent("}");
- };
-
- function make_block(statements) {
- if (!statements) return ";";
- if (statements.length == 0) return "{}";
- return "{" + newline + with_indent(function(){
- return make_block_statements(statements).join(newline);
- }) + newline + indent("}");
- };
-
- function make_1vardef(def) {
- var name = def[0], val = def[1];
- if (val != null)
- name = add_spaces([ make_name(name), "=", parenthesize(val, "seq") ]);
- return name;
- };
-
-};
-
-function split_lines(code, max_line_length) {
- var splits = [ 0 ];
- jsp.parse(function(){
- var next_token = jsp.tokenizer(code);
- var last_split = 0;
- var prev_token;
- function current_length(tok) {
- return tok.pos - last_split;
- };
- function split_here(tok) {
- last_split = tok.pos;
- splits.push(last_split);
- };
- function custom(){
- var tok = next_token.apply(this, arguments);
- out: {
- if (prev_token) {
- if (prev_token.type == "keyword") break out;
- }
- if (current_length(tok) > max_line_length) {
- switch (tok.type) {
- case "keyword":
- case "atom":
- case "name":
- case "punc":
- split_here(tok);
- break out;
- }
- }
- }
- prev_token = tok;
- return tok;
- };
- custom.context = function() {
- return next_token.context.apply(this, arguments);
- };
- return custom;
- }());
- return splits.map(function(pos, i){
- return code.substring(pos, splits[i + 1] || code.length);
- }).join("\n");
-};
-
-/* -----[ Utilities ]----- */
-
-function repeat_string(str, i) {
- if (i <= 0) return "";
- if (i == 1) return str;
- var d = repeat_string(str, i >> 1);
- d += d;
- if (i & 1) d += str;
- return d;
-};
-
-function defaults(args, defs) {
- var ret = {};
- if (args === true)
- args = {};
- for (var i in defs) if (HOP(defs, i)) {
- ret[i] = (args && HOP(args, i)) ? args[i] : defs[i];
- }
- return ret;
-};
-
-function is_identifier(name) {
- return /^[a-z_$][a-z0-9_$]*$/i.test(name)
- && name != "this"
- && !HOP(jsp.KEYWORDS_ATOM, name)
- && !HOP(jsp.RESERVED_WORDS, name)
- && !HOP(jsp.KEYWORDS, name);
-};
-
-function HOP(obj, prop) {
- return Object.prototype.hasOwnProperty.call(obj, prop);
-};
-
-// some utilities
-
-var MAP;
-
-(function(){
- MAP = function(a, f, o) {
- var ret = [], top = [], i;
- function doit() {
- var val = f.call(o, a[i], i);
- if (val instanceof AtTop) {
- val = val.v;
- if (val instanceof Splice) {
- top.push.apply(top, val.v);
- } else {
- top.push(val);
- }
- }
- else if (val != skip) {
- if (val instanceof Splice) {
- ret.push.apply(ret, val.v);
- } else {
- ret.push(val);
- }
- }
- };
- if (a instanceof Array) for (i = 0; i < a.length; ++i) doit();
- else for (i in a) if (HOP(a, i)) doit();
- return top.concat(ret);
- };
- MAP.at_top = function(val) { return new AtTop(val) };
- MAP.splice = function(val) { return new Splice(val) };
- var skip = MAP.skip = {};
- function AtTop(val) { this.v = val };
- function Splice(val) { this.v = val };
-})();
-
-/* -----[ Exports ]----- */
-
-exports.ast_walker = ast_walker;
-exports.ast_mangle = ast_mangle;
-exports.ast_squeeze = ast_squeeze;
-exports.ast_lift_variables = ast_lift_variables;
-exports.gen_code = gen_code;
-exports.ast_add_scope = ast_add_scope;
-exports.set_logger = function(logger) { warn = logger };
-exports.make_string = make_string;
-exports.split_lines = split_lines;
-exports.MAP = MAP;
-
-// keep this last!
-exports.ast_squeeze_more = require("./squeeze-more").ast_squeeze_more;
\ No newline at end of file
diff --git a/build/lib/squeeze-more.js b/build/lib/squeeze-more.js
deleted file mode 100644
index 8aba082f..00000000
--- a/build/lib/squeeze-more.js
+++ /dev/null
@@ -1,73 +0,0 @@
-var jsp = require("./parse-js"),
- pro = require("./process"),
- slice = jsp.slice,
- member = jsp.member,
- curry = jsp.curry,
- MAP = pro.MAP,
- PRECEDENCE = jsp.PRECEDENCE,
- OPERATORS = jsp.OPERATORS;
-
-function ast_squeeze_more(ast) {
- var w = pro.ast_walker(), walk = w.walk, scope;
- function with_scope(s, cont) {
- var save = scope, ret;
- scope = s;
- ret = cont();
- scope = save;
- return ret;
- };
- function _lambda(name, args, body) {
- return [ this[0], name, args, with_scope(body.scope, curry(MAP, body, walk)) ];
- };
- return w.with_walkers({
- "toplevel": function(body) {
- return [ this[0], with_scope(this.scope, curry(MAP, body, walk)) ];
- },
- "function": _lambda,
- "defun": _lambda,
- "new": function(ctor, args) {
- if (ctor[0] == "name") {
- if (ctor[1] == "Array" && !scope.has("Array")) {
- if (args.length != 1) {
- return [ "array", args ];
- } else {
- return walk([ "call", [ "name", "Array" ], args ]);
- }
- } else if (ctor[1] == "Object" && !scope.has("Object")) {
- if (!args.length) {
- return [ "object", [] ];
- } else {
- return walk([ "call", [ "name", "Object" ], args ]);
- }
- } else if ((ctor[1] == "RegExp" || ctor[1] == "Function" || ctor[1] == "Error") && !scope.has(ctor[1])) {
- return walk([ "call", [ "name", ctor[1] ], args]);
- }
- }
- },
- "call": function(expr, args) {
- if (expr[0] == "dot" && expr[1][0] == "string" && args.length == 1
- && (args[0][1] > 0 && expr[2] == "substring" || expr[2] == "substr")) {
- return [ "call", [ "dot", expr[1], "slice"], args];
- }
- if (expr[0] == "dot" && expr[2] == "toString" && args.length == 0) {
- // foo.toString() ==> foo+""
- return [ "binary", "+", expr[1], [ "string", "" ]];
- }
- if (expr[0] == "name") {
- if (expr[1] == "Array" && args.length != 1 && !scope.has("Array")) {
- return [ "array", args ];
- }
- if (expr[1] == "Object" && !args.length && !scope.has("Object")) {
- return [ "object", [] ];
- }
- if (expr[1] == "String" && !scope.has("String")) {
- return [ "binary", "+", args[0], [ "string", "" ]];
- }
- }
- }
- }, function() {
- return walk(pro.ast_add_scope(ast));
- });
-};
-
-exports.ast_squeeze_more = ast_squeeze_more;
\ No newline at end of file
diff --git a/build/lib/uglify-js.js b/build/lib/uglify-js.js
deleted file mode 100644
index 3a56dec2..00000000
--- a/build/lib/uglify-js.js
+++ /dev/null
@@ -1,18 +0,0 @@
-//convienence function(src, [options]);
-function uglify(orig_code, options){
- options || (options = {});
- var jsp = uglify.parser;
- var pro = uglify.uglify;
-
- var ast = jsp.parse(orig_code, options.strict_semicolons); // parse code and get the initial AST
- ast = pro.ast_mangle(ast, options.mangle_options); // get a new AST with mangled names
- ast = pro.ast_squeeze(ast, options.squeeze_options); // get an AST with compression optimizations
- var final_code = pro.gen_code(ast, options.gen_options); // compressed code here
- return final_code;
-};
-
-uglify.parser = require("./parse-js");
-uglify.uglify = require("./process");
-uglify.consolidator = require("./consolidator");
-
-module.exports = uglify
\ No newline at end of file
diff --git a/build/uglify.js b/build/uglify.js
deleted file mode 100644
index e55c1ac8..00000000
--- a/build/uglify.js
+++ /dev/null
@@ -1,332 +0,0 @@
-#!/usr/bin/env node
-// -*- js -*-
-
-global.sys = require(/^v0\.[012]/.test(process.version) ? "sys" : "util");
-var fs = require("fs");
-var uglify = require("./lib/uglify-js"), // symlink ~/.node_libraries/uglify-js.js to ../uglify-js.js
- consolidator = uglify.consolidator,
- jsp = uglify.parser,
- pro = uglify.uglify;
-
-var options = {
- ast: false,
- consolidate: false,
- mangle: true,
- mangle_toplevel: false,
- no_mangle_functions: false,
- squeeze: true,
- make_seqs: true,
- dead_code: true,
- verbose: false,
- show_copyright: true,
- out_same_file: false,
- max_line_length: 32 * 1024,
- unsafe: false,
- reserved_names: null,
- defines: { },
- lift_vars: false,
- codegen_options: {
- ascii_only: false,
- beautify: false,
- indent_level: 4,
- indent_start: 0,
- quote_keys: false,
- space_colon: false,
- inline_script: false
- },
- make: false,
- output: true // stdout
-};
-
-var args = jsp.slice(process.argv, 2);
-var filename;
-
-out: while (args.length > 0) {
- var v = args.shift();
- switch (v) {
- case "-b":
- case "--beautify":
- options.codegen_options.beautify = true;
- break;
- case "-c":
- case "--consolidate-primitive-values":
- options.consolidate = true;
- break;
- case "-i":
- case "--indent":
- options.codegen_options.indent_level = args.shift();
- break;
- case "-q":
- case "--quote-keys":
- options.codegen_options.quote_keys = true;
- break;
- case "-mt":
- case "--mangle-toplevel":
- options.mangle_toplevel = true;
- break;
- case "-nmf":
- case "--no-mangle-functions":
- options.no_mangle_functions = true;
- break;
- case "--no-mangle":
- case "-nm":
- options.mangle = false;
- break;
- case "--no-squeeze":
- case "-ns":
- options.squeeze = false;
- break;
- case "--no-seqs":
- options.make_seqs = false;
- break;
- case "--no-dead-code":
- options.dead_code = false;
- break;
- case "--no-copyright":
- case "-nc":
- options.show_copyright = false;
- break;
- case "-o":
- case "--output":
- options.output = args.shift();
- break;
- case "--overwrite":
- options.out_same_file = true;
- break;
- case "-v":
- case "--verbose":
- options.verbose = true;
- break;
- case "--ast":
- options.ast = true;
- break;
- case "--unsafe":
- options.unsafe = true;
- break;
- case "--max-line-len":
- options.max_line_length = parseInt(args.shift(), 10);
- break;
- case "--reserved-names":
- options.reserved_names = args.shift().split(",");
- break;
- case "--lift-vars":
- options.lift_vars = true;
- break;
- case "-d":
- case "--define":
- var defarg = args.shift();
- try {
- var defsym = function(sym) {
- // KEYWORDS_ATOM doesn't include NaN or Infinity - should we check
- // for them too ?? We don't check reserved words and the like as the
- // define values are only substituted AFTER parsing
- if (jsp.KEYWORDS_ATOM.hasOwnProperty(sym)) {
- throw "Don't define values for inbuilt constant '"+sym+"'";
- }
- return sym;
- },
- defval = function(v) {
- if (v.match(/^"(.*)"$/) || v.match(/^'(.*)'$/)) {
- return [ "string", RegExp.$1 ];
- }
- else if (!isNaN(parseFloat(v))) {
- return [ "num", parseFloat(v) ];
- }
- else if (v.match(/^[a-z\$_][a-z\$_0-9]*$/i)) {
- return [ "name", v ];
- }
- else if (!v.match(/"/)) {
- return [ "string", v ];
- }
- else if (!v.match(/'/)) {
- return [ "string", v ];
- }
- throw "Can't understand the specified value: "+v;
- };
- if (defarg.match(/^([a-z_\$][a-z_\$0-9]*)(=(.*))?$/i)) {
- var sym = defsym(RegExp.$1),
- val = RegExp.$2 ? defval(RegExp.$2.substr(1)) : [ 'name', 'true' ];
- options.defines[sym] = val;
- }
- else {
- throw "The --define option expects SYMBOL[=value]";
- }
- } catch(ex) {
- sys.print("ERROR: In option --define "+defarg+"\n"+ex+"\n");
- process.exit(1);
- }
- break;
- case "--define-from-module":
- var defmodarg = args.shift(),
- defmodule = require(defmodarg),
- sym,
- val;
- for (sym in defmodule) {
- if (defmodule.hasOwnProperty(sym)) {
- options.defines[sym] = function(val) {
- if (typeof val == "string")
- return [ "string", val ];
- if (typeof val == "number")
- return [ "num", val ];
- if (val === true)
- return [ 'name', 'true' ];
- if (val === false)
- return [ 'name', 'false' ];
- if (val === null)
- return [ 'name', 'null' ];
- if (val === undefined)
- return [ 'name', 'undefined' ];
- sys.print("ERROR: In option --define-from-module "+defmodarg+"\n");
- sys.print("ERROR: Unknown object type for: "+sym+"="+val+"\n");
- process.exit(1);
- return null;
- }(defmodule[sym]);
- }
- }
- break;
- case "--ascii":
- options.codegen_options.ascii_only = true;
- break;
- case "--make":
- options.make = true;
- break;
- case "--inline-script":
- options.codegen_options.inline_script = true;
- break;
- default:
- filename = v;
- break out;
- }
-}
-
-if (options.verbose) {
- pro.set_logger(function(msg){
- sys.debug(msg);
- });
-}
-
-jsp.set_logger(function(msg){
- sys.debug(msg);
-});
-
-if (options.make) {
- options.out_same_file = false; // doesn't make sense in this case
- var makefile = JSON.parse(fs.readFileSync(filename || "Makefile.uglify.js").toString());
- output(makefile.files.map(function(file){
- var code = fs.readFileSync(file.name);
- if (file.module) {
- code = "!function(exports, global){global = this;\n" + code + "\n;this." + file.module + " = exports;}({})";
- }
- else if (file.hide) {
- code = "(function(){" + code + "}());";
- }
- return squeeze_it(code);
- }).join("\n"));
-}
-else if (filename) {
- fs.readFile(filename, "utf8", function(err, text){
- if (err) throw err;
- output(squeeze_it(text));
- });
-}
-else {
- var stdin = process.openStdin();
- stdin.setEncoding("utf8");
- var text = "";
- stdin.on("data", function(chunk){
- text += chunk;
- });
- stdin.on("end", function() {
- output(squeeze_it(text));
- });
-}
-
-function output(text) {
- var out;
- if (options.out_same_file && filename)
- options.output = filename;
- if (options.output === true) {
- out = process.stdout;
- } else {
- out = fs.createWriteStream(options.output, {
- flags: "w",
- encoding: "utf8",
- mode: 0644
- });
- }
- out.write(text.replace(/;*$/, ";"));
- if (options.output !== true) {
- out.end();
- }
-};
-
-// --------- main ends here.
-
-function show_copyright(comments) {
- var ret = "";
- for (var i = 0; i < comments.length; ++i) {
- var c = comments[i];
- if (c.type == "comment1") {
- ret += "//" + c.value + "\n";
- } else {
- ret += "/*" + c.value + "*/";
- }
- }
- return ret;
-};
-
-function squeeze_it(code) {
- var result = "";
- if (options.show_copyright) {
- var tok = jsp.tokenizer(code), c;
- c = tok();
- result += show_copyright(c.comments_before);
- }
- try {
- var ast = time_it("parse", function(){ return jsp.parse(code); });
- if (options.consolidate) ast = time_it("consolidate", function(){
- return consolidator.ast_consolidate(ast);
- });
- if (options.lift_vars) {
- ast = time_it("lift", function(){ return pro.ast_lift_variables(ast); });
- }
- if (options.mangle) ast = time_it("mangle", function(){
- return pro.ast_mangle(ast, {
- toplevel : options.mangle_toplevel,
- defines : options.defines,
- except : options.reserved_names,
- no_functions : options.no_mangle_functions
- });
- });
- if (options.squeeze) ast = time_it("squeeze", function(){
- ast = pro.ast_squeeze(ast, {
- make_seqs : options.make_seqs,
- dead_code : options.dead_code,
- keep_comps : !options.unsafe
- });
- if (options.unsafe)
- ast = pro.ast_squeeze_more(ast);
- return ast;
- });
- if (options.ast)
- return sys.inspect(ast, null, null);
- result += time_it("generate", function(){ return pro.gen_code(ast, options.codegen_options) });
- if (!options.codegen_options.beautify && options.max_line_length) {
- result = time_it("split", function(){ return pro.split_lines(result, options.max_line_length) });
- }
- return result;
- } catch(ex) {
- sys.debug(ex.stack);
- sys.debug(sys.inspect(ex));
- sys.debug(JSON.stringify(ex));
- process.exit(1);
- }
-};
-
-function time_it(name, cont) {
- if (!options.verbose)
- return cont();
- var t1 = new Date().getTime();
- try { return cont(); }
- finally { sys.debug("// " + name + ": " + ((new Date().getTime() - t1) / 1000).toFixed(3) + " sec."); }
-};
\ No newline at end of file
diff --git a/build/yuicompressor.jar b/build/yuicompressor.jar
deleted file mode 100644
index c29470bd..00000000
Binary files a/build/yuicompressor.jar and /dev/null differ
diff --git a/dist/basic/jquery.qtip.css b/dist/basic/jquery.qtip.css
new file mode 100644
index 00000000..77d6074f
--- /dev/null
+++ b/dist/basic/jquery.qtip.css
@@ -0,0 +1,3 @@
+/*! qTip2 - Pretty powerful tooltips - v2.0.0 - 2012-07-19
+* http://craigsworks.com/projects/qtip2/
+* Copyright (c) 2012 Craig Michael Thompson; Licensed MIT, GPL */
diff --git a/dist/jquery.qtip.basic.js b/dist/basic/jquery.qtip.js
similarity index 94%
rename from dist/jquery.qtip.basic.js
rename to dist/basic/jquery.qtip.js
index f2b28031..cd0cb56e 100644
--- a/dist/jquery.qtip.basic.js
+++ b/dist/basic/jquery.qtip.js
@@ -1,22 +1,13 @@
-/*!
-* qTip2 - Pretty powerful tooltips
+/*! qTip2 - Pretty powerful tooltips - v2.0.0 - 2012-07-19
* http://craigsworks.com/projects/qtip2/
-*
-* Version: 2.0.0pre
-* Copyright 2009-2010 Craig Michael Thompson - http://craigsworks.com
-*
-* Dual licensed under MIT or GPLv2 licenses
-* http://en.wikipedia.org/wiki/MIT_License
-* http://en.wikipedia.org/wiki/GNU_General_Public_License
-*
-* Date: Mon Jul 16 00:57:01 2012 +0100
-*/
+* Copyright (c) 2012 Craig Michael Thompson; Licensed MIT, GPL */
/*jslint browser: true, onevar: true, undef: true, nomen: true, bitwise: true, regexp: true, newcap: true, immed: true, strict: true */
/*global window: false, jQuery: false, console: false, define: false */
// Uses AMD or browser globals to create a jQuery plugin.
(function(factory) {
+ "use strict";
if(typeof define === 'function' && define.amd) {
define(['jquery'], factory);
}
@@ -32,7 +23,6 @@
var TRUE = true,
FALSE = false,
NULL = null,
- undefined,
// Side names and other stuff
X = 'x', Y = 'y',
@@ -83,6 +73,7 @@
}
}
+
// Option object sanitizer
function sanitizeOptions(opts)
{
@@ -441,10 +432,10 @@ function QTip(target, options, id, attr)
}
/*
- * If we're still rendering... insert into 'fx' queue our image dimension
- * checker which will halt the showing of the tooltip until image dimensions
- * can be detected properly.
- */
+ * If we're still rendering... insert into 'fx' queue our image dimension
+ * checker which will halt the showing of the tooltip until image dimensions
+ * can be detected properly.
+ */
if(self.rendered < 0) { tooltip.queue('fx', detectImages); }
// We're fully rendered, so reset isDrawing flag and proceed without queue delay
@@ -560,9 +551,9 @@ function QTip(target, options, id, attr)
}
/*
- * Make sure hoverIntent functions properly by using mouseleave to clear show timer if
- * mouseenter/mouseout is used for show.event, even if it isn't in the users options.
- */
+ * Make sure hoverIntent functions properly by using mouseleave to clear show timer if
+ * mouseenter/mouseout is used for show.event, even if it isn't in the users options.
+ */
else if(/mouse(over|enter)/i.test(options.show.event)) {
targets.hide.bind('mouseleave'+namespace, function(event) {
clearTimeout(self.timers.show);
@@ -844,9 +835,9 @@ function QTip(target, options, id, attr)
assignEvents();
/* Queue this part of the render process in our fx queue so we can
- * load images before the tooltip renders fully.
- *
- * See: updateContent method
+ * load images before the tooltip renders fully.
+ *
+ * See: updateContent method
*/
tooltip.queue('fx', function(next) {
// Trigger tooltiprender event and pass original triggering event as original
@@ -944,10 +935,10 @@ function QTip(target, options, id, attr)
sanitizeOptions(options);
/*
- * Execute any valid callbacks for the set options
- * Also set isPositioning/isDrawing so we don't get loads of redundant repositioning
- * and redraw calls.
- */
+ * Execute any valid callbacks for the set options
+ * Also set isPositioning/isDrawing so we don't get loads of redundant repositioning
+ * and redraw calls.
+ */
isPositioning = isDrawing = 1; $.each(option, callback); isPositioning = isDrawing = 0;
// Update position / redraw if needed
@@ -1460,7 +1451,7 @@ function init(id, opts)
html5 = elem.data(opts.metadata.name || 'qtipopts');
// If we don't get an object returned attempt to parse it manualyl without parseJSON
- try { html5 = typeof html5 === 'string' ? (new Function("return " + html5))() : html5; }
+ try { html5 = typeof html5 === 'string' ? $.parseJSON(html5) : html5; }
catch(e) { log('Unable to parse HTML5 attribute data: ' + html5); }
// Merge in and sanitize metadata
@@ -1613,19 +1604,19 @@ QTIP.bind = function(opts, event)
};
/*
- * Make sure hoverIntent functions properly by using mouseleave as a hide event if
- * mouseenter/mouseout is used for show.event, even if it isn't in the users options.
- */
+ * Make sure hoverIntent functions properly by using mouseleave as a hide event if
+ * mouseenter/mouseout is used for show.event, even if it isn't in the users options.
+ */
if(/mouse(over|enter)/i.test(events.show) && !/mouse(out|leave)/i.test(events.hide)) {
events.hide += ' mouseleave' + namespace;
}
/*
- * Also make sure initial mouse targetting works correctly by caching mousemove coords
- * on show targets before the tooltip has rendered.
- *
- * Also set onTarget when triggered to keep mouse tracking working
- */
+ * Also make sure initial mouse targetting works correctly by caching mousemove coords
+ * on show targets before the tooltip has rendered.
+ *
+ * Also set onTarget when triggered to keep mouse tracking working
+ */
targets.show.bind('mousemove'+namespace, function(event) {
MOUSE = { pageX: event.pageX, pageY: event.pageY, type: 'mousemove' };
api.cache.onTarget = TRUE;
@@ -1731,16 +1722,16 @@ PLUGINS = QTIP.plugins = {
},
/*
- * iOS version detection
- */
+ * iOS version detection
+ */
iOS: parseFloat(
('' + (/CPU.*OS ([0-9_]{1,5})|(CPU like).*AppleWebKit.*Mobile/i.exec(navigator.userAgent) || [0,''])[1])
.replace('undefined', '3_2').replace('_', '.').replace('_', '')
) || FALSE,
/*
- * jQuery-specific $.fn overrides
- */
+ * jQuery-specific $.fn overrides
+ */
fn: {
/* Allow other plugins to successfully retrieve the title of an element with a qTip applied */
attr: function(attr, val) {
@@ -1814,7 +1805,7 @@ if(!$.ui) {
}
// Set global qTip properties
-QTIP.version = '2.0.0pre';
+QTIP.version = '@VERSION';
QTIP.nextid = 0;
QTIP.inactiveEvents = 'click dblclick mousedown mouseup mousemove mouseleave mouseenter'.split(' ');
QTIP.zindex = 15000;
@@ -1892,4 +1883,5 @@ QTIP.defaults = {
};
+
}));
\ No newline at end of file
diff --git a/dist/basic/jquery.qtip.min.css b/dist/basic/jquery.qtip.min.css
new file mode 100644
index 00000000..950b6033
--- /dev/null
+++ b/dist/basic/jquery.qtip.min.css
@@ -0,0 +1 @@
+/*! qTip2 v2.0.0 | http://craigsworks.com/projects/qtip2/ | Licensed MIT, GPL */
diff --git a/dist/basic/jquery.qtip.min.js b/dist/basic/jquery.qtip.min.js
new file mode 100644
index 00000000..d7682d65
--- /dev/null
+++ b/dist/basic/jquery.qtip.min.js
@@ -0,0 +1,2 @@
+/*! qTip2 v2.0.0 | http://craigsworks.com/projects/qtip2/ | Licensed MIT, GPL */
+(function(a){"use strict",typeof define=="function"&&define.amd?define(["jquery"],a):jQuery&&!jQuery.fn.qtip&&a(jQuery)})(function(a){function G(){G.history=G.history||[],G.history.push(arguments);if("object"==typeof console){var a=console[console.warn?"warn":"log"],b=Array.prototype.slice.call(arguments),c;typeof arguments[0]=="string"&&(b[0]="qTip2: "+b[0]),c=a.apply?a.apply(console,b):a(b)}}function H(b){var e;if(!b||"object"!=typeof b)return c;if(b.metadata===d||"object"!=typeof b.metadata)b.metadata={type:b.metadata};if("content"in b){if(b.content===d||"object"!=typeof b.content||b.content.jquery)b.content={text:b.content};e=b.content.text||c,!a.isFunction(e)&&(!e&&!e.attr||e.length<1||"object"==typeof e&&!e.jquery)&&(b.content.text=c);if("title"in b.content){if(b.content.title===d||"object"!=typeof b.content.title)b.content.title={text:b.content.title};e=b.content.title.text||c,!a.isFunction(e)&&(!e&&!e.attr||e.length<1||"object"==typeof e&&!e.jquery)&&(b.content.title.text=c)}}return"position"in b&&(b.position===d||"object"!=typeof b.position)&&(b.position={my:b.position,at:b.position}),"show"in b&&(b.show===d||"object"!=typeof b.show)&&(b.show.jquery?b.show={target:b.show}:b.show={event:b.show}),"hide"in b&&(b.hide===d||"object"!=typeof b.hide)&&(b.hide.jquery?b.hide={target:b.hide}:b.hide={event:b.hide}),"style"in b&&(b.style===d||"object"!=typeof b.style)&&(b.style={classes:b.style}),a.each(r,function(){this.sanitize&&this.sanitize(b)}),b}function I(e,f,n,o){function N(a){var b=0,c,d=f,e=a.split(".");while(d=d[e[b++]])b",{"class":"ui-state-default ui-tooltip-close "+(f.style.widget?"":u+"-icon"),title:e,"aria-label":e}).prepend(a("",{"class":"ui-icon ui-icon-close",html:"×"})),L.button.appendTo(L.titlebar).attr("role","button").click(function(a){return J.hasClass(w)||p.hide(a),c}),p.redraw()}function R(){var c=D+"-title";L.titlebar&&P(),L.titlebar=a("",{"class":u+"-titlebar "+(f.style.widget?"ui-widget-header":"")}).append(L.title=a("",{id:c,"class":u+"-title","aria-atomic":b})).insertBefore(L.content).delegate(".ui-tooltip-close","mousedown keydown mouseup keyup mouseout",function(b){a(this).toggleClass("ui-state-active ui-state-focus",b.type.substr(-4)==="down")}).delegate(".ui-tooltip-close","mouseover mouseout",function(b){a(this).toggleClass("ui-state-hover",b.type==="mouseover")}),f.content.title.button?Q():p.rendered&&p.redraw()}function S(a){var b=L.button,d=L.title;if(!p.rendered)return c;a?(d||R(),Q()):b.remove()}function T(b,d){var f=L.title;if(!p.rendered||!b)return c;a.isFunction(b)&&(b=b.call(e,M.event,p));if(b===c||!b&&b!=="")return P(c);b.jquery&&b.length>0?f.empty().append(b.css({display:"block"})):f.html(b),p.redraw(),d!==c&&p.rendered&&J[0].offsetWidth>0&&p.reposition(M.event)}function U(b,d){function g(b){function h(e){e&&(delete g[e.src],clearTimeout(p.timers.img[e.src]),a(e).unbind(K)),a.isEmptyObject(g)&&(p.redraw(),d!==c&&p.reposition(M.event),b())}var e,g={};if((e=f.find("img[src]:not([height]):not([width])")).length===0)return h();e.each(function(b,c){if(g[c.src]!==undefined)return;var d=0,e=3;(function f(){if(c.height||c.width||d>e)return h(c);d+=1,p.timers.img[c.src]=setTimeout(f,700)})(),a(c).bind("error"+K+" load"+K,function(){h(this)}),g[c.src]=c})}var f=L.content;return!p.rendered||!b?c:(a.isFunction(b)&&(b=b.call(e,M.event,p)||""),b.jquery&&b.length>0?f.empty().append(b.css({display:"block"})):f.html(b),p.rendered<0?J.queue("fx",g):(I=0,g(a.noop)),p)}function V(){function j(a){if(J.hasClass(w))return c;clearTimeout(p.timers.show),clearTimeout(p.timers.hide);var d=function(){p.toggle(b,a)};f.show.delay>0?p.timers.show=setTimeout(d,f.show.delay):d()}function k(b){if(J.hasClass(w)||G||I)return c;var e=a(b.relatedTarget||b.target),h=e.closest(x)[0]===J[0],i=e[0]===g.show[0];clearTimeout(p.timers.show),clearTimeout(p.timers.hide);if(d.target==="mouse"&&h||f.hide.fixed&&/mouse(out|leave|move)/.test(b.type)&&(h||i)){try{b.preventDefault(),b.stopImmediatePropagation()}catch(j){}return}f.hide.delay>0?p.timers.hide=setTimeout(function(){p.hide(b)},f.hide.delay):p.hide(b)}function l(a){if(J.hasClass(w))return c;clearTimeout(p.timers.inactive),p.timers.inactive=setTimeout(function(){p.hide(a)},f.hide.inactive)}function m(a){p.rendered&&J[0].offsetWidth>0&&p.reposition(a)}var d=f.position,g={show:f.show.target,hide:f.hide.target,viewport:a(d.viewport),document:a(document),body:a(document.body),window:a(window)},h={show:a.trim(""+f.show.event).split(" "),hide:a.trim(""+f.hide.event).split(" ")},i=a.browser.msie&&parseInt(a.browser.version,10)===6;J.bind("mouseenter"+K+" mouseleave"+K,function(a){var b=a.type==="mouseenter";b&&p.focus(a),J.toggleClass(A,b)}),/mouse(out|leave)/i.test(f.hide.event)&&f.hide.leave==="window"&&g.window.bind("mouseleave"+K+" blur"+K,function(a){!/select|option/.test(a.target.nodeName)&&!a.relatedTarget&&p.hide(a)}),f.hide.fixed?(g.hide=g.hide.add(J),J.bind("mouseover"+K,function(){J.hasClass(w)||clearTimeout(p.timers.hide)})):/mouse(over|enter)/i.test(f.show.event)&&g.hide.bind("mouseleave"+K,function(a){clearTimeout(p.timers.show)}),(""+f.hide.event).indexOf("unfocus")>-1&&d.container.closest("html").bind("mousedown"+K,function(b){var c=a(b.target),d=p.rendered&&!J.hasClass(w)&&J[0].offsetWidth>0,f=c.parents(x).filter(J[0]).length>0;c[0]!==e[0]&&c[0]!==J[0]&&!f&&!e.has(c[0]).length&&!c.attr("disabled")&&p.hide(b)}),"number"==typeof f.hide.inactive&&(g.show.bind("qtip-"+n+"-inactive",l),a.each(q.inactiveEvents,function(a,b){g.hide.add(L.tooltip).bind(b+K+"-inactive",l)})),a.each(h.hide,function(b,c){var d=a.inArray(c,h.show),e=a(g.hide);d>-1&&e.add(g.show).length===e.length||c==="unfocus"?(g.show.bind(c+K,function(a){J[0].offsetWidth>0?k(a):j(a)}),delete h.show[d]):g.hide.bind(c+K,k)}),a.each(h.show,function(a,b){g.show.bind(b+K,j)}),"number"==typeof f.hide.distance&&g.show.add(J).bind("mousemove"+K,function(a){var b=M.origin||{},c=f.hide.distance,d=Math.abs;(d(a.pageX-b.pageX)>=c||d(a.pageY-b.pageY)>=c)&&p.hide(a)}),d.target==="mouse"&&(g.show.bind("mousemove"+K,function(a){s={pageX:a.pageX,pageY:a.pageY,type:"mousemove"}}),d.adjust.mouse&&(f.hide.event&&(J.bind("mouseleave"+K,function(a){(a.relatedTarget||a.target)!==g.show[0]&&p.hide(a)}),L.target.bind("mouseenter"+K+" mouseleave"+K,function(a){M.onTarget=a.type==="mouseenter"})),g.document.bind("mousemove"+K,function(a){p.rendered&&M.onTarget&&!J.hasClass(w)&&J[0].offsetWidth>0&&p.reposition(a||s)}))),(d.adjust.resize||g.viewport.length)&&(a.event.special.resize?g.viewport:g.window).bind("resize"+K,m),(g.viewport.length||i&&J.css("position")==="fixed")&&g.viewport.bind("scroll"+K,m)}function W(){var b=[f.show.target[0],f.hide.target[0],p.rendered&&L.tooltip[0],f.position.container[0],f.position.viewport[0],window,document];p.rendered?a([]).pushStack(a.grep(b,function(a){return typeof a=="object"})).unbind(K):f.show.target.unbind(K+"-create")}var p=this,C=document.body,D=u+"-"+n,G=0,I=0,J=a(),K=".qtip-"+n,L,M;p.id=n,p.rendered=c,p.destroyed=c,p.elements=L={target:e},p.timers={img:{}},p.options=f,p.checks={},p.plugins={},p.cache=M={event:{},target:a(),disabled:c,attr:o,onTarget:c,lastClass:""},p.checks.builtin={"^id$":function(d,e,f){var g=f===b?q.nextid:f,h=u+"-"+g;g!==c&&g.length>0&&!a("#"+h).length&&(J[0].id=h,L.content[0].id=h+"-content",L.title[0].id=h+"-title")},"^content.text$":function(a,b,c){U(c)},"^content.title.text$":function(a,b,c){if(!c)return P();!L.title&&c&&R(),T(c)},"^content.title.button$":function(a,b,c){S(c)},"^position.(my|at)$":function(a,b,c){"string"==typeof c&&(a[b]=new r.Corner(c))},"^position.container$":function(a,b,c){p.rendered&&J.appendTo(c)},"^show.ready$":function(){p.rendered?p.toggle(b):p.render(1)},"^style.classes$":function(a,b,c){J.attr("class",u+" qtip ui-helper-reset "+c)},"^style.widget|content.title":O,"^events.(render|show|move|hide|focus|blur)$":function(b,c,d){J[(a.isFunction(d)?"":"un")+"bind"]("tooltip"+c,d)},"^(show|hide|position).(event|target|fixed|inactive|leave|distance|viewport|adjust)":function(){var a=f.position;J.attr("tracking",a.target==="mouse"&&a.adjust.mouse),W(),V()}},a.extend(p,{render:function(d){if(p.rendered)return p;var g=f.content.text,h=f.content.title.text,i=f.position,j=a.Event("tooltiprender");return a.attr(e[0],"aria-describedby",D),J=L.tooltip=a("",{id:D,"class":u+" qtip ui-helper-reset "+y+" "+f.style.classes+" "+u+"-pos-"+f.position.my.abbrev(),width:f.style.width||"",height:f.style.height||"",tracking:i.target==="mouse"&&i.adjust.mouse,role:"alert","aria-live":"polite","aria-atomic":c,"aria-describedby":D+"-content","aria-hidden":b}).toggleClass(w,M.disabled).data("qtip",p).appendTo(f.position.container).append(L.content=a("",{"class":u+"-content",id:D+"-content","aria-atomic":b})),p.rendered=-1,I=1,G=1,h&&(R(),a.isFunction(h)||T(h,c)),a.isFunction(g)||U(g,c),p.rendered=b,O(),a.each(f.events,function(b,c){a.isFunction(c)&&J.bind(b==="toggle"?"tooltipshow tooltiphide":"tooltip"+b,c)}),a.each(r,function(){this.initialize==="render"&&this(p)}),V(),J.queue("fx",function(a){j.originalEvent=M.event,J.trigger(j,[p]),I=0,G=0,p.redraw(),(f.show.ready||d)&&p.toggle(b,M.event,c),a()}),p},get:function(a){var b,c;switch(a.toLowerCase()){case"dimensions":b={height:J.outerHeight(),width:J.outerWidth()};break;case"offset":b=r.offset(J,f.position.container);break;default:c=N(a.toLowerCase()),b=c[0][c[1]],b=b.precedance?b.string():b}return b},set:function(e,g){function n(a,b){var c,d,e;for(c in l)for(d in l[c])if(e=(new RegExp(d,"i")).exec(a))b.push(e),l[c][d].apply(p,b)}var h=/^position\.(my|at|adjust|target|container)|style|content|show\.ready/i,i=/^content\.(title|attr)|style/i,j=c,k=c,l=p.checks,m;return"string"==typeof e?(m=e,e={},e[m]=g):e=a.extend(b,{},e),a.each(e,function(b,c){var d=N(b.toLowerCase()),f;f=d[0][d[1]],d[0][d[1]]="object"==typeof c&&c.nodeType?a(c):c,e[b]=[d[0],d[1],c,f],j=h.test(b)||j,k=i.test(b)||k}),H(f),G=I=1,a.each(e,n),G=I=0,p.rendered&&J[0].offsetWidth>0&&(j&&p.reposition(f.position.target==="mouse"?d:M.event),k&&p.redraw()),p},toggle:function(e,g){function u(){e?(a.browser.msie&&J[0].style.removeAttribute("filter"),J.css("overflow",""),"string"==typeof i.autofocus&&a(i.autofocus,J).focus(),i.target.trigger("qtip-"+n+"-inactive")):J.css({display:"",visibility:"",opacity:"",left:"",top:""}),t=a.Event("tooltip"+(e?"visible":"hidden")),t.originalEvent=g?M.event:d,J.trigger(t,[p])}if(!p.rendered)return e?p.render(1):p;var h=e?"show":"hide",i=f[h],j=f[e?"hide":"show"],k=f.position,l=f.content,m=J[0].offsetWidth>0,o=e||i.target.length===1,q=!g||i.target.length<2||M.target[0]===g.target,r,t;(typeof e).search("boolean|number")&&(e=!m);if(!J.is(":animated")&&m===e&&q)return p;if(g){if(/over|enter/.test(g.type)&&/out|leave/.test(M.event.type)&&f.show.target.add(g.target).length===f.show.target.length&&J.has(g.relatedTarget).length)return p;M.event=a.extend({},g)}return t=a.Event("tooltip"+h),t.originalEvent=g?M.event:d,J.trigger(t,[p,90]),t.isDefaultPrevented()?p:(a.attr(J[0],"aria-hidden",!e),e?(M.origin=a.extend({},s),p.focus(g),a.isFunction(l.text)&&U(l.text,c),a.isFunction(l.title.text)&&T(l.title.text,c),!F&&k.target==="mouse"&&k.adjust.mouse&&(a(document).bind("mousemove.qtip",function(a){s={pageX:a.pageX,pageY:a.pageY,type:"mousemove"}}),F=b),p.reposition(g,arguments[2]),(t.solo=!!i.solo)&&a(x,i.solo).not(J).qtip("hide",t)):(clearTimeout(p.timers.show),delete M.origin,F&&!a(x+'[tracking="true"]:visible',i.solo).not(J).length&&(a(document).unbind("mousemove.qtip"),F=c),p.blur(g)),i.effect===c||o===c?(J[h](),u.call(J)):a.isFunction(i.effect)?(J.stop(1,1),i.effect.call(J,p),J.queue("fx",function(a){u(),a()})):J.fadeTo(90,e?1:0,u),e&&i.target.trigger("qtip-"+n+"-inactive"),p)},show:function(a){return p.toggle(b,a)},hide:function(a){return p.toggle(c,a)},focus:function(b){if(!p.rendered)return p;var c=a(x),d=parseInt(J[0].style.zIndex,10),e=q.zindex+c.length,f=a.extend({},b),g,h;return J.hasClass(z)||(h=a.Event("tooltipfocus"),h.originalEvent=f,J.trigger(h,[p,e]),h.isDefaultPrevented()||(d!==e&&(c.each(function(){this.style.zIndex>d&&(this.style.zIndex=this.style.zIndex-1)}),c.filter("."+z).qtip("blur",f)),J.addClass(z)[0].style.zIndex=e)),p},blur:function(b){var c=a.extend({},b),d;return J.removeClass(z),d=a.Event("tooltipblur"),d.originalEvent=c,J.trigger(d,[p]),p},reposition:function(b,d){if(!p.rendered||G)return p;G=1;var e=f.position.target,g=f.position,h=g.my,n=g.at,o=g.adjust,q=o.method.split(" "),t=J.outerWidth(),u=J.outerHeight(),v=0,w=0,x=a.Event("tooltipmove"),y=J.css("position")==="fixed",z=g.viewport,A={left:0,top:0},B=g.container,C=J[0].offsetWidth>0,D,E,F;if(a.isArray(e)&&e.length===2)n={x:j,y:i},A={left:e[0],top:e[1]};else if(e==="mouse"&&(b&&b.pageX||M.event.pageX))n={x:j,y:i},b=(b&&(b.type==="resize"||b.type==="scroll")?M.event:b&&b.pageX&&b.type==="mousemove"?b:s&&s.pageX&&(o.mouse||!b||!b.pageX)?{pageX:s.pageX,pageY:s.pageY}:!o.mouse&&M.origin&&M.origin.pageX&&f.show.distance?M.origin:b)||b||M.event||s||{},A={top:b.pageY,left:b.pageX};else{e==="event"&&b&&b.target&&b.type!=="scroll"&&b.type!=="resize"?M.target=a(b.target):e!=="event"&&(M.target=a(e.jquery?e:L.target)),e=M.target,e=a(e).eq(0);if(e.length===0)return p;e[0]===document||e[0]===window?(v=r.iOS?window.innerWidth:e.width(),w=r.iOS?window.innerHeight:e.height(),e[0]===window&&(A={top:(z||e).scrollTop(),left:(z||e).scrollLeft()})):r.imagemap&&e.is("area")?D=r.imagemap(p,e,n,r.viewport?q:c):r.svg&&typeof e[0].xmlbase=="string"?D=r.svg(p,e,n,r.viewport?q:c):(v=e.outerWidth(),w=e.outerHeight(),A=r.offset(e,B)),D&&(v=D.width,w=D.height,E=D.offset,A=D.position);if(r.iOS>3.1&&r.iOS<4.1||r.iOS>=4.3&&r.iOS<4.33||!r.iOS&&y)F=a(window),A.left-=F.scrollLeft(),A.top-=F.scrollTop();A.left+=n.x===l?v:n.x===m?v/2:0,A.top+=n.y===k?w:n.y===m?w/2:0}return A.left+=o.x+(h.x===l?-t:h.x===m?-t/2:0),A.top+=o.y+(h.y===k?-u:h.y===m?-u/2:0),r.viewport?(A.adjusted=r.viewport(p,A,g,v,w,t,u),E&&A.adjusted.left&&(A.left+=E.left),E&&A.adjusted.top&&(A.top+=E.top)):A.adjusted={left:0,top:0},x.originalEvent=a.extend({},b),J.trigger(x,[p,A,z.elem||z]),x.isDefaultPrevented()?p:(delete A.adjusted,d===c||!C||isNaN(A.left)||isNaN(A.top)||e==="mouse"||!a.isFunction(g.effect)?J.css(A):a.isFunction(g.effect)&&(g.effect.call(J,p,a.extend({},A)),J.queue(function(b){a(this).css({opacity:"",height:""}),a.browser.msie&&this.style.removeAttribute("filter"),b()})),G=0,p)},redraw:function(){if(p.rendered<1||I)return p;var a=f.position.container,b,c,d,e;return I=1,f.style.height&&J.css(h,f.style.height),f.style.width?J.css(g,f.style.width):(J.css(g,"").addClass(B),c=J.width()+1,d=J.css("max-width")||"",e=J.css("min-width")||"",b=(d+e).indexOf("%")>-1?a.width()/100:0,d=(d.indexOf("%")>-1?b:1)*parseInt(d,10)||c,e=(e.indexOf("%")>-1?b:1)*parseInt(e,10)||0,c=d+e?Math.min(Math.max(c,e),d):c,J.css(g,Math.round(c)).removeClass(B)),I=0,p},disable:function(b){return"boolean"!=typeof b&&(b=!J.hasClass(w)&&!M.disabled),p.rendered?(J.toggleClass(w,b),a.attr(J[0],"aria-disabled",b)):M.disabled=!!b,p},enable:function(){return p.disable(c)},destroy:function(){var c=e[0],d=a.attr(c,E),g=e.data("qtip");p.destroyed=b,p.rendered&&(J.stop(1,0).remove(),a.each(p.plugins,function(){this.destroy&&this.destroy()})),clearTimeout(p.timers.show),clearTimeout(p.timers.hide),W();if(!g||p===g)a.removeData(c,"qtip"),f.suppress&&d&&(a.attr(c,"title",d),e.removeAttr(E)),e.removeAttr("aria-describedby");return e.unbind(".qtip-"+n),delete t[p.id],e}})}function J(e,f){var g,h,i,j,k,l=a(this),m=a(document.body),n=this===document?m:l,o=l.metadata?l.metadata(f.metadata):d,p=f.metadata.type==="html5"&&o?o[f.metadata.name]:d,s=l.data(f.metadata.name||"qtipopts");try{s=typeof s=="string"?a.parseJSON(s):s}catch(t){G("Unable to parse HTML5 attribute data: "+s)}j=a.extend(b,{},q.defaults,f,typeof s=="object"?H(s):d,H(p||o)),h=j.position,j.id=e;if("boolean"==typeof j.content.text){i=l.attr(j.content.attr);if(j.content.attr!==c&&i)j.content.text=i;else return G("Unable to locate content for tooltip! Aborting render of tooltip on element: ",l),c}h.container.length||(h.container=m),h.target===c&&(h.target=n),j.show.target===c&&(j.show.target=n),j.show.solo===b&&(j.show.solo=h.container.closest("body")),j.hide.target===c&&(j.hide.target=n),j.position.viewport===b&&(j.position.viewport=h.container),h.container=h.container.eq(0),h.at=new r.Corner(h.at),h.my=new r.Corner(h.my);if(a.data(this,"qtip"))if(j.overwrite)l.qtip("destroy");else if(j.overwrite===c)return c;return j.suppress&&(k=a.attr(this,"title"))&&a(this).removeAttr("title").attr(E,k).attr("title",""),g=new I(l,j,e,!!i),a.data(this,"qtip",g),l.bind("remove.qtip-"+e+" removeqtip.qtip-"+e,function(){g.destroy()}),g}"use strict";var b=!0,c=!1,d=null,e="x",f="y",g="width",h="height",i="top",j="left",k="bottom",l="right",m="center",n="flip",o="flipinvert",p="shift",q,r,s,t={},u="ui-tooltip",v="ui-widget",w="ui-state-disabled",x="div.qtip."+u,y=u+"-default",z=u+"-focus",A=u+"-hover",B=u+"-fluid",C="-31000px",D="_replacedByqTip",E="oldtitle",F;q=a.fn.qtip=function(e,f,g){var h=(""+e).toLowerCase(),i=d,j=a.makeArray(arguments).slice(1),k=j[j.length-1],l=this[0]?a.data(this[0],"qtip"):d;if(!arguments.length&&l||h==="api")return l;if("string"==typeof e)return this.each(function(){var d=a.data(this,"qtip");if(!d)return b;k&&k.timeStamp&&(d.cache.event=k);if(h!=="option"&&h!=="options"||!f)d[h]&&d[h].apply(d[h],j);else if(a.isPlainObject(f)||g!==undefined)d.set(f,g);else return i=d.get(f),c}),i!==d?i:this;if("object"==typeof e||!arguments.length)return l=H(a.extend(b,{},e)),q.bind.call(this,l,k)},q.bind=function(d,e){return this.each(function(f){function m(b){function d(){k.render(typeof b=="object"||g.show.ready),h.show.add(h.hide).unbind(j)}if(k.cache.disabled)return c;k.cache.event=a.extend({},b),k.cache.target=b?a(b.target):[undefined],g.show.delay>0?(clearTimeout(k.timers.show),k.timers.show=setTimeout(d,g.show.delay),i.show!==i.hide&&h.hide.bind(i.hide,function(){clearTimeout(k.timers.show)})):d()}var g,h,i,j,k,l;l=a.isArray(d.id)?d.id[f]:d.id,l=!l||l===c||l.length<1||t[l]?q.nextid++:t[l]=l,j=".qtip-"+l+"-create",k=J.call(this,l,d);if(k===c)return b;g=k.options,a.each(r,function(){this.initialize==="initialize"&&this(k)}),h={show:g.show.target,hide:g.hide.target},i={show:a.trim(""+g.show.event).replace(/ /g,j+" ")+j,hide:a.trim(""+g.hide.event).replace(/ /g,j+" ")+j},/mouse(over|enter)/i.test(i.show)&&!/mouse(out|leave)/i.test(i.hide)&&(i.hide+=" mouseleave"+j),h.show.bind("mousemove"+j,function(a){s={pageX:a.pageX,pageY:a.pageY,type:"mousemove"},k.cache.onTarget=b}),h.show.bind(i.show,m),(g.show.ready||g.prerender)&&m(e)})},r=q.plugins={Corner:function(a){a=(""+a).replace(/([A-Z])/," $1").replace(/middle/gi,m).toLowerCase(),this.x=(a.match(/left|right/i)||a.match(/center/)||["inherit"])[0].toLowerCase(),this.y=(a.match(/top|bottom|center/i)||["inherit"])[0].toLowerCase();var b=a.charAt(0);this.precedance=b==="t"||b==="b"?f:e,this.string=function(){return this.precedance===f?this.y+this.x:this.x+this.y},this.abbrev=function(){var a=this.x.substr(0,1),b=this.y.substr(0,1);return a===b?a:this.precedance===f?b+a:a+b},this.invertx=function(a){this.x=this.x===j?l:this.x===l?j:a||this.x},this.inverty=function(a){this.y=this.y===i?k:this.y===k?i:a||this.y},this.clone=function(){return{x:this.x,y:this.y,precedance:this.precedance,string:this.string,abbrev:this.abbrev,clone:this.clone,invertx:this.invertx,inverty:this.inverty}}},offset:function(b,c){function j(a,b){d.left+=b*a.scrollLeft(),d.top+=b*a.scrollTop()}var d=b.offset(),e=b.closest("body")[0],f=c,g,h,i;if(f){do f.css("position")!=="static"&&(h=f.position(),d.left-=h.left+(parseInt(f.css("borderLeftWidth"),10)||0)+(parseInt(f.css("marginLeft"),10)||0),d.top-=h.top+(parseInt(f.css("borderTopWidth"),10)||0)+(parseInt(f.css("marginTop"),10)||0),!g&&(i=f.css("overflow"))!=="hidden"&&i!=="visible"&&(g=f));while((f=a(f[0].offsetParent)).length);g&&g[0]!==e&&j(g,1)}return d},iOS:parseFloat((""+(/CPU.*OS ([0-9_]{1,5})|(CPU like).*AppleWebKit.*Mobile/i.exec(navigator.userAgent)||[0,""])[1]).replace("undefined","3_2").replace("_",".").replace("_",""))||c,fn:{attr:function(b,c){if(this.length){var d=this[0],e="title",f=a.data(d,"qtip");if(b===e&&f&&"object"==typeof f&&f.options.suppress)return arguments.length<2?a.attr(d,E):(f&&f.options.content.attr===e&&f.cache.attr&&f.set("content.text",c),this.attr(E,c))}return a.fn["attr"+D].apply(this,arguments)},clone:function(b){var c=a([]),d="title",e=a.fn["clone"+D].apply(this,arguments);return b||e.filter("["+E+"]").attr("title",function(){return a.attr(this,E)}).removeAttr(E),e}}},a.each(r.fn,function(c,d){if(!d||a.fn[c+D])return b;var e=a.fn[c+D]=a.fn[c];a.fn[c]=function(){return d.apply(this,arguments)||e.apply(this,arguments)}}),a.ui||(a["cleanData"+D]=a.cleanData,a.cleanData=function(b){for(var c=0,d;(d=b[c])!==undefined;c++)try{a(d).triggerHandler("removeqtip")}catch(e){}a["cleanData"+D](b)}),q.version="@VERSION",q.nextid=0,q.inactiveEvents="click dblclick mousedown mouseup mousemove mouseleave mouseenter".split(" "),q.zindex=15e3,q.defaults={prerender:c,id:c,overwrite:b,suppress:b,content:{text:b,attr:"title",title:{text:c,button:c}},position:{my:"top left",at:"bottom right",target:c,container:c,viewport:c,adjust:{x:0,y:0,mouse:b,resize:b,method:"flip flip"},effect:function(b,d,e){a(this).animate(d,{duration:200,queue:c})}},show:{target:c,event:"mouseenter",effect:b,delay:90,solo:c,ready:c,autofocus:c},hide:{target:c,event:"mouseleave",effect:b,delay:0,fixed:c,inactive:c,leave:"window",distance:c},style:{classes:"",widget:c,width:c,height:c,def:b},events:{render:d,move:d,show:d,hide:d,toggle:d,visible:d,hidden:d,focus:d,blur:d}}});
\ No newline at end of file
diff --git a/dist/comment b/dist/comment
new file mode 100644
index 00000000..ef0f9c7b
--- /dev/null
+++ b/dist/comment
@@ -0,0 +1 @@
+Fixed issue with non-poly shapes not correctly getting coordinates. Thanks sambowler.
\ No newline at end of file
diff --git a/dist/jquery.qtip.css b/dist/jquery.qtip.css
index f26317e2..17968578 100644
--- a/dist/jquery.qtip.css
+++ b/dist/jquery.qtip.css
@@ -1,16 +1,6 @@
-/*!
-* qTip2 - Pretty powerful tooltips
+/*! qTip2 - Pretty powerful tooltips - v2.0.0 - 2012-07-19
* http://craigsworks.com/projects/qtip2/
-*
-* Version: 2.0.0pre
-* Copyright 2009-2010 Craig Michael Thompson - http://craigsworks.com
-*
-* Dual licensed under MIT or GPLv2 licenses
-* http://en.wikipedia.org/wiki/MIT_License
-* http://en.wikipedia.org/wiki/GNU_General_Public_License
-*
-* Date: Mon Jul 16 00:57:01 2012 +0100
-*/
+* Copyright (c) 2012 Craig Michael Thompson; Licensed MIT, GPL */
/* Core qTip styles */
.ui-tooltip, .qtip{
@@ -44,7 +34,6 @@
text-align: left;
word-wrap: break-word;
- overflow: hidden;
}
.ui-tooltip-titlebar{
@@ -57,9 +46,9 @@
font-weight: bold;
}
- .ui-tooltip-titlebar + .ui-tooltip-content{ border-top-width: 0px !important; }
+ .ui-tooltip-titlebar + .ui-tooltip-content{ border-top-width: 0 !important; }
- /*! Default close button class */
+ /* Default close button class */
.ui-tooltip-titlebar .ui-state-default{
position: absolute;
right: 4px;
@@ -79,6 +68,7 @@
.ui-tooltip-icon .ui-icon{
display: block;
text-indent: -1000em;
+ direction: ltr;
}
.ui-tooltip-icon, .ui-tooltip-icon .ui-icon{
@@ -102,17 +92,12 @@
/* Applied to 'focused' tooltips e.g. most recently displayed/interacted with */
-.ui-tooltip-focus{
-
-}
+.ui-tooltip-focus{}
/* Applied on hover of tooltips i.e. added/removed on mouseenter/mouseleave respectively */
-.ui-tooltip-hover{
-
-}
+.ui-tooltip-hover{}
-
-/*! Default tooltip style */
+/* Default tooltip style */
.ui-tooltip-default{
border-color: #F1D031;
background-color: #FFFFA3;
@@ -134,162 +119,15 @@
color: #111;
}
-/* Modal plugin */
-#qtip-overlay{
- position: fixed;
- left: -10000em;
- top: -10000em;
-}
-
- /* Applied to modals with show.modal.blur set to true */
- #qtip-overlay.blurs{ cursor: pointer; }
-
- /* Change opacity of overlay here */
- #qtip-overlay div{
- position: absolute;
- left: 0; top: 0;
- width: 100%; height: 100%;
-
- background-color: black;
-
- opacity: 0.7;
- filter:alpha(opacity=70);
- -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=70)";
- }
-
-/* Tips plugin */
-.ui-tooltip .ui-tooltip-tip{
- margin: 0 auto;
- overflow: hidden;
- z-index: 10;
-}
-
- .ui-tooltip .ui-tooltip-tip,
- .ui-tooltip .ui-tooltip-tip *{
- position: absolute;
-
- line-height: 0.1px !important;
- font-size: 0.1px !important;
- color: #123456;
-
- background: transparent;
- border: 0px dashed transparent;
- }
-
- .ui-tooltip .ui-tooltip-tip canvas{ top: 0; left: 0; }
-
-
-/*! Light tooltip style */
-.ui-tooltip-light{
- background-color: white;
- border-color: #E2E2E2;
- color: #454545;
-}
-
- .ui-tooltip-light .ui-tooltip-titlebar{
- background-color: #f1f1f1;
- }
-
-
-/*! Dark tooltip style */
-.ui-tooltip-dark{
- background-color: #505050;
- border-color: #303030;
- color: #f3f3f3;
-}
-
- .ui-tooltip-dark .ui-tooltip-titlebar{
- background-color: #404040;
- }
-
- .ui-tooltip-dark .ui-tooltip-icon{
- border-color: #444;
- }
-
- .ui-tooltip-dark .ui-tooltip-titlebar .ui-state-hover{
- border-color: #303030;
- }
-
-
-/*! Cream tooltip style */
-.ui-tooltip-cream{
- background-color: #FBF7AA;
- border-color: #F9E98E;
- color: #A27D35;
-}
-
- .ui-tooltip-cream .ui-tooltip-titlebar{
- background-color: #F0DE7D;
- }
-
- .ui-tooltip-cream .ui-state-default .ui-tooltip-icon{
- background-position: -82px 0;
- }
-
-
-/*! Red tooltip style */
-.ui-tooltip-red{
- background-color: #F78B83;
- border-color: #D95252;
- color: #912323;
-}
-
- .ui-tooltip-red .ui-tooltip-titlebar{
- background-color: #F06D65;
- }
- .ui-tooltip-red .ui-state-default .ui-tooltip-icon{
- background-position: -102px 0;
- }
-
- .ui-tooltip-red .ui-tooltip-icon{
- border-color: #D95252;
- }
-
- .ui-tooltip-red .ui-tooltip-titlebar .ui-state-hover{
- border-color: #D95252;
- }
-
-
-/*! Green tooltip style */
-.ui-tooltip-green{
- background-color: #CAED9E;
- border-color: #90D93F;
- color: #3F6219;
-}
-
- .ui-tooltip-green .ui-tooltip-titlebar{
- background-color: #B0DE78;
- }
-
- .ui-tooltip-green .ui-state-default .ui-tooltip-icon{
- background-position: -42px 0;
- }
-
-
-/*! Blue tooltip style */
-.ui-tooltip-blue{
- background-color: #E5F6FE;
- border-color: #ADD9ED;
- color: #5E99BD;
-}
-
- .ui-tooltip-blue .ui-tooltip-titlebar{
- background-color: #D0E9F5;
- }
-
- .ui-tooltip-blue .ui-state-default .ui-tooltip-icon{
- background-position: -2px 0;
- }
-
-/*! Add shadows to your tooltips in: FF3+, Chrome 2+, Opera 10.6+, IE9+, Safari 2+ */
+/* Add shadows to your tooltips in: FF3+, Chrome 2+, Opera 10.6+, IE9+, Safari 2+ */
.ui-tooltip-shadow{
-webkit-box-shadow: 1px 1px 3px 1px rgba(0, 0, 0, 0.15);
-moz-box-shadow: 1px 1px 3px 1px rgba(0, 0, 0, 0.15);
box-shadow: 1px 1px 3px 1px rgba(0, 0, 0, 0.15);
}
-/*! Add rounded corners to your tooltips in: FF3+, Chrome 2+, Opera 10.6+, IE9+, Safari 2+ */
+/* Add rounded corners to your tooltips in: FF3+, Chrome 2+, Opera 10.6+, IE9+, Safari 2+ */
.ui-tooltip-rounded,
.ui-tooltip-tipsy,
.ui-tooltip-bootstrap{
@@ -298,7 +136,7 @@
border-radius: 5px;
}
-/*! Youtube tooltip style */
+/* Youtube tooltip style */
.ui-tooltip-youtube{
-moz-border-radius: 2px;
-webkit-border-radius: 2px;
@@ -312,12 +150,11 @@
border-width: 0;
background: #4A4A4A;
+ background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0,#4A4A4A),color-stop(100%,black));
+ background-image: -webkit-linear-gradient(top,#4A4A4A 0,black 100%);
background-image: -moz-linear-gradient(top,#4A4A4A 0,black 100%);
background-image: -ms-linear-gradient(top,#4A4A4A 0,black 100%);
background-image: -o-linear-gradient(top,#4A4A4A 0,black 100%);
- background-image: -webkit-gradient(linear,left top,left bottom,color-stop(0,#4A4A4A),color-stop(100%,black));
- background-image: -webkit-linear-gradient(top,#4A4A4A 0,black 100%);
- background-image: linear-gradient(to bottom,#4A4A4A 0,black 100%);
}
.ui-tooltip-youtube .ui-tooltip-titlebar{
@@ -346,8 +183,11 @@
.ui-tooltip-jtools{
background: #232323;
background: rgba(0, 0, 0, 0.7);
- background-image: -moz-linear-gradient(top, #717171, #232323);
background-image: -webkit-gradient(linear, left top, left bottom, from(#717171), to(#232323));
+ background-image: -moz-linear-gradient(top, #717171, #232323);
+ background-image: -webkit-linear-gradient(top, #717171, #232323);
+ background-image: -ms-linear-gradient(top, #717171, #232323);
+ background-image: -o-linear-gradient(top, #717171, #232323);
border: 2px solid #ddd;
border: 2px solid rgba(241,241,241,1);
@@ -421,7 +261,7 @@
background: rgba(0, 0, 0, .87);
color: white;
- border: 0px solid transparent;
+ border: 0 solid transparent;
font-size: 11px;
font-family: 'Lucida Grande', sans-serif;
@@ -469,8 +309,11 @@
color: white;
background: #3A79B8;
- background-image: -moz-linear-gradient(top, #3A79B8, #2E629D);
background-image: -webkit-gradient(linear, left top, left bottom, from(#3A79B8), to(#2E629D));
+ background-image: -webkit-linear-gradient(top, #3A79B8, #2E629D);
+ background-image: -moz-linear-gradient(top, #3A79B8, #2E629D);
+ background-image: -ms-linear-gradient(top, #3A79B8, #2E629D);
+ background-image: -o-linear-gradient(top, #3A79B8, #2E629D);
filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#3A79B8,endColorstr=#2E629D);
-ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#3A79B8,endColorstr=#2E629D)";
}
@@ -565,3 +408,50 @@
-ms-filter: none;
}
+
+/* Tips plugin */
+.ui-tooltip .ui-tooltip-tip{
+ margin: 0 auto;
+ overflow: hidden;
+ z-index: 10;
+}
+
+ .ui-tooltip .ui-tooltip-tip,
+ .ui-tooltip .ui-tooltip-tip *{
+ position: absolute;
+
+ line-height: 0.1px !important;
+ font-size: 0.1px !important;
+ color: #123456;
+
+ background: transparent;
+ border: 0 dashed transparent;
+ }
+
+ .ui-tooltip .ui-tooltip-tip canvas{ top: 0; left: 0; }
+
+
+
+/* Modal plugin */
+#qtip-overlay{
+ position: fixed;
+ left: -10000em;
+ top: -10000em;
+}
+
+ /* Applied to modals with show.modal.blur set to true */
+ #qtip-overlay.blurs{ cursor: pointer; }
+
+ /* Change opacity of overlay here */
+ #qtip-overlay div{
+ position: absolute;
+ left: 0; top: 0;
+ width: 100%; height: 100%;
+
+ background-color: black;
+
+ opacity: 0.7;
+ filter:alpha(opacity=70);
+ -ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=70)";
+ }
+
diff --git a/dist/jquery.qtip.js b/dist/jquery.qtip.js
index fb9a2acf..b8c2b705 100644
--- a/dist/jquery.qtip.js
+++ b/dist/jquery.qtip.js
@@ -1,22 +1,13 @@
-/*!
-* qTip2 - Pretty powerful tooltips
+/*! qTip2 - Pretty powerful tooltips - v2.0.0 - 2012-07-19
* http://craigsworks.com/projects/qtip2/
-*
-* Version: 2.0.0pre
-* Copyright 2009-2010 Craig Michael Thompson - http://craigsworks.com
-*
-* Dual licensed under MIT or GPLv2 licenses
-* http://en.wikipedia.org/wiki/MIT_License
-* http://en.wikipedia.org/wiki/GNU_General_Public_License
-*
-* Date: Mon Jul 16 00:57:01 2012 +0100
-*/
+* Copyright (c) 2012 Craig Michael Thompson; Licensed MIT, GPL */
/*jslint browser: true, onevar: true, undef: true, nomen: true, bitwise: true, regexp: true, newcap: true, immed: true, strict: true */
/*global window: false, jQuery: false, console: false, define: false */
// Uses AMD or browser globals to create a jQuery plugin.
(function(factory) {
+ "use strict";
if(typeof define === 'function' && define.amd) {
define(['jquery'], factory);
}
@@ -32,7 +23,6 @@
var TRUE = true,
FALSE = false,
NULL = null,
- undefined,
// Side names and other stuff
X = 'x', Y = 'y',
@@ -83,6 +73,7 @@
}
}
+
// Option object sanitizer
function sanitizeOptions(opts)
{
@@ -441,10 +432,10 @@ function QTip(target, options, id, attr)
}
/*
- * If we're still rendering... insert into 'fx' queue our image dimension
- * checker which will halt the showing of the tooltip until image dimensions
- * can be detected properly.
- */
+ * If we're still rendering... insert into 'fx' queue our image dimension
+ * checker which will halt the showing of the tooltip until image dimensions
+ * can be detected properly.
+ */
if(self.rendered < 0) { tooltip.queue('fx', detectImages); }
// We're fully rendered, so reset isDrawing flag and proceed without queue delay
@@ -560,9 +551,9 @@ function QTip(target, options, id, attr)
}
/*
- * Make sure hoverIntent functions properly by using mouseleave to clear show timer if
- * mouseenter/mouseout is used for show.event, even if it isn't in the users options.
- */
+ * Make sure hoverIntent functions properly by using mouseleave to clear show timer if
+ * mouseenter/mouseout is used for show.event, even if it isn't in the users options.
+ */
else if(/mouse(over|enter)/i.test(options.show.event)) {
targets.hide.bind('mouseleave'+namespace, function(event) {
clearTimeout(self.timers.show);
@@ -844,9 +835,9 @@ function QTip(target, options, id, attr)
assignEvents();
/* Queue this part of the render process in our fx queue so we can
- * load images before the tooltip renders fully.
- *
- * See: updateContent method
+ * load images before the tooltip renders fully.
+ *
+ * See: updateContent method
*/
tooltip.queue('fx', function(next) {
// Trigger tooltiprender event and pass original triggering event as original
@@ -944,10 +935,10 @@ function QTip(target, options, id, attr)
sanitizeOptions(options);
/*
- * Execute any valid callbacks for the set options
- * Also set isPositioning/isDrawing so we don't get loads of redundant repositioning
- * and redraw calls.
- */
+ * Execute any valid callbacks for the set options
+ * Also set isPositioning/isDrawing so we don't get loads of redundant repositioning
+ * and redraw calls.
+ */
isPositioning = isDrawing = 1; $.each(option, callback); isPositioning = isDrawing = 0;
// Update position / redraw if needed
@@ -1460,7 +1451,7 @@ function init(id, opts)
html5 = elem.data(opts.metadata.name || 'qtipopts');
// If we don't get an object returned attempt to parse it manualyl without parseJSON
- try { html5 = typeof html5 === 'string' ? (new Function("return " + html5))() : html5; }
+ try { html5 = typeof html5 === 'string' ? $.parseJSON(html5) : html5; }
catch(e) { log('Unable to parse HTML5 attribute data: ' + html5); }
// Merge in and sanitize metadata
@@ -1613,19 +1604,19 @@ QTIP.bind = function(opts, event)
};
/*
- * Make sure hoverIntent functions properly by using mouseleave as a hide event if
- * mouseenter/mouseout is used for show.event, even if it isn't in the users options.
- */
+ * Make sure hoverIntent functions properly by using mouseleave as a hide event if
+ * mouseenter/mouseout is used for show.event, even if it isn't in the users options.
+ */
if(/mouse(over|enter)/i.test(events.show) && !/mouse(out|leave)/i.test(events.hide)) {
events.hide += ' mouseleave' + namespace;
}
/*
- * Also make sure initial mouse targetting works correctly by caching mousemove coords
- * on show targets before the tooltip has rendered.
- *
- * Also set onTarget when triggered to keep mouse tracking working
- */
+ * Also make sure initial mouse targetting works correctly by caching mousemove coords
+ * on show targets before the tooltip has rendered.
+ *
+ * Also set onTarget when triggered to keep mouse tracking working
+ */
targets.show.bind('mousemove'+namespace, function(event) {
MOUSE = { pageX: event.pageX, pageY: event.pageY, type: 'mousemove' };
api.cache.onTarget = TRUE;
@@ -1731,16 +1722,16 @@ PLUGINS = QTIP.plugins = {
},
/*
- * iOS version detection
- */
+ * iOS version detection
+ */
iOS: parseFloat(
('' + (/CPU.*OS ([0-9_]{1,5})|(CPU like).*AppleWebKit.*Mobile/i.exec(navigator.userAgent) || [0,''])[1])
.replace('undefined', '3_2').replace('_', '.').replace('_', '')
) || FALSE,
/*
- * jQuery-specific $.fn overrides
- */
+ * jQuery-specific $.fn overrides
+ */
fn: {
/* Allow other plugins to successfully retrieve the title of an element with a qTip applied */
attr: function(attr, val) {
@@ -1814,7 +1805,7 @@ if(!$.ui) {
}
// Set global qTip properties
-QTIP.version = '2.0.0pre';
+QTIP.version = '@VERSION';
QTIP.nextid = 0;
QTIP.inactiveEvents = 'click dblclick mousedown mouseup mousemove mouseleave mouseenter'.split(' ');
QTIP.zindex = 15000;
@@ -1891,6 +1882,7 @@ QTIP.defaults = {
}
};
+
function Ajax(api)
{
var self = this,
@@ -2058,1338 +2050,1347 @@ $.extend(TRUE, QTIP.defaults, {
}
});
-/*
- * BGIFrame adaption (http://plugins.jquery.com/project/bgiframe)
- * Special thanks to Brandon Aaron
- */
-function BGIFrame(api)
+
+PLUGINS.viewport = function(api, position, posOptions, targetWidth, targetHeight, elemWidth, elemHeight)
{
- var self = this,
- elems = api.elements,
- tooltip = elems.tooltip,
- namespace = '.bgiframe-' + api.id;
+ var target = posOptions.target,
+ tooltip = api.elements.tooltip,
+ my = posOptions.my,
+ at = posOptions.at,
+ adjust = posOptions.adjust,
+ method = adjust.method.split(' '),
+ methodX = method[0],
+ methodY = method[1] || method[0],
+ viewport = posOptions.viewport,
+ container = posOptions.container,
+ cache = api.cache,
+ tip = api.plugins.tip,
+ adjusted = { left: 0, top: 0 },
+ fixed, newMy, newClass;
- $.extend(self, {
- init: function()
- {
- // Create the BGIFrame element
- elems.bgiframe = $('');
+ // If viewport is not a jQuery element, or it's the window/document or no adjustment method is used... return
+ if(!viewport.jquery || target[0] === window || target[0] === document.body || adjust.method === 'none') {
+ return adjusted;
+ }
- // Append the new element to the tooltip
- elems.bgiframe.appendTo(tooltip);
+ // Cache our viewport details
+ fixed = tooltip.css('position') === 'fixed';
+ viewport = {
+ elem: viewport,
+ height: viewport[ (viewport[0] === window ? 'h' : 'outerH') + 'eight' ](),
+ width: viewport[ (viewport[0] === window ? 'w' : 'outerW') + 'idth' ](),
+ scrollleft: fixed ? 0 : viewport.scrollLeft(),
+ scrolltop: fixed ? 0 : viewport.scrollTop(),
+ offset: viewport.offset() || { left: 0, top: 0 }
+ };
+ container = {
+ elem: container,
+ scrollLeft: container.scrollLeft(),
+ scrollTop: container.scrollTop(),
+ offset: container.offset() || { left: 0, top: 0 }
+ };
- // Update BGIFrame on tooltip move
- tooltip.bind('tooltipmove'+namespace, self.adjust);
- },
+ // Generic calculation method
+ function calculate(side, otherSide, type, adjust, side1, side2, lengthName, targetLength, elemLength) {
+ var initialPos = position[side1],
+ mySide = my[side], atSide = at[side],
+ isShift = type === SHIFT,
+ viewportScroll = -container.offset[side1] + viewport.offset[side1] + viewport['scroll'+side1],
+ myLength = mySide === side1 ? elemLength : mySide === side2 ? -elemLength : -elemLength / 2,
+ atLength = atSide === side1 ? targetLength : atSide === side2 ? -targetLength : -targetLength / 2,
+ tipLength = tip && tip.size ? tip.size[lengthName] || 0 : 0,
+ tipAdjust = tip && tip.corner && tip.corner.precedance === side && !isShift ? tipLength : 0,
+ overflow1 = viewportScroll - initialPos + tipAdjust,
+ overflow2 = initialPos + elemLength - viewport[lengthName] - viewportScroll + tipAdjust,
+ offset = myLength - (my.precedance === side || mySide === my[otherSide] ? atLength : 0) - (atSide === CENTER ? targetLength / 2 : 0);
- adjust: function()
- {
- var dimensions = api.get('dimensions'), // Determine current tooltip dimensions
- plugin = api.plugins.tip,
- tip = elems.tip,
- tipAdjust, offset;
+ // shift
+ if(isShift) {
+ tipAdjust = tip && tip.corner && tip.corner.precedance === otherSide ? tipLength : 0;
+ offset = (mySide === side1 ? 1 : -1) * myLength - tipAdjust;
- // Adjust border offset
- offset = parseInt(tooltip.css('border-left-width'), 10) || 0;
- offset = { left: -offset, top: -offset };
+ // Adjust position but keep it within viewport dimensions
+ position[side1] += overflow1 > 0 ? overflow1 : overflow2 > 0 ? -overflow2 : 0;
+ position[side1] = Math.max(
+ -container.offset[side1] + viewport.offset[side1] + (tipAdjust && tip.corner[side] === CENTER ? tip.offset : 0),
+ initialPos - offset,
+ Math.min(
+ Math.max(-container.offset[side1] + viewport.offset[side1] + viewport[lengthName], initialPos + offset),
+ position[side1]
+ )
+ );
+ }
- // Adjust for tips plugin
- if(plugin && tip) {
- tipAdjust = (plugin.corner.precedance === 'x') ? ['width', 'left'] : ['height', 'top'];
- offset[ tipAdjust[1] ] -= tip[ tipAdjust[0] ]();
- }
+ // flip/flipinvert
+ else {
+ // Update adjustment amount depending on if using flipinvert or flip
+ adjust *= (type === FLIPINVERT ? 2 : 0);
- // Update bgiframe
- elems.bgiframe.css(offset).css(dimensions);
- },
+ // Check for overflow on the left/top
+ if(overflow1 > 0 && (mySide !== side1 || overflow2 > 0)) {
+ position[side1] -= offset + adjust;
+ newMy['invert'+side](side1);
+ }
- destroy: function()
- {
- // Remove iframe
- elems.bgiframe.remove();
+ // Check for overflow on the bottom/right
+ else if(overflow2 > 0 && (mySide !== side2 || overflow1 > 0) ) {
+ position[side1] -= (mySide === CENTER ? -offset : offset) + adjust;
+ newMy['invert'+side](side2);
+ }
- // Remove bound events
- tooltip.unbind(namespace);
+ // Make sure we haven't made things worse with the adjustment and reset if so
+ if(position[side1] < viewportScroll && -position[side1] > overflow2) {
+ position[side1] = initialPos; newMy = undefined;
+ }
}
- });
- self.init();
-}
-
-PLUGINS.bgiframe = function(api)
-{
- var browser = $.browser,
- self = api.plugins.bgiframe;
-
- // Proceed only if the browser is IE6 and offending elements are present
- if($('select, object').length < 1 || !(browser.msie && (''+browser.version).charAt(0) === '6')) {
- return FALSE;
+ return position[side1] - initialPos;
}
- return 'object' === typeof self ? self : (api.plugins.bgiframe = new BGIFrame(api));
-};
-
-// Plugin needs to be initialized on render
-PLUGINS.bgiframe.initialize = 'render';
+ // Set newMy if using flip or flipinvert methods
+ if(methodX !== 'shift' || methodY !== 'shift') { newMy = my.clone(); }
-PLUGINS.imagemap = function(api, area, corner, adjustMethod)
-{
- if(!area.jquery) { area = $(area); }
+ // Adjust position based onviewport and adjustment options
+ adjusted = {
+ left: methodX !== 'none' ? calculate( X, Y, methodX, adjust.x, LEFT, RIGHT, WIDTH, targetWidth, elemWidth ) : 0,
+ top: methodY !== 'none' ? calculate( Y, X, methodY, adjust.y, TOP, BOTTOM, HEIGHT, targetHeight, elemHeight ) : 0
+ };
- var cache = (api.cache.areas = {}),
- shape = (area[0].shape || area.attr('shape')).toLowerCase(),
- coordsString = area[0].coords || area.attr('coords'),
- baseCoords = coordsString.split(','),
- coords = [],
- image = $('img[usemap="#'+area.parent('map').attr('name')+'"]'),
- imageOffset = image.offset(),
- result = {
- width: 0, height: 0,
- position: {
- top: 1e10, right: 0,
- bottom: 0, left: 1e10
- }
- },
- i = 0, next = 0, dimensions;
+ // Set tooltip position class if it's changed
+ if(newMy && cache.lastClass !== (newClass = uitooltip + '-pos-' + newMy.abbrev())) {
+ tooltip.removeClass(api.cache.lastClass).addClass( (api.cache.lastClass = newClass) );
+ }
- // POLY area coordinate calculator
- // Special thanks to Ed Cradock for helping out with this.
- // Uses a binary search algorithm to find suitable coordinates.
- function polyCoordinates(result, coords, corner)
- {
- var i = 0,
- compareX = 1, compareY = 1,
- realX = 0, realY = 0,
- newWidth = result.width,
- newHeight = result.height;
+ return adjusted;
+};
+// Tip coordinates calculator
+function calculateTip(corner, width, height)
+{
+ var width2 = Math.ceil(width / 2), height2 = Math.ceil(height / 2),
- // Use a binary search algorithm to locate most suitable coordinate (hopefully)
- while(newWidth > 0 && newHeight > 0 && compareX > 0 && compareY > 0)
- {
- newWidth = Math.floor(newWidth / 2);
- newHeight = Math.floor(newHeight / 2);
+ // Define tip coordinates in terms of height and width values
+ tips = {
+ bottomright: [[0,0], [width,height], [width,0]],
+ bottomleft: [[0,0], [width,0], [0,height]],
+ topright: [[0,height], [width,0], [width,height]],
+ topleft: [[0,0], [0,height], [width,height]],
+ topcenter: [[0,height], [width2,0], [width,height]],
+ bottomcenter: [[0,0], [width,0], [width2,height]],
+ rightcenter: [[0,0], [width,height2], [0,height]],
+ leftcenter: [[width,0], [width,height], [0,height2]]
+ };
- if(corner.x === LEFT){ compareX = newWidth; }
- else if(corner.x === RIGHT){ compareX = result.width - newWidth; }
- else{ compareX += Math.floor(newWidth / 2); }
+ // Set common side shapes
+ tips.lefttop = tips.bottomright; tips.righttop = tips.bottomleft;
+ tips.leftbottom = tips.topright; tips.rightbottom = tips.topleft;
- if(corner.y === TOP){ compareY = newHeight; }
- else if(corner.y === BOTTOM){ compareY = result.height - newHeight; }
- else{ compareY += Math.floor(newHeight / 2); }
+ return tips[ corner.string() ];
+}
- i = coords.length; while(i--)
- {
- if(coords.length < 2){ break; }
- realX = coords[i][0] - result.position.left;
- realY = coords[i][1] - result.position.top;
+function Tip(qTip, command)
+{
+ var self = this,
+ opts = qTip.options.style.tip,
+ elems = qTip.elements,
+ tooltip = elems.tooltip,
+ cache = { top: 0, left: 0 },
+ size = {
+ width: opts.width,
+ height: opts.height
+ },
+ color = { },
+ border = opts.border || 0,
+ namespace = '.qtip-tip',
+ hasCanvas = !!($('')[0] || {}).getContext;
- if((corner.x === LEFT && realX >= compareX) ||
- (corner.x === RIGHT && realX <= compareX) ||
- (corner.x === CENTER && (realX < compareX || realX > (result.width - compareX))) ||
- (corner.y === TOP && realY >= compareY) ||
- (corner.y === BOTTOM && realY <= compareY) ||
- (corner.y === CENTER && (realY < compareY || realY > (result.height - compareY)))) {
- coords.splice(i, 1);
- }
- }
- }
+ self.corner = NULL;
+ self.mimic = NULL;
+ self.border = border;
+ self.offset = opts.offset;
+ self.size = size;
- return { left: coords[0][0], top: coords[0][1] };
- }
-
- // Make sure we account for padding and borders on the image
- imageOffset.left += Math.ceil((image.outerWidth() - image.width()) / 2);
- imageOffset.top += Math.ceil((image.outerHeight() - image.height()) / 2);
-
- // Parse coordinates into proper array
- if(shape === 'poly') {
- i = baseCoords.length; while(i--)
- {
- next = [ parseInt(baseCoords[--i], 10), parseInt(baseCoords[i+1], 10) ];
+ // Add new option checks for the plugin
+ qTip.checks.tip = {
+ '^position.my|style.tip.(corner|mimic|border)$': function() {
+ // Make sure a tip can be drawn
+ if(!self.init()) {
+ self.destroy();
+ }
- if(next[0] > result.position.right){ result.position.right = next[0]; }
- if(next[0] < result.position.left){ result.position.left = next[0]; }
- if(next[1] > result.position.bottom){ result.position.bottom = next[1]; }
- if(next[1] < result.position.top){ result.position.top = next[1]; }
+ // Reposition the tooltip
+ qTip.reposition();
+ },
+ '^style.tip.(height|width)$': function() {
+ // Re-set dimensions and redraw the tip
+ size = {
+ width: opts.width,
+ height: opts.height
+ };
+ self.create();
+ self.update();
- coords.push(next);
+ // Reposition the tooltip
+ qTip.reposition();
+ },
+ '^content.title.text|style.(classes|widget)$': function() {
+ if(elems.tip && elems.tip.length) {
+ self.update();
+ }
}
+ };
+
+ function swapDimensions() {
+ size.width = opts.height;
+ size.height = opts.width;
}
- else {
- i = -1; while(i++ < baseCoords.length) {
- coords.push( parseInt(baseCoords[i], 10) );
- }
+
+ function resetDimensions() {
+ size.width = opts.width;
+ size.height = opts.height;
}
- // Calculate details
- switch(shape)
- {
- case 'rect':
- result = {
- width: Math.abs(coords[2] - coords[0]),
- height: Math.abs(coords[3] - coords[1]),
- position: {
- left: Math.min(coords[0], coords[2]),
- top: Math.min(coords[1], coords[3])
- }
- };
- break;
+ function reposition(event, api, pos, viewport) {
+ if(!elems.tip) { return; }
- case 'circle':
- result = {
- width: coords[2] + 2,
- height: coords[2] + 2,
- position: { left: coords[0], top: coords[1] }
- };
- break;
+ var newCorner = self.corner.clone(),
+ adjust = pos.adjusted,
+ method = qTip.options.position.adjust.method.split(' '),
+ horizontal = method[0],
+ vertical = method[1] || method[0],
+ shift = { left: FALSE, top: FALSE, x: 0, y: 0 },
+ offset, css = {}, props;
- case 'poly':
- result.width = Math.abs(result.position.right - result.position.left);
- result.height = Math.abs(result.position.bottom - result.position.top)
+ // If our tip position isn't fixed e.g. doesn't adjust with viewport...
+ if(self.corner.fixed !== TRUE) {
+ // Horizontal - Shift or flip method
+ if(horizontal === SHIFT && newCorner.precedance === X && adjust.left && newCorner.y !== CENTER) {
+ newCorner.precedance = newCorner.precedance === X ? Y : X;
+ }
+ else if(horizontal !== SHIFT && adjust.left){
+ newCorner.x = newCorner.x === CENTER ? (adjust.left > 0 ? LEFT : RIGHT) : (newCorner.x === LEFT ? RIGHT : LEFT);
+ }
- if(corner.abbrev() === 'c') {
- result.position = {
- left: result.position.left + (result.width / 2),
- top: result.position.top + (result.height / 2)
- };
+ // Vertical - Shift or flip method
+ if(vertical === SHIFT && newCorner.precedance === Y && adjust.top && newCorner.x !== CENTER) {
+ newCorner.precedance = newCorner.precedance === Y ? X : Y;
+ }
+ else if(vertical !== SHIFT && adjust.top) {
+ newCorner.y = newCorner.y === CENTER ? (adjust.top > 0 ? TOP : BOTTOM) : (newCorner.y === TOP ? BOTTOM : TOP);
}
- else {
- // Calculate if we can't find a cached value
- if(!cache[corner+coordsString]) {
- result.position = polyCoordinates(result, coords.slice(), corner);
- // If flip adjustment is enabled, also calculate the closest opposite point
- if(adjustMethod && (adjustMethod[0] === 'flip' || adjustMethod[1] === 'flip')) {
- result.offset = polyCoordinates(result, coords.slice(), {
- x: corner.x === LEFT ? RIGHT : corner.x === RIGHT ? LEFT : CENTER,
- y: corner.y === TOP ? BOTTOM : corner.y === BOTTOM ? TOP : CENTER
- });
+ // Update and redraw the tip if needed (check cached details of last drawn tip)
+ if(newCorner.string() !== cache.corner.string() && (cache.top !== adjust.top || cache.left !== adjust.left)) {
+ self.update(newCorner, FALSE);
+ }
+ }
- result.offset.left -= result.position.left;
- result.offset.top -= result.position.top;
- }
+ // Setup tip offset properties
+ offset = self.position(newCorner, adjust);
+ offset[ newCorner.x ] += borderWidth(newCorner, newCorner.x, TRUE);
+ offset[ newCorner.y ] += borderWidth(newCorner, newCorner.y, TRUE);
- // Store the result
- cache[corner+coordsString] = result;
- }
+ // Readjust offset object to make it left/top
+ if(offset.right !== undefined) { offset.left = -offset.right; }
+ if(offset.bottom !== undefined) { offset.top = -offset.bottom; }
+ offset.user = Math.max(0, opts.offset);
- // Grab the cached result
- result = cache[corner+coordsString];
+ // Viewport "shift" specific adjustments
+ if(shift.left = (horizontal === SHIFT && !!adjust.left)) {
+ if(newCorner.x === CENTER) {
+ css['margin-left'] = shift.x = offset['margin-left'] - adjust.left;
}
+ else {
+ props = offset.right !== undefined ?
+ [ adjust.left, -offset.left ] : [ -adjust.left, offset.left ];
- result.width = result.height = 0;
- break;
- }
+ if( (shift.x = Math.max(props[0], props[1])) > props[0] ) {
+ pos.left -= adjust.left;
+ shift.left = FALSE;
+ }
+
+ css[ offset.right !== undefined ? RIGHT : LEFT ] = shift.x;
+ }
+ }
+ if(shift.top = (vertical === SHIFT && !!adjust.top)) {
+ if(newCorner.y === CENTER) {
+ css['margin-top'] = shift.y = offset['margin-top'] - adjust.top;
+ }
+ else {
+ props = offset.bottom !== undefined ?
+ [ adjust.top, -offset.top ] : [ -adjust.top, offset.top ];
- // Add image position to offset coordinates
- result.position.left += imageOffset.left;
- result.position.top += imageOffset.top;
+ if( (shift.y = Math.max(props[0], props[1])) > props[0] ) {
+ pos.top -= adjust.top;
+ shift.top = FALSE;
+ }
- return result;
-};
+ css[ offset.bottom !== undefined ? BOTTOM : TOP ] = shift.y;
+ }
+ }
-function Modal(api)
-{
- var self = this,
- options = api.options.show.modal,
- elems = api.elements,
- tooltip = elems.tooltip,
- overlaySelector = '#qtip-overlay',
- globalNamespace = '.qtipmodal',
- namespace = globalNamespace + api.id,
- attr = 'is-modal-qtip',
- docBody = $(document.body),
- focusableSelector = PLUGINS.modal.focusable.join(','),
- focusableElems = {}, overlay;
+ /*
+ * If the tip is adjusted in both dimensions, or in a
+ * direction that would cause it to be anywhere but the
+ * outer border, hide it!
+ */
+ elems.tip.css(css).toggle(
+ !((shift.x && shift.y) || (newCorner.x === CENTER && shift.y) || (newCorner.y === CENTER && shift.x))
+ );
- // Setup option set checks
- api.checks.modal = {
- '^show.modal.(on|blur)$': function() {
- // Initialise
- self.init();
-
- // Show the modal if not visible already and tooltip is visible
- elems.overlay.toggle( tooltip.is(':visible') );
- },
- '^content.text$': updateFocusable
- };
+ // Adjust position to accomodate tip dimensions
+ pos.left -= offset.left.charAt ? offset.user : horizontal !== SHIFT || shift.top || !shift.left && !shift.top ? offset.left : 0;
+ pos.top -= offset.top.charAt ? offset.user : vertical !== SHIFT || shift.left || !shift.left && !shift.top ? offset.top : 0;
- function updateFocusable() {
- focusableElems = $(focusableSelector, tooltip).not('[disabled]').map(function() {
- return typeof this.focus === 'function' ? this : null;
- });
+ // Cache details
+ cache.left = adjust.left; cache.top = adjust.top;
+ cache.corner = newCorner.clone();
}
- function focusInputs(blurElems) {
- // Blurring body element in IE causes window.open windows to unfocus!
- if(focusableElems.length < 1 && blurElems.length) { blurElems.not('body').blur(); }
+ /* border width calculator */
+ function borderWidth(corner, side, backup) {
+ side = !side ? corner[corner.precedance] : side;
+
+ var isFluid = tooltip.hasClass(fluidClass),
+ isTitleTop = elems.titlebar && corner.y === TOP,
+ elem = isTitleTop ? elems.titlebar : elems.tooltip,
+ css = 'border-' + side + '-width',
+ val;
- // Focus the inputs
- else { focusableElems.first().focus(); }
- }
+ // Grab the border-width value (add fluid class if needed)
+ tooltip.addClass(fluidClass);
+ val = parseInt(elem.css(css), 10);
+ val = (backup ? val || parseInt(tooltip.css(css), 10) : val) || 0;
+ tooltip.toggleClass(fluidClass, isFluid);
- function stealFocus(event) {
- var target = $(event.target),
- container = target.closest('.qtip'),
- targetOnTop;
+ return val;
+ }
- // Determine if input container target is above this
- targetOnTop = container.length < 1 ? FALSE :
- (parseInt(container[0].style.zIndex, 10) > parseInt(tooltip[0].style.zIndex, 10));
+ function borderRadius(corner) {
+ var isTitleTop = elems.titlebar && corner.y === TOP,
+ elem = isTitleTop ? elems.titlebar : elems.content,
+ moz = $.browser.mozilla,
+ prefix = moz ? '-moz-' : $.browser.webkit ? '-webkit-' : '',
+ side = corner.y + (moz ? '' : '-') + corner.x,
+ css = prefix + (moz ? 'border-radius-' + side : 'border-' + side + '-radius');
- // If we're showing a modal, but focus has landed on an input below
- // this modal, divert focus to the first visible input in this modal
- // or if we can't find one... the tooltip itself
- if(!targetOnTop && ($(event.target).closest(selector)[0] !== tooltip[0])) {
- focusInputs(target);
- }
+ return parseInt(elem.css(css), 10) || parseInt(tooltip.css(css), 10) || 0;
}
- $.extend(self, {
- init: function()
- {
- // If modal is disabled... return
- if(!options.on) { return self; }
+ function calculateSize(corner) {
+ var y = corner.precedance === Y,
+ width = size [ y ? WIDTH : HEIGHT ],
+ height = size [ y ? HEIGHT : WIDTH ],
+ isCenter = corner.string().indexOf(CENTER) > -1,
+ base = width * (isCenter ? 0.5 : 1),
+ pow = Math.pow,
+ round = Math.round,
+ bigHyp, ratio, result,
- // Create the overlay if needed
- overlay = self.create();
+ smallHyp = Math.sqrt( pow(base, 2) + pow(height, 2) ),
+
+ hyp = [
+ (border / base) * smallHyp, (border / height) * smallHyp
+ ];
+ hyp[2] = Math.sqrt( pow(hyp[0], 2) - pow(border, 2) );
+ hyp[3] = Math.sqrt( pow(hyp[1], 2) - pow(border, 2) );
- // Add unique attribute so we can grab modal tooltips easily via a selector
- tooltip.attr(attr, TRUE)
+ bigHyp = smallHyp + hyp[2] + hyp[3] + (isCenter ? 0 : hyp[0]);
+ ratio = bigHyp / smallHyp;
- // Set z-index
- .css('z-index', PLUGINS.modal.zindex + $(selector+'['+attr+']').length)
+ result = [ round(ratio * height), round(ratio * width) ];
+ return { height: result[ y ? 0 : 1 ], width: result[ y ? 1 : 0 ] };
+ }
+
+ $.extend(self, {
+ init: function()
+ {
+ var enabled = self.detectCorner() && (hasCanvas || $.browser.msie);
+
+ // Determine tip corner and type
+ if(enabled) {
+ // Create a new tip and draw it
+ self.create();
+ self.update();
+
+ // Bind update events
+ tooltip.unbind(namespace).bind('tooltipmove'+namespace, reposition);
+ }
- // Remove previous bound events in globalNamespace
- .unbind(globalNamespace).unbind(namespace)
+ return enabled;
+ },
- // Apply our show/hide/focus modal events
- .bind('tooltipshow'+globalNamespace+' tooltiphide'+globalNamespace, function(event, api, duration) {
- var oEvent = event.originalEvent;
+ detectCorner: function()
+ {
+ var corner = opts.corner,
+ posOptions = qTip.options.position,
+ at = posOptions.at,
+ my = posOptions.my.string ? posOptions.my.string() : posOptions.my;
- // Make sure mouseout doesn't trigger a hide when showing the modal and mousing onto backdrop
- if(event.target === tooltip[0]) {
- if(oEvent && event.type === 'tooltiphide' && /mouse(leave|enter)/.test(oEvent.type) && $(oEvent.relatedTarget).closest(overlay[0]).length) {
- try { event.preventDefault(); } catch(e) {}
- }
- else if(!oEvent || (oEvent && !oEvent.solo)) {
- self[ event.type.replace('tooltip', '') ](event, duration);
- }
+ // Detect corner and mimic properties
+ if(corner === FALSE || (my === FALSE && at === FALSE)) {
+ return FALSE;
+ }
+ else {
+ if(corner === TRUE) {
+ self.corner = new PLUGINS.Corner(my);
}
- })
+ else if(!corner.string) {
+ self.corner = new PLUGINS.Corner(corner);
+ self.corner.fixed = TRUE;
+ }
+ }
- // Adjust modal z-index on tooltip focus
- .bind('tooltipfocus'+globalNamespace, function(event) {
- // If focus was cancelled before it reearch us, don't do anything
- if(event.isDefaultPrevented() || event.target !== tooltip[0]) { return; }
+ // Cache it
+ cache.corner = new PLUGINS.Corner( self.corner.string() );
- var qtips = $(selector).filter('['+attr+']'),
+ return self.corner.string() !== 'centercenter';
+ },
- // Keep the modal's lower than other, regular qtips
- newIndex = PLUGINS.modal.zindex + qtips.length,
- curIndex = parseInt(tooltip[0].style.zIndex, 10);
+ detectColours: function(actual) {
+ var i, fill, border,
+ tip = elems.tip.css('cssText', ''),
+ corner = actual || self.corner,
+ precedance = corner[ corner.precedance ],
- // Set overlay z-index
- overlay[0].style.zIndex = newIndex - 2;
+ borderSide = 'border-' + precedance + '-color',
+ borderSideCamel = 'border' + precedance.charAt(0) + precedance.substr(1) + 'Color',
- // Reduce modal z-index's and keep them properly ordered
- qtips.each(function() {
- if(this.style.zIndex > curIndex) {
- this.style.zIndex -= 1;
- }
- });
+ invalid = /rgba?\(0, 0, 0(, 0)?\)|transparent|#123456/i,
+ backgroundColor = 'background-color',
+ transparent = 'transparent',
+ important = ' !important',
- // Fire blur event for focused tooltip
- qtips.end().filter('.' + focusClass).qtip('blur', event.originalEvent);
+ useTitle = elems.titlebar && (corner.y === TOP || (corner.y === CENTER && tip.position().top + (size.height / 2) + opts.offset < elems.titlebar.outerHeight(1))),
+ colorElem = useTitle ? elems.titlebar : elems.tooltip;
- // Set the new z-index
- tooltip.addClass(focusClass)[0].style.zIndex = newIndex;
+ // Apply the fluid class so we can see our CSS values properly
+ tooltip.addClass(fluidClass);
- // Prevent default handling
- try { event.preventDefault(); } catch(e) {}
- })
+ // Detect tip colours from CSS styles
+ color.fill = fill = tip.css(backgroundColor);
+ color.border = border = tip[0].style[ borderSideCamel ] || tip.css(borderSide) || tooltip.css(borderSide);
- // Focus any other visible modals when this one hides
- .bind('tooltiphide'+globalNamespace, function(event) {
- if(event.target === tooltip[0]) {
- $('[' + attr + ']').filter(':visible').not(tooltip).last().qtip('focus', event);
+ // Make sure colours are valid
+ if(!fill || invalid.test(fill)) {
+ color.fill = colorElem.css(backgroundColor) || transparent;
+ if(invalid.test(color.fill)) {
+ color.fill = tooltip.css(backgroundColor) || fill;
}
- });
-
- // Apply keyboard "Escape key" close handler
- if(options.escape) {
- $(document).unbind(namespace).bind('keydown'+namespace, function(event) {
- if(event.keyCode === 27 && tooltip.hasClass(focusClass)) {
- api.hide(event);
- }
- });
}
-
- // Apply click handler for blur option
- if(options.blur) {
- elems.overlay.unbind(namespace).bind('click'+namespace, function(event) {
- if(tooltip.hasClass(focusClass)) { api.hide(event); }
- });
+ if(!border || invalid.test(border) || border === $(document.body).css('color')) {
+ color.border = colorElem.css(borderSide) || transparent;
+ if(invalid.test(color.border) || color.border === colorElem.css('color')) {
+ color.border = tooltip.css(borderSide) || tooltip.css(borderSideCamel) || border;
+ }
}
- // Update focusable elements
- updateFocusable();
+ // Reset background and border colours
+ $('*', tip).add(tip).css('cssText', backgroundColor+':'+transparent+important+';border:0'+important+';');
- return self;
+ // Remove fluid class
+ tooltip.removeClass(fluidClass);
},
create: function()
{
- var elem = $(overlaySelector);
+ var width = size.width,
+ height = size.height,
+ vml;
- // Return if overlay is already rendered
- if(elem.length) {
- // Modal overlay should always be below all tooltips if possible
- return (elems.overlay = elem.insertAfter( $(selector).last() ));
- }
+ // Remove previous tip element if present
+ if(elems.tip) { elems.tip.remove(); }
- // Create document overlay
- overlay = elems.overlay = $('', {
- id: overlaySelector.substr(1),
- html: '',
- mousedown: function() { return FALSE; }
- })
- .hide()
- .insertAfter( $(selector).last() );
+ // Create tip element and prepend to the tooltip
+ elems.tip = $('', { 'class': 'ui-tooltip-tip' }).css({ width: width, height: height }).prependTo(tooltip);
- // Update position on window resize or scroll
- function resize() {
- overlay.css({
- height: $(window).height(),
- width: $(window).width()
- });
+ // Create tip drawing element(s)
+ if(hasCanvas) {
+ // save() as soon as we create the canvas element so FF2 doesn't bork on our first restore()!
+ $('').appendTo(elems.tip)[0].getContext('2d').save();
}
- $(window).unbind(globalNamespace).bind('resize'+globalNamespace, resize);
- resize(); // Fire it initially too
+ else {
+ vml = '';
+ elems.tip.html(vml + vml);
- return overlay;
+ // Prevent mousing down on the tip since it causes problems with .live() handling in IE due to VML
+ $('*', elems.tip).bind('click mousedown', function(event) { event.stopPropagation(); });
+ }
},
- toggle: function(event, state, duration)
+ update: function(corner, position)
{
- // Make sure default event hasn't been prevented
- if(event && event.isDefaultPrevented()) { return self; }
-
- var effect = options.effect,
- type = state ? 'show': 'hide',
- visible = overlay.is(':visible'),
- modals = $('[' + attr + ']').filter(':visible').not(tooltip),
- zindex;
-
- // Create our overlay if it isn't present already
- if(!overlay) { overlay = self.create(); }
-
- // Prevent modal from conflicting with show.solo, and don't hide backdrop is other modals are visible
- if((overlay.is(':animated') && visible === state) || (!state && modals.length)) { return self; }
+ var tip = elems.tip,
+ inner = tip.children(),
+ width = size.width,
+ height = size.height,
+ regular = 'px solid ',
+ transparent = 'px dashed transparent', // Dashed IE6 border-transparency hack. Awesome!
+ mimic = opts.mimic,
+ round = Math.round,
+ precedance, context, coords, translate, newSize;
- // State specific...
- if(state) {
- // Set position
- overlay.css({ left: 0, top: 0 });
+ // Re-determine tip if not already set
+ if(!corner) { corner = cache.corner || self.corner; }
- // Toggle backdrop cursor style on show
- overlay.toggleClass('blurs', options.blur);
+ // Use corner property if we detect an invalid mimic value
+ if(mimic === FALSE) { mimic = corner; }
- // IF the modal can steal the focus
- if(options.stealfocus !== FALSE) {
- // Make sure we can't focus anything outside the tooltip
- docBody.bind('focusin'+namespace, stealFocus);
+ // Otherwise inherit mimic properties from the corner object as necessary
+ else {
+ mimic = new PLUGINS.Corner(mimic);
+ mimic.precedance = corner.precedance;
- // Blur the current item and focus anything in the modal we an
- focusInputs( $('body *') );
+ if(mimic.x === 'inherit') { mimic.x = corner.x; }
+ else if(mimic.y === 'inherit') { mimic.y = corner.y; }
+ else if(mimic.x === mimic.y) {
+ mimic[ corner.precedance ] = corner[ corner.precedance ];
}
}
- else {
- // Undelegate focus handler
- docBody.unbind('focusin'+namespace);
- }
+ precedance = mimic.precedance;
- // Stop all animations
- overlay.stop(TRUE, FALSE);
+ // Ensure the tip width.height are relative to the tip position
+ if(corner.precedance === X) { swapDimensions(); }
+ else { resetDimensions(); }
- // Use custom function if provided
- if($.isFunction(effect)) {
- effect.call(overlay, state);
- }
+ // Set the tip dimensions
+ elems.tip.css({
+ width: (width = size.width),
+ height: (height = size.height)
+ });
- // If no effect type is supplied, use a simple toggle
- else if(effect === FALSE) {
- overlay[ type ]();
+ // Update our colours
+ self.detectColours(corner);
+
+ // Detect border width, taking into account colours
+ if(color.border !== 'transparent') {
+ // Grab border width
+ border = borderWidth(corner, NULL, TRUE);
+
+ // If border width isn't zero, use border color as fill (1.0 style tips)
+ if(opts.border === 0 && border > 0) { color.fill = color.border; }
+
+ // Set border width (use detected border width if opts.border is true)
+ self.border = border = opts.border !== TRUE ? opts.border : border;
}
- // Use basic fade function
+ // Border colour was invalid, set border to zero
+ else { self.border = border = 0; }
+
+ // Calculate coordinates
+ coords = calculateTip(mimic, width , height);
+
+ // Determine tip size
+ self.size = newSize = calculateSize(corner);
+ tip.css(newSize);
+
+ // Calculate tip translation
+ if(corner.precedance === Y) {
+ translate = [
+ round(mimic.x === LEFT ? border : mimic.x === RIGHT ? newSize.width - width - border : (newSize.width - width) / 2),
+ round(mimic.y === TOP ? newSize.height - height : 0)
+ ];
+ }
else {
- overlay.fadeTo( parseInt(duration, 10) || 90, state ? 1 : 0, function() {
- if(!state) { $(this).hide(); }
- });
+ translate = [
+ round(mimic.x === LEFT ? newSize.width - width : 0),
+ round(mimic.y === TOP ? border : mimic.y === BOTTOM ? newSize.height - height - border : (newSize.height - height) / 2)
+ ];
}
- // Reset position on hide
- if(!state) {
- overlay.queue(function(next) {
- overlay.css({ left: '', top: '' });
- next();
+ // Canvas drawing implementation
+ if(hasCanvas) {
+ // Set the canvas size using calculated size
+ inner.attr(newSize);
+
+ // Grab canvas context and clear/save it
+ context = inner[0].getContext('2d');
+ context.restore(); context.save();
+ context.clearRect(0,0,3000,3000);
+
+ // Set properties
+ context.fillStyle = color.fill;
+ context.strokeStyle = color.border;
+ context.lineWidth = border * 2;
+ context.lineJoin = 'miter';
+ context.miterLimit = 100;
+
+ // Translate origin
+ context.translate(translate[0], translate[1]);
+
+ // Draw the tip
+ context.beginPath();
+ context.moveTo(coords[0][0], coords[0][1]);
+ context.lineTo(coords[1][0], coords[1][1]);
+ context.lineTo(coords[2][0], coords[2][1]);
+ context.closePath();
+
+ // Apply fill and border
+ if(border) {
+ // Make sure transparent borders are supported by doing a stroke
+ // of the background colour before the stroke colour
+ if(tooltip.css('background-clip') === 'border-box') {
+ context.strokeStyle = color.fill;
+ context.stroke();
+ }
+ context.strokeStyle = color.border;
+ context.stroke();
+ }
+ context.fill();
+ }
+
+ // VML (IE Proprietary implementation)
+ else {
+ // Setup coordinates string
+ coords = 'm' + coords[0][0] + ',' + coords[0][1] + ' l' + coords[1][0] +
+ ',' + coords[1][1] + ' ' + coords[2][0] + ',' + coords[2][1] + ' xe';
+
+ // Setup VML-specific offset for pixel-perfection
+ translate[2] = border && /^(r|b)/i.test(corner.string()) ?
+ parseFloat($.browser.version, 10) === 8 ? 2 : 1 : 0;
+
+ // Set initial CSS
+ inner.css({
+ antialias: ''+(mimic.string().indexOf(CENTER) > -1),
+ left: translate[0] - (translate[2] * Number(precedance === X)),
+ top: translate[1] - (translate[2] * Number(precedance === Y)),
+ width: width + border,
+ height: height + border
+ })
+ .each(function(i) {
+ var $this = $(this);
+
+ // Set shape specific attributes
+ $this[ $this.prop ? 'prop' : 'attr' ]({
+ coordsize: (width+border) + ' ' + (height+border),
+ path: coords,
+ fillcolor: color.fill,
+ filled: !!i,
+ stroked: !i
+ })
+ .css({ display: border || i ? 'block' : 'none' });
+
+ // Check if border is enabled and add stroke element
+ if(!i && $this.html() === '') {
+ $this.html(
+ ''
+ );
+ }
});
}
- return self;
+ // Position if needed
+ if(position !== FALSE) { self.position(corner); }
},
- show: function(event, duration) { return self.toggle(event, TRUE, duration); },
- hide: function(event, duration) { return self.toggle(event, FALSE, duration); },
-
- destroy: function()
+ // Tip positioning method
+ position: function(corner)
{
- var delBlanket = overlay;
+ var tip = elems.tip,
+ position = {},
+ userOffset = Math.max(0, opts.offset),
+ precedance, dimensions, corners;
- if(delBlanket) {
- // Check if any other modal tooltips are present
- delBlanket = $('[' + attr + ']').not(tooltip).length < 1;
+ // Return if tips are disabled or tip is not yet rendered
+ if(opts.corner === FALSE || !tip) { return FALSE; }
- // Remove overlay if needed
- if(delBlanket) {
- elems.overlay.remove();
- $(document).unbind(globalNamespace);
+ // Inherit corner if not provided
+ corner = corner || self.corner;
+ precedance = corner.precedance;
+
+ // Determine which tip dimension to use for adjustment
+ dimensions = calculateSize(corner);
+
+ // Setup corners and offset array
+ corners = [ corner.x, corner.y ];
+ if(precedance === X) { corners.reverse(); }
+
+ // Calculate tip position
+ $.each(corners, function(i, side) {
+ var b, br;
+
+ if(side === CENTER) {
+ b = precedance === Y ? LEFT : TOP;
+ position[ b ] = '50%';
+ position['margin-' + b] = -Math.round(dimensions[ precedance === Y ? WIDTH : HEIGHT ] / 2) + userOffset;
}
else {
- elems.overlay.unbind(globalNamespace+api.id);
+ b = borderWidth(corner, side);
+ br = borderRadius(corner);
+
+ position[ side ] = i ? 0 : (userOffset + (br > b ? br : -b));
}
+ });
- // Undelegate focus handler
- docBody.undelegate('*', 'focusin'+namespace);
- }
+ // Adjust for tip dimensions
+ position[ corner[precedance] ] -= dimensions[ precedance === X ? WIDTH : HEIGHT ];
- // Remove bound events
- return tooltip.removeAttr(attr).unbind(globalNamespace);
+ // Set and return new position
+ tip.css({ top: '', bottom: '', left: '', right: '', margin: '' }).css(position);
+ return position;
+ },
+
+ destroy: function()
+ {
+ // Remove the tip element
+ if(elems.tip) { elems.tip.remove(); }
+ elems.tip = false;
+
+ // Unbind events
+ tooltip.unbind(namespace);
}
});
self.init();
}
-PLUGINS.modal = function(api) {
- var self = api.plugins.modal;
-
- return 'object' === typeof self ? self : (api.plugins.modal = new Modal(api));
+PLUGINS.tip = function(api)
+{
+ var self = api.plugins.tip;
+
+ return 'object' === typeof self ? self : (api.plugins.tip = new Tip(api));
};
-// Plugin needs to be initialized on render
-PLUGINS.modal.initialize = 'render';
+// Initialize tip on render
+PLUGINS.tip.initialize = 'render';
-// Setup sanitiztion rules
-PLUGINS.modal.sanitize = function(opts) {
- if(opts.show) {
- if(typeof opts.show.modal !== 'object') { opts.show.modal = { on: !!opts.show.modal }; }
- else if(typeof opts.show.modal.on === 'undefined') { opts.show.modal.on = TRUE; }
+// Setup plugin sanitization options
+PLUGINS.tip.sanitize = function(options)
+{
+ var style = options.style, opts;
+ if(style && 'tip' in style) {
+ opts = options.style.tip;
+ if(typeof opts !== 'object'){ options.style.tip = { corner: opts }; }
+ if(!(/string|boolean/i).test(typeof opts.corner)) { opts.corner = TRUE; }
+ if(typeof opts.width !== 'number'){ delete opts.width; }
+ if(typeof opts.height !== 'number'){ delete opts.height; }
+ if(typeof opts.border !== 'number' && opts.border !== TRUE){ delete opts.border; }
+ if(typeof opts.offset !== 'number'){ delete opts.offset; }
}
};
-// Base z-index for all modal tooltips (use qTip core z-index as a base)
-PLUGINS.modal.zindex = QTIP.zindex + 1000;
+// Extend original qTip defaults
+$.extend(TRUE, QTIP.defaults, {
+ style: {
+ tip: {
+ corner: TRUE,
+ mimic: FALSE,
+ width: 6,
+ height: 6,
+ border: TRUE,
+ offset: 0
+ }
+ }
+});
-// Defines the selector used to select all 'focusable' elements within the modal when using the show.modal.stealfocus option.
-// Selectors initially taken from http://stackoverflow.com/questions/7668525/is-there-a-jquery-selector-to-get-all-elements-that-can-get-focus
-PLUGINS.modal.focusable = ['a[href]', 'area[href]', 'input', 'select', 'textarea', 'button', 'iframe', 'object', 'embed', '[tabindex]', '[contenteditable]'];
-// Extend original api defaults
-$.extend(TRUE, QTIP.defaults, {
- show: {
- modal: {
- on: FALSE,
- effect: TRUE,
- blur: TRUE,
- stealfocus: TRUE,
- escape: TRUE
- }
- }
-});
-
-PLUGINS.svg = function(api, svg, corner, adjustMethod)
+function Modal(api)
{
- var doc = $(document),
- elem = svg[0],
- result = {
- width: 0, height: 0,
- position: { top: 1e10, left: 1e10 }
- },
- box, mtx, root, point, tPoint;
+ var self = this,
+ options = api.options.show.modal,
+ elems = api.elements,
+ tooltip = elems.tooltip,
+ overlaySelector = '#qtip-overlay',
+ globalNamespace = '.qtipmodal',
+ namespace = globalNamespace + api.id,
+ attr = 'is-modal-qtip',
+ docBody = $(document.body),
+ focusableSelector = PLUGINS.modal.focusable.join(','),
+ focusableElems = {}, overlay;
- // Ascend the parentNode chain until we find an element with getBBox()
- while(!elem.getBBox) { elem = elem.parentNode; }
+ // Setup option set checks
+ api.checks.modal = {
+ '^show.modal.(on|blur)$': function() {
+ // Initialise
+ self.init();
+
+ // Show the modal if not visible already and tooltip is visible
+ elems.overlay.toggle( tooltip.is(':visible') );
+ },
+ '^content.text$': function() {
+ updateFocusable();
+ }
+ };
- // Check for a valid bounding box method
- if (elem.getBBox && elem.parentNode) {
- box = elem.getBBox();
- mtx = elem.getScreenCTM();
- root = elem.farthestViewportElement || elem;
+ function updateFocusable() {
+ focusableElems = $(focusableSelector, tooltip).not('[disabled]').map(function() {
+ return typeof this.focus === 'function' ? this : null;
+ });
+ }
- // Return if no method is found
- if(!root.createSVGPoint) { return result; }
+ function focusInputs(blurElems) {
+ // Blurring body element in IE causes window.open windows to unfocus!
+ if(focusableElems.length < 1 && blurElems.length) { blurElems.not('body').blur(); }
- // Create our point var
- point = root.createSVGPoint();
+ // Focus the inputs
+ else { focusableElems.first().focus(); }
+ }
- // Adjust top and left
- point.x = box.x;
- point.y = box.y;
- tPoint = point.matrixTransform(mtx);
- result.position.left = tPoint.x;
- result.position.top = tPoint.y;
+ function stealFocus(event) {
+ var target = $(event.target),
+ container = target.closest('.qtip'),
+ targetOnTop;
- // Adjust width and height
- point.x += box.width;
- point.y += box.height;
- tPoint = point.matrixTransform(mtx);
- result.width = tPoint.x - result.position.left;
- result.height = tPoint.y - result.position.top;
+ // Determine if input container target is above this
+ targetOnTop = container.length < 1 ? FALSE :
+ (parseInt(container[0].style.zIndex, 10) > parseInt(tooltip[0].style.zIndex, 10));
- // Adjust by scroll offset
- result.position.left += doc.scrollLeft();
- result.position.top += doc.scrollTop();
+ // If we're showing a modal, but focus has landed on an input below
+ // this modal, divert focus to the first visible input in this modal
+ // or if we can't find one... the tooltip itself
+ if(!targetOnTop && ($(event.target).closest(selector)[0] !== tooltip[0])) {
+ focusInputs(target);
+ }
}
- return result;
-};
+ $.extend(self, {
+ init: function()
+ {
+ // If modal is disabled... return
+ if(!options.on) { return self; }
-// Tip coordinates calculator
-function calculateTip(corner, width, height)
-{
- var width2 = Math.ceil(width / 2), height2 = Math.ceil(height / 2),
+ // Create the overlay if needed
+ overlay = self.create();
- // Define tip coordinates in terms of height and width values
- tips = {
- bottomright: [[0,0], [width,height], [width,0]],
- bottomleft: [[0,0], [width,0], [0,height]],
- topright: [[0,height], [width,0], [width,height]],
- topleft: [[0,0], [0,height], [width,height]],
- topcenter: [[0,height], [width2,0], [width,height]],
- bottomcenter: [[0,0], [width,0], [width2,height]],
- rightcenter: [[0,0], [width,height2], [0,height]],
- leftcenter: [[width,0], [width,height], [0,height2]]
- };
+ // Add unique attribute so we can grab modal tooltips easily via a selector
+ tooltip.attr(attr, TRUE)
- // Set common side shapes
- tips.lefttop = tips.bottomright; tips.righttop = tips.bottomleft;
- tips.leftbottom = tips.topright; tips.rightbottom = tips.topleft;
+ // Set z-index
+ .css('z-index', PLUGINS.modal.zindex + $(selector+'['+attr+']').length)
+
+ // Remove previous bound events in globalNamespace
+ .unbind(globalNamespace).unbind(namespace)
- return tips[ corner.string() ];
-}
+ // Apply our show/hide/focus modal events
+ .bind('tooltipshow'+globalNamespace+' tooltiphide'+globalNamespace, function(event, api, duration) {
+ var oEvent = event.originalEvent;
+ // Make sure mouseout doesn't trigger a hide when showing the modal and mousing onto backdrop
+ if(event.target === tooltip[0]) {
+ if(oEvent && event.type === 'tooltiphide' && /mouse(leave|enter)/.test(oEvent.type) && $(oEvent.relatedTarget).closest(overlay[0]).length) {
+ try { event.preventDefault(); } catch(e) {}
+ }
+ else if(!oEvent || (oEvent && !oEvent.solo)) {
+ self[ event.type.replace('tooltip', '') ](event, duration);
+ }
+ }
+ })
-function Tip(qTip, command)
-{
- var self = this,
- opts = qTip.options.style.tip,
- elems = qTip.elements,
- tooltip = elems.tooltip,
- cache = { top: 0, left: 0 },
- size = {
- width: opts.width,
- height: opts.height
- },
- color = { },
- border = opts.border || 0,
- namespace = '.qtip-tip',
- hasCanvas = !!($('')[0] || {}).getContext;
+ // Adjust modal z-index on tooltip focus
+ .bind('tooltipfocus'+globalNamespace, function(event) {
+ // If focus was cancelled before it reearch us, don't do anything
+ if(event.isDefaultPrevented() || event.target !== tooltip[0]) { return; }
- self.corner = NULL;
- self.mimic = NULL;
- self.border = border;
- self.offset = opts.offset;
- self.size = size;
+ var qtips = $(selector).filter('['+attr+']'),
- // Add new option checks for the plugin
- qTip.checks.tip = {
- '^position.my|style.tip.(corner|mimic|border)$': function() {
- // Make sure a tip can be drawn
- if(!self.init()) {
- self.destroy();
- }
+ // Keep the modal's lower than other, regular qtips
+ newIndex = PLUGINS.modal.zindex + qtips.length,
+ curIndex = parseInt(tooltip[0].style.zIndex, 10);
- // Reposition the tooltip
- qTip.reposition();
- },
- '^style.tip.(height|width)$': function() {
- // Re-set dimensions and redraw the tip
- size = {
- width: opts.width,
- height: opts.height
- };
- self.create();
- self.update();
+ // Set overlay z-index
+ overlay[0].style.zIndex = newIndex - 2;
- // Reposition the tooltip
- qTip.reposition();
- },
- '^content.title.text|style.(classes|widget)$': function() {
- if(elems.tip && elems.tip.length) {
- self.update();
- }
- }
- };
+ // Reduce modal z-index's and keep them properly ordered
+ qtips.each(function() {
+ if(this.style.zIndex > curIndex) {
+ this.style.zIndex -= 1;
+ }
+ });
- function swapDimensions() {
- size.width = opts.height;
- size.height = opts.width;
- }
+ // Fire blur event for focused tooltip
+ qtips.end().filter('.' + focusClass).qtip('blur', event.originalEvent);
- function resetDimensions() {
- size.width = opts.width;
- size.height = opts.height;
- }
+ // Set the new z-index
+ tooltip.addClass(focusClass)[0].style.zIndex = newIndex;
- function reposition(event, api, pos, viewport) {
- if(!elems.tip) { return; }
+ // Prevent default handling
+ try { event.preventDefault(); } catch(e) {}
+ })
- var newCorner = self.corner.clone(),
- adjust = pos.adjusted,
- method = qTip.options.position.adjust.method.split(' '),
- horizontal = method[0],
- vertical = method[1] || method[0],
- shift = { left: FALSE, top: FALSE, x: 0, y: 0 },
- offset, css = {}, props;
+ // Focus any other visible modals when this one hides
+ .bind('tooltiphide'+globalNamespace, function(event) {
+ if(event.target === tooltip[0]) {
+ $('[' + attr + ']').filter(':visible').not(tooltip).last().qtip('focus', event);
+ }
+ });
- // If our tip position isn't fixed e.g. doesn't adjust with viewport...
- if(self.corner.fixed !== TRUE) {
- // Horizontal - Shift or flip method
- if(horizontal === SHIFT && newCorner.precedance === X && adjust.left && newCorner.y !== CENTER) {
- newCorner.precedance = newCorner.precedance === X ? Y : X;
- }
- else if(horizontal !== SHIFT && adjust.left){
- newCorner.x = newCorner.x === CENTER ? (adjust.left > 0 ? LEFT : RIGHT) : (newCorner.x === LEFT ? RIGHT : LEFT);
+ // Apply keyboard "Escape key" close handler
+ if(options.escape) {
+ $(document).unbind(namespace).bind('keydown'+namespace, function(event) {
+ if(event.keyCode === 27 && tooltip.hasClass(focusClass)) {
+ api.hide(event);
+ }
+ });
}
- // Vertical - Shift or flip method
- if(vertical === SHIFT && newCorner.precedance === Y && adjust.top && newCorner.x !== CENTER) {
- newCorner.precedance = newCorner.precedance === Y ? X : Y;
+ // Apply click handler for blur option
+ if(options.blur) {
+ elems.overlay.unbind(namespace).bind('click'+namespace, function(event) {
+ if(tooltip.hasClass(focusClass)) { api.hide(event); }
+ });
}
- else if(vertical !== SHIFT && adjust.top) {
- newCorner.y = newCorner.y === CENTER ? (adjust.top > 0 ? TOP : BOTTOM) : (newCorner.y === TOP ? BOTTOM : TOP);
- }
-
- // Update and redraw the tip if needed (check cached details of last drawn tip)
- if(newCorner.string() !== cache.corner.string() && (cache.top !== adjust.top || cache.left !== adjust.left)) {
- self.update(newCorner, FALSE);
- }
- }
- // Setup tip offset properties
- offset = self.position(newCorner, adjust);
- offset[ newCorner.x ] += borderWidth(newCorner, newCorner.x, TRUE);
- offset[ newCorner.y ] += borderWidth(newCorner, newCorner.y, TRUE);
+ // Update focusable elements
+ updateFocusable();
- // Readjust offset object to make it left/top
- if(offset.right !== undefined) { offset.left = -offset.right; }
- if(offset.bottom !== undefined) { offset.top = -offset.bottom; }
- offset.user = Math.max(0, opts.offset);
+ return self;
+ },
- // Viewport "shift" specific adjustments
- if(shift.left = (horizontal === SHIFT && !!adjust.left)) {
- if(newCorner.x === CENTER) {
- css['margin-left'] = shift.x = offset['margin-left'] - adjust.left;
- }
- else {
- props = offset.right !== undefined ?
- [ adjust.left, -offset.left ] : [ -adjust.left, offset.left ];
+ create: function()
+ {
+ var elem = $(overlaySelector);
- if( (shift.x = Math.max(props[0], props[1])) > props[0] ) {
- pos.left -= adjust.left;
- shift.left = FALSE;
- }
-
- css[ offset.right !== undefined ? RIGHT : LEFT ] = shift.x;
- }
- }
- if(shift.top = (vertical === SHIFT && !!adjust.top)) {
- if(newCorner.y === CENTER) {
- css['margin-top'] = shift.y = offset['margin-top'] - adjust.top;
+ // Return if overlay is already rendered
+ if(elem.length) {
+ // Modal overlay should always be below all tooltips if possible
+ return (elems.overlay = elem.insertAfter( $(selector).last() ));
}
- else {
- props = offset.bottom !== undefined ?
- [ adjust.top, -offset.top ] : [ -adjust.top, offset.top ];
- if( (shift.y = Math.max(props[0], props[1])) > props[0] ) {
- pos.top -= adjust.top;
- shift.top = FALSE;
- }
+ // Create document overlay
+ overlay = elems.overlay = $('', {
+ id: overlaySelector.substr(1),
+ html: '',
+ mousedown: function() { return FALSE; }
+ })
+ .hide()
+ .insertAfter( $(selector).last() );
- css[ offset.bottom !== undefined ? BOTTOM : TOP ] = shift.y;
+ // Update position on window resize or scroll
+ function resize() {
+ overlay.css({
+ height: $(window).height(),
+ width: $(window).width()
+ });
}
- }
-
- /*
- * If the tip is adjusted in both dimensions, or in a
- * direction that would cause it to be anywhere but the
- * outer border, hide it!
- */
- elems.tip.css(css).toggle(
- !((shift.x && shift.y) || (newCorner.x === CENTER && shift.y) || (newCorner.y === CENTER && shift.x))
- );
+ $(window).unbind(globalNamespace).bind('resize'+globalNamespace, resize);
+ resize(); // Fire it initially too
- // Adjust position to accomodate tip dimensions
- pos.left -= offset.left.charAt ? offset.user : horizontal !== SHIFT || shift.top || !shift.left && !shift.top ? offset.left : 0;
- pos.top -= offset.top.charAt ? offset.user : vertical !== SHIFT || shift.left || !shift.left && !shift.top ? offset.top : 0;
+ return overlay;
+ },
- // Cache details
- cache.left = adjust.left; cache.top = adjust.top;
- cache.corner = newCorner.clone();
- }
+ toggle: function(event, state, duration)
+ {
+ // Make sure default event hasn't been prevented
+ if(event && event.isDefaultPrevented()) { return self; }
- /* border width calculator */
- function borderWidth(corner, side, backup) {
- side = !side ? corner[corner.precedance] : side;
-
- var isFluid = tooltip.hasClass(fluidClass),
- isTitleTop = elems.titlebar && corner.y === TOP,
- elem = isTitleTop ? elems.titlebar : elems.tooltip,
- css = 'border-' + side + '-width',
- val;
+ var effect = options.effect,
+ type = state ? 'show': 'hide',
+ visible = overlay.is(':visible'),
+ modals = $('[' + attr + ']').filter(':visible').not(tooltip),
+ zindex;
- // Grab the border-width value (add fluid class if needed)
- tooltip.addClass(fluidClass);
- val = parseInt(elem.css(css), 10);
- val = (backup ? val || parseInt(tooltip.css(css), 10) : val) || 0;
- tooltip.toggleClass(fluidClass, isFluid);
+ // Create our overlay if it isn't present already
+ if(!overlay) { overlay = self.create(); }
- return val;
- }
+ // Prevent modal from conflicting with show.solo, and don't hide backdrop is other modals are visible
+ if((overlay.is(':animated') && visible === state) || (!state && modals.length)) { return self; }
- function borderRadius(corner) {
- var isTitleTop = elems.titlebar && corner.y === TOP,
- elem = isTitleTop ? elems.titlebar : elems.content,
- moz = $.browser.mozilla,
- prefix = moz ? '-moz-' : $.browser.webkit ? '-webkit-' : '',
- side = corner.y + (moz ? '' : '-') + corner.x,
- css = prefix + (moz ? 'border-radius-' + side : 'border-' + side + '-radius');
+ // State specific...
+ if(state) {
+ // Set position
+ overlay.css({ left: 0, top: 0 });
- return parseInt(elem.css(css), 10) || parseInt(tooltip.css(css), 10) || 0;
- }
+ // Toggle backdrop cursor style on show
+ overlay.toggleClass('blurs', options.blur);
- function calculateSize(corner) {
- var y = corner.precedance === Y,
- width = size [ y ? WIDTH : HEIGHT ],
- height = size [ y ? HEIGHT : WIDTH ],
- isCenter = corner.string().indexOf(CENTER) > -1,
- base = width * (isCenter ? 0.5 : 1),
- pow = Math.pow,
- round = Math.round,
- bigHyp, ratio, result,
+ // IF the modal can steal the focus
+ if(options.stealfocus !== FALSE) {
+ // Make sure we can't focus anything outside the tooltip
+ docBody.bind('focusin'+namespace, stealFocus);
- smallHyp = Math.sqrt( pow(base, 2) + pow(height, 2) ),
-
- hyp = [
- (border / base) * smallHyp, (border / height) * smallHyp
- ];
- hyp[2] = Math.sqrt( pow(hyp[0], 2) - pow(border, 2) );
- hyp[3] = Math.sqrt( pow(hyp[1], 2) - pow(border, 2) );
+ // Blur the current item and focus anything in the modal we an
+ focusInputs( $('body *') );
+ }
+ }
+ else {
+ // Undelegate focus handler
+ docBody.unbind('focusin'+namespace);
+ }
- bigHyp = smallHyp + hyp[2] + hyp[3] + (isCenter ? 0 : hyp[0]);
- ratio = bigHyp / smallHyp;
+ // Stop all animations
+ overlay.stop(TRUE, FALSE);
- result = [ round(ratio * height), round(ratio * width) ];
- return { height: result[ y ? 0 : 1 ], width: result[ y ? 1 : 0 ] };
- }
+ // Use custom function if provided
+ if($.isFunction(effect)) {
+ effect.call(overlay, state);
+ }
- $.extend(self, {
- init: function()
- {
- var enabled = self.detectCorner() && (hasCanvas || $.browser.msie);
+ // If no effect type is supplied, use a simple toggle
+ else if(effect === FALSE) {
+ overlay[ type ]();
+ }
- // Determine tip corner and type
- if(enabled) {
- // Create a new tip and draw it
- self.create();
- self.update();
+ // Use basic fade function
+ else {
+ overlay.fadeTo( parseInt(duration, 10) || 90, state ? 1 : 0, function() {
+ if(!state) { $(this).hide(); }
+ });
+ }
- // Bind update events
- tooltip.unbind(namespace).bind('tooltipmove'+namespace, reposition);
+ // Reset position on hide
+ if(!state) {
+ overlay.queue(function(next) {
+ overlay.css({ left: '', top: '' });
+ next();
+ });
}
-
- return enabled;
+
+ return self;
},
- detectCorner: function()
+ show: function(event, duration) { return self.toggle(event, TRUE, duration); },
+ hide: function(event, duration) { return self.toggle(event, FALSE, duration); },
+
+ destroy: function()
{
- var corner = opts.corner,
- posOptions = qTip.options.position,
- at = posOptions.at,
- my = posOptions.my.string ? posOptions.my.string() : posOptions.my;
+ var delBlanket = overlay;
- // Detect corner and mimic properties
- if(corner === FALSE || (my === FALSE && at === FALSE)) {
- return FALSE;
- }
- else {
- if(corner === TRUE) {
- self.corner = new PLUGINS.Corner(my);
+ if(delBlanket) {
+ // Check if any other modal tooltips are present
+ delBlanket = $('[' + attr + ']').not(tooltip).length < 1;
+
+ // Remove overlay if needed
+ if(delBlanket) {
+ elems.overlay.remove();
+ $(document).unbind(globalNamespace);
}
- else if(!corner.string) {
- self.corner = new PLUGINS.Corner(corner);
- self.corner.fixed = TRUE;
+ else {
+ elems.overlay.unbind(globalNamespace+api.id);
}
+
+ // Undelegate focus handler
+ docBody.undelegate('*', 'focusin'+namespace);
}
- // Cache it
- cache.corner = new PLUGINS.Corner( self.corner.string() );
+ // Remove bound events
+ return tooltip.removeAttr(attr).unbind(globalNamespace);
+ }
+ });
- return self.corner.string() !== 'centercenter';
- },
+ self.init();
+}
- detectColours: function(actual) {
- var i, fill, border,
- tip = elems.tip.css('cssText', ''),
- corner = actual || self.corner,
- precedance = corner[ corner.precedance ],
+PLUGINS.modal = function(api) {
+ var self = api.plugins.modal;
- borderSide = 'border-' + precedance + '-color',
- borderSideCamel = 'border' + precedance.charAt(0) + precedance.substr(1) + 'Color',
+ return 'object' === typeof self ? self : (api.plugins.modal = new Modal(api));
+};
- invalid = /rgba?\(0, 0, 0(, 0)?\)|transparent|#123456/i,
- backgroundColor = 'background-color',
- transparent = 'transparent',
- important = ' !important',
-
- useTitle = elems.titlebar && (corner.y === TOP || (corner.y === CENTER && tip.position().top + (size.height / 2) + opts.offset < elems.titlebar.outerHeight(1))),
- colorElem = useTitle ? elems.titlebar : elems.tooltip;
-
- // Apply the fluid class so we can see our CSS values properly
- tooltip.addClass(fluidClass);
-
- // Detect tip colours from CSS styles
- color.fill = fill = tip.css(backgroundColor);
- color.border = border = tip[0].style[ borderSideCamel ] || tip.css(borderSide) || tooltip.css(borderSide);
-
- // Make sure colours are valid
- if(!fill || invalid.test(fill)) {
- color.fill = colorElem.css(backgroundColor) || transparent;
- if(invalid.test(color.fill)) {
- color.fill = tooltip.css(backgroundColor) || fill;
- }
- }
- if(!border || invalid.test(border) || border === $(document.body).css('color')) {
- color.border = colorElem.css(borderSide) || transparent;
- if(invalid.test(color.border) || color.border === colorElem.css('color')) {
- color.border = tooltip.css(borderSide) || tooltip.css(borderSideCamel) || border;
- }
- }
+// Plugin needs to be initialized on render
+PLUGINS.modal.initialize = 'render';
- // Reset background and border colours
- $('*', tip).add(tip).css('cssText', backgroundColor+':'+transparent+important+';border:0'+important+';');
+// Setup sanitiztion rules
+PLUGINS.modal.sanitize = function(opts) {
+ if(opts.show) {
+ if(typeof opts.show.modal !== 'object') { opts.show.modal = { on: !!opts.show.modal }; }
+ else if(typeof opts.show.modal.on === 'undefined') { opts.show.modal.on = TRUE; }
+ }
+};
- // Remove fluid class
- tooltip.removeClass(fluidClass);
- },
+// Base z-index for all modal tooltips (use qTip core z-index as a base)
+PLUGINS.modal.zindex = QTIP.zindex + 1000;
- create: function()
- {
- var width = size.width,
- height = size.height,
- vml;
+// Defines the selector used to select all 'focusable' elements within the modal when using the show.modal.stealfocus option.
+// Selectors initially taken from http://stackoverflow.com/questions/7668525/is-there-a-jquery-selector-to-get-all-elements-that-can-get-focus
+PLUGINS.modal.focusable = ['a[href]', 'area[href]', 'input', 'select', 'textarea', 'button', 'iframe', 'object', 'embed', '[tabindex]', '[contenteditable]'];
- // Remove previous tip element if present
- if(elems.tip) { elems.tip.remove(); }
+// Extend original api defaults
+$.extend(TRUE, QTIP.defaults, {
+ show: {
+ modal: {
+ on: FALSE,
+ effect: TRUE,
+ blur: TRUE,
+ stealfocus: TRUE,
+ escape: TRUE
+ }
+ }
+});
- // Create tip element and prepend to the tooltip
- elems.tip = $('', { 'class': 'ui-tooltip-tip' }).css({ width: width, height: height }).prependTo(tooltip);
- // Create tip drawing element(s)
- if(hasCanvas) {
- // save() as soon as we create the canvas element so FF2 doesn't bork on our first restore()!
- $('').appendTo(elems.tip)[0].getContext('2d').save();
- }
- else {
- vml = '';
- elems.tip.html(vml + vml);
+PLUGINS.imagemap = function(api, area, corner, adjustMethod)
+{
+ if(!area.jquery) { area = $(area); }
- // Prevent mousing down on the tip since it causes problems with .live() handling in IE due to VML
- $('*', elems.tip).bind('click mousedown', function(event) { event.stopPropagation(); });
+ var cache = (api.cache.areas = {}),
+ shape = (area[0].shape || area.attr('shape')).toLowerCase(),
+ coordsString = area[0].coords || area.attr('coords'),
+ baseCoords = coordsString.split(','),
+ coords = [],
+ image = $('img[usemap="#'+area.parent('map').attr('name')+'"]'),
+ imageOffset = image.offset(),
+ result = {
+ width: 0, height: 0,
+ position: {
+ top: 1e10, right: 0,
+ bottom: 0, left: 1e10
}
},
+ i = 0, next = 0, dimensions;
- update: function(corner, position)
+ // POLY area coordinate calculator
+ // Special thanks to Ed Cradock for helping out with this.
+ // Uses a binary search algorithm to find suitable coordinates.
+ function polyCoordinates(result, coords, corner)
+ {
+ var i = 0,
+ compareX = 1, compareY = 1,
+ realX = 0, realY = 0,
+ newWidth = result.width,
+ newHeight = result.height;
+
+ // Use a binary search algorithm to locate most suitable coordinate (hopefully)
+ while(newWidth > 0 && newHeight > 0 && compareX > 0 && compareY > 0)
{
- var tip = elems.tip,
- inner = tip.children(),
- width = size.width,
- height = size.height,
- regular = 'px solid ',
- transparent = 'px dashed transparent', // Dashed IE6 border-transparency hack. Awesome!
- mimic = opts.mimic,
- round = Math.round,
- precedance, context, coords, translate, newSize;
+ newWidth = Math.floor(newWidth / 2);
+ newHeight = Math.floor(newHeight / 2);
- // Re-determine tip if not already set
- if(!corner) { corner = cache.corner || self.corner; }
+ if(corner.x === LEFT){ compareX = newWidth; }
+ else if(corner.x === RIGHT){ compareX = result.width - newWidth; }
+ else{ compareX += Math.floor(newWidth / 2); }
- // Use corner property if we detect an invalid mimic value
- if(mimic === FALSE) { mimic = corner; }
+ if(corner.y === TOP){ compareY = newHeight; }
+ else if(corner.y === BOTTOM){ compareY = result.height - newHeight; }
+ else{ compareY += Math.floor(newHeight / 2); }
- // Otherwise inherit mimic properties from the corner object as necessary
- else {
- mimic = new PLUGINS.Corner(mimic);
- mimic.precedance = corner.precedance;
+ i = coords.length; while(i--)
+ {
+ if(coords.length < 2){ break; }
- if(mimic.x === 'inherit') { mimic.x = corner.x; }
- else if(mimic.y === 'inherit') { mimic.y = corner.y; }
- else if(mimic.x === mimic.y) {
- mimic[ corner.precedance ] = corner[ corner.precedance ];
+ realX = coords[i][0] - result.position.left;
+ realY = coords[i][1] - result.position.top;
+
+ if((corner.x === LEFT && realX >= compareX) ||
+ (corner.x === RIGHT && realX <= compareX) ||
+ (corner.x === CENTER && (realX < compareX || realX > (result.width - compareX))) ||
+ (corner.y === TOP && realY >= compareY) ||
+ (corner.y === BOTTOM && realY <= compareY) ||
+ (corner.y === CENTER && (realY < compareY || realY > (result.height - compareY)))) {
+ coords.splice(i, 1);
}
}
- precedance = mimic.precedance;
-
- // Ensure the tip width.height are relative to the tip position
- if(corner.precedance === X) { swapDimensions(); }
- else { resetDimensions(); }
+ }
- // Set the tip dimensions
- elems.tip.css({
- width: (width = size.width),
- height: (height = size.height)
- });
+ return { left: coords[0][0], top: coords[0][1] };
+ }
- // Update our colours
- self.detectColours(corner);
+ // Make sure we account for padding and borders on the image
+ imageOffset.left += Math.ceil((image.outerWidth() - image.width()) / 2);
+ imageOffset.top += Math.ceil((image.outerHeight() - image.height()) / 2);
- // Detect border width, taking into account colours
- if(color.border !== 'transparent') {
- // Grab border width
- border = borderWidth(corner, NULL, TRUE);
+ // Parse coordinates into proper array
+ if(shape === 'poly') {
+ i = baseCoords.length; while(i--)
+ {
+ next = [ parseInt(baseCoords[--i], 10), parseInt(baseCoords[i+1], 10) ];
- // If border width isn't zero, use border color as fill (1.0 style tips)
- if(opts.border === 0 && border > 0) { color.fill = color.border; }
+ if(next[0] > result.position.right){ result.position.right = next[0]; }
+ if(next[0] < result.position.left){ result.position.left = next[0]; }
+ if(next[1] > result.position.bottom){ result.position.bottom = next[1]; }
+ if(next[1] < result.position.top){ result.position.top = next[1]; }
- // Set border width (use detected border width if opts.border is true)
- self.border = border = opts.border !== TRUE ? opts.border : border;
- }
+ coords.push(next);
+ }
+ }
+ else {
+ i = -1; while(i++ < baseCoords.length) {
+ coords.push( parseInt(baseCoords[i], 10) );
+ }
+ }
- // Border colour was invalid, set border to zero
- else { self.border = border = 0; }
+ // Calculate details
+ switch(shape)
+ {
+ case 'rect':
+ result = {
+ width: Math.abs(coords[2] - coords[0]),
+ height: Math.abs(coords[3] - coords[1]),
+ position: {
+ left: Math.min(coords[0], coords[2]),
+ top: Math.min(coords[1], coords[3])
+ }
+ };
+ break;
- // Calculate coordinates
- coords = calculateTip(mimic, width , height);
+ case 'circle':
+ result = {
+ width: coords[2] + 2,
+ height: coords[2] + 2,
+ position: { left: coords[0], top: coords[1] }
+ };
+ break;
- // Determine tip size
- self.size = newSize = calculateSize(corner);
- tip.css(newSize);
+ case 'poly':
+ result.width = Math.abs(result.position.right - result.position.left);
+ result.height = Math.abs(result.position.bottom - result.position.top);
- // Calculate tip translation
- if(corner.precedance === Y) {
- translate = [
- round(mimic.x === LEFT ? border : mimic.x === RIGHT ? newSize.width - width - border : (newSize.width - width) / 2),
- round(mimic.y === TOP ? newSize.height - height : 0)
- ];
+ if(corner.abbrev() === 'c') {
+ result.position = {
+ left: result.position.left + (result.width / 2),
+ top: result.position.top + (result.height / 2)
+ };
}
else {
- translate = [
- round(mimic.x === LEFT ? newSize.width - width : 0),
- round(mimic.y === TOP ? border : mimic.y === BOTTOM ? newSize.height - height - border : (newSize.height - height) / 2)
- ];
- }
-
- // Canvas drawing implementation
- if(hasCanvas) {
- // Set the canvas size using calculated size
- inner.attr(newSize);
-
- // Grab canvas context and clear/save it
- context = inner[0].getContext('2d');
- context.restore(); context.save();
- context.clearRect(0,0,3000,3000);
-
- // Set properties
- context.fillStyle = color.fill;
- context.strokeStyle = color.border;
- context.lineWidth = border * 2;
- context.lineJoin = 'miter';
- context.miterLimit = 100;
-
- // Translate origin
- context.translate(translate[0], translate[1]);
+ // Calculate if we can't find a cached value
+ if(!cache[corner+coordsString]) {
+ result.position = polyCoordinates(result, coords.slice(), corner);
- // Draw the tip
- context.beginPath();
- context.moveTo(coords[0][0], coords[0][1]);
- context.lineTo(coords[1][0], coords[1][1]);
- context.lineTo(coords[2][0], coords[2][1]);
- context.closePath();
+ // If flip adjustment is enabled, also calculate the closest opposite point
+ if(adjustMethod && (adjustMethod[0] === 'flip' || adjustMethod[1] === 'flip')) {
+ result.offset = polyCoordinates(result, coords.slice(), {
+ x: corner.x === LEFT ? RIGHT : corner.x === RIGHT ? LEFT : CENTER,
+ y: corner.y === TOP ? BOTTOM : corner.y === BOTTOM ? TOP : CENTER
+ });
- // Apply fill and border
- if(border) {
- // Make sure transparent borders are supported by doing a stroke
- // of the background colour before the stroke colour
- if(tooltip.css('background-clip') === 'border-box') {
- context.strokeStyle = color.fill;
- context.stroke();
+ result.offset.left -= result.position.left;
+ result.offset.top -= result.position.top;
}
- context.strokeStyle = color.border;
- context.stroke();
- }
- context.fill();
- }
-
- // VML (IE Proprietary implementation)
- else {
- // Setup coordinates string
- coords = 'm' + coords[0][0] + ',' + coords[0][1] + ' l' + coords[1][0] +
- ',' + coords[1][1] + ' ' + coords[2][0] + ',' + coords[2][1] + ' xe';
-
- // Setup VML-specific offset for pixel-perfection
- translate[2] = border && /^(r|b)/i.test(corner.string()) ?
- parseFloat($.browser.version, 10) === 8 ? 2 : 1 : 0;
-
- // Set initial CSS
- inner.css({
- antialias: ''+(mimic.string().indexOf(CENTER) > -1),
- left: translate[0] - (translate[2] * Number(precedance === X)),
- top: translate[1] - (translate[2] * Number(precedance === Y)),
- width: width + border,
- height: height + border
- })
- .each(function(i) {
- var $this = $(this);
- // Set shape specific attributes
- $this[ $this.prop ? 'prop' : 'attr' ]({
- coordsize: (width+border) + ' ' + (height+border),
- path: coords,
- fillcolor: color.fill,
- filled: !!i,
- stroked: !i
- })
- .css({ display: border || i ? 'block' : 'none' });
+ // Store the result
+ cache[corner+coordsString] = result;
+ }
- // Check if border is enabled and add stroke element
- if(!i && $this.html() === '') {
- $this.html(
- ''
- );
- }
- });
+ // Grab the cached result
+ result = cache[corner+coordsString];
}
- // Position if needed
- if(position !== FALSE) { self.position(corner); }
- },
-
- // Tip positioning method
- position: function(corner)
- {
- var tip = elems.tip,
- position = {},
- userOffset = Math.max(0, opts.offset),
- precedance, dimensions, corners;
-
- // Return if tips are disabled or tip is not yet rendered
- if(opts.corner === FALSE || !tip) { return FALSE; }
-
- // Inherit corner if not provided
- corner = corner || self.corner;
- precedance = corner.precedance;
-
- // Determine which tip dimension to use for adjustment
- dimensions = calculateSize(corner);
-
- // Setup corners and offset array
- corners = [ corner.x, corner.y ];
- if(precedance === X) { corners.reverse(); }
-
- // Calculate tip position
- $.each(corners, function(i, side) {
- var b, br;
-
- if(side === CENTER) {
- b = precedance === Y ? LEFT : TOP;
- position[ b ] = '50%';
- position['margin-' + b] = -Math.round(dimensions[ precedance === Y ? WIDTH : HEIGHT ] / 2) + userOffset;
- }
- else {
- b = borderWidth(corner, side);
- br = borderRadius(corner);
+ result.width = result.height = 0;
+ break;
+ }
- position[ side ] = i ? 0 : (userOffset + (br > b ? br : -b));
- }
- });
+ // Add image position to offset coordinates
+ result.position.left += imageOffset.left;
+ result.position.top += imageOffset.top;
- // Adjust for tip dimensions
- position[ corner[precedance] ] -= dimensions[ precedance === X ? WIDTH : HEIGHT ];
+ return result;
+};
- // Set and return new position
- tip.css({ top: '', bottom: '', left: '', right: '', margin: '' }).css(position);
- return position;
+
+PLUGINS.svg = function(api, svg, corner, adjustMethod)
+{
+ var doc = $(document),
+ elem = svg[0],
+ result = {
+ width: 0, height: 0,
+ position: { top: 1e10, left: 1e10 }
},
-
- destroy: function()
- {
- // Remove the tip element
- if(elems.tip) { elems.tip.remove(); }
- elems.tip = false;
+ box, mtx, root, point, tPoint;
- // Unbind events
- tooltip.unbind(namespace);
- }
- });
+ // Ascend the parentNode chain until we find an element with getBBox()
+ while(!elem.getBBox) { elem = elem.parentNode; }
- self.init();
-}
+ // Check for a valid bounding box method
+ if (elem.getBBox && elem.parentNode) {
+ box = elem.getBBox();
+ mtx = elem.getScreenCTM();
+ root = elem.farthestViewportElement || elem;
-PLUGINS.tip = function(api)
-{
- var self = api.plugins.tip;
-
- return 'object' === typeof self ? self : (api.plugins.tip = new Tip(api));
-};
+ // Return if no method is found
+ if(!root.createSVGPoint) { return result; }
-// Initialize tip on render
-PLUGINS.tip.initialize = 'render';
+ // Create our point var
+ point = root.createSVGPoint();
-// Setup plugin sanitization options
-PLUGINS.tip.sanitize = function(options)
-{
- var style = options.style, opts;
- if(style && 'tip' in style) {
- opts = options.style.tip;
- if(typeof opts !== 'object'){ options.style.tip = { corner: opts }; }
- if(!(/string|boolean/i).test(typeof opts.corner)) { opts.corner = TRUE; }
- if(typeof opts.width !== 'number'){ delete opts.width; }
- if(typeof opts.height !== 'number'){ delete opts.height; }
- if(typeof opts.border !== 'number' && opts.border !== TRUE){ delete opts.border; }
- if(typeof opts.offset !== 'number'){ delete opts.offset; }
+ // Adjust top and left
+ point.x = box.x;
+ point.y = box.y;
+ tPoint = point.matrixTransform(mtx);
+ result.position.left = tPoint.x;
+ result.position.top = tPoint.y;
+
+ // Adjust width and height
+ point.x += box.width;
+ point.y += box.height;
+ tPoint = point.matrixTransform(mtx);
+ result.width = tPoint.x - result.position.left;
+ result.height = tPoint.y - result.position.top;
+
+ // Adjust by scroll offset
+ result.position.left += doc.scrollLeft();
+ result.position.top += doc.scrollTop();
}
+
+ return result;
};
-// Extend original qTip defaults
-$.extend(TRUE, QTIP.defaults, {
- style: {
- tip: {
- corner: TRUE,
- mimic: FALSE,
- width: 6,
- height: 6,
- border: TRUE,
- offset: 0
- }
- }
-});
-PLUGINS.viewport = function(api, position, posOptions, targetWidth, targetHeight, elemWidth, elemHeight)
+/*
+ * BGIFrame adaption (http://plugins.jquery.com/project/bgiframe)
+ * Special thanks to Brandon Aaron
+ */
+function BGIFrame(api)
{
- var target = posOptions.target,
- tooltip = api.elements.tooltip,
- my = posOptions.my,
- at = posOptions.at,
- adjust = posOptions.adjust,
- method = adjust.method.split(' '),
- methodX = method[0],
- methodY = method[1] || method[0],
- viewport = posOptions.viewport,
- container = posOptions.container,
- cache = api.cache,
- tip = api.plugins.tip,
- adjusted = { left: 0, top: 0 },
- fixed, newMy, newClass;
-
- // If viewport is not a jQuery element, or it's the window/document or no adjustment method is used... return
- if(!viewport.jquery || target[0] === window || target[0] === document.body || adjust.method === 'none') {
- return adjusted;
- }
+ var self = this,
+ elems = api.elements,
+ tooltip = elems.tooltip,
+ namespace = '.bgiframe-' + api.id;
- // Cache our viewport details
- fixed = tooltip.css('position') === 'fixed';
- viewport = {
- elem: viewport,
- height: viewport[ (viewport[0] === window ? 'h' : 'outerH') + 'eight' ](),
- width: viewport[ (viewport[0] === window ? 'w' : 'outerW') + 'idth' ](),
- scrollleft: fixed ? 0 : viewport.scrollLeft(),
- scrolltop: fixed ? 0 : viewport.scrollTop(),
- offset: viewport.offset() || { left: 0, top: 0 }
- };
- container = {
- elem: container,
- scrollLeft: container.scrollLeft(),
- scrollTop: container.scrollTop(),
- offset: container.offset() || { left: 0, top: 0 }
- };
+ $.extend(self, {
+ init: function()
+ {
+ // Create the BGIFrame element
+ elems.bgiframe = $('');
- // Generic calculation method
- function calculate(side, otherSide, type, adjust, side1, side2, lengthName, targetLength, elemLength) {
- var initialPos = position[side1],
- mySide = my[side], atSide = at[side],
- isShift = type === SHIFT,
- viewportScroll = -container.offset[side1] + viewport.offset[side1] + viewport['scroll'+side1],
- myLength = mySide === side1 ? elemLength : mySide === side2 ? -elemLength : -elemLength / 2,
- atLength = atSide === side1 ? targetLength : atSide === side2 ? -targetLength : -targetLength / 2,
- tipLength = tip && tip.size ? tip.size[lengthName] || 0 : 0,
- tipAdjust = tip && tip.corner && tip.corner.precedance === side && !isShift ? tipLength : 0,
- overflow1 = viewportScroll - initialPos + tipAdjust,
- overflow2 = initialPos + elemLength - viewport[lengthName] - viewportScroll + tipAdjust,
- offset = myLength - (my.precedance === side || mySide === my[otherSide] ? atLength : 0) - (atSide === CENTER ? targetLength / 2 : 0);
+ // Append the new element to the tooltip
+ elems.bgiframe.appendTo(tooltip);
- // shift
- if(isShift) {
- tipAdjust = tip && tip.corner && tip.corner.precedance === otherSide ? tipLength : 0;
- offset = (mySide === side1 ? 1 : -1) * myLength - tipAdjust;
+ // Update BGIFrame on tooltip move
+ tooltip.bind('tooltipmove'+namespace, self.adjust);
+ },
- // Adjust position but keep it within viewport dimensions
- position[side1] += overflow1 > 0 ? overflow1 : overflow2 > 0 ? -overflow2 : 0;
- position[side1] = Math.max(
- -container.offset[side1] + viewport.offset[side1] + (tipAdjust && tip.corner[side] === CENTER ? tip.offset : 0),
- initialPos - offset,
- Math.min(
- Math.max(-container.offset[side1] + viewport.offset[side1] + viewport[lengthName], initialPos + offset),
- position[side1]
- )
- );
- }
+ adjust: function()
+ {
+ var dimensions = api.get('dimensions'), // Determine current tooltip dimensions
+ plugin = api.plugins.tip,
+ tip = elems.tip,
+ tipAdjust, offset;
- // flip/flipinvert
- else {
- // Update adjustment amount depending on if using flipinvert or flip
- adjust *= (type === FLIPINVERT ? 2 : 0);
+ // Adjust border offset
+ offset = parseInt(tooltip.css('border-left-width'), 10) || 0;
+ offset = { left: -offset, top: -offset };
- // Check for overflow on the left/top
- if(overflow1 > 0 && (mySide !== side1 || overflow2 > 0)) {
- position[side1] -= offset + adjust;
- newMy['invert'+side](side1);
+ // Adjust for tips plugin
+ if(plugin && tip) {
+ tipAdjust = (plugin.corner.precedance === 'x') ? ['width', 'left'] : ['height', 'top'];
+ offset[ tipAdjust[1] ] -= tip[ tipAdjust[0] ]();
}
- // Check for overflow on the bottom/right
- else if(overflow2 > 0 && (mySide !== side2 || overflow1 > 0) ) {
- position[side1] -= (mySide === CENTER ? -offset : offset) + adjust;
- newMy['invert'+side](side2);
- }
+ // Update bgiframe
+ elems.bgiframe.css(offset).css(dimensions);
+ },
- // Make sure we haven't made things worse with the adjustment and reset if so
- if(position[side1] < viewportScroll && -position[side1] > overflow2) {
- position[side1] = initialPos; newMy = undefined;
- }
+ destroy: function()
+ {
+ // Remove iframe
+ elems.bgiframe.remove();
+
+ // Remove bound events
+ tooltip.unbind(namespace);
}
+ });
- return position[side1] - initialPos;
+ self.init();
+}
+
+PLUGINS.bgiframe = function(api)
+{
+ var browser = $.browser,
+ self = api.plugins.bgiframe;
+
+ // Proceed only if the browser is IE6 and offending elements are present
+ if($('select, object').length < 1 || !(browser.msie && (''+browser.version).charAt(0) === '6')) {
+ return FALSE;
}
- // Set newMy if using flip or flipinvert methods
- if(methodX !== 'shift' || methodY !== 'shift') { newMy = my.clone(); }
+ return 'object' === typeof self ? self : (api.plugins.bgiframe = new BGIFrame(api));
+};
+
+// Plugin needs to be initialized on render
+PLUGINS.bgiframe.initialize = 'render';
- // Adjust position based onviewport and adjustment options
- adjusted = {
- left: methodX !== 'none' ? calculate( X, Y, methodX, adjust.x, LEFT, RIGHT, WIDTH, targetWidth, elemWidth ) : 0,
- top: methodY !== 'none' ? calculate( Y, X, methodY, adjust.y, TOP, BOTTOM, HEIGHT, targetHeight, elemHeight ) : 0
- };
- // Set tooltip position class if it's changed
- if(newMy && cache.lastClass !== (newClass = uitooltip + '-pos-' + newMy.abbrev())) {
- tooltip.removeClass(api.cache.lastClass).addClass( (api.cache.lastClass = newClass) );
- }
- return adjusted;
-};
}));
\ No newline at end of file
diff --git a/dist/jquery.qtip.min.css b/dist/jquery.qtip.min.css
index 4092e364..fcf7bec4 100644
--- a/dist/jquery.qtip.min.css
+++ b/dist/jquery.qtip.min.css
@@ -1 +1,2 @@
-/*!* qTip2 - Pretty powerful tooltips * http://craigsworks.com/projects/qtip2/ * * Version:2.0.0pre * Copyright 2009-2010 Craig Michael Thompson - http://craigsworks.com * * Dual licensed under MIT or GPLv2 licenses * http://en.wikipedia.org/wiki/MIT_License * http://en.wikipedia.org/wiki/GNU_General_Public_License * * Date:Mon Jul 16 00:57:01 2012+0100 */ .ui-tooltip,.qtip{position:absolute;left:-28000px;top:-28000px;display:none;max-width:280px;min-width:50px;font-size:10.5px;line-height:12px;border-width:1px;border-style:solid;}.ui-tooltip-fluid{display:block;visibility:hidden;position:static!important;float:left!important;}.ui-tooltip-content{position:relative;padding:5px 9px;overflow:hidden;text-align:left;word-wrap:break-word;overflow:hidden;}.ui-tooltip-titlebar{position:relative;min-height:14px;padding:5px 35px 5px 10px;overflow:hidden;border-width:0 0 1px;font-weight:bold;}.ui-tooltip-titlebar+.ui-tooltip-content{border-top-width:0!important;}/*!Default close button class */ .ui-tooltip-titlebar .ui-state-default{position:absolute;right:4px;top:50%;margin-top:-9px;cursor:pointer;outline:medium none;border-width:1px;border-style:solid;}* html .ui-tooltip-titlebar .ui-state-default{top:16px;}.ui-tooltip-titlebar .ui-icon,.ui-tooltip-icon .ui-icon{display:block;text-indent:-1000em;}.ui-tooltip-icon,.ui-tooltip-icon .ui-icon{-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;text-decoration:none;}.ui-tooltip-icon .ui-icon{width:18px;height:14px;text-align:center;text-indent:0;font:normal bold 10px/13px Tahoma,sans-serif;color:inherit;background:transparent none no-repeat -100em -100em;}/*!Default tooltip style */ .ui-tooltip-default{border-color:#F1D031;background-color:#FFFFA3;color:#555;}.ui-tooltip-default .ui-tooltip-titlebar{background-color:#FFEF93;}.ui-tooltip-default .ui-tooltip-icon{border-color:#CCC;background:#F1F1F1;color:#777;}.ui-tooltip-default .ui-tooltip-titlebar .ui-state-hover{border-color:#AAA;color:#111;}#qtip-overlay{position:fixed;left:-10000em;top:-10000em;}#qtip-overlay.blurs{cursor:pointer;}#qtip-overlay div{position:absolute;left:0;top:0;width:100%;height:100%;background-color:black;opacity:.7;filter:alpha(opacity=70);-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=70)";}.ui-tooltip .ui-tooltip-tip{margin:0 auto;overflow:hidden;z-index:10;}.ui-tooltip .ui-tooltip-tip,.ui-tooltip .ui-tooltip-tip *{position:absolute;line-height:.1px!important;font-size:.1px!important;color:#123456;background:transparent;border:0 dashed transparent;}.ui-tooltip .ui-tooltip-tip canvas{top:0;left:0;}/*!Light tooltip style */ .ui-tooltip-light{background-color:white;border-color:#E2E2E2;color:#454545;}.ui-tooltip-light .ui-tooltip-titlebar{background-color:#f1f1f1;}/*!Dark tooltip style */ .ui-tooltip-dark{background-color:#505050;border-color:#303030;color:#f3f3f3;}.ui-tooltip-dark .ui-tooltip-titlebar{background-color:#404040;}.ui-tooltip-dark .ui-tooltip-icon{border-color:#444;}.ui-tooltip-dark .ui-tooltip-titlebar .ui-state-hover{border-color:#303030;}/*!Cream tooltip style */ .ui-tooltip-cream{background-color:#FBF7AA;border-color:#F9E98E;color:#A27D35;}.ui-tooltip-cream .ui-tooltip-titlebar{background-color:#F0DE7D;}.ui-tooltip-cream .ui-state-default .ui-tooltip-icon{background-position:-82px 0;}/*!Red tooltip style */ .ui-tooltip-red{background-color:#F78B83;border-color:#D95252;color:#912323;}.ui-tooltip-red .ui-tooltip-titlebar{background-color:#F06D65;}.ui-tooltip-red .ui-state-default .ui-tooltip-icon{background-position:-102px 0;}.ui-tooltip-red .ui-tooltip-icon{border-color:#D95252;}.ui-tooltip-red .ui-tooltip-titlebar .ui-state-hover{border-color:#D95252;}/*!Green tooltip style */ .ui-tooltip-green{background-color:#CAED9E;border-color:#90D93F;color:#3F6219;}.ui-tooltip-green .ui-tooltip-titlebar{background-color:#B0DE78;}.ui-tooltip-green .ui-state-default .ui-tooltip-icon{background-position:-42px 0;}/*!Blue tooltip style */ .ui-tooltip-blue{background-color:#E5F6FE;border-color:#ADD9ED;color:#5E99BD;}.ui-tooltip-blue .ui-tooltip-titlebar{background-color:#D0E9F5;}.ui-tooltip-blue .ui-state-default .ui-tooltip-icon{background-position:-2px 0;}/*!Add shadows to your tooltips in:FF3+,Chrome 2+,Opera 10.6+,IE9+,Safari 2+*/ .ui-tooltip-shadow{-webkit-box-shadow:1px 1px 3px 1px rgba(0,0,0,0.15);-moz-box-shadow:1px 1px 3px 1px rgba(0,0,0,0.15);box-shadow:1px 1px 3px 1px rgba(0,0,0,0.15);}/*!Add rounded corners to your tooltips in:FF3+,Chrome 2+,Opera 10.6+,IE9+,Safari 2+*/ .ui-tooltip-rounded,.ui-tooltip-tipsy,.ui-tooltip-bootstrap{-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px;}/*!Youtube tooltip style */ .ui-tooltip-youtube{-moz-border-radius:2px;-webkit-border-radius:2px;border-radius:2px;-webkit-box-shadow:0 0 3px #333;-moz-box-shadow:0 0 3px #333;box-shadow:0 0 3px #333;color:white;border-width:0;background:#4A4A4A;background-image:-moz-linear-gradient(top,#4A4A4A 0,black 100%);background-image:-ms-linear-gradient(top,#4A4A4A 0,black 100%);background-image:-o-linear-gradient(top,#4A4A4A 0,black 100%);background-image:-webkit-gradient(linear,left top,left bottom,color-stop(0,#4A4A4A),color-stop(100%,black));background-image:-webkit-linear-gradient(top,#4A4A4A 0,black 100%);background-image:linear-gradient(to bottom,#4A4A4A 0,black 100%);}.ui-tooltip-youtube .ui-tooltip-titlebar{background-color:#4A4A4A;background-color:rgba(0,0,0,0);}.ui-tooltip-youtube .ui-tooltip-content{padding:.75em;font:12px arial,sans-serif;filter:progid:DXImageTransform.Microsoft.Gradient(GradientType=0,StartColorStr=#4a4a4a,EndColorStr=#000000);-ms-filter:"progid:DXImageTransform.Microsoft.Gradient(GradientType=0,StartColorStr=#4a4a4a,EndColorStr=#000000);";}.ui-tooltip-youtube .ui-tooltip-icon{border-color:#222;}.ui-tooltip-youtube .ui-tooltip-titlebar .ui-state-hover{border-color:#303030;}.ui-tooltip-jtools{background:#232323;background:rgba(0,0,0,0.7);background-image:-moz-linear-gradient(top,#717171,#232323);background-image:-webkit-gradient(linear,left top,left bottom,from(#717171),to(#232323));border:2px solid #ddd;border:2px solid rgba(241,241,241,1);-moz-border-radius:2px;-webkit-border-radius:2px;border-radius:2px;-webkit-box-shadow:0 0 12px #333;-moz-box-shadow:0 0 12px #333;box-shadow:0 0 12px #333;}.ui-tooltip-jtools .ui-tooltip-titlebar{background-color:transparent;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#717171,endColorstr=#4A4A4A);-ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorstr=#717171,endColorstr=#4A4A4A)";}.ui-tooltip-jtools .ui-tooltip-content{filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#4A4A4A,endColorstr=#232323);-ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorstr=#4A4A4A,endColorstr=#232323)";}.ui-tooltip-jtools .ui-tooltip-titlebar,.ui-tooltip-jtools .ui-tooltip-content{background:transparent;color:white;border:0 dashed transparent;}.ui-tooltip-jtools .ui-tooltip-icon{border-color:#555;}.ui-tooltip-jtools .ui-tooltip-titlebar .ui-state-hover{border-color:#333;}.ui-tooltip-cluetip{-webkit-box-shadow:4px 4px 5px rgba(0,0,0,0.4);-moz-box-shadow:4px 4px 5px rgba(0,0,0,0.4);box-shadow:4px 4px 5px rgba(0,0,0,0.4);background-color:#D9D9C2;color:#111;border:0 dashed transparent;}.ui-tooltip-cluetip .ui-tooltip-titlebar{background-color:#87876A;color:white;border:0 dashed transparent;}.ui-tooltip-cluetip .ui-tooltip-icon{border-color:#808064;}.ui-tooltip-cluetip .ui-tooltip-titlebar .ui-state-hover{border-color:#696952;color:#696952;}.ui-tooltip-tipsy{background:black;background:rgba(0,0,0,.87);color:white;border:0 solid transparent;font-size:11px;font-family:'Lucida Grande',sans-serif;font-weight:bold;line-height:16px;text-shadow:0 1px black;}.ui-tooltip-tipsy .ui-tooltip-titlebar{padding:6px 35px 0 10;background-color:transparent;}.ui-tooltip-tipsy .ui-tooltip-content{padding:6px 10;}.ui-tooltip-tipsy .ui-tooltip-icon{border-color:#222;text-shadow:none;}.ui-tooltip-tipsy .ui-tooltip-titlebar .ui-state-hover{border-color:#303030;}.ui-tooltip-tipped{border:3px solid #959FA9;-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;background-color:#F9F9F9;color:#454545;font-weight:normal;font-family:serif;}.ui-tooltip-tipped .ui-tooltip-titlebar{border-bottom-width:0;color:white;background:#3A79B8;background-image:-moz-linear-gradient(top,#3A79B8,#2E629D);background-image:-webkit-gradient(linear,left top,left bottom,from(#3A79B8),to(#2E629D));filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#3A79B8,endColorstr=#2E629D);-ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorstr=#3A79B8,endColorstr=#2E629D)";}.ui-tooltip-tipped .ui-tooltip-icon{border:2px solid #285589;background:#285589;}.ui-tooltip-tipped .ui-tooltip-icon .ui-icon{background-color:#FBFBFB;color:#555;}.ui-tooltip-bootstrap{font-size:13px;line-height:18px;color:#333;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);*border-right-width:2px;*border-bottom-width:2px;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);-moz-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box;}.ui-tooltip-bootstrap .ui-tooltip-titlebar{font-size:18px;line-height:22px;border-bottom:1px solid #ccc;background-color:transparent;}.ui-tooltip-bootstrap .ui-tooltip-titlebar .ui-state-default{right:9px;top:49%;border-style:none;}.ui-tooltip-bootstrap .ui-tooltip-icon{background:white;}.ui-tooltip-bootstrap .ui-tooltip-icon .ui-icon{width:auto;height:auto;float:right;font-size:20px;font-weight:bold;line-height:18px;color:#000;text-shadow:0 1px 0 #fff;opacity:.2;filter:alpha(opacity=20);}.ui-tooltip-bootstrap .ui-tooltip-icon .ui-icon:hover{color:#000;text-decoration:none;cursor:pointer;opacity:.4;filter:alpha(opacity=40);}.ui-tooltip:not(.ie9haxors) div.ui-tooltip-content,.ui-tooltip:not(.ie9haxors) div.ui-tooltip-titlebar{filter:none;-ms-filter:none;}
\ No newline at end of file
+/*! qTip2 v2.0.0 | http://craigsworks.com/projects/qtip2/ | Licensed MIT, GPL */
+.ui-tooltip,.qtip{position:absolute;left:-28000px;top:-28000px;display:none;max-width:280px;min-width:50px;font-size:10.5px;line-height:12px;border-width:1px;border-style:solid}.ui-tooltip-fluid{display:block;visibility:hidden;position:static!important;float:left!important}.ui-tooltip-content{position:relative;padding:5px 9px;overflow:hidden;text-align:left;word-wrap:break-word}.ui-tooltip-titlebar{position:relative;min-height:14px;padding:5px 35px 5px 10px;overflow:hidden;border-width:0 0 1px;font-weight:bold}.ui-tooltip-titlebar + .ui-tooltip-content{border-top-width:0!important}.ui-tooltip-titlebar .ui-state-default{position:absolute;right:4px;top:50%;margin-top:-9px;cursor:pointer;outline:medium none;border-width:1px;border-style:solid}* html .ui-tooltip-titlebar .ui-state-default{top:16px}.ui-tooltip-titlebar .ui-icon,.ui-tooltip-icon .ui-icon{display:block;text-indent:-1000em;direction:ltr}.ui-tooltip-icon,.ui-tooltip-icon .ui-icon{-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;text-decoration:none}.ui-tooltip-icon .ui-icon{width:18px;height:14px;text-align:center;text-indent:0;font:normal bold 10px/13px Tahoma,sans-serif;color:inherit;background:transparent none no-repeat -100em -100em}.ui-tooltip-focus{}.ui-tooltip-hover{}.ui-tooltip-default{border-color:#F1D031;background-color:#FFFFA3;color:#555}.ui-tooltip-default .ui-tooltip-titlebar{background-color:#FFEF93}.ui-tooltip-default .ui-tooltip-icon{border-color:#CCC;background:#F1F1F1;color:#777}.ui-tooltip-default .ui-tooltip-titlebar .ui-state-hover{border-color:#AAA;color:#111}.ui-tooltip-shadow{-webkit-box-shadow:1px 1px 3px 1px rgba(0,0,0,0.15);-moz-box-shadow:1px 1px 3px 1px rgba(0,0,0,0.15);box-shadow:1px 1px 3px 1px rgba(0,0,0,0.15)}.ui-tooltip-rounded,.ui-tooltip-tipsy,.ui-tooltip-bootstrap{-moz-border-radius:5px;-webkit-border-radius:5px;border-radius:5px}.ui-tooltip-youtube{-moz-border-radius:2px;-webkit-border-radius:2px;border-radius:2px;-webkit-box-shadow:0 0 3px #333;-moz-box-shadow:0 0 3px #333;box-shadow:0 0 3px #333;color:white;border-width:0;background:#4A4A4A;background-image:-webkit-gradient(linear,left top,left bottom,color-stop(0,#4A4A4A),color-stop(100%,black));background-image:-webkit-linear-gradient(top,#4A4A4A 0,black 100%);background-image:-moz-linear-gradient(top,#4A4A4A 0,black 100%);background-image:-ms-linear-gradient(top,#4A4A4A 0,black 100%);background-image:-o-linear-gradient(top,#4A4A4A 0,black 100%)}.ui-tooltip-youtube .ui-tooltip-titlebar{background-color:#4A4A4A;background-color:rgba(0,0,0,0)}.ui-tooltip-youtube .ui-tooltip-content{padding:.75em;font:12px arial,sans-serif;filter:progid:DXImageTransform.Microsoft.Gradient(GradientType=0,StartColorStr=#4a4a4a,EndColorStr=#000);-ms-filter:"progid:DXImageTransform.Microsoft.Gradient(GradientType=0,StartColorStr=#4a4a4a,EndColorStr=#000);"}.ui-tooltip-youtube .ui-tooltip-icon{border-color:#222}.ui-tooltip-youtube .ui-tooltip-titlebar .ui-state-hover{border-color:#303030}.ui-tooltip-jtools{background:#232323;background:rgba(0,0,0,0.7);background-image:-webkit-gradient(linear,left top,left bottom,from(#717171),to(#232323));background-image:-moz-linear-gradient(top,#717171,#232323);background-image:-webkit-linear-gradient(top,#717171,#232323);background-image:-ms-linear-gradient(top,#717171,#232323);background-image:-o-linear-gradient(top,#717171,#232323);border:2px solid #ddd;border:2px solid rgba(241,241,241,1);-moz-border-radius:2px;-webkit-border-radius:2px;border-radius:2px;-webkit-box-shadow:0 0 12px #333;-moz-box-shadow:0 0 12px #333;box-shadow:0 0 12px #333}.ui-tooltip-jtools .ui-tooltip-titlebar{background-color:transparent;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#717171,endColorstr=#4A4A4A);-ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorstr=#717171,endColorstr=#4A4A4A)"}.ui-tooltip-jtools .ui-tooltip-content{filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#4A4A4A,endColorstr=#232323);-ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorstr=#4A4A4A,endColorstr=#232323)"}.ui-tooltip-jtools .ui-tooltip-titlebar,.ui-tooltip-jtools .ui-tooltip-content{background:transparent;color:white;border:0 dashed transparent}.ui-tooltip-jtools .ui-tooltip-icon{border-color:#555}.ui-tooltip-jtools .ui-tooltip-titlebar .ui-state-hover{border-color:#333}.ui-tooltip-cluetip{-webkit-box-shadow:4px 4px 5px rgba(0,0,0,0.4);-moz-box-shadow:4px 4px 5px rgba(0,0,0,0.4);box-shadow:4px 4px 5px rgba(0,0,0,0.4);background-color:#D9D9C2;color:#111;border:0 dashed transparent}.ui-tooltip-cluetip .ui-tooltip-titlebar{background-color:#87876A;color:white;border:0 dashed transparent}.ui-tooltip-cluetip .ui-tooltip-icon{border-color:#808064}.ui-tooltip-cluetip .ui-tooltip-titlebar .ui-state-hover{border-color:#696952;color:#696952}.ui-tooltip-tipsy{background:black;background:rgba(0,0,0,.87);color:white;border:0 solid transparent;font-size:11px;font-family:'Lucida Grande',sans-serif;font-weight:bold;line-height:16px;text-shadow:0 1px black}.ui-tooltip-tipsy .ui-tooltip-titlebar{padding:6px 35px 0 10;background-color:transparent}.ui-tooltip-tipsy .ui-tooltip-content{padding:6px 10}.ui-tooltip-tipsy .ui-tooltip-icon{border-color:#222;text-shadow:none}.ui-tooltip-tipsy .ui-tooltip-titlebar .ui-state-hover{border-color:#303030}.ui-tooltip-tipped{border:3px solid #959FA9;-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;background-color:#F9F9F9;color:#454545;font-weight:normal;font-family:serif}.ui-tooltip-tipped .ui-tooltip-titlebar{border-bottom-width:0;color:white;background:#3A79B8;background-image:-webkit-gradient(linear,left top,left bottom,from(#3A79B8),to(#2E629D));background-image:-webkit-linear-gradient(top,#3A79B8,#2E629D);background-image:-moz-linear-gradient(top,#3A79B8,#2E629D);background-image:-ms-linear-gradient(top,#3A79B8,#2E629D);background-image:-o-linear-gradient(top,#3A79B8,#2E629D);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr=#3A79B8,endColorstr=#2E629D);-ms-filter:"progid:DXImageTransform.Microsoft.gradient(startColorstr=#3A79B8,endColorstr=#2E629D)"}.ui-tooltip-tipped .ui-tooltip-icon{border:2px solid #285589;background:#285589}.ui-tooltip-tipped .ui-tooltip-icon .ui-icon{background-color:#FBFBFB;color:#555}.ui-tooltip-bootstrap{font-size:13px;line-height:18px;color:#333;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,0.2);*border-right-width:2px;*border-bottom-width:2px;-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,0.2);-moz-box-shadow:0 5px 10px rgba(0,0,0,0.2);box-shadow:0 5px 10px rgba(0,0,0,0.2);-webkit-background-clip:padding-box;-moz-background-clip:padding;background-clip:padding-box}.ui-tooltip-bootstrap .ui-tooltip-titlebar{font-size:18px;line-height:22px;border-bottom:1px solid #ccc;background-color:transparent}.ui-tooltip-bootstrap .ui-tooltip-titlebar .ui-state-default{right:9px;top:49%;border-style:none}.ui-tooltip-bootstrap .ui-tooltip-icon{background:white}.ui-tooltip-bootstrap .ui-tooltip-icon .ui-icon{width:auto;height:auto;float:right;font-size:20px;font-weight:bold;line-height:18px;color:#000;text-shadow:0 1px 0 #fff;opacity:0.2;filter:alpha(opacity=20)}.ui-tooltip-bootstrap .ui-tooltip-icon .ui-icon:hover{color:#000;text-decoration:none;cursor:pointer;opacity:0.4;filter:alpha(opacity=40)}.ui-tooltip:not(.ie9haxors) div.ui-tooltip-content,.ui-tooltip:not(.ie9haxors) div.ui-tooltip-titlebar{filter:none;-ms-filter:none}.ui-tooltip .ui-tooltip-tip{margin:0 auto;overflow:hidden;z-index:10}.ui-tooltip .ui-tooltip-tip,.ui-tooltip .ui-tooltip-tip *{position:absolute;line-height:0.1px!important;font-size:0.1px!important;color:#123456;background:transparent;border:0 dashed transparent}.ui-tooltip .ui-tooltip-tip canvas{top:0;left:0}#qtip-overlay{position:fixed;left:-10000em;top:-10000em}#qtip-overlay.blurs{cursor:pointer}#qtip-overlay div{position:absolute;left:0;top:0;width:100%;height:100%;background-color:black;opacity:0.7;filter:alpha(opacity=70);-ms-filter:"progid:DXImageTransform.Microsoft.Alpha(Opacity=70)"}
\ No newline at end of file
diff --git a/dist/jquery.qtip.min.js b/dist/jquery.qtip.min.js
index e7a439b6..188219ba 100644
--- a/dist/jquery.qtip.min.js
+++ b/dist/jquery.qtip.min.js
@@ -1,15 +1,2 @@
-/*!
-* qTip2 - Pretty powerful tooltips
-* http://craigsworks.com/projects/qtip2/
-*
-* Version: 2.0.0pre
-* Copyright 2009-2010 Craig Michael Thompson - http://craigsworks.com
-*
-* Dual licensed under MIT or GPLv2 licenses
-* http://en.wikipedia.org/wiki/MIT_License
-* http://en.wikipedia.org/wiki/GNU_General_Public_License
-*
-* Date: Mon Jul 16 00:57:01 2012 +0100
-*//*jslint browser: true, onevar: true, undef: true, nomen: true, bitwise: true, regexp: true, newcap: true, immed: true, strict: true *//*global window: false, jQuery: false, console: false, define: false */// Uses AMD or browser globals to create a jQuery plugin.
-(function(a){typeof define=="function"&&define.amd?define(["jquery"],a):jQuery&&!jQuery.fn.qtip&&a(jQuery)})(function(a){"use strict";function H(){H.history=H.history||[],H.history.push(arguments);if("object"==typeof console){var a=console[console.warn?"warn":"log"],b=Array.prototype.slice.call(arguments),c;typeof arguments[0]=="string"&&(b[0]="qTip2: "+b[0]),c=a.apply?a.apply(console,b):a(b)}}function I(b){var e;if(!b||"object"!=typeof b)return c;if(b.metadata===d||"object"!=typeof b.metadata)b.metadata={type:b.metadata};if("content"in b){if(b.content===d||"object"!=typeof b.content||b.content.jquery)b.content={text:b.content};e=b.content.text||c,!a.isFunction(e)&&(!e&&!e.attr||e.length<1||"object"==typeof e&&!e.jquery)&&(b.content.text=c);if("title"in b.content){if(b.content.title===d||"object"!=typeof b.content.title)b.content.title={text:b.content.title};e=b.content.title.text||c,!a.isFunction(e)&&(!e&&!e.attr||e.length<1||"object"==typeof e&&!e.jquery)&&(b.content.title.text=c)}}return"position"in b&&(b.position===d||"object"!=typeof b.position)&&(b.position={my:b.position,at:b.position}),"show"in b&&(b.show===d||"object"!=typeof b.show)&&(b.show.jquery?b.show={target:b.show}:b.show={event:b.show}),"hide"in b&&(b.hide===d||"object"!=typeof b.hide)&&(b.hide.jquery?b.hide={target:b.hide}:b.hide={event:b.hide}),"style"in b&&(b.style===d||"object"!=typeof b.style)&&(b.style={classes:b.style}),a.each(s,function(){this.sanitize&&this.sanitize(b)}),b}function J(f,g,o,p){function O(a){var b=0,c,d=g,e=a.split(".");while(d=d[e[b++]])b",{"class":"ui-state-default ui-tooltip-close "+(g.style.widget?"":v+"-icon"),title:e,"aria-label":e}).prepend(a("",{"class":"ui-icon ui-icon-close",html:"×"})),M.button.appendTo(M.titlebar).attr("role","button").click(function(a){return K.hasClass(x)||q.hide(a),c}),q.redraw()}function S(){var c=E+"-title";M.titlebar&&Q(),M.titlebar=a("",{"class":v+"-titlebar "+(g.style.widget?"ui-widget-header":"")}).append(M.title=a("",{id:c,"class":v+"-title","aria-atomic":b})).insertBefore(M.content).delegate(".ui-tooltip-close","mousedown keydown mouseup keyup mouseout",function(b){a(this).toggleClass("ui-state-active ui-state-focus",b.type.substr(-4)==="down")}).delegate(".ui-tooltip-close","mouseover mouseout",function(b){a(this).toggleClass("ui-state-hover",b.type==="mouseover")}),g.content.title.button?R():q.rendered&&q.redraw()}function T(a){var b=M.button,d=M.title;if(!q.rendered)return c;a?(d||S(),R()):b.remove()}function U(b,d){var e=M.title;if(!q.rendered||!b)return c;a.isFunction(b)&&(b=b.call(f,N.event,q));if(b===c||!b&&b!=="")return Q(c);b.jquery&&b.length>0?e.empty().append(b.css({display:"block"})):e.html(b),q.redraw(),d!==c&&q.rendered&&K[0].offsetWidth>0&&q.reposition(N.event)}function V(b,d){function h(b){function i(e){e&&(delete h[e.src],clearTimeout(q.timers.img[e.src]),a(e).unbind(L)),a.isEmptyObject(h)&&(q.redraw(),d!==c&&q.reposition(N.event),b())}var f,h={};if((f=g.find("img[src]:not([height]):not([width])")).length===0)return i();f.each(function(b,c){if(h[c.src]!==e)return;var d=0,f=3;(function g(){if(c.height||c.width||d>f)return i(c);d+=1,q.timers.img[c.src]=setTimeout(g,700)})(),a(c).bind("error"+L+" load"+L,function(){i(this)}),h[c.src]=c})}var g=M.content;return!q.rendered||!b?c:(a.isFunction(b)&&(b=b.call(f,N.event,q)||""),b.jquery&&b.length>0?g.empty().append(b.css({display:"block"})):g.html(b),q.rendered<0?K.queue("fx",h):(J=0,h(a.noop)),q)}function W(){function j(a){if(K.hasClass(x))return c;clearTimeout(q.timers.show),clearTimeout(q.timers.hide);var d=function(){q.toggle(b,a)};g.show.delay>0?q.timers.show=setTimeout(d,g.show.delay):d()}function k(b){if(K.hasClass(x)||H||J)return c;var f=a(b.relatedTarget||b.target),h=f.closest(y)[0]===K[0],i=f[0]===e.show[0];clearTimeout(q.timers.show),clearTimeout(q.timers.hide);if(d.target==="mouse"&&h||g.hide.fixed&&/mouse(out|leave|move)/.test(b.type)&&(h||i)){try{b.preventDefault(),b.stopImmediatePropagation()}catch(j){}return}g.hide.delay>0?q.timers.hide=setTimeout(function(){q.hide(b)},g.hide.delay):q.hide(b)}function l(a){if(K.hasClass(x))return c;clearTimeout(q.timers.inactive),q.timers.inactive=setTimeout(function(){q.hide(a)},g.hide.inactive)}function m(a){q.rendered&&K[0].offsetWidth>0&&q.reposition(a)}var d=g.position,e={show:g.show.target,hide:g.hide.target,viewport:a(d.viewport),document:a(document),body:a(document.body),window:a(window)},h={show:a.trim(""+g.show.event).split(" "),hide:a.trim(""+g.hide.event).split(" ")},i=a.browser.msie&&parseInt(a.browser.version,10)===6;K.bind("mouseenter"+L+" mouseleave"+L,function(a){var b=a.type==="mouseenter";b&&q.focus(a),K.toggleClass(B,b)}),/mouse(out|leave)/i.test(g.hide.event)&&g.hide.leave==="window"&&e.window.bind("mouseleave"+L+" blur"+L,function(a){!/select|option/.test(a.target.nodeName)&&!a.relatedTarget&&q.hide(a)}),g.hide.fixed?(e.hide=e.hide.add(K),K.bind("mouseover"+L,function(){K.hasClass(x)||clearTimeout(q.timers.hide)})):/mouse(over|enter)/i.test(g.show.event)&&e.hide.bind("mouseleave"+L,function(a){clearTimeout(q.timers.show)}),(""+g.hide.event).indexOf("unfocus")>-1&&d.container.closest("html").bind("mousedown"+L,function(b){var c=a(b.target),d=q.rendered&&!K.hasClass(x)&&K[0].offsetWidth>0,e=c.parents(y).filter(K[0]).length>0;c[0]!==f[0]&&c[0]!==K[0]&&!e&&!f.has(c[0]).length&&!c.attr("disabled")&&q.hide(b)}),"number"==typeof g.hide.inactive&&(e.show.bind("qtip-"+o+"-inactive",l),a.each(r.inactiveEvents,function(a,b){e.hide.add(M.tooltip).bind(b+L+"-inactive",l)})),a.each(h.hide,function(b,c){var d=a.inArray(c,h.show),f=a(e.hide);d>-1&&f.add(e.show).length===f.length||c==="unfocus"?(e.show.bind(c+L,function(a){K[0].offsetWidth>0?k(a):j(a)}),delete h.show[d]):e.hide.bind(c+L,k)}),a.each(h.show,function(a,b){e.show.bind(b+L,j)}),"number"==typeof g.hide.distance&&e.show.add(K).bind("mousemove"+L,function(a){var b=N.origin||{},c=g.hide.distance,d=Math.abs;(d(a.pageX-b.pageX)>=c||d(a.pageY-b.pageY)>=c)&&q.hide(a)}),d.target==="mouse"&&(e.show.bind("mousemove"+L,function(a){t={pageX:a.pageX,pageY:a.pageY,type:"mousemove"}}),d.adjust.mouse&&(g.hide.event&&(K.bind("mouseleave"+L,function(a){(a.relatedTarget||a.target)!==e.show[0]&&q.hide(a)}),M.target.bind("mouseenter"+L+" mouseleave"+L,function(a){N.onTarget=a.type==="mouseenter"})),e.document.bind("mousemove"+L,function(a){q.rendered&&N.onTarget&&!K.hasClass(x)&&K[0].offsetWidth>0&&q.reposition(a||t)}))),(d.adjust.resize||e.viewport.length)&&(a.event.special.resize?e.viewport:e.window).bind("resize"+L,m),(e.viewport.length||i&&K.css("position")==="fixed")&&e.viewport.bind("scroll"+L,m)}function X(){var b=[g.show.target[0],g.hide.target[0],q.rendered&&M.tooltip[0],g.position.container[0],g.position.viewport[0],window,document];q.rendered?a([]).pushStack(a.grep(b,function(a){return typeof a=="object"})).unbind(L):g.show.target.unbind(L+"-create")}var q=this,D=document.body,E=v+"-"+o,H=0,J=0,K=a(),L=".qtip-"+o,M,N;q.id=o,q.rendered=c,q.destroyed=c,q.elements=M={target:f},q.timers={img:{}},q.options=g,q.checks={},q.plugins={},q.cache=N={event:{},target:a(),disabled:c,attr:p,onTarget:c,lastClass:""},q.checks.builtin={"^id$":function(d,e,f){var g=f===b?r.nextid:f,h=v+"-"+g;g!==c&&g.length>0&&!a("#"+h).length&&(K[0].id=h,M.content[0].id=h+"-content",M.title[0].id=h+"-title")},"^content.text$":function(a,b,c){V(c)},"^content.title.text$":function(a,b,c){if(!c)return Q();!M.title&&c&&S(),U(c)},"^content.title.button$":function(a,b,c){T(c)},"^position.(my|at)$":function(a,b,c){"string"==typeof c&&(a[b]=new s.Corner(c))},"^position.container$":function(a,b,c){q.rendered&&K.appendTo(c)},"^show.ready$":function(){q.rendered?q.toggle(b):q.render(1)},"^style.classes$":function(a,b,c){K.attr("class",v+" qtip ui-helper-reset "+c)},"^style.widget|content.title":P,"^events.(render|show|move|hide|focus|blur)$":function(b,c,d){K[(a.isFunction(d)?"":"un")+"bind"]("tooltip"+c,d)},"^(show|hide|position).(event|target|fixed|inactive|leave|distance|viewport|adjust)":function(){var a=g.position;K.attr("tracking",a.target==="mouse"&&a.adjust.mouse),X(),W()}},a.extend(q,{render:function(d){if(q.rendered)return q;var e=g.content.text,h=g.content.title.text,i=g.position,j=a.Event("tooltiprender");return a.attr(f[0],"aria-describedby",E),K=M.tooltip=a("",{id:E,"class":v+" qtip ui-helper-reset "+z+" "+g.style.classes+" "+v+"-pos-"+g.position.my.abbrev(),width:g.style.width||"",height:g.style.height||"",tracking:i.target==="mouse"&&i.adjust.mouse,role:"alert","aria-live":"polite","aria-atomic":c,"aria-describedby":E+"-content","aria-hidden":b}).toggleClass(x,N.disabled).data("qtip",q).appendTo(g.position.container).append(M.content=a("",{"class":v+"-content",id:E+"-content","aria-atomic":b})),q.rendered=-1,J=1,H=1,h&&(S(),a.isFunction(h)||U(h,c)),a.isFunction(e)||V(e,c),q.rendered=b,P(),a.each(g.events,function(b,c){a.isFunction(c)&&K.bind(b==="toggle"?"tooltipshow tooltiphide":"tooltip"+b,c)}),a.each(s,function(){this.initialize==="render"&&this(q)}),W(),K.queue("fx",function(a){j.originalEvent=N.event,K.trigger(j,[q]),J=0,H=0,q.redraw(),(g.show.ready||d)&&q.toggle(b,N.event,c),a()}),q},get:function(a){var b,c;switch(a.toLowerCase()){case"dimensions":b={height:K.outerHeight(),width:K.outerWidth()};break;case"offset":b=s.offset(K,g.position.container);break;default:c=O(a.toLowerCase()),b=c[0][c[1]],b=b.precedance?b.string():b}return b},set:function(e,f){function n(a,b){var c,d,e;for(c in l)for(d in l[c])if(e=(new RegExp(d,"i")).exec(a))b.push(e),l[c][d].apply(q,b)}var h=/^position\.(my|at|adjust|target|container)|style|content|show\.ready/i,i=/^content\.(title|attr)|style/i,j=c,k=c,l=q.checks,m;return"string"==typeof e?(m=e,e={},e[m]=f):e=a.extend(b,{},e),a.each(e,function(b,c){var d=O(b.toLowerCase()),f;f=d[0][d[1]],d[0][d[1]]="object"==typeof c&&c.nodeType?a(c):c,e[b]=[d[0],d[1],c,f],j=h.test(b)||j,k=i.test(b)||k}),I(g),H=J=1,a.each(e,n),H=J=0,q.rendered&&K[0].offsetWidth>0&&(j&&q.reposition(g.position.target==="mouse"?d:N.event),k&&q.redraw()),q},toggle:function(e,f){function u(){e?(a.browser.msie&&K[0].style.removeAttribute("filter"),K.css("overflow",""),"string"==typeof i.autofocus&&a(i.autofocus,K).focus(),i.target.trigger("qtip-"+o+"-inactive")):K.css({display:"",visibility:"",opacity:"",left:"",top:""}),s=a.Event("tooltip"+(e?"visible":"hidden")),s.originalEvent=f?N.event:d,K.trigger(s,[q])}if(!q.rendered)return e?q.render(1):q;var h=e?"show":"hide",i=g[h],j=g[e?"hide":"show"],k=g.position,l=g.content,m=K[0].offsetWidth>0,n=e||i.target.length===1,p=!f||i.target.length<2||N.target[0]===f.target,r,s;(typeof e).search("boolean|number")&&(e=!m);if(!K.is(":animated")&&m===e&&p)return q;if(f){if(/over|enter/.test(f.type)&&/out|leave/.test(N.event.type)&&g.show.target.add(f.target).length===g.show.target.length&&K.has(f.relatedTarget).length)return q;N.event=a.extend({},f)}return s=a.Event("tooltip"+h),s.originalEvent=f?N.event:d,K.trigger(s,[q,90]),s.isDefaultPrevented()?q:(a.attr(K[0],"aria-hidden",!e),e?(N.origin=a.extend({},t),q.focus(f),a.isFunction(l.text)&&V(l.text,c),a.isFunction(l.title.text)&&U(l.title.text,c),!G&&k.target==="mouse"&&k.adjust.mouse&&(a(document).bind("mousemove.qtip",function(a){t={pageX:a.pageX,pageY:a.pageY,type:"mousemove"}}),G=b),q.reposition(f,arguments[2]),(s.solo=!!i.solo)&&a(y,i.solo).not(K).qtip("hide",s)):(clearTimeout(q.timers.show),delete N.origin,G&&!a(y+'[tracking="true"]:visible',i.solo).not(K).length&&(a(document).unbind("mousemove.qtip"),G=c),q.blur(f)),i.effect===c||n===c?(K[h](),u.call(K)):a.isFunction(i.effect)?(K.stop(1,1),i.effect.call(K,q),K.queue("fx",function(a){u(),a()})):K.fadeTo(90,e?1:0,u),e&&i.target.trigger("qtip-"+o+"-inactive"),q)},show:function(a){return q.toggle(b,a)},hide:function(a){return q.toggle(c,a)},focus:function(b){if(!q.rendered)return q;var c=a(y),d=parseInt(K[0].style.zIndex,10),e=r.zindex+c.length,f=a.extend({},b),g,h;return K.hasClass(A)||(h=a.Event("tooltipfocus"),h.originalEvent=f,K.trigger(h,[q,e]),h.isDefaultPrevented()||(d!==e&&(c.each(function(){this.style.zIndex>d&&(this.style.zIndex=this.style.zIndex-1)}),c.filter("."+A).qtip("blur",f)),K.addClass(A)[0].style.zIndex=e)),q},blur:function(b){var c=a.extend({},b),d;return K.removeClass(A),d=a.Event("tooltipblur"),d.originalEvent=c,K.trigger(d,[q]),q},reposition:function(b,d){if(!q.rendered||H)return q;H=1;var e=g.position.target,f=g.position,h=f.my,i=f.at,o=f.adjust,p=o.method.split(" "),r=K.outerWidth(),u=K.outerHeight(),v=0,w=0,x=a.Event("tooltipmove"),y=K.css("position")==="fixed",z=f.viewport,A={left:0,top:0},B=f.container,C=K[0].offsetWidth>0,D,E,F;if(a.isArray(e)&&e.length===2)i={x:k,y:j},A={left:e[0],top:e[1]};else if(e==="mouse"&&(b&&b.pageX||N.event.pageX))i={x:k,y:j},b=(!b||b.type!=="resize"&&b.type!=="scroll"?b&&b.pageX&&b.type==="mousemove"?b:t&&t.pageX&&(o.mouse||!b||!b.pageX)?{pageX:t.pageX,pageY:t.pageY}:!o.mouse&&N.origin&&N.origin.pageX&&g.show.distance?N.origin:b:N.event)||b||N.event||t||{},A={top:b.pageY,left:b.pageX};else{e==="event"&&b&&b.target&&b.type!=="scroll"&&b.type!=="resize"?N.target=a(b.target):e!=="event"&&(N.target=a(e.jquery?e:M.target)),e=N.target,e=a(e).eq(0);if(e.length===0)return q;e[0]===document||e[0]===window?(v=s.iOS?window.innerWidth:e.width(),w=s.iOS?window.innerHeight:e.height(),e[0]===window&&(A={top:(z||e).scrollTop(),left:(z||e).scrollLeft()})):s.imagemap&&e.is("area")?D=s.imagemap(q,e,i,s.viewport?p:c):s.svg&&typeof e[0].xmlbase=="string"?D=s.svg(q,e,i,s.viewport?p:c):(v=e.outerWidth(),w=e.outerHeight(),A=s.offset(e,B)),D&&(v=D.width,w=D.height,E=D.offset,A=D.position);if(s.iOS>3.1&&s.iOS<4.1||s.iOS>=4.3&&s.iOS<4.33||!s.iOS&&y)F=a(window),A.left-=F.scrollLeft(),A.top-=F.scrollTop();A.left+=i.x===m?v:i.x===n?v/2:0,A.top+=i.y===l?w:i.y===n?w/2:0}return A.left+=o.x+(h.x===m?-r:h.x===n?-r/2:0),A.top+=o.y+(h.y===l?-u:h.y===n?-u/2:0),s.viewport?(A.adjusted=s.viewport(q,A,f,v,w,r,u),E&&A.adjusted.left&&(A.left+=E.left),E&&A.adjusted.top&&(A.top+=E.top)):A.adjusted={left:0,top:0},x.originalEvent=a.extend({},b),K.trigger(x,[q,A,z.elem||z]),x.isDefaultPrevented()?q:(delete A.adjusted,d===c||!C||isNaN(A.left)||isNaN(A.top)||e==="mouse"||!a.isFunction(f.effect)?K.css(A):a.isFunction(f.effect)&&(f.effect.call(K,q,a.extend({},A)),K.queue(function(b){a(this).css({opacity:"",height:""}),a.browser.msie&&this.style.removeAttribute("filter"),b()})),H=0,q)},redraw:function(){if(q.rendered<1||J)return q;var a=g.position.container,b,c,d,e;return J=1,g.style.height&&K.css(i,g.style.height),g.style.width?K.css(h,g.style.width):(K.css(h,"").addClass(C),c=K.width()+1,d=K.css("max-width")||"",e=K.css("min-width")||"",b=(d+e).indexOf("%")>-1?a.width()/100:0,d=(d.indexOf("%")>-1?b:1)*parseInt(d,10)||c,e=(e.indexOf("%")>-1?b:1)*parseInt(e,10)||0,c=d+e?Math.min(Math.max(c,e),d):c,K.css(h,Math.round(c)).removeClass(C)),J=0,q},disable:function(b){return"boolean"!=typeof b&&(b=!K.hasClass(x)&&!N.disabled),q.rendered?(K.toggleClass(x,b),a.attr(K[0],"aria-disabled",b)):N.disabled=!!b,q},enable:function(){return q.disable(c)},destroy:function(){var c=f[0],d=a.attr(c,F),e=f.data("qtip");q.destroyed=b,q.rendered&&(K.stop(1,0).remove(),a.each(q.plugins,function(){this.destroy&&this.destroy()})),clearTimeout(q.timers.show),clearTimeout(q.timers.hide),X();if(!e||q===e)a.removeData(c,"qtip"),g.suppress&&d&&(a.attr(c,"title",d),f.removeAttr(F)),f.removeAttr("aria-describedby");return f.unbind(".qtip-"+o),delete u[q.id],f}})}function K(e,f){var g,h,i,j,k,l=a(this),m=a(document.body),n=this===document?m:l,o=l.metadata?l.metadata(f.metadata):d,p=f.metadata.type==="html5"&&o?o[f.metadata.name]:d,q=l.data(f.metadata.name||"qtipopts");try{q=typeof q=="string"?(new Function("return "+q))():q}catch(t){H("Unable to parse HTML5 attribute data: "+q)}j=a.extend(b,{},r.defaults,f,typeof q=="object"?I(q):d,I(p||o)),h=j.position,j.id=e;if("boolean"==typeof j.content.text){i=l.attr(j.content.attr);if(j.content.attr===c||!i)return H("Unable to locate content for tooltip! Aborting render of tooltip on element: ",l),c;j.content.text=i}h.container.length||(h.container=m),h.target===c&&(h.target=n),j.show.target===c&&(j.show.target=n),j.show.solo===b&&(j.show.solo=h.container.closest("body")),j.hide.target===c&&(j.hide.target=n),j.position.viewport===b&&(j.position.viewport=h.container),h.container=h.container.eq(0),h.at=new s.Corner(h.at),h.my=new s.Corner(h.my);if(a.data(this,"qtip"))if(j.overwrite)l.qtip("destroy");else if(j.overwrite===c)return c;return j.suppress&&(k=a.attr(this,"title"))&&a(this).removeAttr("title").attr(F,k).attr("title",""),g=new J(l,j,e,!!i),a.data(this,"qtip",g),l.bind("remove.qtip-"+e+" removeqtip.qtip-"+e,function(){g.destroy()}),g}function L(d){var e=this,f=d.elements.tooltip,g=d.options.content.ajax,h=r.defaults.content.ajax,i=".qtip-ajax",j=/');
+}());
diff --git a/libs/jquery/jquery.js b/libs/jquery/jquery.js
new file mode 100644
index 00000000..3774ff98
--- /dev/null
+++ b/libs/jquery/jquery.js
@@ -0,0 +1,9404 @@
+/*!
+ * jQuery JavaScript Library v1.7.2
+ * http://jquery.com/
+ *
+ * Copyright 2011, John Resig
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * Includes Sizzle.js
+ * http://sizzlejs.com/
+ * Copyright 2011, The Dojo Foundation
+ * Released under the MIT, BSD, and GPL Licenses.
+ *
+ * Date: Wed Mar 21 12:46:34 2012 -0700
+ */
+(function( window, undefined ) {
+
+// Use the correct document accordingly with window argument (sandbox)
+var document = window.document,
+ navigator = window.navigator,
+ location = window.location;
+var jQuery = (function() {
+
+// Define a local copy of jQuery
+var jQuery = function( selector, context ) {
+ // The jQuery object is actually just the init constructor 'enhanced'
+ return new jQuery.fn.init( selector, context, rootjQuery );
+ },
+
+ // Map over jQuery in case of overwrite
+ _jQuery = window.jQuery,
+
+ // Map over the $ in case of overwrite
+ _$ = window.$,
+
+ // A central reference to the root jQuery(document)
+ rootjQuery,
+
+ // A simple way to check for HTML strings or ID strings
+ // Prioritize #id over to avoid XSS via location.hash (#9521)
+ quickExpr = /^(?:[^#<]*(<[\w\W]+>)[^>]*$|#([\w\-]*)$)/,
+
+ // Check if a string has a non-whitespace character in it
+ rnotwhite = /\S/,
+
+ // Used for trimming whitespace
+ trimLeft = /^\s+/,
+ trimRight = /\s+$/,
+
+ // Match a standalone tag
+ rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/,
+
+ // JSON RegExp
+ rvalidchars = /^[\],:{}\s]*$/,
+ rvalidescape = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,
+ rvalidtokens = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,
+ rvalidbraces = /(?:^|:|,)(?:\s*\[)+/g,
+
+ // Useragent RegExp
+ rwebkit = /(webkit)[ \/]([\w.]+)/,
+ ropera = /(opera)(?:.*version)?[ \/]([\w.]+)/,
+ rmsie = /(msie) ([\w.]+)/,
+ rmozilla = /(mozilla)(?:.*? rv:([\w.]+))?/,
+
+ // Matches dashed string for camelizing
+ rdashAlpha = /-([a-z]|[0-9])/ig,
+ rmsPrefix = /^-ms-/,
+
+ // Used by jQuery.camelCase as callback to replace()
+ fcamelCase = function( all, letter ) {
+ return ( letter + "" ).toUpperCase();
+ },
+
+ // Keep a UserAgent string for use with jQuery.browser
+ userAgent = navigator.userAgent,
+
+ // For matching the engine and version of the browser
+ browserMatch,
+
+ // The deferred used on DOM ready
+ readyList,
+
+ // The ready event handler
+ DOMContentLoaded,
+
+ // Save a reference to some core methods
+ toString = Object.prototype.toString,
+ hasOwn = Object.prototype.hasOwnProperty,
+ push = Array.prototype.push,
+ slice = Array.prototype.slice,
+ trim = String.prototype.trim,
+ indexOf = Array.prototype.indexOf,
+
+ // [[Class]] -> type pairs
+ class2type = {};
+
+jQuery.fn = jQuery.prototype = {
+ constructor: jQuery,
+ init: function( selector, context, rootjQuery ) {
+ var match, elem, ret, doc;
+
+ // Handle $(""), $(null), or $(undefined)
+ if ( !selector ) {
+ return this;
+ }
+
+ // Handle $(DOMElement)
+ if ( selector.nodeType ) {
+ this.context = this[0] = selector;
+ this.length = 1;
+ return this;
+ }
+
+ // The body element only exists once, optimize finding it
+ if ( selector === "body" && !context && document.body ) {
+ this.context = document;
+ this[0] = document.body;
+ this.selector = selector;
+ this.length = 1;
+ return this;
+ }
+
+ // Handle HTML strings
+ if ( typeof selector === "string" ) {
+ // Are we dealing with HTML string or an ID?
+ if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) {
+ // Assume that strings that start and end with <> are HTML and skip the regex check
+ match = [ null, selector, null ];
+
+ } else {
+ match = quickExpr.exec( selector );
+ }
+
+ // Verify a match, and that no context was specified for #id
+ if ( match && (match[1] || !context) ) {
+
+ // HANDLE: $(html) -> $(array)
+ if ( match[1] ) {
+ context = context instanceof jQuery ? context[0] : context;
+ doc = ( context ? context.ownerDocument || context : document );
+
+ // If a single string is passed in and it's a single tag
+ // just do a createElement and skip the rest
+ ret = rsingleTag.exec( selector );
+
+ if ( ret ) {
+ if ( jQuery.isPlainObject( context ) ) {
+ selector = [ document.createElement( ret[1] ) ];
+ jQuery.fn.attr.call( selector, context, true );
+
+ } else {
+ selector = [ doc.createElement( ret[1] ) ];
+ }
+
+ } else {
+ ret = jQuery.buildFragment( [ match[1] ], [ doc ] );
+ selector = ( ret.cacheable ? jQuery.clone(ret.fragment) : ret.fragment ).childNodes;
+ }
+
+ return jQuery.merge( this, selector );
+
+ // HANDLE: $("#id")
+ } else {
+ elem = document.getElementById( match[2] );
+
+ // Check parentNode to catch when Blackberry 4.6 returns
+ // nodes that are no longer in the document #6963
+ if ( elem && elem.parentNode ) {
+ // Handle the case where IE and Opera return items
+ // by name instead of ID
+ if ( elem.id !== match[2] ) {
+ return rootjQuery.find( selector );
+ }
+
+ // Otherwise, we inject the element directly into the jQuery object
+ this.length = 1;
+ this[0] = elem;
+ }
+
+ this.context = document;
+ this.selector = selector;
+ return this;
+ }
+
+ // HANDLE: $(expr, $(...))
+ } else if ( !context || context.jquery ) {
+ return ( context || rootjQuery ).find( selector );
+
+ // HANDLE: $(expr, context)
+ // (which is just equivalent to: $(context).find(expr)
+ } else {
+ return this.constructor( context ).find( selector );
+ }
+
+ // HANDLE: $(function)
+ // Shortcut for document ready
+ } else if ( jQuery.isFunction( selector ) ) {
+ return rootjQuery.ready( selector );
+ }
+
+ if ( selector.selector !== undefined ) {
+ this.selector = selector.selector;
+ this.context = selector.context;
+ }
+
+ return jQuery.makeArray( selector, this );
+ },
+
+ // Start with an empty selector
+ selector: "",
+
+ // The current version of jQuery being used
+ jquery: "1.7.2",
+
+ // The default length of a jQuery object is 0
+ length: 0,
+
+ // The number of elements contained in the matched element set
+ size: function() {
+ return this.length;
+ },
+
+ toArray: function() {
+ return slice.call( this, 0 );
+ },
+
+ // Get the Nth element in the matched element set OR
+ // Get the whole matched element set as a clean array
+ get: function( num ) {
+ return num == null ?
+
+ // Return a 'clean' array
+ this.toArray() :
+
+ // Return just the object
+ ( num < 0 ? this[ this.length + num ] : this[ num ] );
+ },
+
+ // Take an array of elements and push it onto the stack
+ // (returning the new matched element set)
+ pushStack: function( elems, name, selector ) {
+ // Build a new jQuery matched element set
+ var ret = this.constructor();
+
+ if ( jQuery.isArray( elems ) ) {
+ push.apply( ret, elems );
+
+ } else {
+ jQuery.merge( ret, elems );
+ }
+
+ // Add the old object onto the stack (as a reference)
+ ret.prevObject = this;
+
+ ret.context = this.context;
+
+ if ( name === "find" ) {
+ ret.selector = this.selector + ( this.selector ? " " : "" ) + selector;
+ } else if ( name ) {
+ ret.selector = this.selector + "." + name + "(" + selector + ")";
+ }
+
+ // Return the newly-formed element set
+ return ret;
+ },
+
+ // Execute a callback for every element in the matched set.
+ // (You can seed the arguments with an array of args, but this is
+ // only used internally.)
+ each: function( callback, args ) {
+ return jQuery.each( this, callback, args );
+ },
+
+ ready: function( fn ) {
+ // Attach the listeners
+ jQuery.bindReady();
+
+ // Add the callback
+ readyList.add( fn );
+
+ return this;
+ },
+
+ eq: function( i ) {
+ i = +i;
+ return i === -1 ?
+ this.slice( i ) :
+ this.slice( i, i + 1 );
+ },
+
+ first: function() {
+ return this.eq( 0 );
+ },
+
+ last: function() {
+ return this.eq( -1 );
+ },
+
+ slice: function() {
+ return this.pushStack( slice.apply( this, arguments ),
+ "slice", slice.call(arguments).join(",") );
+ },
+
+ map: function( callback ) {
+ return this.pushStack( jQuery.map(this, function( elem, i ) {
+ return callback.call( elem, i, elem );
+ }));
+ },
+
+ end: function() {
+ return this.prevObject || this.constructor(null);
+ },
+
+ // For internal use only.
+ // Behaves like an Array's method, not like a jQuery method.
+ push: push,
+ sort: [].sort,
+ splice: [].splice
+};
+
+// Give the init function the jQuery prototype for later instantiation
+jQuery.fn.init.prototype = jQuery.fn;
+
+jQuery.extend = jQuery.fn.extend = function() {
+ var options, name, src, copy, copyIsArray, clone,
+ target = arguments[0] || {},
+ i = 1,
+ length = arguments.length,
+ deep = false;
+
+ // Handle a deep copy situation
+ if ( typeof target === "boolean" ) {
+ deep = target;
+ target = arguments[1] || {};
+ // skip the boolean and the target
+ i = 2;
+ }
+
+ // Handle case when target is a string or something (possible in deep copy)
+ if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
+ target = {};
+ }
+
+ // extend jQuery itself if only one argument is passed
+ if ( length === i ) {
+ target = this;
+ --i;
+ }
+
+ for ( ; i < length; i++ ) {
+ // Only deal with non-null/undefined values
+ if ( (options = arguments[ i ]) != null ) {
+ // Extend the base object
+ for ( name in options ) {
+ src = target[ name ];
+ copy = options[ name ];
+
+ // Prevent never-ending loop
+ if ( target === copy ) {
+ continue;
+ }
+
+ // Recurse if we're merging plain objects or arrays
+ if ( deep && copy && ( jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)) ) ) {
+ if ( copyIsArray ) {
+ copyIsArray = false;
+ clone = src && jQuery.isArray(src) ? src : [];
+
+ } else {
+ clone = src && jQuery.isPlainObject(src) ? src : {};
+ }
+
+ // Never move original objects, clone them
+ target[ name ] = jQuery.extend( deep, clone, copy );
+
+ // Don't bring in undefined values
+ } else if ( copy !== undefined ) {
+ target[ name ] = copy;
+ }
+ }
+ }
+ }
+
+ // Return the modified object
+ return target;
+};
+
+jQuery.extend({
+ noConflict: function( deep ) {
+ if ( window.$ === jQuery ) {
+ window.$ = _$;
+ }
+
+ if ( deep && window.jQuery === jQuery ) {
+ window.jQuery = _jQuery;
+ }
+
+ return jQuery;
+ },
+
+ // Is the DOM ready to be used? Set to true once it occurs.
+ isReady: false,
+
+ // A counter to track how many items to wait for before
+ // the ready event fires. See #6781
+ readyWait: 1,
+
+ // Hold (or release) the ready event
+ holdReady: function( hold ) {
+ if ( hold ) {
+ jQuery.readyWait++;
+ } else {
+ jQuery.ready( true );
+ }
+ },
+
+ // Handle when the DOM is ready
+ ready: function( wait ) {
+ // Either a released hold or an DOMready/load event and not yet ready
+ if ( (wait === true && !--jQuery.readyWait) || (wait !== true && !jQuery.isReady) ) {
+ // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
+ if ( !document.body ) {
+ return setTimeout( jQuery.ready, 1 );
+ }
+
+ // Remember that the DOM is ready
+ jQuery.isReady = true;
+
+ // If a normal DOM Ready event fired, decrement, and wait if need be
+ if ( wait !== true && --jQuery.readyWait > 0 ) {
+ return;
+ }
+
+ // If there are functions bound, to execute
+ readyList.fireWith( document, [ jQuery ] );
+
+ // Trigger any bound ready events
+ if ( jQuery.fn.trigger ) {
+ jQuery( document ).trigger( "ready" ).off( "ready" );
+ }
+ }
+ },
+
+ bindReady: function() {
+ if ( readyList ) {
+ return;
+ }
+
+ readyList = jQuery.Callbacks( "once memory" );
+
+ // Catch cases where $(document).ready() is called after the
+ // browser event has already occurred.
+ if ( document.readyState === "complete" ) {
+ // Handle it asynchronously to allow scripts the opportunity to delay ready
+ return setTimeout( jQuery.ready, 1 );
+ }
+
+ // Mozilla, Opera and webkit nightlies currently support this event
+ if ( document.addEventListener ) {
+ // Use the handy event callback
+ document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
+
+ // A fallback to window.onload, that will always work
+ window.addEventListener( "load", jQuery.ready, false );
+
+ // If IE event model is used
+ } else if ( document.attachEvent ) {
+ // ensure firing before onload,
+ // maybe late but safe also for iframes
+ document.attachEvent( "onreadystatechange", DOMContentLoaded );
+
+ // A fallback to window.onload, that will always work
+ window.attachEvent( "onload", jQuery.ready );
+
+ // If IE and not a frame
+ // continually check to see if the document is ready
+ var toplevel = false;
+
+ try {
+ toplevel = window.frameElement == null;
+ } catch(e) {}
+
+ if ( document.documentElement.doScroll && toplevel ) {
+ doScrollCheck();
+ }
+ }
+ },
+
+ // See test/unit/core.js for details concerning isFunction.
+ // Since version 1.3, DOM methods and functions like alert
+ // aren't supported. They return false on IE (#2968).
+ isFunction: function( obj ) {
+ return jQuery.type(obj) === "function";
+ },
+
+ isArray: Array.isArray || function( obj ) {
+ return jQuery.type(obj) === "array";
+ },
+
+ isWindow: function( obj ) {
+ return obj != null && obj == obj.window;
+ },
+
+ isNumeric: function( obj ) {
+ return !isNaN( parseFloat(obj) ) && isFinite( obj );
+ },
+
+ type: function( obj ) {
+ return obj == null ?
+ String( obj ) :
+ class2type[ toString.call(obj) ] || "object";
+ },
+
+ isPlainObject: function( obj ) {
+ // Must be an Object.
+ // Because of IE, we also have to check the presence of the constructor property.
+ // Make sure that DOM nodes and window objects don't pass through, as well
+ if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
+ return false;
+ }
+
+ try {
+ // Not own constructor property must be Object
+ if ( obj.constructor &&
+ !hasOwn.call(obj, "constructor") &&
+ !hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) {
+ return false;
+ }
+ } catch ( e ) {
+ // IE8,9 Will throw exceptions on certain host objects #9897
+ return false;
+ }
+
+ // Own properties are enumerated firstly, so to speed up,
+ // if last one is own, then all properties are own.
+
+ var key;
+ for ( key in obj ) {}
+
+ return key === undefined || hasOwn.call( obj, key );
+ },
+
+ isEmptyObject: function( obj ) {
+ for ( var name in obj ) {
+ return false;
+ }
+ return true;
+ },
+
+ error: function( msg ) {
+ throw new Error( msg );
+ },
+
+ parseJSON: function( data ) {
+ if ( typeof data !== "string" || !data ) {
+ return null;
+ }
+
+ // Make sure leading/trailing whitespace is removed (IE can't handle it)
+ data = jQuery.trim( data );
+
+ // Attempt to parse using the native JSON parser first
+ if ( window.JSON && window.JSON.parse ) {
+ return window.JSON.parse( data );
+ }
+
+ // Make sure the incoming data is actual JSON
+ // Logic borrowed from http://json.org/json2.js
+ if ( rvalidchars.test( data.replace( rvalidescape, "@" )
+ .replace( rvalidtokens, "]" )
+ .replace( rvalidbraces, "")) ) {
+
+ return ( new Function( "return " + data ) )();
+
+ }
+ jQuery.error( "Invalid JSON: " + data );
+ },
+
+ // Cross-browser xml parsing
+ parseXML: function( data ) {
+ if ( typeof data !== "string" || !data ) {
+ return null;
+ }
+ var xml, tmp;
+ try {
+ if ( window.DOMParser ) { // Standard
+ tmp = new DOMParser();
+ xml = tmp.parseFromString( data , "text/xml" );
+ } else { // IE
+ xml = new ActiveXObject( "Microsoft.XMLDOM" );
+ xml.async = "false";
+ xml.loadXML( data );
+ }
+ } catch( e ) {
+ xml = undefined;
+ }
+ if ( !xml || !xml.documentElement || xml.getElementsByTagName( "parsererror" ).length ) {
+ jQuery.error( "Invalid XML: " + data );
+ }
+ return xml;
+ },
+
+ noop: function() {},
+
+ // Evaluates a script in a global context
+ // Workarounds based on findings by Jim Driscoll
+ // http://weblogs.java.net/blog/driscoll/archive/2009/09/08/eval-javascript-global-context
+ globalEval: function( data ) {
+ if ( data && rnotwhite.test( data ) ) {
+ // We use execScript on Internet Explorer
+ // We use an anonymous function so that context is window
+ // rather than jQuery in Firefox
+ ( window.execScript || function( data ) {
+ window[ "eval" ].call( window, data );
+ } )( data );
+ }
+ },
+
+ // Convert dashed to camelCase; used by the css and data modules
+ // Microsoft forgot to hump their vendor prefix (#9572)
+ camelCase: function( string ) {
+ return string.replace( rmsPrefix, "ms-" ).replace( rdashAlpha, fcamelCase );
+ },
+
+ nodeName: function( elem, name ) {
+ return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase();
+ },
+
+ // args is for internal usage only
+ each: function( object, callback, args ) {
+ var name, i = 0,
+ length = object.length,
+ isObj = length === undefined || jQuery.isFunction( object );
+
+ if ( args ) {
+ if ( isObj ) {
+ for ( name in object ) {
+ if ( callback.apply( object[ name ], args ) === false ) {
+ break;
+ }
+ }
+ } else {
+ for ( ; i < length; ) {
+ if ( callback.apply( object[ i++ ], args ) === false ) {
+ break;
+ }
+ }
+ }
+
+ // A special, fast, case for the most common use of each
+ } else {
+ if ( isObj ) {
+ for ( name in object ) {
+ if ( callback.call( object[ name ], name, object[ name ] ) === false ) {
+ break;
+ }
+ }
+ } else {
+ for ( ; i < length; ) {
+ if ( callback.call( object[ i ], i, object[ i++ ] ) === false ) {
+ break;
+ }
+ }
+ }
+ }
+
+ return object;
+ },
+
+ // Use native String.trim function wherever possible
+ trim: trim ?
+ function( text ) {
+ return text == null ?
+ "" :
+ trim.call( text );
+ } :
+
+ // Otherwise use our own trimming functionality
+ function( text ) {
+ return text == null ?
+ "" :
+ text.toString().replace( trimLeft, "" ).replace( trimRight, "" );
+ },
+
+ // results is for internal usage only
+ makeArray: function( array, results ) {
+ var ret = results || [];
+
+ if ( array != null ) {
+ // The window, strings (and functions) also have 'length'
+ // Tweaked logic slightly to handle Blackberry 4.7 RegExp issues #6930
+ var type = jQuery.type( array );
+
+ if ( array.length == null || type === "string" || type === "function" || type === "regexp" || jQuery.isWindow( array ) ) {
+ push.call( ret, array );
+ } else {
+ jQuery.merge( ret, array );
+ }
+ }
+
+ return ret;
+ },
+
+ inArray: function( elem, array, i ) {
+ var len;
+
+ if ( array ) {
+ if ( indexOf ) {
+ return indexOf.call( array, elem, i );
+ }
+
+ len = array.length;
+ i = i ? i < 0 ? Math.max( 0, len + i ) : i : 0;
+
+ for ( ; i < len; i++ ) {
+ // Skip accessing in sparse arrays
+ if ( i in array && array[ i ] === elem ) {
+ return i;
+ }
+ }
+ }
+
+ return -1;
+ },
+
+ merge: function( first, second ) {
+ var i = first.length,
+ j = 0;
+
+ if ( typeof second.length === "number" ) {
+ for ( var l = second.length; j < l; j++ ) {
+ first[ i++ ] = second[ j ];
+ }
+
+ } else {
+ while ( second[j] !== undefined ) {
+ first[ i++ ] = second[ j++ ];
+ }
+ }
+
+ first.length = i;
+
+ return first;
+ },
+
+ grep: function( elems, callback, inv ) {
+ var ret = [], retVal;
+ inv = !!inv;
+
+ // Go through the array, only saving the items
+ // that pass the validator function
+ for ( var i = 0, length = elems.length; i < length; i++ ) {
+ retVal = !!callback( elems[ i ], i );
+ if ( inv !== retVal ) {
+ ret.push( elems[ i ] );
+ }
+ }
+
+ return ret;
+ },
+
+ // arg is for internal usage only
+ map: function( elems, callback, arg ) {
+ var value, key, ret = [],
+ i = 0,
+ length = elems.length,
+ // jquery objects are treated as arrays
+ isArray = elems instanceof jQuery || length !== undefined && typeof length === "number" && ( ( length > 0 && elems[ 0 ] && elems[ length -1 ] ) || length === 0 || jQuery.isArray( elems ) ) ;
+
+ // Go through the array, translating each of the items to their
+ if ( isArray ) {
+ for ( ; i < length; i++ ) {
+ value = callback( elems[ i ], i, arg );
+
+ if ( value != null ) {
+ ret[ ret.length ] = value;
+ }
+ }
+
+ // Go through every key on the object,
+ } else {
+ for ( key in elems ) {
+ value = callback( elems[ key ], key, arg );
+
+ if ( value != null ) {
+ ret[ ret.length ] = value;
+ }
+ }
+ }
+
+ // Flatten any nested arrays
+ return ret.concat.apply( [], ret );
+ },
+
+ // A global GUID counter for objects
+ guid: 1,
+
+ // Bind a function to a context, optionally partially applying any
+ // arguments.
+ proxy: function( fn, context ) {
+ if ( typeof context === "string" ) {
+ var tmp = fn[ context ];
+ context = fn;
+ fn = tmp;
+ }
+
+ // Quick check to determine if target is callable, in the spec
+ // this throws a TypeError, but we will just return undefined.
+ if ( !jQuery.isFunction( fn ) ) {
+ return undefined;
+ }
+
+ // Simulated bind
+ var args = slice.call( arguments, 2 ),
+ proxy = function() {
+ return fn.apply( context, args.concat( slice.call( arguments ) ) );
+ };
+
+ // Set the guid of unique handler to the same of original handler, so it can be removed
+ proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++;
+
+ return proxy;
+ },
+
+ // Mutifunctional method to get and set values to a collection
+ // The value/s can optionally be executed if it's a function
+ access: function( elems, fn, key, value, chainable, emptyGet, pass ) {
+ var exec,
+ bulk = key == null,
+ i = 0,
+ length = elems.length;
+
+ // Sets many values
+ if ( key && typeof key === "object" ) {
+ for ( i in key ) {
+ jQuery.access( elems, fn, i, key[i], 1, emptyGet, value );
+ }
+ chainable = 1;
+
+ // Sets one value
+ } else if ( value !== undefined ) {
+ // Optionally, function values get executed if exec is true
+ exec = pass === undefined && jQuery.isFunction( value );
+
+ if ( bulk ) {
+ // Bulk operations only iterate when executing function values
+ if ( exec ) {
+ exec = fn;
+ fn = function( elem, key, value ) {
+ return exec.call( jQuery( elem ), value );
+ };
+
+ // Otherwise they run against the entire set
+ } else {
+ fn.call( elems, value );
+ fn = null;
+ }
+ }
+
+ if ( fn ) {
+ for (; i < length; i++ ) {
+ fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass );
+ }
+ }
+
+ chainable = 1;
+ }
+
+ return chainable ?
+ elems :
+
+ // Gets
+ bulk ?
+ fn.call( elems ) :
+ length ? fn( elems[0], key ) : emptyGet;
+ },
+
+ now: function() {
+ return ( new Date() ).getTime();
+ },
+
+ // Use of jQuery.browser is frowned upon.
+ // More details: http://docs.jquery.com/Utilities/jQuery.browser
+ uaMatch: function( ua ) {
+ ua = ua.toLowerCase();
+
+ var match = rwebkit.exec( ua ) ||
+ ropera.exec( ua ) ||
+ rmsie.exec( ua ) ||
+ ua.indexOf("compatible") < 0 && rmozilla.exec( ua ) ||
+ [];
+
+ return { browser: match[1] || "", version: match[2] || "0" };
+ },
+
+ sub: function() {
+ function jQuerySub( selector, context ) {
+ return new jQuerySub.fn.init( selector, context );
+ }
+ jQuery.extend( true, jQuerySub, this );
+ jQuerySub.superclass = this;
+ jQuerySub.fn = jQuerySub.prototype = this();
+ jQuerySub.fn.constructor = jQuerySub;
+ jQuerySub.sub = this.sub;
+ jQuerySub.fn.init = function init( selector, context ) {
+ if ( context && context instanceof jQuery && !(context instanceof jQuerySub) ) {
+ context = jQuerySub( context );
+ }
+
+ return jQuery.fn.init.call( this, selector, context, rootjQuerySub );
+ };
+ jQuerySub.fn.init.prototype = jQuerySub.fn;
+ var rootjQuerySub = jQuerySub(document);
+ return jQuerySub;
+ },
+
+ browser: {}
+});
+
+// Populate the class2type map
+jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) {
+ class2type[ "[object " + name + "]" ] = name.toLowerCase();
+});
+
+browserMatch = jQuery.uaMatch( userAgent );
+if ( browserMatch.browser ) {
+ jQuery.browser[ browserMatch.browser ] = true;
+ jQuery.browser.version = browserMatch.version;
+}
+
+// Deprecated, use jQuery.browser.webkit instead
+if ( jQuery.browser.webkit ) {
+ jQuery.browser.safari = true;
+}
+
+// IE doesn't match non-breaking spaces with \s
+if ( rnotwhite.test( "\xA0" ) ) {
+ trimLeft = /^[\s\xA0]+/;
+ trimRight = /[\s\xA0]+$/;
+}
+
+// All jQuery objects should point back to these
+rootjQuery = jQuery(document);
+
+// Cleanup functions for the document ready method
+if ( document.addEventListener ) {
+ DOMContentLoaded = function() {
+ document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false );
+ jQuery.ready();
+ };
+
+} else if ( document.attachEvent ) {
+ DOMContentLoaded = function() {
+ // Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
+ if ( document.readyState === "complete" ) {
+ document.detachEvent( "onreadystatechange", DOMContentLoaded );
+ jQuery.ready();
+ }
+ };
+}
+
+// The DOM ready check for Internet Explorer
+function doScrollCheck() {
+ if ( jQuery.isReady ) {
+ return;
+ }
+
+ try {
+ // If IE is used, use the trick by Diego Perini
+ // http://javascript.nwbox.com/IEContentLoaded/
+ document.documentElement.doScroll("left");
+ } catch(e) {
+ setTimeout( doScrollCheck, 1 );
+ return;
+ }
+
+ // and execute any waiting functions
+ jQuery.ready();
+}
+
+return jQuery;
+
+})();
+
+
+// String to Object flags format cache
+var flagsCache = {};
+
+// Convert String-formatted flags into Object-formatted ones and store in cache
+function createFlags( flags ) {
+ var object = flagsCache[ flags ] = {},
+ i, length;
+ flags = flags.split( /\s+/ );
+ for ( i = 0, length = flags.length; i < length; i++ ) {
+ object[ flags[i] ] = true;
+ }
+ return object;
+}
+
+/*
+ * Create a callback list using the following parameters:
+ *
+ * flags: an optional list of space-separated flags that will change how
+ * the callback list behaves
+ *
+ * By default a callback list will act like an event callback list and can be
+ * "fired" multiple times.
+ *
+ * Possible flags:
+ *
+ * once: will ensure the callback list can only be fired once (like a Deferred)
+ *
+ * memory: will keep track of previous values and will call any callback added
+ * after the list has been fired right away with the latest "memorized"
+ * values (like a Deferred)
+ *
+ * unique: will ensure a callback can only be added once (no duplicate in the list)
+ *
+ * stopOnFalse: interrupt callings when a callback returns false
+ *
+ */
+jQuery.Callbacks = function( flags ) {
+
+ // Convert flags from String-formatted to Object-formatted
+ // (we check in cache first)
+ flags = flags ? ( flagsCache[ flags ] || createFlags( flags ) ) : {};
+
+ var // Actual callback list
+ list = [],
+ // Stack of fire calls for repeatable lists
+ stack = [],
+ // Last fire value (for non-forgettable lists)
+ memory,
+ // Flag to know if list was already fired
+ fired,
+ // Flag to know if list is currently firing
+ firing,
+ // First callback to fire (used internally by add and fireWith)
+ firingStart,
+ // End of the loop when firing
+ firingLength,
+ // Index of currently firing callback (modified by remove if needed)
+ firingIndex,
+ // Add one or several callbacks to the list
+ add = function( args ) {
+ var i,
+ length,
+ elem,
+ type,
+ actual;
+ for ( i = 0, length = args.length; i < length; i++ ) {
+ elem = args[ i ];
+ type = jQuery.type( elem );
+ if ( type === "array" ) {
+ // Inspect recursively
+ add( elem );
+ } else if ( type === "function" ) {
+ // Add if not in unique mode and callback is not in
+ if ( !flags.unique || !self.has( elem ) ) {
+ list.push( elem );
+ }
+ }
+ }
+ },
+ // Fire callbacks
+ fire = function( context, args ) {
+ args = args || [];
+ memory = !flags.memory || [ context, args ];
+ fired = true;
+ firing = true;
+ firingIndex = firingStart || 0;
+ firingStart = 0;
+ firingLength = list.length;
+ for ( ; list && firingIndex < firingLength; firingIndex++ ) {
+ if ( list[ firingIndex ].apply( context, args ) === false && flags.stopOnFalse ) {
+ memory = true; // Mark as halted
+ break;
+ }
+ }
+ firing = false;
+ if ( list ) {
+ if ( !flags.once ) {
+ if ( stack && stack.length ) {
+ memory = stack.shift();
+ self.fireWith( memory[ 0 ], memory[ 1 ] );
+ }
+ } else if ( memory === true ) {
+ self.disable();
+ } else {
+ list = [];
+ }
+ }
+ },
+ // Actual Callbacks object
+ self = {
+ // Add a callback or a collection of callbacks to the list
+ add: function() {
+ if ( list ) {
+ var length = list.length;
+ add( arguments );
+ // Do we need to add the callbacks to the
+ // current firing batch?
+ if ( firing ) {
+ firingLength = list.length;
+ // With memory, if we're not firing then
+ // we should call right away, unless previous
+ // firing was halted (stopOnFalse)
+ } else if ( memory && memory !== true ) {
+ firingStart = length;
+ fire( memory[ 0 ], memory[ 1 ] );
+ }
+ }
+ return this;
+ },
+ // Remove a callback from the list
+ remove: function() {
+ if ( list ) {
+ var args = arguments,
+ argIndex = 0,
+ argLength = args.length;
+ for ( ; argIndex < argLength ; argIndex++ ) {
+ for ( var i = 0; i < list.length; i++ ) {
+ if ( args[ argIndex ] === list[ i ] ) {
+ // Handle firingIndex and firingLength
+ if ( firing ) {
+ if ( i <= firingLength ) {
+ firingLength--;
+ if ( i <= firingIndex ) {
+ firingIndex--;
+ }
+ }
+ }
+ // Remove the element
+ list.splice( i--, 1 );
+ // If we have some unicity property then
+ // we only need to do this once
+ if ( flags.unique ) {
+ break;
+ }
+ }
+ }
+ }
+ }
+ return this;
+ },
+ // Control if a given callback is in the list
+ has: function( fn ) {
+ if ( list ) {
+ var i = 0,
+ length = list.length;
+ for ( ; i < length; i++ ) {
+ if ( fn === list[ i ] ) {
+ return true;
+ }
+ }
+ }
+ return false;
+ },
+ // Remove all callbacks from the list
+ empty: function() {
+ list = [];
+ return this;
+ },
+ // Have the list do nothing anymore
+ disable: function() {
+ list = stack = memory = undefined;
+ return this;
+ },
+ // Is it disabled?
+ disabled: function() {
+ return !list;
+ },
+ // Lock the list in its current state
+ lock: function() {
+ stack = undefined;
+ if ( !memory || memory === true ) {
+ self.disable();
+ }
+ return this;
+ },
+ // Is it locked?
+ locked: function() {
+ return !stack;
+ },
+ // Call all callbacks with the given context and arguments
+ fireWith: function( context, args ) {
+ if ( stack ) {
+ if ( firing ) {
+ if ( !flags.once ) {
+ stack.push( [ context, args ] );
+ }
+ } else if ( !( flags.once && memory ) ) {
+ fire( context, args );
+ }
+ }
+ return this;
+ },
+ // Call all the callbacks with the given arguments
+ fire: function() {
+ self.fireWith( this, arguments );
+ return this;
+ },
+ // To know if the callbacks have already been called at least once
+ fired: function() {
+ return !!fired;
+ }
+ };
+
+ return self;
+};
+
+
+
+
+var // Static reference to slice
+ sliceDeferred = [].slice;
+
+jQuery.extend({
+
+ Deferred: function( func ) {
+ var doneList = jQuery.Callbacks( "once memory" ),
+ failList = jQuery.Callbacks( "once memory" ),
+ progressList = jQuery.Callbacks( "memory" ),
+ state = "pending",
+ lists = {
+ resolve: doneList,
+ reject: failList,
+ notify: progressList
+ },
+ promise = {
+ done: doneList.add,
+ fail: failList.add,
+ progress: progressList.add,
+
+ state: function() {
+ return state;
+ },
+
+ // Deprecated
+ isResolved: doneList.fired,
+ isRejected: failList.fired,
+
+ then: function( doneCallbacks, failCallbacks, progressCallbacks ) {
+ deferred.done( doneCallbacks ).fail( failCallbacks ).progress( progressCallbacks );
+ return this;
+ },
+ always: function() {
+ deferred.done.apply( deferred, arguments ).fail.apply( deferred, arguments );
+ return this;
+ },
+ pipe: function( fnDone, fnFail, fnProgress ) {
+ return jQuery.Deferred(function( newDefer ) {
+ jQuery.each( {
+ done: [ fnDone, "resolve" ],
+ fail: [ fnFail, "reject" ],
+ progress: [ fnProgress, "notify" ]
+ }, function( handler, data ) {
+ var fn = data[ 0 ],
+ action = data[ 1 ],
+ returned;
+ if ( jQuery.isFunction( fn ) ) {
+ deferred[ handler ](function() {
+ returned = fn.apply( this, arguments );
+ if ( returned && jQuery.isFunction( returned.promise ) ) {
+ returned.promise().then( newDefer.resolve, newDefer.reject, newDefer.notify );
+ } else {
+ newDefer[ action + "With" ]( this === deferred ? newDefer : this, [ returned ] );
+ }
+ });
+ } else {
+ deferred[ handler ]( newDefer[ action ] );
+ }
+ });
+ }).promise();
+ },
+ // Get a promise for this deferred
+ // If obj is provided, the promise aspect is added to the object
+ promise: function( obj ) {
+ if ( obj == null ) {
+ obj = promise;
+ } else {
+ for ( var key in promise ) {
+ obj[ key ] = promise[ key ];
+ }
+ }
+ return obj;
+ }
+ },
+ deferred = promise.promise({}),
+ key;
+
+ for ( key in lists ) {
+ deferred[ key ] = lists[ key ].fire;
+ deferred[ key + "With" ] = lists[ key ].fireWith;
+ }
+
+ // Handle state
+ deferred.done( function() {
+ state = "resolved";
+ }, failList.disable, progressList.lock ).fail( function() {
+ state = "rejected";
+ }, doneList.disable, progressList.lock );
+
+ // Call given func if any
+ if ( func ) {
+ func.call( deferred, deferred );
+ }
+
+ // All done!
+ return deferred;
+ },
+
+ // Deferred helper
+ when: function( firstParam ) {
+ var args = sliceDeferred.call( arguments, 0 ),
+ i = 0,
+ length = args.length,
+ pValues = new Array( length ),
+ count = length,
+ pCount = length,
+ deferred = length <= 1 && firstParam && jQuery.isFunction( firstParam.promise ) ?
+ firstParam :
+ jQuery.Deferred(),
+ promise = deferred.promise();
+ function resolveFunc( i ) {
+ return function( value ) {
+ args[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments, 0 ) : value;
+ if ( !( --count ) ) {
+ deferred.resolveWith( deferred, args );
+ }
+ };
+ }
+ function progressFunc( i ) {
+ return function( value ) {
+ pValues[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments, 0 ) : value;
+ deferred.notifyWith( promise, pValues );
+ };
+ }
+ if ( length > 1 ) {
+ for ( ; i < length; i++ ) {
+ if ( args[ i ] && args[ i ].promise && jQuery.isFunction( args[ i ].promise ) ) {
+ args[ i ].promise().then( resolveFunc(i), deferred.reject, progressFunc(i) );
+ } else {
+ --count;
+ }
+ }
+ if ( !count ) {
+ deferred.resolveWith( deferred, args );
+ }
+ } else if ( deferred !== firstParam ) {
+ deferred.resolveWith( deferred, length ? [ firstParam ] : [] );
+ }
+ return promise;
+ }
+});
+
+
+
+
+jQuery.support = (function() {
+
+ var support,
+ all,
+ a,
+ select,
+ opt,
+ input,
+ fragment,
+ tds,
+ events,
+ eventName,
+ i,
+ isSupported,
+ div = document.createElement( "div" ),
+ documentElement = document.documentElement;
+
+ // Preliminary tests
+ div.setAttribute("className", "t");
+ div.innerHTML = "
a";
+
+ all = div.getElementsByTagName( "*" );
+ a = div.getElementsByTagName( "a" )[ 0 ];
+
+ // Can't get basic test support
+ if ( !all || !all.length || !a ) {
+ return {};
+ }
+
+ // First batch of supports tests
+ select = document.createElement( "select" );
+ opt = select.appendChild( document.createElement("option") );
+ input = div.getElementsByTagName( "input" )[ 0 ];
+
+ support = {
+ // IE strips leading whitespace when .innerHTML is used
+ leadingWhitespace: ( div.firstChild.nodeType === 3 ),
+
+ // Make sure that tbody elements aren't automatically inserted
+ // IE will insert them into empty tables
+ tbody: !div.getElementsByTagName("tbody").length,
+
+ // Make sure that link elements get serialized correctly by innerHTML
+ // This requires a wrapper element in IE
+ htmlSerialize: !!div.getElementsByTagName("link").length,
+
+ // Get the style information from getAttribute
+ // (IE uses .cssText instead)
+ style: /top/.test( a.getAttribute("style") ),
+
+ // Make sure that URLs aren't manipulated
+ // (IE normalizes it by default)
+ hrefNormalized: ( a.getAttribute("href") === "/a" ),
+
+ // Make sure that element opacity exists
+ // (IE uses filter instead)
+ // Use a regex to work around a WebKit issue. See #5145
+ opacity: /^0.55/.test( a.style.opacity ),
+
+ // Verify style float existence
+ // (IE uses styleFloat instead of cssFloat)
+ cssFloat: !!a.style.cssFloat,
+
+ // Make sure that if no value is specified for a checkbox
+ // that it defaults to "on".
+ // (WebKit defaults to "" instead)
+ checkOn: ( input.value === "on" ),
+
+ // Make sure that a selected-by-default option has a working selected property.
+ // (WebKit defaults to false instead of true, IE too, if it's in an optgroup)
+ optSelected: opt.selected,
+
+ // Test setAttribute on camelCase class. If it works, we need attrFixes when doing get/setAttribute (ie6/7)
+ getSetAttribute: div.className !== "t",
+
+ // Tests for enctype support on a form(#6743)
+ enctype: !!document.createElement("form").enctype,
+
+ // Makes sure cloning an html5 element does not cause problems
+ // Where outerHTML is undefined, this still works
+ html5Clone: document.createElement("nav").cloneNode( true ).outerHTML !== "<:nav>",
+
+ // Will be defined later
+ submitBubbles: true,
+ changeBubbles: true,
+ focusinBubbles: false,
+ deleteExpando: true,
+ noCloneEvent: true,
+ inlineBlockNeedsLayout: false,
+ shrinkWrapBlocks: false,
+ reliableMarginRight: true,
+ pixelMargin: true
+ };
+
+ // jQuery.boxModel DEPRECATED in 1.3, use jQuery.support.boxModel instead
+ jQuery.boxModel = support.boxModel = (document.compatMode === "CSS1Compat");
+
+ // Make sure checked status is properly cloned
+ input.checked = true;
+ support.noCloneChecked = input.cloneNode( true ).checked;
+
+ // Make sure that the options inside disabled selects aren't marked as disabled
+ // (WebKit marks them as disabled)
+ select.disabled = true;
+ support.optDisabled = !opt.disabled;
+
+ // Test to see if it's possible to delete an expando from an element
+ // Fails in Internet Explorer
+ try {
+ delete div.test;
+ } catch( e ) {
+ support.deleteExpando = false;
+ }
+
+ if ( !div.addEventListener && div.attachEvent && div.fireEvent ) {
+ div.attachEvent( "onclick", function() {
+ // Cloning a node shouldn't copy over any
+ // bound event handlers (IE does this)
+ support.noCloneEvent = false;
+ });
+ div.cloneNode( true ).fireEvent( "onclick" );
+ }
+
+ // Check if a radio maintains its value
+ // after being appended to the DOM
+ input = document.createElement("input");
+ input.value = "t";
+ input.setAttribute("type", "radio");
+ support.radioValue = input.value === "t";
+
+ input.setAttribute("checked", "checked");
+
+ // #11217 - WebKit loses check when the name is after the checked attribute
+ input.setAttribute( "name", "t" );
+
+ div.appendChild( input );
+ fragment = document.createDocumentFragment();
+ fragment.appendChild( div.lastChild );
+
+ // WebKit doesn't clone checked state correctly in fragments
+ support.checkClone = fragment.cloneNode( true ).cloneNode( true ).lastChild.checked;
+
+ // Check if a disconnected checkbox will retain its checked
+ // value of true after appended to the DOM (IE6/7)
+ support.appendChecked = input.checked;
+
+ fragment.removeChild( input );
+ fragment.appendChild( div );
+
+ // Technique from Juriy Zaytsev
+ // http://perfectionkills.com/detecting-event-support-without-browser-sniffing/
+ // We only care about the case where non-standard event systems
+ // are used, namely in IE. Short-circuiting here helps us to
+ // avoid an eval call (in setAttribute) which can cause CSP
+ // to go haywire. See: https://developer.mozilla.org/en/Security/CSP
+ if ( div.attachEvent ) {
+ for ( i in {
+ submit: 1,
+ change: 1,
+ focusin: 1
+ }) {
+ eventName = "on" + i;
+ isSupported = ( eventName in div );
+ if ( !isSupported ) {
+ div.setAttribute( eventName, "return;" );
+ isSupported = ( typeof div[ eventName ] === "function" );
+ }
+ support[ i + "Bubbles" ] = isSupported;
+ }
+ }
+
+ fragment.removeChild( div );
+
+ // Null elements to avoid leaks in IE
+ fragment = select = opt = div = input = null;
+
+ // Run tests that need a body at doc ready
+ jQuery(function() {
+ var container, outer, inner, table, td, offsetSupport,
+ marginDiv, conMarginTop, style, html, positionTopLeftWidthHeight,
+ paddingMarginBorderVisibility, paddingMarginBorder,
+ body = document.getElementsByTagName("body")[0];
+
+ if ( !body ) {
+ // Return for frameset docs that don't have a body
+ return;
+ }
+
+ conMarginTop = 1;
+ paddingMarginBorder = "padding:0;margin:0;border:";
+ positionTopLeftWidthHeight = "position:absolute;top:0;left:0;width:1px;height:1px;";
+ paddingMarginBorderVisibility = paddingMarginBorder + "0;visibility:hidden;";
+ style = "style='" + positionTopLeftWidthHeight + paddingMarginBorder + "5px solid #000;";
+ html = "
" +
+ "
" +
+ "
";
+
+ container = document.createElement("div");
+ container.style.cssText = paddingMarginBorderVisibility + "width:0;height:0;position:static;top:0;margin-top:" + conMarginTop + "px";
+ body.insertBefore( container, body.firstChild );
+
+ // Construct the test element
+ div = document.createElement("div");
+ container.appendChild( div );
+
+ // Check if table cells still have offsetWidth/Height when they are set
+ // to display:none and there are still other visible table cells in a
+ // table row; if so, offsetWidth/Height are not reliable for use when
+ // determining if an element has been hidden directly using
+ // display:none (it is still safe to use offsets if a parent element is
+ // hidden; don safety goggles and see bug #4512 for more information).
+ // (only IE 8 fails this test)
+ div.innerHTML = "
t
";
+ tds = div.getElementsByTagName( "td" );
+ isSupported = ( tds[ 0 ].offsetHeight === 0 );
+
+ tds[ 0 ].style.display = "";
+ tds[ 1 ].style.display = "none";
+
+ // Check if empty table cells still have offsetWidth/Height
+ // (IE <= 8 fail this test)
+ support.reliableHiddenOffsets = isSupported && ( tds[ 0 ].offsetHeight === 0 );
+
+ // Check if div with explicit width and no margin-right incorrectly
+ // gets computed margin-right based on width of container. For more
+ // info see bug #3333
+ // Fails in WebKit before Feb 2011 nightlies
+ // WebKit Bug 13343 - getComputedStyle returns wrong value for margin-right
+ if ( window.getComputedStyle ) {
+ div.innerHTML = "";
+ marginDiv = document.createElement( "div" );
+ marginDiv.style.width = "0";
+ marginDiv.style.marginRight = "0";
+ div.style.width = "2px";
+ div.appendChild( marginDiv );
+ support.reliableMarginRight =
+ ( parseInt( ( window.getComputedStyle( marginDiv, null ) || { marginRight: 0 } ).marginRight, 10 ) || 0 ) === 0;
+ }
+
+ if ( typeof div.style.zoom !== "undefined" ) {
+ // Check if natively block-level elements act like inline-block
+ // elements when setting their display to 'inline' and giving
+ // them layout
+ // (IE < 8 does this)
+ div.innerHTML = "";
+ div.style.width = div.style.padding = "1px";
+ div.style.border = 0;
+ div.style.overflow = "hidden";
+ div.style.display = "inline";
+ div.style.zoom = 1;
+ support.inlineBlockNeedsLayout = ( div.offsetWidth === 3 );
+
+ // Check if elements with layout shrink-wrap their children
+ // (IE 6 does this)
+ div.style.display = "block";
+ div.style.overflow = "visible";
+ div.innerHTML = "";
+ support.shrinkWrapBlocks = ( div.offsetWidth !== 3 );
+ }
+
+ div.style.cssText = positionTopLeftWidthHeight + paddingMarginBorderVisibility;
+ div.innerHTML = html;
+
+ outer = div.firstChild;
+ inner = outer.firstChild;
+ td = outer.nextSibling.firstChild.firstChild;
+
+ offsetSupport = {
+ doesNotAddBorder: ( inner.offsetTop !== 5 ),
+ doesAddBorderForTableAndCells: ( td.offsetTop === 5 )
+ };
+
+ inner.style.position = "fixed";
+ inner.style.top = "20px";
+
+ // safari subtracts parent border width here which is 5px
+ offsetSupport.fixedPosition = ( inner.offsetTop === 20 || inner.offsetTop === 15 );
+ inner.style.position = inner.style.top = "";
+
+ outer.style.overflow = "hidden";
+ outer.style.position = "relative";
+
+ offsetSupport.subtractsBorderForOverflowNotVisible = ( inner.offsetTop === -5 );
+ offsetSupport.doesNotIncludeMarginInBodyOffset = ( body.offsetTop !== conMarginTop );
+
+ if ( window.getComputedStyle ) {
+ div.style.marginTop = "1%";
+ support.pixelMargin = ( window.getComputedStyle( div, null ) || { marginTop: 0 } ).marginTop !== "1%";
+ }
+
+ if ( typeof container.style.zoom !== "undefined" ) {
+ container.style.zoom = 1;
+ }
+
+ body.removeChild( container );
+ marginDiv = div = container = null;
+
+ jQuery.extend( support, offsetSupport );
+ });
+
+ return support;
+})();
+
+
+
+
+var rbrace = /^(?:\{.*\}|\[.*\])$/,
+ rmultiDash = /([A-Z])/g;
+
+jQuery.extend({
+ cache: {},
+
+ // Please use with caution
+ uuid: 0,
+
+ // Unique for each copy of jQuery on the page
+ // Non-digits removed to match rinlinejQuery
+ expando: "jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /\D/g, "" ),
+
+ // The following elements throw uncatchable exceptions if you
+ // attempt to add expando properties to them.
+ noData: {
+ "embed": true,
+ // Ban all objects except for Flash (which handle expandos)
+ "object": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",
+ "applet": true
+ },
+
+ hasData: function( elem ) {
+ elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ];
+ return !!elem && !isEmptyDataObject( elem );
+ },
+
+ data: function( elem, name, data, pvt /* Internal Use Only */ ) {
+ if ( !jQuery.acceptData( elem ) ) {
+ return;
+ }
+
+ var privateCache, thisCache, ret,
+ internalKey = jQuery.expando,
+ getByName = typeof name === "string",
+
+ // We have to handle DOM nodes and JS objects differently because IE6-7
+ // can't GC object references properly across the DOM-JS boundary
+ isNode = elem.nodeType,
+
+ // Only DOM nodes need the global jQuery cache; JS object data is
+ // attached directly to the object so GC can occur automatically
+ cache = isNode ? jQuery.cache : elem,
+
+ // Only defining an ID for JS objects if its cache already exists allows
+ // the code to shortcut on the same path as a DOM node with no cache
+ id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey,
+ isEvents = name === "events";
+
+ // Avoid doing any more work than we need to when trying to get data on an
+ // object that has no data at all
+ if ( (!id || !cache[id] || (!isEvents && !pvt && !cache[id].data)) && getByName && data === undefined ) {
+ return;
+ }
+
+ if ( !id ) {
+ // Only DOM nodes need a new unique ID for each element since their data
+ // ends up in the global cache
+ if ( isNode ) {
+ elem[ internalKey ] = id = ++jQuery.uuid;
+ } else {
+ id = internalKey;
+ }
+ }
+
+ if ( !cache[ id ] ) {
+ cache[ id ] = {};
+
+ // Avoids exposing jQuery metadata on plain JS objects when the object
+ // is serialized using JSON.stringify
+ if ( !isNode ) {
+ cache[ id ].toJSON = jQuery.noop;
+ }
+ }
+
+ // An object can be passed to jQuery.data instead of a key/value pair; this gets
+ // shallow copied over onto the existing cache
+ if ( typeof name === "object" || typeof name === "function" ) {
+ if ( pvt ) {
+ cache[ id ] = jQuery.extend( cache[ id ], name );
+ } else {
+ cache[ id ].data = jQuery.extend( cache[ id ].data, name );
+ }
+ }
+
+ privateCache = thisCache = cache[ id ];
+
+ // jQuery data() is stored in a separate object inside the object's internal data
+ // cache in order to avoid key collisions between internal data and user-defined
+ // data.
+ if ( !pvt ) {
+ if ( !thisCache.data ) {
+ thisCache.data = {};
+ }
+
+ thisCache = thisCache.data;
+ }
+
+ if ( data !== undefined ) {
+ thisCache[ jQuery.camelCase( name ) ] = data;
+ }
+
+ // Users should not attempt to inspect the internal events object using jQuery.data,
+ // it is undocumented and subject to change. But does anyone listen? No.
+ if ( isEvents && !thisCache[ name ] ) {
+ return privateCache.events;
+ }
+
+ // Check for both converted-to-camel and non-converted data property names
+ // If a data property was specified
+ if ( getByName ) {
+
+ // First Try to find as-is property data
+ ret = thisCache[ name ];
+
+ // Test for null|undefined property data
+ if ( ret == null ) {
+
+ // Try to find the camelCased property
+ ret = thisCache[ jQuery.camelCase( name ) ];
+ }
+ } else {
+ ret = thisCache;
+ }
+
+ return ret;
+ },
+
+ removeData: function( elem, name, pvt /* Internal Use Only */ ) {
+ if ( !jQuery.acceptData( elem ) ) {
+ return;
+ }
+
+ var thisCache, i, l,
+
+ // Reference to internal data cache key
+ internalKey = jQuery.expando,
+
+ isNode = elem.nodeType,
+
+ // See jQuery.data for more information
+ cache = isNode ? jQuery.cache : elem,
+
+ // See jQuery.data for more information
+ id = isNode ? elem[ internalKey ] : internalKey;
+
+ // If there is already no cache entry for this object, there is no
+ // purpose in continuing
+ if ( !cache[ id ] ) {
+ return;
+ }
+
+ if ( name ) {
+
+ thisCache = pvt ? cache[ id ] : cache[ id ].data;
+
+ if ( thisCache ) {
+
+ // Support array or space separated string names for data keys
+ if ( !jQuery.isArray( name ) ) {
+
+ // try the string as a key before any manipulation
+ if ( name in thisCache ) {
+ name = [ name ];
+ } else {
+
+ // split the camel cased version by spaces unless a key with the spaces exists
+ name = jQuery.camelCase( name );
+ if ( name in thisCache ) {
+ name = [ name ];
+ } else {
+ name = name.split( " " );
+ }
+ }
+ }
+
+ for ( i = 0, l = name.length; i < l; i++ ) {
+ delete thisCache[ name[i] ];
+ }
+
+ // If there is no data left in the cache, we want to continue
+ // and let the cache object itself get destroyed
+ if ( !( pvt ? isEmptyDataObject : jQuery.isEmptyObject )( thisCache ) ) {
+ return;
+ }
+ }
+ }
+
+ // See jQuery.data for more information
+ if ( !pvt ) {
+ delete cache[ id ].data;
+
+ // Don't destroy the parent cache unless the internal data object
+ // had been the only thing left in it
+ if ( !isEmptyDataObject(cache[ id ]) ) {
+ return;
+ }
+ }
+
+ // Browsers that fail expando deletion also refuse to delete expandos on
+ // the window, but it will allow it on all other JS objects; other browsers
+ // don't care
+ // Ensure that `cache` is not a window object #10080
+ if ( jQuery.support.deleteExpando || !cache.setInterval ) {
+ delete cache[ id ];
+ } else {
+ cache[ id ] = null;
+ }
+
+ // We destroyed the cache and need to eliminate the expando on the node to avoid
+ // false lookups in the cache for entries that no longer exist
+ if ( isNode ) {
+ // IE does not allow us to delete expando properties from nodes,
+ // nor does it have a removeAttribute function on Document nodes;
+ // we must handle all of these cases
+ if ( jQuery.support.deleteExpando ) {
+ delete elem[ internalKey ];
+ } else if ( elem.removeAttribute ) {
+ elem.removeAttribute( internalKey );
+ } else {
+ elem[ internalKey ] = null;
+ }
+ }
+ },
+
+ // For internal use only.
+ _data: function( elem, name, data ) {
+ return jQuery.data( elem, name, data, true );
+ },
+
+ // A method for determining if a DOM node can handle the data expando
+ acceptData: function( elem ) {
+ if ( elem.nodeName ) {
+ var match = jQuery.noData[ elem.nodeName.toLowerCase() ];
+
+ if ( match ) {
+ return !(match === true || elem.getAttribute("classid") !== match);
+ }
+ }
+
+ return true;
+ }
+});
+
+jQuery.fn.extend({
+ data: function( key, value ) {
+ var parts, part, attr, name, l,
+ elem = this[0],
+ i = 0,
+ data = null;
+
+ // Gets all values
+ if ( key === undefined ) {
+ if ( this.length ) {
+ data = jQuery.data( elem );
+
+ if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) {
+ attr = elem.attributes;
+ for ( l = attr.length; i < l; i++ ) {
+ name = attr[i].name;
+
+ if ( name.indexOf( "data-" ) === 0 ) {
+ name = jQuery.camelCase( name.substring(5) );
+
+ dataAttr( elem, name, data[ name ] );
+ }
+ }
+ jQuery._data( elem, "parsedAttrs", true );
+ }
+ }
+
+ return data;
+ }
+
+ // Sets multiple values
+ if ( typeof key === "object" ) {
+ return this.each(function() {
+ jQuery.data( this, key );
+ });
+ }
+
+ parts = key.split( ".", 2 );
+ parts[1] = parts[1] ? "." + parts[1] : "";
+ part = parts[1] + "!";
+
+ return jQuery.access( this, function( value ) {
+
+ if ( value === undefined ) {
+ data = this.triggerHandler( "getData" + part, [ parts[0] ] );
+
+ // Try to fetch any internally stored data first
+ if ( data === undefined && elem ) {
+ data = jQuery.data( elem, key );
+ data = dataAttr( elem, key, data );
+ }
+
+ return data === undefined && parts[1] ?
+ this.data( parts[0] ) :
+ data;
+ }
+
+ parts[1] = value;
+ this.each(function() {
+ var self = jQuery( this );
+
+ self.triggerHandler( "setData" + part, parts );
+ jQuery.data( this, key, value );
+ self.triggerHandler( "changeData" + part, parts );
+ });
+ }, null, value, arguments.length > 1, null, false );
+ },
+
+ removeData: function( key ) {
+ return this.each(function() {
+ jQuery.removeData( this, key );
+ });
+ }
+});
+
+function dataAttr( elem, key, data ) {
+ // If nothing was found internally, try to fetch any
+ // data from the HTML5 data-* attribute
+ if ( data === undefined && elem.nodeType === 1 ) {
+
+ var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase();
+
+ data = elem.getAttribute( name );
+
+ if ( typeof data === "string" ) {
+ try {
+ data = data === "true" ? true :
+ data === "false" ? false :
+ data === "null" ? null :
+ jQuery.isNumeric( data ) ? +data :
+ rbrace.test( data ) ? jQuery.parseJSON( data ) :
+ data;
+ } catch( e ) {}
+
+ // Make sure we set the data so it isn't changed later
+ jQuery.data( elem, key, data );
+
+ } else {
+ data = undefined;
+ }
+ }
+
+ return data;
+}
+
+// checks a cache object for emptiness
+function isEmptyDataObject( obj ) {
+ for ( var name in obj ) {
+
+ // if the public data object is empty, the private is still empty
+ if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) {
+ continue;
+ }
+ if ( name !== "toJSON" ) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+
+
+
+function handleQueueMarkDefer( elem, type, src ) {
+ var deferDataKey = type + "defer",
+ queueDataKey = type + "queue",
+ markDataKey = type + "mark",
+ defer = jQuery._data( elem, deferDataKey );
+ if ( defer &&
+ ( src === "queue" || !jQuery._data(elem, queueDataKey) ) &&
+ ( src === "mark" || !jQuery._data(elem, markDataKey) ) ) {
+ // Give room for hard-coded callbacks to fire first
+ // and eventually mark/queue something else on the element
+ setTimeout( function() {
+ if ( !jQuery._data( elem, queueDataKey ) &&
+ !jQuery._data( elem, markDataKey ) ) {
+ jQuery.removeData( elem, deferDataKey, true );
+ defer.fire();
+ }
+ }, 0 );
+ }
+}
+
+jQuery.extend({
+
+ _mark: function( elem, type ) {
+ if ( elem ) {
+ type = ( type || "fx" ) + "mark";
+ jQuery._data( elem, type, (jQuery._data( elem, type ) || 0) + 1 );
+ }
+ },
+
+ _unmark: function( force, elem, type ) {
+ if ( force !== true ) {
+ type = elem;
+ elem = force;
+ force = false;
+ }
+ if ( elem ) {
+ type = type || "fx";
+ var key = type + "mark",
+ count = force ? 0 : ( (jQuery._data( elem, key ) || 1) - 1 );
+ if ( count ) {
+ jQuery._data( elem, key, count );
+ } else {
+ jQuery.removeData( elem, key, true );
+ handleQueueMarkDefer( elem, type, "mark" );
+ }
+ }
+ },
+
+ queue: function( elem, type, data ) {
+ var q;
+ if ( elem ) {
+ type = ( type || "fx" ) + "queue";
+ q = jQuery._data( elem, type );
+
+ // Speed up dequeue by getting out quickly if this is just a lookup
+ if ( data ) {
+ if ( !q || jQuery.isArray(data) ) {
+ q = jQuery._data( elem, type, jQuery.makeArray(data) );
+ } else {
+ q.push( data );
+ }
+ }
+ return q || [];
+ }
+ },
+
+ dequeue: function( elem, type ) {
+ type = type || "fx";
+
+ var queue = jQuery.queue( elem, type ),
+ fn = queue.shift(),
+ hooks = {};
+
+ // If the fx queue is dequeued, always remove the progress sentinel
+ if ( fn === "inprogress" ) {
+ fn = queue.shift();
+ }
+
+ if ( fn ) {
+ // Add a progress sentinel to prevent the fx queue from being
+ // automatically dequeued
+ if ( type === "fx" ) {
+ queue.unshift( "inprogress" );
+ }
+
+ jQuery._data( elem, type + ".run", hooks );
+ fn.call( elem, function() {
+ jQuery.dequeue( elem, type );
+ }, hooks );
+ }
+
+ if ( !queue.length ) {
+ jQuery.removeData( elem, type + "queue " + type + ".run", true );
+ handleQueueMarkDefer( elem, type, "queue" );
+ }
+ }
+});
+
+jQuery.fn.extend({
+ queue: function( type, data ) {
+ var setter = 2;
+
+ if ( typeof type !== "string" ) {
+ data = type;
+ type = "fx";
+ setter--;
+ }
+
+ if ( arguments.length < setter ) {
+ return jQuery.queue( this[0], type );
+ }
+
+ return data === undefined ?
+ this :
+ this.each(function() {
+ var queue = jQuery.queue( this, type, data );
+
+ if ( type === "fx" && queue[0] !== "inprogress" ) {
+ jQuery.dequeue( this, type );
+ }
+ });
+ },
+ dequeue: function( type ) {
+ return this.each(function() {
+ jQuery.dequeue( this, type );
+ });
+ },
+ // Based off of the plugin by Clint Helfers, with permission.
+ // http://blindsignals.com/index.php/2009/07/jquery-delay/
+ delay: function( time, type ) {
+ time = jQuery.fx ? jQuery.fx.speeds[ time ] || time : time;
+ type = type || "fx";
+
+ return this.queue( type, function( next, hooks ) {
+ var timeout = setTimeout( next, time );
+ hooks.stop = function() {
+ clearTimeout( timeout );
+ };
+ });
+ },
+ clearQueue: function( type ) {
+ return this.queue( type || "fx", [] );
+ },
+ // Get a promise resolved when queues of a certain type
+ // are emptied (fx is the type by default)
+ promise: function( type, object ) {
+ if ( typeof type !== "string" ) {
+ object = type;
+ type = undefined;
+ }
+ type = type || "fx";
+ var defer = jQuery.Deferred(),
+ elements = this,
+ i = elements.length,
+ count = 1,
+ deferDataKey = type + "defer",
+ queueDataKey = type + "queue",
+ markDataKey = type + "mark",
+ tmp;
+ function resolve() {
+ if ( !( --count ) ) {
+ defer.resolveWith( elements, [ elements ] );
+ }
+ }
+ while( i-- ) {
+ if (( tmp = jQuery.data( elements[ i ], deferDataKey, undefined, true ) ||
+ ( jQuery.data( elements[ i ], queueDataKey, undefined, true ) ||
+ jQuery.data( elements[ i ], markDataKey, undefined, true ) ) &&
+ jQuery.data( elements[ i ], deferDataKey, jQuery.Callbacks( "once memory" ), true ) )) {
+ count++;
+ tmp.add( resolve );
+ }
+ }
+ resolve();
+ return defer.promise( object );
+ }
+});
+
+
+
+
+var rclass = /[\n\t\r]/g,
+ rspace = /\s+/,
+ rreturn = /\r/g,
+ rtype = /^(?:button|input)$/i,
+ rfocusable = /^(?:button|input|object|select|textarea)$/i,
+ rclickable = /^a(?:rea)?$/i,
+ rboolean = /^(?:autofocus|autoplay|async|checked|controls|defer|disabled|hidden|loop|multiple|open|readonly|required|scoped|selected)$/i,
+ getSetAttribute = jQuery.support.getSetAttribute,
+ nodeHook, boolHook, fixSpecified;
+
+jQuery.fn.extend({
+ attr: function( name, value ) {
+ return jQuery.access( this, jQuery.attr, name, value, arguments.length > 1 );
+ },
+
+ removeAttr: function( name ) {
+ return this.each(function() {
+ jQuery.removeAttr( this, name );
+ });
+ },
+
+ prop: function( name, value ) {
+ return jQuery.access( this, jQuery.prop, name, value, arguments.length > 1 );
+ },
+
+ removeProp: function( name ) {
+ name = jQuery.propFix[ name ] || name;
+ return this.each(function() {
+ // try/catch handles cases where IE balks (such as removing a property on window)
+ try {
+ this[ name ] = undefined;
+ delete this[ name ];
+ } catch( e ) {}
+ });
+ },
+
+ addClass: function( value ) {
+ var classNames, i, l, elem,
+ setClass, c, cl;
+
+ if ( jQuery.isFunction( value ) ) {
+ return this.each(function( j ) {
+ jQuery( this ).addClass( value.call(this, j, this.className) );
+ });
+ }
+
+ if ( value && typeof value === "string" ) {
+ classNames = value.split( rspace );
+
+ for ( i = 0, l = this.length; i < l; i++ ) {
+ elem = this[ i ];
+
+ if ( elem.nodeType === 1 ) {
+ if ( !elem.className && classNames.length === 1 ) {
+ elem.className = value;
+
+ } else {
+ setClass = " " + elem.className + " ";
+
+ for ( c = 0, cl = classNames.length; c < cl; c++ ) {
+ if ( !~setClass.indexOf( " " + classNames[ c ] + " " ) ) {
+ setClass += classNames[ c ] + " ";
+ }
+ }
+ elem.className = jQuery.trim( setClass );
+ }
+ }
+ }
+ }
+
+ return this;
+ },
+
+ removeClass: function( value ) {
+ var classNames, i, l, elem, className, c, cl;
+
+ if ( jQuery.isFunction( value ) ) {
+ return this.each(function( j ) {
+ jQuery( this ).removeClass( value.call(this, j, this.className) );
+ });
+ }
+
+ if ( (value && typeof value === "string") || value === undefined ) {
+ classNames = ( value || "" ).split( rspace );
+
+ for ( i = 0, l = this.length; i < l; i++ ) {
+ elem = this[ i ];
+
+ if ( elem.nodeType === 1 && elem.className ) {
+ if ( value ) {
+ className = (" " + elem.className + " ").replace( rclass, " " );
+ for ( c = 0, cl = classNames.length; c < cl; c++ ) {
+ className = className.replace(" " + classNames[ c ] + " ", " ");
+ }
+ elem.className = jQuery.trim( className );
+
+ } else {
+ elem.className = "";
+ }
+ }
+ }
+ }
+
+ return this;
+ },
+
+ toggleClass: function( value, stateVal ) {
+ var type = typeof value,
+ isBool = typeof stateVal === "boolean";
+
+ if ( jQuery.isFunction( value ) ) {
+ return this.each(function( i ) {
+ jQuery( this ).toggleClass( value.call(this, i, this.className, stateVal), stateVal );
+ });
+ }
+
+ return this.each(function() {
+ if ( type === "string" ) {
+ // toggle individual class names
+ var className,
+ i = 0,
+ self = jQuery( this ),
+ state = stateVal,
+ classNames = value.split( rspace );
+
+ while ( (className = classNames[ i++ ]) ) {
+ // check each className given, space seperated list
+ state = isBool ? state : !self.hasClass( className );
+ self[ state ? "addClass" : "removeClass" ]( className );
+ }
+
+ } else if ( type === "undefined" || type === "boolean" ) {
+ if ( this.className ) {
+ // store className if set
+ jQuery._data( this, "__className__", this.className );
+ }
+
+ // toggle whole className
+ this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || "";
+ }
+ });
+ },
+
+ hasClass: function( selector ) {
+ var className = " " + selector + " ",
+ i = 0,
+ l = this.length;
+ for ( ; i < l; i++ ) {
+ if ( this[i].nodeType === 1 && (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) > -1 ) {
+ return true;
+ }
+ }
+
+ return false;
+ },
+
+ val: function( value ) {
+ var hooks, ret, isFunction,
+ elem = this[0];
+
+ if ( !arguments.length ) {
+ if ( elem ) {
+ hooks = jQuery.valHooks[ elem.type ] || jQuery.valHooks[ elem.nodeName.toLowerCase() ];
+
+ if ( hooks && "get" in hooks && (ret = hooks.get( elem, "value" )) !== undefined ) {
+ return ret;
+ }
+
+ ret = elem.value;
+
+ return typeof ret === "string" ?
+ // handle most common string cases
+ ret.replace(rreturn, "") :
+ // handle cases where value is null/undef or number
+ ret == null ? "" : ret;
+ }
+
+ return;
+ }
+
+ isFunction = jQuery.isFunction( value );
+
+ return this.each(function( i ) {
+ var self = jQuery(this), val;
+
+ if ( this.nodeType !== 1 ) {
+ return;
+ }
+
+ if ( isFunction ) {
+ val = value.call( this, i, self.val() );
+ } else {
+ val = value;
+ }
+
+ // Treat null/undefined as ""; convert numbers to string
+ if ( val == null ) {
+ val = "";
+ } else if ( typeof val === "number" ) {
+ val += "";
+ } else if ( jQuery.isArray( val ) ) {
+ val = jQuery.map(val, function ( value ) {
+ return value == null ? "" : value + "";
+ });
+ }
+
+ hooks = jQuery.valHooks[ this.type ] || jQuery.valHooks[ this.nodeName.toLowerCase() ];
+
+ // If set returns undefined, fall back to normal setting
+ if ( !hooks || !("set" in hooks) || hooks.set( this, val, "value" ) === undefined ) {
+ this.value = val;
+ }
+ });
+ }
+});
+
+jQuery.extend({
+ valHooks: {
+ option: {
+ get: function( elem ) {
+ // attributes.value is undefined in Blackberry 4.7 but
+ // uses .value. See #6932
+ var val = elem.attributes.value;
+ return !val || val.specified ? elem.value : elem.text;
+ }
+ },
+ select: {
+ get: function( elem ) {
+ var value, i, max, option,
+ index = elem.selectedIndex,
+ values = [],
+ options = elem.options,
+ one = elem.type === "select-one";
+
+ // Nothing was selected
+ if ( index < 0 ) {
+ return null;
+ }
+
+ // Loop through all the selected options
+ i = one ? index : 0;
+ max = one ? index + 1 : options.length;
+ for ( ; i < max; i++ ) {
+ option = options[ i ];
+
+ // Don't return options that are disabled or in a disabled optgroup
+ if ( option.selected && (jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null) &&
+ (!option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" )) ) {
+
+ // Get the specific value for the option
+ value = jQuery( option ).val();
+
+ // We don't need an array for one selects
+ if ( one ) {
+ return value;
+ }
+
+ // Multi-Selects return an array
+ values.push( value );
+ }
+ }
+
+ // Fixes Bug #2551 -- select.val() broken in IE after form.reset()
+ if ( one && !values.length && options.length ) {
+ return jQuery( options[ index ] ).val();
+ }
+
+ return values;
+ },
+
+ set: function( elem, value ) {
+ var values = jQuery.makeArray( value );
+
+ jQuery(elem).find("option").each(function() {
+ this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0;
+ });
+
+ if ( !values.length ) {
+ elem.selectedIndex = -1;
+ }
+ return values;
+ }
+ }
+ },
+
+ attrFn: {
+ val: true,
+ css: true,
+ html: true,
+ text: true,
+ data: true,
+ width: true,
+ height: true,
+ offset: true
+ },
+
+ attr: function( elem, name, value, pass ) {
+ var ret, hooks, notxml,
+ nType = elem.nodeType;
+
+ // don't get/set attributes on text, comment and attribute nodes
+ if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
+ return;
+ }
+
+ if ( pass && name in jQuery.attrFn ) {
+ return jQuery( elem )[ name ]( value );
+ }
+
+ // Fallback to prop when attributes are not supported
+ if ( typeof elem.getAttribute === "undefined" ) {
+ return jQuery.prop( elem, name, value );
+ }
+
+ notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
+
+ // All attributes are lowercase
+ // Grab necessary hook if one is defined
+ if ( notxml ) {
+ name = name.toLowerCase();
+ hooks = jQuery.attrHooks[ name ] || ( rboolean.test( name ) ? boolHook : nodeHook );
+ }
+
+ if ( value !== undefined ) {
+
+ if ( value === null ) {
+ jQuery.removeAttr( elem, name );
+ return;
+
+ } else if ( hooks && "set" in hooks && notxml && (ret = hooks.set( elem, value, name )) !== undefined ) {
+ return ret;
+
+ } else {
+ elem.setAttribute( name, "" + value );
+ return value;
+ }
+
+ } else if ( hooks && "get" in hooks && notxml && (ret = hooks.get( elem, name )) !== null ) {
+ return ret;
+
+ } else {
+
+ ret = elem.getAttribute( name );
+
+ // Non-existent attributes return null, we normalize to undefined
+ return ret === null ?
+ undefined :
+ ret;
+ }
+ },
+
+ removeAttr: function( elem, value ) {
+ var propName, attrNames, name, l, isBool,
+ i = 0;
+
+ if ( value && elem.nodeType === 1 ) {
+ attrNames = value.toLowerCase().split( rspace );
+ l = attrNames.length;
+
+ for ( ; i < l; i++ ) {
+ name = attrNames[ i ];
+
+ if ( name ) {
+ propName = jQuery.propFix[ name ] || name;
+ isBool = rboolean.test( name );
+
+ // See #9699 for explanation of this approach (setting first, then removal)
+ // Do not do this for boolean attributes (see #10870)
+ if ( !isBool ) {
+ jQuery.attr( elem, name, "" );
+ }
+ elem.removeAttribute( getSetAttribute ? name : propName );
+
+ // Set corresponding property to false for boolean attributes
+ if ( isBool && propName in elem ) {
+ elem[ propName ] = false;
+ }
+ }
+ }
+ }
+ },
+
+ attrHooks: {
+ type: {
+ set: function( elem, value ) {
+ // We can't allow the type property to be changed (since it causes problems in IE)
+ if ( rtype.test( elem.nodeName ) && elem.parentNode ) {
+ jQuery.error( "type property can't be changed" );
+ } else if ( !jQuery.support.radioValue && value === "radio" && jQuery.nodeName(elem, "input") ) {
+ // Setting the type on a radio button after the value resets the value in IE6-9
+ // Reset value to it's default in case type is set after value
+ // This is for element creation
+ var val = elem.value;
+ elem.setAttribute( "type", value );
+ if ( val ) {
+ elem.value = val;
+ }
+ return value;
+ }
+ }
+ },
+ // Use the value property for back compat
+ // Use the nodeHook for button elements in IE6/7 (#1954)
+ value: {
+ get: function( elem, name ) {
+ if ( nodeHook && jQuery.nodeName( elem, "button" ) ) {
+ return nodeHook.get( elem, name );
+ }
+ return name in elem ?
+ elem.value :
+ null;
+ },
+ set: function( elem, value, name ) {
+ if ( nodeHook && jQuery.nodeName( elem, "button" ) ) {
+ return nodeHook.set( elem, value, name );
+ }
+ // Does not return so that setAttribute is also used
+ elem.value = value;
+ }
+ }
+ },
+
+ propFix: {
+ tabindex: "tabIndex",
+ readonly: "readOnly",
+ "for": "htmlFor",
+ "class": "className",
+ maxlength: "maxLength",
+ cellspacing: "cellSpacing",
+ cellpadding: "cellPadding",
+ rowspan: "rowSpan",
+ colspan: "colSpan",
+ usemap: "useMap",
+ frameborder: "frameBorder",
+ contenteditable: "contentEditable"
+ },
+
+ prop: function( elem, name, value ) {
+ var ret, hooks, notxml,
+ nType = elem.nodeType;
+
+ // don't get/set properties on text, comment and attribute nodes
+ if ( !elem || nType === 3 || nType === 8 || nType === 2 ) {
+ return;
+ }
+
+ notxml = nType !== 1 || !jQuery.isXMLDoc( elem );
+
+ if ( notxml ) {
+ // Fix name and attach hooks
+ name = jQuery.propFix[ name ] || name;
+ hooks = jQuery.propHooks[ name ];
+ }
+
+ if ( value !== undefined ) {
+ if ( hooks && "set" in hooks && (ret = hooks.set( elem, value, name )) !== undefined ) {
+ return ret;
+
+ } else {
+ return ( elem[ name ] = value );
+ }
+
+ } else {
+ if ( hooks && "get" in hooks && (ret = hooks.get( elem, name )) !== null ) {
+ return ret;
+
+ } else {
+ return elem[ name ];
+ }
+ }
+ },
+
+ propHooks: {
+ tabIndex: {
+ get: function( elem ) {
+ // elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set
+ // http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
+ var attributeNode = elem.getAttributeNode("tabindex");
+
+ return attributeNode && attributeNode.specified ?
+ parseInt( attributeNode.value, 10 ) :
+ rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ?
+ 0 :
+ undefined;
+ }
+ }
+ }
+});
+
+// Add the tabIndex propHook to attrHooks for back-compat (different case is intentional)
+jQuery.attrHooks.tabindex = jQuery.propHooks.tabIndex;
+
+// Hook for boolean attributes
+boolHook = {
+ get: function( elem, name ) {
+ // Align boolean attributes with corresponding properties
+ // Fall back to attribute presence where some booleans are not supported
+ var attrNode,
+ property = jQuery.prop( elem, name );
+ return property === true || typeof property !== "boolean" && ( attrNode = elem.getAttributeNode(name) ) && attrNode.nodeValue !== false ?
+ name.toLowerCase() :
+ undefined;
+ },
+ set: function( elem, value, name ) {
+ var propName;
+ if ( value === false ) {
+ // Remove boolean attributes when set to false
+ jQuery.removeAttr( elem, name );
+ } else {
+ // value is true since we know at this point it's type boolean and not false
+ // Set boolean attributes to the same name and set the DOM property
+ propName = jQuery.propFix[ name ] || name;
+ if ( propName in elem ) {
+ // Only set the IDL specifically if it already exists on the element
+ elem[ propName ] = true;
+ }
+
+ elem.setAttribute( name, name.toLowerCase() );
+ }
+ return name;
+ }
+};
+
+// IE6/7 do not support getting/setting some attributes with get/setAttribute
+if ( !getSetAttribute ) {
+
+ fixSpecified = {
+ name: true,
+ id: true,
+ coords: true
+ };
+
+ // Use this for any attribute in IE6/7
+ // This fixes almost every IE6/7 issue
+ nodeHook = jQuery.valHooks.button = {
+ get: function( elem, name ) {
+ var ret;
+ ret = elem.getAttributeNode( name );
+ return ret && ( fixSpecified[ name ] ? ret.nodeValue !== "" : ret.specified ) ?
+ ret.nodeValue :
+ undefined;
+ },
+ set: function( elem, value, name ) {
+ // Set the existing or create a new attribute node
+ var ret = elem.getAttributeNode( name );
+ if ( !ret ) {
+ ret = document.createAttribute( name );
+ elem.setAttributeNode( ret );
+ }
+ return ( ret.nodeValue = value + "" );
+ }
+ };
+
+ // Apply the nodeHook to tabindex
+ jQuery.attrHooks.tabindex.set = nodeHook.set;
+
+ // Set width and height to auto instead of 0 on empty string( Bug #8150 )
+ // This is for removals
+ jQuery.each([ "width", "height" ], function( i, name ) {
+ jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], {
+ set: function( elem, value ) {
+ if ( value === "" ) {
+ elem.setAttribute( name, "auto" );
+ return value;
+ }
+ }
+ });
+ });
+
+ // Set contenteditable to false on removals(#10429)
+ // Setting to empty string throws an error as an invalid value
+ jQuery.attrHooks.contenteditable = {
+ get: nodeHook.get,
+ set: function( elem, value, name ) {
+ if ( value === "" ) {
+ value = "false";
+ }
+ nodeHook.set( elem, value, name );
+ }
+ };
+}
+
+
+// Some attributes require a special call on IE
+if ( !jQuery.support.hrefNormalized ) {
+ jQuery.each([ "href", "src", "width", "height" ], function( i, name ) {
+ jQuery.attrHooks[ name ] = jQuery.extend( jQuery.attrHooks[ name ], {
+ get: function( elem ) {
+ var ret = elem.getAttribute( name, 2 );
+ return ret === null ? undefined : ret;
+ }
+ });
+ });
+}
+
+if ( !jQuery.support.style ) {
+ jQuery.attrHooks.style = {
+ get: function( elem ) {
+ // Return undefined in the case of empty string
+ // Normalize to lowercase since IE uppercases css property names
+ return elem.style.cssText.toLowerCase() || undefined;
+ },
+ set: function( elem, value ) {
+ return ( elem.style.cssText = "" + value );
+ }
+ };
+}
+
+// Safari mis-reports the default selected property of an option
+// Accessing the parent's selectedIndex property fixes it
+if ( !jQuery.support.optSelected ) {
+ jQuery.propHooks.selected = jQuery.extend( jQuery.propHooks.selected, {
+ get: function( elem ) {
+ var parent = elem.parentNode;
+
+ if ( parent ) {
+ parent.selectedIndex;
+
+ // Make sure that it also works with optgroups, see #5701
+ if ( parent.parentNode ) {
+ parent.parentNode.selectedIndex;
+ }
+ }
+ return null;
+ }
+ });
+}
+
+// IE6/7 call enctype encoding
+if ( !jQuery.support.enctype ) {
+ jQuery.propFix.enctype = "encoding";
+}
+
+// Radios and checkboxes getter/setter
+if ( !jQuery.support.checkOn ) {
+ jQuery.each([ "radio", "checkbox" ], function() {
+ jQuery.valHooks[ this ] = {
+ get: function( elem ) {
+ // Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified
+ return elem.getAttribute("value") === null ? "on" : elem.value;
+ }
+ };
+ });
+}
+jQuery.each([ "radio", "checkbox" ], function() {
+ jQuery.valHooks[ this ] = jQuery.extend( jQuery.valHooks[ this ], {
+ set: function( elem, value ) {
+ if ( jQuery.isArray( value ) ) {
+ return ( elem.checked = jQuery.inArray( jQuery(elem).val(), value ) >= 0 );
+ }
+ }
+ });
+});
+
+
+
+
+var rformElems = /^(?:textarea|input|select)$/i,
+ rtypenamespace = /^([^\.]*)?(?:\.(.+))?$/,
+ rhoverHack = /(?:^|\s)hover(\.\S+)?\b/,
+ rkeyEvent = /^key/,
+ rmouseEvent = /^(?:mouse|contextmenu)|click/,
+ rfocusMorph = /^(?:focusinfocus|focusoutblur)$/,
+ rquickIs = /^(\w*)(?:#([\w\-]+))?(?:\.([\w\-]+))?$/,
+ quickParse = function( selector ) {
+ var quick = rquickIs.exec( selector );
+ if ( quick ) {
+ // 0 1 2 3
+ // [ _, tag, id, class ]
+ quick[1] = ( quick[1] || "" ).toLowerCase();
+ quick[3] = quick[3] && new RegExp( "(?:^|\\s)" + quick[3] + "(?:\\s|$)" );
+ }
+ return quick;
+ },
+ quickIs = function( elem, m ) {
+ var attrs = elem.attributes || {};
+ return (
+ (!m[1] || elem.nodeName.toLowerCase() === m[1]) &&
+ (!m[2] || (attrs.id || {}).value === m[2]) &&
+ (!m[3] || m[3].test( (attrs[ "class" ] || {}).value ))
+ );
+ },
+ hoverHack = function( events ) {
+ return jQuery.event.special.hover ? events : events.replace( rhoverHack, "mouseenter$1 mouseleave$1" );
+ };
+
+/*
+ * Helper functions for managing events -- not part of the public interface.
+ * Props to Dean Edwards' addEvent library for many of the ideas.
+ */
+jQuery.event = {
+
+ add: function( elem, types, handler, data, selector ) {
+
+ var elemData, eventHandle, events,
+ t, tns, type, namespaces, handleObj,
+ handleObjIn, quick, handlers, special;
+
+ // Don't attach events to noData or text/comment nodes (allow plain objects tho)
+ if ( elem.nodeType === 3 || elem.nodeType === 8 || !types || !handler || !(elemData = jQuery._data( elem )) ) {
+ return;
+ }
+
+ // Caller can pass in an object of custom data in lieu of the handler
+ if ( handler.handler ) {
+ handleObjIn = handler;
+ handler = handleObjIn.handler;
+ selector = handleObjIn.selector;
+ }
+
+ // Make sure that the handler has a unique ID, used to find/remove it later
+ if ( !handler.guid ) {
+ handler.guid = jQuery.guid++;
+ }
+
+ // Init the element's event structure and main handler, if this is the first
+ events = elemData.events;
+ if ( !events ) {
+ elemData.events = events = {};
+ }
+ eventHandle = elemData.handle;
+ if ( !eventHandle ) {
+ elemData.handle = eventHandle = function( e ) {
+ // Discard the second event of a jQuery.event.trigger() and
+ // when an event is called after a page has unloaded
+ return typeof jQuery !== "undefined" && (!e || jQuery.event.triggered !== e.type) ?
+ jQuery.event.dispatch.apply( eventHandle.elem, arguments ) :
+ undefined;
+ };
+ // Add elem as a property of the handle fn to prevent a memory leak with IE non-native events
+ eventHandle.elem = elem;
+ }
+
+ // Handle multiple events separated by a space
+ // jQuery(...).bind("mouseover mouseout", fn);
+ types = jQuery.trim( hoverHack(types) ).split( " " );
+ for ( t = 0; t < types.length; t++ ) {
+
+ tns = rtypenamespace.exec( types[t] ) || [];
+ type = tns[1];
+ namespaces = ( tns[2] || "" ).split( "." ).sort();
+
+ // If event changes its type, use the special event handlers for the changed type
+ special = jQuery.event.special[ type ] || {};
+
+ // If selector defined, determine special event api type, otherwise given type
+ type = ( selector ? special.delegateType : special.bindType ) || type;
+
+ // Update special based on newly reset type
+ special = jQuery.event.special[ type ] || {};
+
+ // handleObj is passed to all event handlers
+ handleObj = jQuery.extend({
+ type: type,
+ origType: tns[1],
+ data: data,
+ handler: handler,
+ guid: handler.guid,
+ selector: selector,
+ quick: selector && quickParse( selector ),
+ namespace: namespaces.join(".")
+ }, handleObjIn );
+
+ // Init the event handler queue if we're the first
+ handlers = events[ type ];
+ if ( !handlers ) {
+ handlers = events[ type ] = [];
+ handlers.delegateCount = 0;
+
+ // Only use addEventListener/attachEvent if the special events handler returns false
+ if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
+ // Bind the global event handler to the element
+ if ( elem.addEventListener ) {
+ elem.addEventListener( type, eventHandle, false );
+
+ } else if ( elem.attachEvent ) {
+ elem.attachEvent( "on" + type, eventHandle );
+ }
+ }
+ }
+
+ if ( special.add ) {
+ special.add.call( elem, handleObj );
+
+ if ( !handleObj.handler.guid ) {
+ handleObj.handler.guid = handler.guid;
+ }
+ }
+
+ // Add to the element's handler list, delegates in front
+ if ( selector ) {
+ handlers.splice( handlers.delegateCount++, 0, handleObj );
+ } else {
+ handlers.push( handleObj );
+ }
+
+ // Keep track of which events have ever been used, for event optimization
+ jQuery.event.global[ type ] = true;
+ }
+
+ // Nullify elem to prevent memory leaks in IE
+ elem = null;
+ },
+
+ global: {},
+
+ // Detach an event or set of events from an element
+ remove: function( elem, types, handler, selector, mappedTypes ) {
+
+ var elemData = jQuery.hasData( elem ) && jQuery._data( elem ),
+ t, tns, type, origType, namespaces, origCount,
+ j, events, special, handle, eventType, handleObj;
+
+ if ( !elemData || !(events = elemData.events) ) {
+ return;
+ }
+
+ // Once for each type.namespace in types; type may be omitted
+ types = jQuery.trim( hoverHack( types || "" ) ).split(" ");
+ for ( t = 0; t < types.length; t++ ) {
+ tns = rtypenamespace.exec( types[t] ) || [];
+ type = origType = tns[1];
+ namespaces = tns[2];
+
+ // Unbind all events (on this namespace, if provided) for the element
+ if ( !type ) {
+ for ( type in events ) {
+ jQuery.event.remove( elem, type + types[ t ], handler, selector, true );
+ }
+ continue;
+ }
+
+ special = jQuery.event.special[ type ] || {};
+ type = ( selector? special.delegateType : special.bindType ) || type;
+ eventType = events[ type ] || [];
+ origCount = eventType.length;
+ namespaces = namespaces ? new RegExp("(^|\\.)" + namespaces.split(".").sort().join("\\.(?:.*\\.)?") + "(\\.|$)") : null;
+
+ // Remove matching events
+ for ( j = 0; j < eventType.length; j++ ) {
+ handleObj = eventType[ j ];
+
+ if ( ( mappedTypes || origType === handleObj.origType ) &&
+ ( !handler || handler.guid === handleObj.guid ) &&
+ ( !namespaces || namespaces.test( handleObj.namespace ) ) &&
+ ( !selector || selector === handleObj.selector || selector === "**" && handleObj.selector ) ) {
+ eventType.splice( j--, 1 );
+
+ if ( handleObj.selector ) {
+ eventType.delegateCount--;
+ }
+ if ( special.remove ) {
+ special.remove.call( elem, handleObj );
+ }
+ }
+ }
+
+ // Remove generic event handler if we removed something and no more handlers exist
+ // (avoids potential for endless recursion during removal of special event handlers)
+ if ( eventType.length === 0 && origCount !== eventType.length ) {
+ if ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) {
+ jQuery.removeEvent( elem, type, elemData.handle );
+ }
+
+ delete events[ type ];
+ }
+ }
+
+ // Remove the expando if it's no longer used
+ if ( jQuery.isEmptyObject( events ) ) {
+ handle = elemData.handle;
+ if ( handle ) {
+ handle.elem = null;
+ }
+
+ // removeData also checks for emptiness and clears the expando if empty
+ // so use it instead of delete
+ jQuery.removeData( elem, [ "events", "handle" ], true );
+ }
+ },
+
+ // Events that are safe to short-circuit if no handlers are attached.
+ // Native DOM events should not be added, they may have inline handlers.
+ customEvent: {
+ "getData": true,
+ "setData": true,
+ "changeData": true
+ },
+
+ trigger: function( event, data, elem, onlyHandlers ) {
+ // Don't do events on text and comment nodes
+ if ( elem && (elem.nodeType === 3 || elem.nodeType === 8) ) {
+ return;
+ }
+
+ // Event object or event type
+ var type = event.type || event,
+ namespaces = [],
+ cache, exclusive, i, cur, old, ontype, special, handle, eventPath, bubbleType;
+
+ // focus/blur morphs to focusin/out; ensure we're not firing them right now
+ if ( rfocusMorph.test( type + jQuery.event.triggered ) ) {
+ return;
+ }
+
+ if ( type.indexOf( "!" ) >= 0 ) {
+ // Exclusive events trigger only for the exact event (no namespaces)
+ type = type.slice(0, -1);
+ exclusive = true;
+ }
+
+ if ( type.indexOf( "." ) >= 0 ) {
+ // Namespaced trigger; create a regexp to match event type in handle()
+ namespaces = type.split(".");
+ type = namespaces.shift();
+ namespaces.sort();
+ }
+
+ if ( (!elem || jQuery.event.customEvent[ type ]) && !jQuery.event.global[ type ] ) {
+ // No jQuery handlers for this event type, and it can't have inline handlers
+ return;
+ }
+
+ // Caller can pass in an Event, Object, or just an event type string
+ event = typeof event === "object" ?
+ // jQuery.Event object
+ event[ jQuery.expando ] ? event :
+ // Object literal
+ new jQuery.Event( type, event ) :
+ // Just the event type (string)
+ new jQuery.Event( type );
+
+ event.type = type;
+ event.isTrigger = true;
+ event.exclusive = exclusive;
+ event.namespace = namespaces.join( "." );
+ event.namespace_re = event.namespace? new RegExp("(^|\\.)" + namespaces.join("\\.(?:.*\\.)?") + "(\\.|$)") : null;
+ ontype = type.indexOf( ":" ) < 0 ? "on" + type : "";
+
+ // Handle a global trigger
+ if ( !elem ) {
+
+ // TODO: Stop taunting the data cache; remove global events and always attach to document
+ cache = jQuery.cache;
+ for ( i in cache ) {
+ if ( cache[ i ].events && cache[ i ].events[ type ] ) {
+ jQuery.event.trigger( event, data, cache[ i ].handle.elem, true );
+ }
+ }
+ return;
+ }
+
+ // Clean up the event in case it is being reused
+ event.result = undefined;
+ if ( !event.target ) {
+ event.target = elem;
+ }
+
+ // Clone any incoming data and prepend the event, creating the handler arg list
+ data = data != null ? jQuery.makeArray( data ) : [];
+ data.unshift( event );
+
+ // Allow special events to draw outside the lines
+ special = jQuery.event.special[ type ] || {};
+ if ( special.trigger && special.trigger.apply( elem, data ) === false ) {
+ return;
+ }
+
+ // Determine event propagation path in advance, per W3C events spec (#9951)
+ // Bubble up to document, then to window; watch for a global ownerDocument var (#9724)
+ eventPath = [[ elem, special.bindType || type ]];
+ if ( !onlyHandlers && !special.noBubble && !jQuery.isWindow( elem ) ) {
+
+ bubbleType = special.delegateType || type;
+ cur = rfocusMorph.test( bubbleType + type ) ? elem : elem.parentNode;
+ old = null;
+ for ( ; cur; cur = cur.parentNode ) {
+ eventPath.push([ cur, bubbleType ]);
+ old = cur;
+ }
+
+ // Only add window if we got to document (e.g., not plain obj or detached DOM)
+ if ( old && old === elem.ownerDocument ) {
+ eventPath.push([ old.defaultView || old.parentWindow || window, bubbleType ]);
+ }
+ }
+
+ // Fire handlers on the event path
+ for ( i = 0; i < eventPath.length && !event.isPropagationStopped(); i++ ) {
+
+ cur = eventPath[i][0];
+ event.type = eventPath[i][1];
+
+ handle = ( jQuery._data( cur, "events" ) || {} )[ event.type ] && jQuery._data( cur, "handle" );
+ if ( handle ) {
+ handle.apply( cur, data );
+ }
+ // Note that this is a bare JS function and not a jQuery handler
+ handle = ontype && cur[ ontype ];
+ if ( handle && jQuery.acceptData( cur ) && handle.apply( cur, data ) === false ) {
+ event.preventDefault();
+ }
+ }
+ event.type = type;
+
+ // If nobody prevented the default action, do it now
+ if ( !onlyHandlers && !event.isDefaultPrevented() ) {
+
+ if ( (!special._default || special._default.apply( elem.ownerDocument, data ) === false) &&
+ !(type === "click" && jQuery.nodeName( elem, "a" )) && jQuery.acceptData( elem ) ) {
+
+ // Call a native DOM method on the target with the same name name as the event.
+ // Can't use an .isFunction() check here because IE6/7 fails that test.
+ // Don't do default actions on window, that's where global variables be (#6170)
+ // IE<9 dies on focus/blur to hidden element (#1486)
+ if ( ontype && elem[ type ] && ((type !== "focus" && type !== "blur") || event.target.offsetWidth !== 0) && !jQuery.isWindow( elem ) ) {
+
+ // Don't re-trigger an onFOO event when we call its FOO() method
+ old = elem[ ontype ];
+
+ if ( old ) {
+ elem[ ontype ] = null;
+ }
+
+ // Prevent re-triggering of the same event, since we already bubbled it above
+ jQuery.event.triggered = type;
+ elem[ type ]();
+ jQuery.event.triggered = undefined;
+
+ if ( old ) {
+ elem[ ontype ] = old;
+ }
+ }
+ }
+ }
+
+ return event.result;
+ },
+
+ dispatch: function( event ) {
+
+ // Make a writable jQuery.Event from the native event object
+ event = jQuery.event.fix( event || window.event );
+
+ var handlers = ( (jQuery._data( this, "events" ) || {} )[ event.type ] || []),
+ delegateCount = handlers.delegateCount,
+ args = [].slice.call( arguments, 0 ),
+ run_all = !event.exclusive && !event.namespace,
+ special = jQuery.event.special[ event.type ] || {},
+ handlerQueue = [],
+ i, j, cur, jqcur, ret, selMatch, matched, matches, handleObj, sel, related;
+
+ // Use the fix-ed jQuery.Event rather than the (read-only) native event
+ args[0] = event;
+ event.delegateTarget = this;
+
+ // Call the preDispatch hook for the mapped type, and let it bail if desired
+ if ( special.preDispatch && special.preDispatch.call( this, event ) === false ) {
+ return;
+ }
+
+ // Determine handlers that should run if there are delegated events
+ // Avoid non-left-click bubbling in Firefox (#3861)
+ if ( delegateCount && !(event.button && event.type === "click") ) {
+
+ // Pregenerate a single jQuery object for reuse with .is()
+ jqcur = jQuery(this);
+ jqcur.context = this.ownerDocument || this;
+
+ for ( cur = event.target; cur != this; cur = cur.parentNode || this ) {
+
+ // Don't process events on disabled elements (#6911, #8165)
+ if ( cur.disabled !== true ) {
+ selMatch = {};
+ matches = [];
+ jqcur[0] = cur;
+ for ( i = 0; i < delegateCount; i++ ) {
+ handleObj = handlers[ i ];
+ sel = handleObj.selector;
+
+ if ( selMatch[ sel ] === undefined ) {
+ selMatch[ sel ] = (
+ handleObj.quick ? quickIs( cur, handleObj.quick ) : jqcur.is( sel )
+ );
+ }
+ if ( selMatch[ sel ] ) {
+ matches.push( handleObj );
+ }
+ }
+ if ( matches.length ) {
+ handlerQueue.push({ elem: cur, matches: matches });
+ }
+ }
+ }
+ }
+
+ // Add the remaining (directly-bound) handlers
+ if ( handlers.length > delegateCount ) {
+ handlerQueue.push({ elem: this, matches: handlers.slice( delegateCount ) });
+ }
+
+ // Run delegates first; they may want to stop propagation beneath us
+ for ( i = 0; i < handlerQueue.length && !event.isPropagationStopped(); i++ ) {
+ matched = handlerQueue[ i ];
+ event.currentTarget = matched.elem;
+
+ for ( j = 0; j < matched.matches.length && !event.isImmediatePropagationStopped(); j++ ) {
+ handleObj = matched.matches[ j ];
+
+ // Triggered event must either 1) be non-exclusive and have no namespace, or
+ // 2) have namespace(s) a subset or equal to those in the bound event (both can have no namespace).
+ if ( run_all || (!event.namespace && !handleObj.namespace) || event.namespace_re && event.namespace_re.test( handleObj.namespace ) ) {
+
+ event.data = handleObj.data;
+ event.handleObj = handleObj;
+
+ ret = ( (jQuery.event.special[ handleObj.origType ] || {}).handle || handleObj.handler )
+ .apply( matched.elem, args );
+
+ if ( ret !== undefined ) {
+ event.result = ret;
+ if ( ret === false ) {
+ event.preventDefault();
+ event.stopPropagation();
+ }
+ }
+ }
+ }
+ }
+
+ // Call the postDispatch hook for the mapped type
+ if ( special.postDispatch ) {
+ special.postDispatch.call( this, event );
+ }
+
+ return event.result;
+ },
+
+ // Includes some event props shared by KeyEvent and MouseEvent
+ // *** attrChange attrName relatedNode srcElement are not normalized, non-W3C, deprecated, will be removed in 1.8 ***
+ props: "attrChange attrName relatedNode srcElement altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),
+
+ fixHooks: {},
+
+ keyHooks: {
+ props: "char charCode key keyCode".split(" "),
+ filter: function( event, original ) {
+
+ // Add which for key events
+ if ( event.which == null ) {
+ event.which = original.charCode != null ? original.charCode : original.keyCode;
+ }
+
+ return event;
+ }
+ },
+
+ mouseHooks: {
+ props: "button buttons clientX clientY fromElement offsetX offsetY pageX pageY screenX screenY toElement".split(" "),
+ filter: function( event, original ) {
+ var eventDoc, doc, body,
+ button = original.button,
+ fromElement = original.fromElement;
+
+ // Calculate pageX/Y if missing and clientX/Y available
+ if ( event.pageX == null && original.clientX != null ) {
+ eventDoc = event.target.ownerDocument || document;
+ doc = eventDoc.documentElement;
+ body = eventDoc.body;
+
+ event.pageX = original.clientX + ( doc && doc.scrollLeft || body && body.scrollLeft || 0 ) - ( doc && doc.clientLeft || body && body.clientLeft || 0 );
+ event.pageY = original.clientY + ( doc && doc.scrollTop || body && body.scrollTop || 0 ) - ( doc && doc.clientTop || body && body.clientTop || 0 );
+ }
+
+ // Add relatedTarget, if necessary
+ if ( !event.relatedTarget && fromElement ) {
+ event.relatedTarget = fromElement === event.target ? original.toElement : fromElement;
+ }
+
+ // Add which for click: 1 === left; 2 === middle; 3 === right
+ // Note: button is not normalized, so don't use it
+ if ( !event.which && button !== undefined ) {
+ event.which = ( button & 1 ? 1 : ( button & 2 ? 3 : ( button & 4 ? 2 : 0 ) ) );
+ }
+
+ return event;
+ }
+ },
+
+ fix: function( event ) {
+ if ( event[ jQuery.expando ] ) {
+ return event;
+ }
+
+ // Create a writable copy of the event object and normalize some properties
+ var i, prop,
+ originalEvent = event,
+ fixHook = jQuery.event.fixHooks[ event.type ] || {},
+ copy = fixHook.props ? this.props.concat( fixHook.props ) : this.props;
+
+ event = jQuery.Event( originalEvent );
+
+ for ( i = copy.length; i; ) {
+ prop = copy[ --i ];
+ event[ prop ] = originalEvent[ prop ];
+ }
+
+ // Fix target property, if necessary (#1925, IE 6/7/8 & Safari2)
+ if ( !event.target ) {
+ event.target = originalEvent.srcElement || document;
+ }
+
+ // Target should not be a text node (#504, Safari)
+ if ( event.target.nodeType === 3 ) {
+ event.target = event.target.parentNode;
+ }
+
+ // For mouse/key events; add metaKey if it's not there (#3368, IE6/7/8)
+ if ( event.metaKey === undefined ) {
+ event.metaKey = event.ctrlKey;
+ }
+
+ return fixHook.filter? fixHook.filter( event, originalEvent ) : event;
+ },
+
+ special: {
+ ready: {
+ // Make sure the ready event is setup
+ setup: jQuery.bindReady
+ },
+
+ load: {
+ // Prevent triggered image.load events from bubbling to window.load
+ noBubble: true
+ },
+
+ focus: {
+ delegateType: "focusin"
+ },
+ blur: {
+ delegateType: "focusout"
+ },
+
+ beforeunload: {
+ setup: function( data, namespaces, eventHandle ) {
+ // We only want to do this special case on windows
+ if ( jQuery.isWindow( this ) ) {
+ this.onbeforeunload = eventHandle;
+ }
+ },
+
+ teardown: function( namespaces, eventHandle ) {
+ if ( this.onbeforeunload === eventHandle ) {
+ this.onbeforeunload = null;
+ }
+ }
+ }
+ },
+
+ simulate: function( type, elem, event, bubble ) {
+ // Piggyback on a donor event to simulate a different one.
+ // Fake originalEvent to avoid donor's stopPropagation, but if the
+ // simulated event prevents default then we do the same on the donor.
+ var e = jQuery.extend(
+ new jQuery.Event(),
+ event,
+ { type: type,
+ isSimulated: true,
+ originalEvent: {}
+ }
+ );
+ if ( bubble ) {
+ jQuery.event.trigger( e, null, elem );
+ } else {
+ jQuery.event.dispatch.call( elem, e );
+ }
+ if ( e.isDefaultPrevented() ) {
+ event.preventDefault();
+ }
+ }
+};
+
+// Some plugins are using, but it's undocumented/deprecated and will be removed.
+// The 1.7 special event interface should provide all the hooks needed now.
+jQuery.event.handle = jQuery.event.dispatch;
+
+jQuery.removeEvent = document.removeEventListener ?
+ function( elem, type, handle ) {
+ if ( elem.removeEventListener ) {
+ elem.removeEventListener( type, handle, false );
+ }
+ } :
+ function( elem, type, handle ) {
+ if ( elem.detachEvent ) {
+ elem.detachEvent( "on" + type, handle );
+ }
+ };
+
+jQuery.Event = function( src, props ) {
+ // Allow instantiation without the 'new' keyword
+ if ( !(this instanceof jQuery.Event) ) {
+ return new jQuery.Event( src, props );
+ }
+
+ // Event object
+ if ( src && src.type ) {
+ this.originalEvent = src;
+ this.type = src.type;
+
+ // Events bubbling up the document may have been marked as prevented
+ // by a handler lower down the tree; reflect the correct value.
+ this.isDefaultPrevented = ( src.defaultPrevented || src.returnValue === false ||
+ src.getPreventDefault && src.getPreventDefault() ) ? returnTrue : returnFalse;
+
+ // Event type
+ } else {
+ this.type = src;
+ }
+
+ // Put explicitly provided properties onto the event object
+ if ( props ) {
+ jQuery.extend( this, props );
+ }
+
+ // Create a timestamp if incoming event doesn't have one
+ this.timeStamp = src && src.timeStamp || jQuery.now();
+
+ // Mark it as fixed
+ this[ jQuery.expando ] = true;
+};
+
+function returnFalse() {
+ return false;
+}
+function returnTrue() {
+ return true;
+}
+
+// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
+// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
+jQuery.Event.prototype = {
+ preventDefault: function() {
+ this.isDefaultPrevented = returnTrue;
+
+ var e = this.originalEvent;
+ if ( !e ) {
+ return;
+ }
+
+ // if preventDefault exists run it on the original event
+ if ( e.preventDefault ) {
+ e.preventDefault();
+
+ // otherwise set the returnValue property of the original event to false (IE)
+ } else {
+ e.returnValue = false;
+ }
+ },
+ stopPropagation: function() {
+ this.isPropagationStopped = returnTrue;
+
+ var e = this.originalEvent;
+ if ( !e ) {
+ return;
+ }
+ // if stopPropagation exists run it on the original event
+ if ( e.stopPropagation ) {
+ e.stopPropagation();
+ }
+ // otherwise set the cancelBubble property of the original event to true (IE)
+ e.cancelBubble = true;
+ },
+ stopImmediatePropagation: function() {
+ this.isImmediatePropagationStopped = returnTrue;
+ this.stopPropagation();
+ },
+ isDefaultPrevented: returnFalse,
+ isPropagationStopped: returnFalse,
+ isImmediatePropagationStopped: returnFalse
+};
+
+// Create mouseenter/leave events using mouseover/out and event-time checks
+jQuery.each({
+ mouseenter: "mouseover",
+ mouseleave: "mouseout"
+}, function( orig, fix ) {
+ jQuery.event.special[ orig ] = {
+ delegateType: fix,
+ bindType: fix,
+
+ handle: function( event ) {
+ var target = this,
+ related = event.relatedTarget,
+ handleObj = event.handleObj,
+ selector = handleObj.selector,
+ ret;
+
+ // For mousenter/leave call the handler if related is outside the target.
+ // NB: No relatedTarget if the mouse left/entered the browser window
+ if ( !related || (related !== target && !jQuery.contains( target, related )) ) {
+ event.type = handleObj.origType;
+ ret = handleObj.handler.apply( this, arguments );
+ event.type = fix;
+ }
+ return ret;
+ }
+ };
+});
+
+// IE submit delegation
+if ( !jQuery.support.submitBubbles ) {
+
+ jQuery.event.special.submit = {
+ setup: function() {
+ // Only need this for delegated form submit events
+ if ( jQuery.nodeName( this, "form" ) ) {
+ return false;
+ }
+
+ // Lazy-add a submit handler when a descendant form may potentially be submitted
+ jQuery.event.add( this, "click._submit keypress._submit", function( e ) {
+ // Node name check avoids a VML-related crash in IE (#9807)
+ var elem = e.target,
+ form = jQuery.nodeName( elem, "input" ) || jQuery.nodeName( elem, "button" ) ? elem.form : undefined;
+ if ( form && !form._submit_attached ) {
+ jQuery.event.add( form, "submit._submit", function( event ) {
+ event._submit_bubble = true;
+ });
+ form._submit_attached = true;
+ }
+ });
+ // return undefined since we don't need an event listener
+ },
+
+ postDispatch: function( event ) {
+ // If form was submitted by the user, bubble the event up the tree
+ if ( event._submit_bubble ) {
+ delete event._submit_bubble;
+ if ( this.parentNode && !event.isTrigger ) {
+ jQuery.event.simulate( "submit", this.parentNode, event, true );
+ }
+ }
+ },
+
+ teardown: function() {
+ // Only need this for delegated form submit events
+ if ( jQuery.nodeName( this, "form" ) ) {
+ return false;
+ }
+
+ // Remove delegated handlers; cleanData eventually reaps submit handlers attached above
+ jQuery.event.remove( this, "._submit" );
+ }
+ };
+}
+
+// IE change delegation and checkbox/radio fix
+if ( !jQuery.support.changeBubbles ) {
+
+ jQuery.event.special.change = {
+
+ setup: function() {
+
+ if ( rformElems.test( this.nodeName ) ) {
+ // IE doesn't fire change on a check/radio until blur; trigger it on click
+ // after a propertychange. Eat the blur-change in special.change.handle.
+ // This still fires onchange a second time for check/radio after blur.
+ if ( this.type === "checkbox" || this.type === "radio" ) {
+ jQuery.event.add( this, "propertychange._change", function( event ) {
+ if ( event.originalEvent.propertyName === "checked" ) {
+ this._just_changed = true;
+ }
+ });
+ jQuery.event.add( this, "click._change", function( event ) {
+ if ( this._just_changed && !event.isTrigger ) {
+ this._just_changed = false;
+ jQuery.event.simulate( "change", this, event, true );
+ }
+ });
+ }
+ return false;
+ }
+ // Delegated event; lazy-add a change handler on descendant inputs
+ jQuery.event.add( this, "beforeactivate._change", function( e ) {
+ var elem = e.target;
+
+ if ( rformElems.test( elem.nodeName ) && !elem._change_attached ) {
+ jQuery.event.add( elem, "change._change", function( event ) {
+ if ( this.parentNode && !event.isSimulated && !event.isTrigger ) {
+ jQuery.event.simulate( "change", this.parentNode, event, true );
+ }
+ });
+ elem._change_attached = true;
+ }
+ });
+ },
+
+ handle: function( event ) {
+ var elem = event.target;
+
+ // Swallow native change events from checkbox/radio, we already triggered them above
+ if ( this !== elem || event.isSimulated || event.isTrigger || (elem.type !== "radio" && elem.type !== "checkbox") ) {
+ return event.handleObj.handler.apply( this, arguments );
+ }
+ },
+
+ teardown: function() {
+ jQuery.event.remove( this, "._change" );
+
+ return rformElems.test( this.nodeName );
+ }
+ };
+}
+
+// Create "bubbling" focus and blur events
+if ( !jQuery.support.focusinBubbles ) {
+ jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) {
+
+ // Attach a single capturing handler while someone wants focusin/focusout
+ var attaches = 0,
+ handler = function( event ) {
+ jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ), true );
+ };
+
+ jQuery.event.special[ fix ] = {
+ setup: function() {
+ if ( attaches++ === 0 ) {
+ document.addEventListener( orig, handler, true );
+ }
+ },
+ teardown: function() {
+ if ( --attaches === 0 ) {
+ document.removeEventListener( orig, handler, true );
+ }
+ }
+ };
+ });
+}
+
+jQuery.fn.extend({
+
+ on: function( types, selector, data, fn, /*INTERNAL*/ one ) {
+ var origFn, type;
+
+ // Types can be a map of types/handlers
+ if ( typeof types === "object" ) {
+ // ( types-Object, selector, data )
+ if ( typeof selector !== "string" ) { // && selector != null
+ // ( types-Object, data )
+ data = data || selector;
+ selector = undefined;
+ }
+ for ( type in types ) {
+ this.on( type, selector, data, types[ type ], one );
+ }
+ return this;
+ }
+
+ if ( data == null && fn == null ) {
+ // ( types, fn )
+ fn = selector;
+ data = selector = undefined;
+ } else if ( fn == null ) {
+ if ( typeof selector === "string" ) {
+ // ( types, selector, fn )
+ fn = data;
+ data = undefined;
+ } else {
+ // ( types, data, fn )
+ fn = data;
+ data = selector;
+ selector = undefined;
+ }
+ }
+ if ( fn === false ) {
+ fn = returnFalse;
+ } else if ( !fn ) {
+ return this;
+ }
+
+ if ( one === 1 ) {
+ origFn = fn;
+ fn = function( event ) {
+ // Can use an empty set, since event contains the info
+ jQuery().off( event );
+ return origFn.apply( this, arguments );
+ };
+ // Use same guid so caller can remove using origFn
+ fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );
+ }
+ return this.each( function() {
+ jQuery.event.add( this, types, fn, data, selector );
+ });
+ },
+ one: function( types, selector, data, fn ) {
+ return this.on( types, selector, data, fn, 1 );
+ },
+ off: function( types, selector, fn ) {
+ if ( types && types.preventDefault && types.handleObj ) {
+ // ( event ) dispatched jQuery.Event
+ var handleObj = types.handleObj;
+ jQuery( types.delegateTarget ).off(
+ handleObj.namespace ? handleObj.origType + "." + handleObj.namespace : handleObj.origType,
+ handleObj.selector,
+ handleObj.handler
+ );
+ return this;
+ }
+ if ( typeof types === "object" ) {
+ // ( types-object [, selector] )
+ for ( var type in types ) {
+ this.off( type, selector, types[ type ] );
+ }
+ return this;
+ }
+ if ( selector === false || typeof selector === "function" ) {
+ // ( types [, fn] )
+ fn = selector;
+ selector = undefined;
+ }
+ if ( fn === false ) {
+ fn = returnFalse;
+ }
+ return this.each(function() {
+ jQuery.event.remove( this, types, fn, selector );
+ });
+ },
+
+ bind: function( types, data, fn ) {
+ return this.on( types, null, data, fn );
+ },
+ unbind: function( types, fn ) {
+ return this.off( types, null, fn );
+ },
+
+ live: function( types, data, fn ) {
+ jQuery( this.context ).on( types, this.selector, data, fn );
+ return this;
+ },
+ die: function( types, fn ) {
+ jQuery( this.context ).off( types, this.selector || "**", fn );
+ return this;
+ },
+
+ delegate: function( selector, types, data, fn ) {
+ return this.on( types, selector, data, fn );
+ },
+ undelegate: function( selector, types, fn ) {
+ // ( namespace ) or ( selector, types [, fn] )
+ return arguments.length == 1? this.off( selector, "**" ) : this.off( types, selector, fn );
+ },
+
+ trigger: function( type, data ) {
+ return this.each(function() {
+ jQuery.event.trigger( type, data, this );
+ });
+ },
+ triggerHandler: function( type, data ) {
+ if ( this[0] ) {
+ return jQuery.event.trigger( type, data, this[0], true );
+ }
+ },
+
+ toggle: function( fn ) {
+ // Save reference to arguments for access in closure
+ var args = arguments,
+ guid = fn.guid || jQuery.guid++,
+ i = 0,
+ toggler = function( event ) {
+ // Figure out which function to execute
+ var lastToggle = ( jQuery._data( this, "lastToggle" + fn.guid ) || 0 ) % i;
+ jQuery._data( this, "lastToggle" + fn.guid, lastToggle + 1 );
+
+ // Make sure that clicks stop
+ event.preventDefault();
+
+ // and execute the function
+ return args[ lastToggle ].apply( this, arguments ) || false;
+ };
+
+ // link all the functions, so any of them can unbind this click handler
+ toggler.guid = guid;
+ while ( i < args.length ) {
+ args[ i++ ].guid = guid;
+ }
+
+ return this.click( toggler );
+ },
+
+ hover: function( fnOver, fnOut ) {
+ return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
+ }
+});
+
+jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " +
+ "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
+ "change select submit keydown keypress keyup error contextmenu").split(" "), function( i, name ) {
+
+ // Handle event binding
+ jQuery.fn[ name ] = function( data, fn ) {
+ if ( fn == null ) {
+ fn = data;
+ data = null;
+ }
+
+ return arguments.length > 0 ?
+ this.on( name, null, data, fn ) :
+ this.trigger( name );
+ };
+
+ if ( jQuery.attrFn ) {
+ jQuery.attrFn[ name ] = true;
+ }
+
+ if ( rkeyEvent.test( name ) ) {
+ jQuery.event.fixHooks[ name ] = jQuery.event.keyHooks;
+ }
+
+ if ( rmouseEvent.test( name ) ) {
+ jQuery.event.fixHooks[ name ] = jQuery.event.mouseHooks;
+ }
+});
+
+
+
+/*!
+ * Sizzle CSS Selector Engine
+ * Copyright 2011, The Dojo Foundation
+ * Released under the MIT, BSD, and GPL Licenses.
+ * More information: http://sizzlejs.com/
+ */
+(function(){
+
+var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
+ expando = "sizcache" + (Math.random() + '').replace('.', ''),
+ done = 0,
+ toString = Object.prototype.toString,
+ hasDuplicate = false,
+ baseHasDuplicate = true,
+ rBackslash = /\\/g,
+ rReturn = /\r\n/g,
+ rNonWord = /\W/;
+
+// Here we check if the JavaScript engine is using some sort of
+// optimization where it does not always call our comparision
+// function. If that is the case, discard the hasDuplicate value.
+// Thus far that includes Google Chrome.
+[0, 0].sort(function() {
+ baseHasDuplicate = false;
+ return 0;
+});
+
+var Sizzle = function( selector, context, results, seed ) {
+ results = results || [];
+ context = context || document;
+
+ var origContext = context;
+
+ if ( context.nodeType !== 1 && context.nodeType !== 9 ) {
+ return [];
+ }
+
+ if ( !selector || typeof selector !== "string" ) {
+ return results;
+ }
+
+ var m, set, checkSet, extra, ret, cur, pop, i,
+ prune = true,
+ contextXML = Sizzle.isXML( context ),
+ parts = [],
+ soFar = selector;
+
+ // Reset the position of the chunker regexp (start from head)
+ do {
+ chunker.exec( "" );
+ m = chunker.exec( soFar );
+
+ if ( m ) {
+ soFar = m[3];
+
+ parts.push( m[1] );
+
+ if ( m[2] ) {
+ extra = m[3];
+ break;
+ }
+ }
+ } while ( m );
+
+ if ( parts.length > 1 && origPOS.exec( selector ) ) {
+
+ if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
+ set = posProcess( parts[0] + parts[1], context, seed );
+
+ } 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, seed );
+ }
+ }
+
+ } else {
+ // Take a shortcut and set the context if the root selector is an ID
+ // (but not if it'll be faster if the inner selector is an ID)
+ if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML &&
+ Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) {
+
+ ret = Sizzle.find( parts.shift(), context, contextXML );
+ context = ret.expr ?
+ Sizzle.filter( ret.expr, ret.set )[0] :
+ ret.set[0];
+ }
+
+ if ( context ) {
+ 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 ) {
+ 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 ) {
+ Sizzle.error( cur || selector );
+ }
+
+ if ( toString.call(checkSet) === "[object Array]" ) {
+ if ( !prune ) {
+ results.push.apply( results, checkSet );
+
+ } else if ( context && context.nodeType === 1 ) {
+ for ( i = 0; checkSet[i] != null; i++ ) {
+ if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && Sizzle.contains(context, checkSet[i])) ) {
+ results.push( set[i] );
+ }
+ }
+
+ } else {
+ for ( 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.matchesSelector = function( node, expr ) {
+ return Sizzle( expr, null, null, [node] ).length > 0;
+};
+
+Sizzle.find = function( expr, context, isXML ) {
+ var set, i, len, match, type, left;
+
+ if ( !expr ) {
+ return [];
+ }
+
+ for ( i = 0, len = Expr.order.length; i < len; i++ ) {
+ type = Expr.order[i];
+
+ if ( (match = Expr.leftMatch[ type ].exec( expr )) ) {
+ left = match[1];
+ match.splice( 1, 1 );
+
+ if ( left.substr( left.length - 1 ) !== "\\" ) {
+ match[1] = (match[1] || "").replace( rBackslash, "" );
+ set = Expr.find[ type ]( match, context, isXML );
+
+ if ( set != null ) {
+ expr = expr.replace( Expr.match[ type ], "" );
+ break;
+ }
+ }
+ }
+ }
+
+ if ( !set ) {
+ set = typeof context.getElementsByTagName !== "undefined" ?
+ context.getElementsByTagName( "*" ) :
+ [];
+ }
+
+ return { set: set, expr: expr };
+};
+
+Sizzle.filter = function( expr, set, inplace, not ) {
+ var match, anyFound,
+ type, found, item, filter, left,
+ i, pass,
+ old = expr,
+ result = [],
+ curLoop = set,
+ isXMLFilter = set && set[0] && Sizzle.isXML( set[0] );
+
+ while ( expr && set.length ) {
+ for ( type in Expr.filter ) {
+ if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) {
+ filter = Expr.filter[ type ];
+ left = match[1];
+
+ anyFound = false;
+
+ match.splice(1,1);
+
+ if ( left.substr( left.length - 1 ) === "\\" ) {
+ continue;
+ }
+
+ 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 ( i = 0; (item = curLoop[i]) != null; i++ ) {
+ if ( item ) {
+ found = filter( item, match, i, curLoop );
+ 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;
+ }
+ }
+ }
+
+ // Improper expression
+ if ( expr === old ) {
+ if ( anyFound == null ) {
+ Sizzle.error( expr );
+
+ } else {
+ break;
+ }
+ }
+
+ old = expr;
+ }
+
+ return curLoop;
+};
+
+Sizzle.error = function( msg ) {
+ throw new Error( "Syntax error, unrecognized expression: " + msg );
+};
+
+/**
+ * Utility function for retreiving the text value of an array of DOM nodes
+ * @param {Array|Element} elem
+ */
+var getText = Sizzle.getText = function( elem ) {
+ var i, node,
+ nodeType = elem.nodeType,
+ ret = "";
+
+ if ( nodeType ) {
+ if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {
+ // Use textContent || innerText for elements
+ if ( typeof elem.textContent === 'string' ) {
+ return elem.textContent;
+ } else if ( typeof elem.innerText === 'string' ) {
+ // Replace IE's carriage returns
+ return elem.innerText.replace( rReturn, '' );
+ } else {
+ // Traverse it's children
+ for ( elem = elem.firstChild; elem; elem = elem.nextSibling) {
+ ret += getText( elem );
+ }
+ }
+ } else if ( nodeType === 3 || nodeType === 4 ) {
+ return elem.nodeValue;
+ }
+ } else {
+
+ // If no nodeType, this is expected to be an array
+ for ( i = 0; (node = elem[i]); i++ ) {
+ // Do not traverse comment nodes
+ if ( node.nodeType !== 8 ) {
+ ret += getText( node );
+ }
+ }
+ }
+ return ret;
+};
+
+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|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/,
+ TAG: /^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,
+ CHILD: /:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/,
+ POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,
+ PSEUDO: /:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/
+ },
+
+ leftMatch: {},
+
+ attrMap: {
+ "class": "className",
+ "for": "htmlFor"
+ },
+
+ attrHandle: {
+ href: function( elem ) {
+ return elem.getAttribute( "href" );
+ },
+ type: function( elem ) {
+ return elem.getAttribute( "type" );
+ }
+ },
+
+ relative: {
+ "+": function(checkSet, part){
+ var isPartStr = typeof part === "string",
+ isTag = isPartStr && !rNonWord.test( part ),
+ isPartStrNotTag = isPartStr && !isTag;
+
+ if ( isTag ) {
+ part = part.toLowerCase();
+ }
+
+ 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.toLowerCase() === part ?
+ elem || false :
+ elem === part;
+ }
+ }
+
+ if ( isPartStrNotTag ) {
+ Sizzle.filter( part, checkSet, true );
+ }
+ },
+
+ ">": function( checkSet, part ) {
+ var elem,
+ isPartStr = typeof part === "string",
+ i = 0,
+ l = checkSet.length;
+
+ if ( isPartStr && !rNonWord.test( part ) ) {
+ part = part.toLowerCase();
+
+ for ( ; i < l; i++ ) {
+ elem = checkSet[i];
+
+ if ( elem ) {
+ var parent = elem.parentNode;
+ checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false;
+ }
+ }
+
+ } else {
+ for ( ; i < l; i++ ) {
+ 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 nodeCheck,
+ doneName = done++,
+ checkFn = dirCheck;
+
+ if ( typeof part === "string" && !rNonWord.test( part ) ) {
+ part = part.toLowerCase();
+ nodeCheck = part;
+ checkFn = dirNodeCheck;
+ }
+
+ checkFn( "parentNode", part, doneName, checkSet, nodeCheck, isXML );
+ },
+
+ "~": function( checkSet, part, isXML ) {
+ var nodeCheck,
+ doneName = done++,
+ checkFn = dirCheck;
+
+ if ( typeof part === "string" && !rNonWord.test( part ) ) {
+ part = part.toLowerCase();
+ nodeCheck = part;
+ 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]);
+ // Check parentNode to catch when Blackberry 4.6 returns
+ // nodes that are no longer in the document #6963
+ return m && m.parentNode ? [m] : [];
+ }
+ },
+
+ NAME: function( match, context ) {
+ 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 ) {
+ if ( typeof context.getElementsByTagName !== "undefined" ) {
+ return context.getElementsByTagName( match[1] );
+ }
+ }
+ },
+ preFilter: {
+ CLASS: function( match, curLoop, inplace, result, not, isXML ) {
+ match = " " + match[1].replace( rBackslash, "" ) + " ";
+
+ if ( isXML ) {
+ return match;
+ }
+
+ for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {
+ if ( elem ) {
+ if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n\r]/g, " ").indexOf(match) >= 0) ) {
+ if ( !inplace ) {
+ result.push( elem );
+ }
+
+ } else if ( inplace ) {
+ curLoop[i] = false;
+ }
+ }
+ }
+
+ return false;
+ },
+
+ ID: function( match ) {
+ return match[1].replace( rBackslash, "" );
+ },
+
+ TAG: function( match, curLoop ) {
+ return match[1].replace( rBackslash, "" ).toLowerCase();
+ },
+
+ CHILD: function( match ) {
+ if ( match[1] === "nth" ) {
+ if ( !match[2] ) {
+ Sizzle.error( match[0] );
+ }
+
+ match[2] = match[2].replace(/^\+|\s*/g, '');
+
+ // parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'
+ var test = /(-?)(\d*)(?:n([+\-]?\d*))?/.exec(
+ match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" ||
+ !/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);
+
+ // calculate the numbers (first)n+(last) including if they are negative
+ match[2] = (test[1] + (test[2] || 1)) - 0;
+ match[3] = test[3] - 0;
+ }
+ else if ( match[2] ) {
+ Sizzle.error( match[0] );
+ }
+
+ // TODO: Move to normal caching system
+ match[0] = done++;
+
+ return match;
+ },
+
+ ATTR: function( match, curLoop, inplace, result, not, isXML ) {
+ var name = match[1] = match[1].replace( rBackslash, "" );
+
+ if ( !isXML && Expr.attrMap[name] ) {
+ match[1] = Expr.attrMap[name];
+ }
+
+ // Handle if an un-quoted value was used
+ match[4] = ( match[4] || match[5] || "" ).replace( rBackslash, "" );
+
+ if ( match[2] === "~=" ) {
+ match[4] = " " + match[4] + " ";
+ }
+
+ return match;
+ },
+
+ PSEUDO: function( match, curLoop, inplace, result, not ) {
+ if ( match[1] === "not" ) {
+ // If we're dealing with a complex expression, or a simple one
+ 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 ) {
+ // Accessing this property makes selected-by-default
+ // options in Safari work properly
+ if ( elem.parentNode ) {
+ 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 ) {
+ var attr = elem.getAttribute( "type" ), type = elem.type;
+ // IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc)
+ // use getAttribute instead to test this case
+ return elem.nodeName.toLowerCase() === "input" && "text" === type && ( attr === type || attr === null );
+ },
+
+ radio: function( elem ) {
+ return elem.nodeName.toLowerCase() === "input" && "radio" === elem.type;
+ },
+
+ checkbox: function( elem ) {
+ return elem.nodeName.toLowerCase() === "input" && "checkbox" === elem.type;
+ },
+
+ file: function( elem ) {
+ return elem.nodeName.toLowerCase() === "input" && "file" === elem.type;
+ },
+
+ password: function( elem ) {
+ return elem.nodeName.toLowerCase() === "input" && "password" === elem.type;
+ },
+
+ submit: function( elem ) {
+ var name = elem.nodeName.toLowerCase();
+ return (name === "input" || name === "button") && "submit" === elem.type;
+ },
+
+ image: function( elem ) {
+ return elem.nodeName.toLowerCase() === "input" && "image" === elem.type;
+ },
+
+ reset: function( elem ) {
+ var name = elem.nodeName.toLowerCase();
+ return (name === "input" || name === "button") && "reset" === elem.type;
+ },
+
+ button: function( elem ) {
+ var name = elem.nodeName.toLowerCase();
+ return name === "input" && "button" === elem.type || name === "button";
+ },
+
+ input: function( elem ) {
+ return (/input|select|textarea|button/i).test( elem.nodeName );
+ },
+
+ focus: function( elem ) {
+ return elem === elem.ownerDocument.activeElement;
+ }
+ },
+ 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 || getText([ elem ]) || "").indexOf(match[3]) >= 0;
+
+ } else if ( name === "not" ) {
+ var not = match[3];
+
+ for ( var j = 0, l = not.length; j < l; j++ ) {
+ if ( not[j] === elem ) {
+ return false;
+ }
+ }
+
+ return true;
+
+ } else {
+ Sizzle.error( name );
+ }
+ },
+
+ CHILD: function( elem, match ) {
+ var first, last,
+ doneName, parent, cache,
+ count, diff,
+ 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;
+
+ /* falls through */
+ case "last":
+ while ( (node = node.nextSibling) ) {
+ if ( node.nodeType === 1 ) {
+ return false;
+ }
+ }
+
+ return true;
+
+ case "nth":
+ first = match[2];
+ last = match[3];
+
+ if ( first === 1 && last === 0 ) {
+ return true;
+ }
+
+ doneName = match[0];
+ parent = elem.parentNode;
+
+ if ( parent && (parent[ expando ] !== doneName || !elem.nodeIndex) ) {
+ count = 0;
+
+ for ( node = parent.firstChild; node; node = node.nextSibling ) {
+ if ( node.nodeType === 1 ) {
+ node.nodeIndex = ++count;
+ }
+ }
+
+ parent[ expando ] = doneName;
+ }
+
+ 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 && elem.nodeName.toLowerCase() === match;
+ },
+
+ CLASS: function( elem, match ) {
+ return (" " + (elem.className || elem.getAttribute("class")) + " ")
+ .indexOf( match ) > -1;
+ },
+
+ ATTR: function( elem, match ) {
+ var name = match[1],
+ result = Sizzle.attr ?
+ Sizzle.attr( elem, name ) :
+ 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 && Sizzle.attr ?
+ result != null :
+ 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,
+ fescape = function(all, num){
+ return "\\" + (num - 0 + 1);
+ };
+
+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.replace(/\\(\d+)/g, fescape) );
+}
+// Expose origPOS
+// "global" as in regardless of relation to brackets/parens
+Expr.match.globalPOS = origPOS;
+
+var makeArray = function( array, results ) {
+ array = Array.prototype.slice.call( array, 0 );
+
+ if ( results ) {
+ results.push.apply( results, array );
+ return results;
+ }
+
+ return array;
+};
+
+// Perform a simple check to determine if the browser is capable of
+// converting a NodeList to an array using builtin methods.
+// Also verifies that the returned array holds DOM nodes
+// (which is not the case in the Blackberry browser)
+try {
+ Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType;
+
+// Provide a fallback method if it does not work
+} catch( e ) {
+ makeArray = function( array, results ) {
+ var i = 0,
+ ret = results || [];
+
+ if ( toString.call(array) === "[object Array]" ) {
+ Array.prototype.push.apply( ret, array );
+
+ } else {
+ if ( typeof array.length === "number" ) {
+ for ( var l = array.length; i < l; i++ ) {
+ ret.push( array[i] );
+ }
+
+ } else {
+ for ( ; array[i]; i++ ) {
+ ret.push( array[i] );
+ }
+ }
+ }
+
+ return ret;
+ };
+}
+
+var sortOrder, siblingCheck;
+
+if ( document.documentElement.compareDocumentPosition ) {
+ sortOrder = function( a, b ) {
+ if ( a === b ) {
+ hasDuplicate = true;
+ return 0;
+ }
+
+ if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) {
+ return a.compareDocumentPosition ? -1 : 1;
+ }
+
+ return a.compareDocumentPosition(b) & 4 ? -1 : 1;
+ };
+
+} else {
+ sortOrder = function( a, b ) {
+ // The nodes are identical, we can exit early
+ if ( a === b ) {
+ hasDuplicate = true;
+ return 0;
+
+ // Fallback to using sourceIndex (in IE) if it's available on both nodes
+ } else if ( a.sourceIndex && b.sourceIndex ) {
+ return a.sourceIndex - b.sourceIndex;
+ }
+
+ var al, bl,
+ ap = [],
+ bp = [],
+ aup = a.parentNode,
+ bup = b.parentNode,
+ cur = aup;
+
+ // If the nodes are siblings (or identical) we can do a quick check
+ if ( aup === bup ) {
+ return siblingCheck( a, b );
+
+ // If no parents were found then the nodes are disconnected
+ } else if ( !aup ) {
+ return -1;
+
+ } else if ( !bup ) {
+ return 1;
+ }
+
+ // Otherwise they're somewhere else in the tree so we need
+ // to build up a full list of the parentNodes for comparison
+ while ( cur ) {
+ ap.unshift( cur );
+ cur = cur.parentNode;
+ }
+
+ cur = bup;
+
+ while ( cur ) {
+ bp.unshift( cur );
+ cur = cur.parentNode;
+ }
+
+ al = ap.length;
+ bl = bp.length;
+
+ // Start walking down the tree looking for a discrepancy
+ for ( var i = 0; i < al && i < bl; i++ ) {
+ if ( ap[i] !== bp[i] ) {
+ return siblingCheck( ap[i], bp[i] );
+ }
+ }
+
+ // We ended someplace up the tree so do a sibling check
+ return i === al ?
+ siblingCheck( a, bp[i], -1 ) :
+ siblingCheck( ap[i], b, 1 );
+ };
+
+ siblingCheck = function( a, b, ret ) {
+ if ( a === b ) {
+ return ret;
+ }
+
+ var cur = a.nextSibling;
+
+ while ( cur ) {
+ if ( cur === b ) {
+ return -1;
+ }
+
+ cur = cur.nextSibling;
+ }
+
+ return 1;
+ };
+}
+
+// Check to see if the browser returns elements by name when
+// querying by getElementById (and provide a workaround)
+(function(){
+ // We're going to inject a fake input element with a specified name
+ var form = document.createElement("div"),
+ id = "script" + (new Date()).getTime(),
+ root = document.documentElement;
+
+ form.innerHTML = "";
+
+ // Inject it into the root element, check its status, and remove it quickly
+ root.insertBefore( form, root.firstChild );
+
+ // The workaround has to do additional checks after a getElementById
+ // Which slows things down for other browsers (hence the branching)
+ 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 );
+
+ // release memory in IE
+ root = form = null;
+})();
+
+(function(){
+ // Check to see if the browser returns only elements
+ // when doing getElementsByTagName("*")
+
+ // Create a fake element
+ var div = document.createElement("div");
+ div.appendChild( document.createComment("") );
+
+ // Make sure no comments are found
+ if ( div.getElementsByTagName("*").length > 0 ) {
+ Expr.find.TAG = function( match, context ) {
+ var results = context.getElementsByTagName( match[1] );
+
+ // Filter out possible comments
+ 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;
+ };
+ }
+
+ // Check to see if an attribute returns normalized href attributes
+ div.innerHTML = "";
+
+ if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" &&
+ div.firstChild.getAttribute("href") !== "#" ) {
+
+ Expr.attrHandle.href = function( elem ) {
+ return elem.getAttribute( "href", 2 );
+ };
+ }
+
+ // release memory in IE
+ div = null;
+})();
+
+if ( document.querySelectorAll ) {
+ (function(){
+ var oldSizzle = Sizzle,
+ div = document.createElement("div"),
+ id = "__sizzle__";
+
+ div.innerHTML = "";
+
+ // Safari can't handle uppercase or unicode characters when
+ // in quirks mode.
+ if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {
+ return;
+ }
+
+ Sizzle = function( query, context, extra, seed ) {
+ context = context || document;
+
+ // Only use querySelectorAll on non-XML documents
+ // (ID selectors don't work in non-HTML documents)
+ if ( !seed && !Sizzle.isXML(context) ) {
+ // See if we find a selector to speed up
+ var match = /^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec( query );
+
+ if ( match && (context.nodeType === 1 || context.nodeType === 9) ) {
+ // Speed-up: Sizzle("TAG")
+ if ( match[1] ) {
+ return makeArray( context.getElementsByTagName( query ), extra );
+
+ // Speed-up: Sizzle(".CLASS")
+ } else if ( match[2] && Expr.find.CLASS && context.getElementsByClassName ) {
+ return makeArray( context.getElementsByClassName( match[2] ), extra );
+ }
+ }
+
+ if ( context.nodeType === 9 ) {
+ // Speed-up: Sizzle("body")
+ // The body element only exists once, optimize finding it
+ if ( query === "body" && context.body ) {
+ return makeArray( [ context.body ], extra );
+
+ // Speed-up: Sizzle("#ID")
+ } else if ( match && match[3] ) {
+ var elem = context.getElementById( match[3] );
+
+ // Check parentNode to catch when Blackberry 4.6 returns
+ // nodes that are no longer in the document #6963
+ if ( elem && elem.parentNode ) {
+ // Handle the case where IE and Opera return items
+ // by name instead of ID
+ if ( elem.id === match[3] ) {
+ return makeArray( [ elem ], extra );
+ }
+
+ } else {
+ return makeArray( [], extra );
+ }
+ }
+
+ try {
+ return makeArray( context.querySelectorAll(query), extra );
+ } catch(qsaError) {}
+
+ // qSA works strangely on Element-rooted queries
+ // We can work around this by specifying an extra ID on the root
+ // and working up from there (Thanks to Andrew Dupont for the technique)
+ // IE 8 doesn't work on object elements
+ } else if ( context.nodeType === 1 && context.nodeName.toLowerCase() !== "object" ) {
+ var oldContext = context,
+ old = context.getAttribute( "id" ),
+ nid = old || id,
+ hasParent = context.parentNode,
+ relativeHierarchySelector = /^\s*[+~]/.test( query );
+
+ if ( !old ) {
+ context.setAttribute( "id", nid );
+ } else {
+ nid = nid.replace( /'/g, "\\$&" );
+ }
+ if ( relativeHierarchySelector && hasParent ) {
+ context = context.parentNode;
+ }
+
+ try {
+ if ( !relativeHierarchySelector || hasParent ) {
+ return makeArray( context.querySelectorAll( "[id='" + nid + "'] " + query ), extra );
+ }
+
+ } catch(pseudoError) {
+ } finally {
+ if ( !old ) {
+ oldContext.removeAttribute( "id" );
+ }
+ }
+ }
+ }
+
+ return oldSizzle(query, context, extra, seed);
+ };
+
+ for ( var prop in oldSizzle ) {
+ Sizzle[ prop ] = oldSizzle[ prop ];
+ }
+
+ // release memory in IE
+ div = null;
+ })();
+}
+
+(function(){
+ var html = document.documentElement,
+ matches = html.matchesSelector || html.mozMatchesSelector || html.webkitMatchesSelector || html.msMatchesSelector;
+
+ if ( matches ) {
+ // Check to see if it's possible to do matchesSelector
+ // on a disconnected node (IE 9 fails this)
+ var disconnectedMatch = !matches.call( document.createElement( "div" ), "div" ),
+ pseudoWorks = false;
+
+ try {
+ // This should fail with an exception
+ // Gecko does not error, returns false instead
+ matches.call( document.documentElement, "[test!='']:sizzle" );
+
+ } catch( pseudoError ) {
+ pseudoWorks = true;
+ }
+
+ Sizzle.matchesSelector = function( node, expr ) {
+ // Make sure that attribute selectors are quoted
+ expr = expr.replace(/\=\s*([^'"\]]*)\s*\]/g, "='$1']");
+
+ if ( !Sizzle.isXML( node ) ) {
+ try {
+ if ( pseudoWorks || !Expr.match.PSEUDO.test( expr ) && !/!=/.test( expr ) ) {
+ var ret = matches.call( node, expr );
+
+ // IE 9's matchesSelector returns false on disconnected nodes
+ if ( ret || !disconnectedMatch ||
+ // As well, disconnected nodes are said to be in a document
+ // fragment in IE 9, so check for that
+ node.document && node.document.nodeType !== 11 ) {
+ return ret;
+ }
+ }
+ } catch(e) {}
+ }
+
+ return Sizzle(expr, null, null, [node]).length > 0;
+ };
+ }
+})();
+
+(function(){
+ var div = document.createElement("div");
+
+ div.innerHTML = "";
+
+ // Opera can't find a second classname (in 9.6)
+ // Also, make sure that getElementsByClassName actually exists
+ if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) {
+ return;
+ }
+
+ // Safari caches class attributes, doesn't catch changes (in 3.2)
+ 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]);
+ }
+ };
+
+ // release memory in IE
+ div = null;
+})();
+
+function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
+ for ( var i = 0, l = checkSet.length; i < l; i++ ) {
+ var elem = checkSet[i];
+
+ if ( elem ) {
+ var match = false;
+
+ elem = elem[dir];
+
+ while ( elem ) {
+ if ( elem[ expando ] === doneName ) {
+ match = checkSet[elem.sizset];
+ break;
+ }
+
+ if ( elem.nodeType === 1 && !isXML ){
+ elem[ expando ] = doneName;
+ elem.sizset = i;
+ }
+
+ if ( elem.nodeName.toLowerCase() === cur ) {
+ match = elem;
+ break;
+ }
+
+ elem = elem[dir];
+ }
+
+ checkSet[i] = match;
+ }
+ }
+}
+
+function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
+ for ( var i = 0, l = checkSet.length; i < l; i++ ) {
+ var elem = checkSet[i];
+
+ if ( elem ) {
+ var match = false;
+
+ elem = elem[dir];
+
+ while ( elem ) {
+ if ( elem[ expando ] === doneName ) {
+ match = checkSet[elem.sizset];
+ break;
+ }
+
+ if ( elem.nodeType === 1 ) {
+ if ( !isXML ) {
+ elem[ expando ] = 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;
+ }
+ }
+}
+
+if ( document.documentElement.contains ) {
+ Sizzle.contains = function( a, b ) {
+ return a !== b && (a.contains ? a.contains(b) : true);
+ };
+
+} else if ( document.documentElement.compareDocumentPosition ) {
+ Sizzle.contains = function( a, b ) {
+ return !!(a.compareDocumentPosition(b) & 16);
+ };
+
+} else {
+ Sizzle.contains = function() {
+ return false;
+ };
+}
+
+Sizzle.isXML = function( elem ) {
+ // documentElement is verified for cases where it doesn't yet exist
+ // (such as loading iframes in IE - #4833)
+ var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement;
+
+ return documentElement ? documentElement.nodeName !== "HTML" : false;
+};
+
+var posProcess = function( selector, context, seed ) {
+ var match,
+ tmpSet = [],
+ later = "",
+ root = context.nodeType ? [context] : context;
+
+ // Position selectors must be done after the filter
+ // And so must :not(positional) so we move all PSEUDOs to the end
+ 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, seed );
+ }
+
+ return Sizzle.filter( later, tmpSet );
+};
+
+// EXPOSE
+// Override sizzle attribute retrieval
+Sizzle.attr = jQuery.attr;
+Sizzle.selectors.attrMap = {};
+jQuery.find = Sizzle;
+jQuery.expr = Sizzle.selectors;
+jQuery.expr[":"] = jQuery.expr.filters;
+jQuery.unique = Sizzle.uniqueSort;
+jQuery.text = Sizzle.getText;
+jQuery.isXMLDoc = Sizzle.isXML;
+jQuery.contains = Sizzle.contains;
+
+
+})();
+
+
+var runtil = /Until$/,
+ rparentsprev = /^(?:parents|prevUntil|prevAll)/,
+ // Note: This RegExp should be improved, or likely pulled from Sizzle
+ rmultiselector = /,/,
+ isSimple = /^.[^:#\[\.,]*$/,
+ slice = Array.prototype.slice,
+ POS = jQuery.expr.match.globalPOS,
+ // methods guaranteed to produce a unique set when starting from a unique set
+ guaranteedUnique = {
+ children: true,
+ contents: true,
+ next: true,
+ prev: true
+ };
+
+jQuery.fn.extend({
+ find: function( selector ) {
+ var self = this,
+ i, l;
+
+ if ( typeof selector !== "string" ) {
+ return jQuery( selector ).filter(function() {
+ for ( i = 0, l = self.length; i < l; i++ ) {
+ if ( jQuery.contains( self[ i ], this ) ) {
+ return true;
+ }
+ }
+ });
+ }
+
+ var ret = this.pushStack( "", "find", selector ),
+ length, n, r;
+
+ for ( i = 0, l = this.length; i < l; i++ ) {
+ length = ret.length;
+ jQuery.find( selector, this[i], ret );
+
+ if ( i > 0 ) {
+ // Make sure that the results are unique
+ for ( n = length; n < ret.length; n++ ) {
+ for ( r = 0; r < length; r++ ) {
+ if ( ret[r] === ret[n] ) {
+ ret.splice(n--, 1);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ return ret;
+ },
+
+ has: function( target ) {
+ var targets = jQuery( target );
+ return this.filter(function() {
+ for ( var i = 0, l = targets.length; i < l; i++ ) {
+ if ( jQuery.contains( this, targets[i] ) ) {
+ return true;
+ }
+ }
+ });
+ },
+
+ not: function( selector ) {
+ return this.pushStack( winnow(this, selector, false), "not", selector);
+ },
+
+ filter: function( selector ) {
+ return this.pushStack( winnow(this, selector, true), "filter", selector );
+ },
+
+ is: function( selector ) {
+ return !!selector && (
+ typeof selector === "string" ?
+ // If this is a positional selector, check membership in the returned set
+ // so $("p:first").is("p:last") won't return true for a doc with two "p".
+ POS.test( selector ) ?
+ jQuery( selector, this.context ).index( this[0] ) >= 0 :
+ jQuery.filter( selector, this ).length > 0 :
+ this.filter( selector ).length > 0 );
+ },
+
+ closest: function( selectors, context ) {
+ var ret = [], i, l, cur = this[0];
+
+ // Array (deprecated as of jQuery 1.7)
+ if ( jQuery.isArray( selectors ) ) {
+ var level = 1;
+
+ while ( cur && cur.ownerDocument && cur !== context ) {
+ for ( i = 0; i < selectors.length; i++ ) {
+
+ if ( jQuery( cur ).is( selectors[ i ] ) ) {
+ ret.push({ selector: selectors[ i ], elem: cur, level: level });
+ }
+ }
+
+ cur = cur.parentNode;
+ level++;
+ }
+
+ return ret;
+ }
+
+ // String
+ var pos = POS.test( selectors ) || typeof selectors !== "string" ?
+ jQuery( selectors, context || this.context ) :
+ 0;
+
+ for ( i = 0, l = this.length; i < l; i++ ) {
+ cur = this[i];
+
+ while ( cur ) {
+ if ( pos ? pos.index(cur) > -1 : jQuery.find.matchesSelector(cur, selectors) ) {
+ ret.push( cur );
+ break;
+
+ } else {
+ cur = cur.parentNode;
+ if ( !cur || !cur.ownerDocument || cur === context || cur.nodeType === 11 ) {
+ break;
+ }
+ }
+ }
+ }
+
+ ret = ret.length > 1 ? jQuery.unique( ret ) : ret;
+
+ return this.pushStack( ret, "closest", selectors );
+ },
+
+ // Determine the position of an element within
+ // the matched set of elements
+ index: function( elem ) {
+
+ // No argument, return index in parent
+ if ( !elem ) {
+ return ( this[0] && this[0].parentNode ) ? this.prevAll().length : -1;
+ }
+
+ // index in selector
+ if ( typeof elem === "string" ) {
+ return jQuery.inArray( this[0], jQuery( elem ) );
+ }
+
+ // Locate the position of the desired element
+ return jQuery.inArray(
+ // If it receives a jQuery object, the first element is used
+ elem.jquery ? elem[0] : elem, this );
+ },
+
+ add: function( selector, context ) {
+ var set = typeof selector === "string" ?
+ jQuery( selector, context ) :
+ jQuery.makeArray( selector && selector.nodeType ? [ selector ] : selector ),
+ all = jQuery.merge( this.get(), set );
+
+ return this.pushStack( isDisconnected( set[0] ) || isDisconnected( all[0] ) ?
+ all :
+ jQuery.unique( all ) );
+ },
+
+ andSelf: function() {
+ return this.add( this.prevObject );
+ }
+});
+
+// A painfully simple check to see if an element is disconnected
+// from a document (should be improved, where feasible).
+function isDisconnected( node ) {
+ return !node || !node.parentNode || node.parentNode.nodeType === 11;
+}
+
+jQuery.each({
+ parent: function( elem ) {
+ var parent = elem.parentNode;
+ return parent && parent.nodeType !== 11 ? parent : null;
+ },
+ parents: function( elem ) {
+ return jQuery.dir( elem, "parentNode" );
+ },
+ parentsUntil: function( elem, i, until ) {
+ return jQuery.dir( elem, "parentNode", until );
+ },
+ next: function( elem ) {
+ return jQuery.nth( elem, 2, "nextSibling" );
+ },
+ prev: function( elem ) {
+ return jQuery.nth( elem, 2, "previousSibling" );
+ },
+ nextAll: function( elem ) {
+ return jQuery.dir( elem, "nextSibling" );
+ },
+ prevAll: function( elem ) {
+ return jQuery.dir( elem, "previousSibling" );
+ },
+ nextUntil: function( elem, i, until ) {
+ return jQuery.dir( elem, "nextSibling", until );
+ },
+ prevUntil: function( elem, i, until ) {
+ return jQuery.dir( elem, "previousSibling", until );
+ },
+ siblings: function( elem ) {
+ return jQuery.sibling( ( elem.parentNode || {} ).firstChild, elem );
+ },
+ children: function( elem ) {
+ return jQuery.sibling( elem.firstChild );
+ },
+ contents: function( elem ) {
+ return jQuery.nodeName( elem, "iframe" ) ?
+ elem.contentDocument || elem.contentWindow.document :
+ jQuery.makeArray( elem.childNodes );
+ }
+}, function( name, fn ) {
+ jQuery.fn[ name ] = function( until, selector ) {
+ var ret = jQuery.map( this, fn, until );
+
+ if ( !runtil.test( name ) ) {
+ selector = until;
+ }
+
+ if ( selector && typeof selector === "string" ) {
+ ret = jQuery.filter( selector, ret );
+ }
+
+ ret = this.length > 1 && !guaranteedUnique[ name ] ? jQuery.unique( ret ) : ret;
+
+ if ( (this.length > 1 || rmultiselector.test( selector )) && rparentsprev.test( name ) ) {
+ ret = ret.reverse();
+ }
+
+ return this.pushStack( ret, name, slice.call( arguments ).join(",") );
+ };
+});
+
+jQuery.extend({
+ filter: function( expr, elems, not ) {
+ if ( not ) {
+ expr = ":not(" + expr + ")";
+ }
+
+ return elems.length === 1 ?
+ jQuery.find.matchesSelector(elems[0], expr) ? [ elems[0] ] : [] :
+ jQuery.find.matches(expr, elems);
+ },
+
+ dir: function( elem, dir, until ) {
+ var matched = [],
+ cur = elem[ dir ];
+
+ while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) {
+ if ( cur.nodeType === 1 ) {
+ matched.push( cur );
+ }
+ cur = cur[dir];
+ }
+ return matched;
+ },
+
+ nth: function( cur, result, dir, elem ) {
+ result = result || 1;
+ var num = 0;
+
+ for ( ; cur; cur = cur[dir] ) {
+ if ( cur.nodeType === 1 && ++num === result ) {
+ break;
+ }
+ }
+
+ return cur;
+ },
+
+ sibling: function( n, elem ) {
+ var r = [];
+
+ for ( ; n; n = n.nextSibling ) {
+ if ( n.nodeType === 1 && n !== elem ) {
+ r.push( n );
+ }
+ }
+
+ return r;
+ }
+});
+
+// Implement the identical functionality for filter and not
+function winnow( elements, qualifier, keep ) {
+
+ // Can't pass null or undefined to indexOf in Firefox 4
+ // Set to 0 to skip string check
+ qualifier = qualifier || 0;
+
+ if ( jQuery.isFunction( qualifier ) ) {
+ return jQuery.grep(elements, function( elem, i ) {
+ var retVal = !!qualifier.call( elem, i, elem );
+ return retVal === keep;
+ });
+
+ } else if ( qualifier.nodeType ) {
+ return jQuery.grep(elements, function( elem, i ) {
+ return ( elem === qualifier ) === keep;
+ });
+
+ } else if ( typeof qualifier === "string" ) {
+ var filtered = jQuery.grep(elements, function( elem ) {
+ return elem.nodeType === 1;
+ });
+
+ if ( isSimple.test( qualifier ) ) {
+ return jQuery.filter(qualifier, filtered, !keep);
+ } else {
+ qualifier = jQuery.filter( qualifier, filtered );
+ }
+ }
+
+ return jQuery.grep(elements, function( elem, i ) {
+ return ( jQuery.inArray( elem, qualifier ) >= 0 ) === keep;
+ });
+}
+
+
+
+
+function createSafeFragment( document ) {
+ var list = nodeNames.split( "|" ),
+ safeFrag = document.createDocumentFragment();
+
+ if ( safeFrag.createElement ) {
+ while ( list.length ) {
+ safeFrag.createElement(
+ list.pop()
+ );
+ }
+ }
+ return safeFrag;
+}
+
+var nodeNames = "abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|" +
+ "header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",
+ rinlinejQuery = / jQuery\d+="(?:\d+|null)"/g,
+ rleadingWhitespace = /^\s+/,
+ rxhtmlTag = /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,
+ rtagName = /<([\w:]+)/,
+ rtbody = /]", "i"),
+ // checked="checked" or checked
+ rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i,
+ rscriptType = /\/(java|ecma)script/i,
+ rcleanScript = /^\s*", "" ],
+ legend: [ 1, "" ],
+ thead: [ 1, "