Commit cb1f7eea authored by scottjehl's avatar scottjehl

Merge remote branch 'upstream/master'

parents 73d060b5 265cf0ef
......@@ -7,11 +7,8 @@ BUILD_DIR = build
PREFIX = .
DIST_DIR = ${PREFIX}/dist
RHINO ?= java -jar ${BUILD_DIR}/js.jar
CLOSURE_COMPILER = ${BUILD_DIR}/google-compiler-20100917.jar
MINJAR ?= java -jar ${CLOSURE_COMPILER}
JS_ENGINE ?= `which node nodejs`
COMPILER = ${JS_ENGINE} ${BUILD_DIR}/uglify.js --unsafe
BASE_FILES = ${SRC_DIR}/core.js\
${SRC_DIR}/support.js\
......@@ -93,17 +90,13 @@ ${SRC_DIR}/selector.js: ${SIZZLE_DIR}/sizzle.js
lint: ${JQ}
@@echo "Checking jQuery against JSLint..."
@@${RHINO} build/jslint-check.js
@@${JS_ENGINE} build/jslint-check.js
min: ${JQ_MIN}
${JQ_MIN}: ${JQ}
@@echo "Building" ${JQ_MIN}
@@head -15 ${JQ} > ${JQ_MIN}
@@${MINJAR} --js ${JQ} --warning_level QUIET --js_output_file ${JQ_MIN}.tmp
@@cat ${JQ_MIN}.tmp >> ${JQ_MIN}
@@rm -f ${JQ_MIN}.tmp
@@${COMPILER} ${JQ} > ${JQ_MIN}
clean:
@@echo "Removing Distribution directory:" ${DIST_DIR}
......
[jQuery](http://jquery.com/) - New Wave Javascript
================================
==================================================
What you need to build your own jQuery
---------------------------------------
* Make sure that you have Java installed (if you want to build a minified version of jQuery).
If not, [go to this page](http://java.sun.com/javase/downloads/index.jsp) and download "Java Runtime Environment (JRE) 5.0"
--------------------------------------
Build Options
--------------
In order to build jQuery, you need to have GNU make 3.8 or later, Node.js 0.2 or later, and git 1.7 or later.
(Earlier versions might work OK, but are not tested.)
You now have **three** options for building jQuery:
Windows users have two options:
* **`make`**: If you have access to common UNIX commands (like `make`, `mkdir`, `rm`, `cat`, and `echo`) then simply type `make` to build all the components.
1. Install [msysgit](https://code.google.com/p/msysgit/) (Full installer for official Git),
[GNU make for Windows](http://gnuwin32.sourceforge.net/packages/make.htm), and a
[binary version of Node.js](http://node-js.prcn.co.cc/). Make sure all three packages are installed to the same
location (by default, this is C:\Program Files\Git).
2. Install [Cygwin](http://cygwin.com/) (make sure you install the git, make, and which packages), then either follow
the [Node.js build instructions](https://github.com/ry/node/wiki/Building-node.js-on-Cygwin-%28Windows%29) or install
the [binary version of Node.js](http://node-js.prcn.co.cc/).
* **`rake`**: If you have Ruby Rake installed (on either Windows or UNIX/Linux), you can simply type `rake` to build all the components.
Mac OS users should install Xcode (comes on your Mac OS install DVD, or downloadable from
[Apple's Xcode site](http://developer.apple.com/technologies/xcode.html)) and
[http://mxcl.github.com/homebrew/](Homebrew). Once Homebrew is installed, run `brew install git` to install git,
and `brew install node` to install Node.js.
* **`ant`**: If you have Ant installed (or are on Windows and don't have access to make). You can download Ant from here: [http://ant.apache.org/bindownload.cgi].
Linux/BSD users should use their appropriate package managers to install make, git, and node, or build from source
if you swing that way. Easy-peasy.
How to build your own jQuery
-----------------------------
*Note: If you are using either `rake` or `ant`, substitute your chosen method in place of `make` in the examples below. They work identically for all intents and purposes. Quick reference is also available for `rake` by typing `rake -T` in the `jquery` directory.*
In the main directory of the distribution (the one that this file is in), type
the following to make all versions of jQuery:
make
*Here are the individual items that are buildable from the Makefile:*
make init
Pull in all the external dependencies (QUnit, Sizzle) for the project.
make jquery
The standard, uncompressed, jQuery code.
Makes: `./dist/jquery.js`
make min
A compressed version of jQuery (made the Closure Compiler).
Makes: `./dist/jquery.min.js`
How to build your own jQuery
----------------------------
make lint
First, clone a copy of the main jQuery git repo by running `git clone git://github.com/jquery/jquery.git`.
Tests a build of jQuery against JSLint, looking for potential errors or bits of confusing code.
Then, to get a complete, minified, jslinted version of jQuery, simply `cd` to the `jquery` directory and type
`make`. If you don't have Node installed and/or want to make a basic, uncompressed, unlinted version of jQuery, use
`make jquery` instead of `make`.
make selector
The built version of jQuery will be put in the `dist/` subdirectory.
Builds the selector library for jQuery from Sizzle.
Makes: `./src/selector.js`
To remove all built files, run `make clean`.
Finally, you can remove all the built files using the command:
make clean
Building to a different directory
----------------------------------
---------------------------------
If you want to build jQuery to a directory that is different from the default location, you can...
If you want to build jQuery to a directory that is different from the default location, you can specify the PREFIX
directory: `make PREFIX=/home/jquery/test/ [command]`
**Make only:** Specify the PREFIX directory, for example:
make PREFIX=/home/john/test/ [command]
With this example, the output files would be contained in `/home/john/test/dist/`
With this example, the output files would end up in `/home/jquery/test/dist/`.
**Rake only:** Define the DIST_DIR directory, for example:
rake DIST_DIR=/home/john/test/ [command]
With this example, the output files would be contained in `/home/john/test/`
Troubleshooting
---------------
*In both examples, `[command]` is optional.*
Sometimes, the various git repositories get into an inconsistent state where builds don't complete properly
(usually this results in the jquery.js or jquery.min.js being 0 bytes). If this happens, run `make clean`, then
run `make` again.
**Ant only:** You cannot currently build to another directory when using Ant.
Questions?
----------
If you have any questions, please feel free to ask them on the Developing jQuery Core
forum, which can be found here:
[http://forum.jquery.com/developing-jquery-core](http://forum.jquery.com/developing-jquery-core)
If you have any questions, please feel free to ask on the
[Developing jQuery Core forum](http://forum.jquery.com/developing-jquery-core) or in #jquery on irc.freenode.net.
prefix = File.dirname( __FILE__ )
# Directory variables
src_dir = File.join( prefix, 'src' )
build_dir = File.join( prefix, 'build' )
test_dir = File.join( prefix, 'test' )
# A different destination directory can be set by
# setting DIST_DIR before calling rake
dist_dir = ENV['DIST_DIR'] || File.join( prefix, 'dist' )
base_files = %w{
intro
core
support
data
queue
attributes
event
selector
traversing
manipulation
css
ajax
ajax/jsonp
ajax/script
ajax/xhr
effects
offset
dimensions
outro
}.map { |js| File.join( src_dir, "#{js}.js" ) }
# Sizzle, QUnit and jQuery files/dirs
sizzle_dir = File.join( src_dir, "sizzle" )
sizzle = File.join( sizzle_dir, "sizzle.js" )
selector = File.join( src_dir, "selector.js" )
qunit_dir = File.join( test_dir, "qunit" )
qunit = File.join( qunit_dir, "qunit", "qunit.js" )
jq = File.join( dist_dir, "jquery.js" )
jq_min = File.join( dist_dir, "jquery.min.js" )
# General Variables
date = `git log -1`[/^Date:\s+(.+)$/, 1]
version = File.read( File.join( prefix, 'version.txt' ) ).strip
# Build tools
rhino = "java -jar #{build_dir}/js.jar"
minfier = "java -jar #{build_dir}/google-compiler-20100917.jar"
# Turn off output other than needed from `sh` and file commands
verbose(false)
# Tasks
task :default => "all"
desc "Builds jQuery; Tests with JSLint; Minifies jQuery"
task :all => [:jquery, :lint, :min] do
puts "jQuery build complete."
end
desc "Builds jQuery: jquery.js (Default task)"
task :jquery => [:selector, jq]
desc "Builds a minified version of jQuery: jquery.min.js"
task :min => jq_min
task :init => [sizzle, qunit] do
sizzle_git = File.join(sizzle_dir, '.git')
qunit_git = File.join(qunit_dir, '.git')
puts "Updating SizzleJS with latest..."
sh "git --git-dir=#{sizzle_git} pull -q origin master"
puts "Updating QUnit with latest..."
sh "git --git-dir=#{qunit_git} pull -q origin master"
end
desc "Removes dist folder, selector.js, and Sizzle/QUnit"
task :clean do
puts "Removing Distribution directory: #{dist_dir}..."
rm_rf dist_dir
puts "Removing built copy of Sizzle..."
rm_rf selector
puts "Removing cloned directories..."
rm_rf qunit_dir
rm_rf sizzle_dir
end
desc "Rebuilds selector.js from SizzleJS"
task :selector => [:init, selector]
desc "Tests built jquery.js against JSLint"
task :lint => jq do
puts "Checking jQuery against JSLint..."
sh "#{rhino} " + File.join(build_dir, 'jslint-check.js')
end
# File and Directory Dependencies
directory dist_dir
file jq => [dist_dir, base_files].flatten do
puts "Building jquery.js..."
File.open(jq, 'w') do |f|
f.write cat(base_files).
gsub(/@DATE/, date).
gsub(/@VERSION/, version).
gsub(/.function..jQuery...\{/, '').
gsub(/\}...jQuery..;/, '')
end
end
file jq_min => jq do
puts "Building jquery.min.js..."
sh "#{minfier} --js #{jq} --warning_level QUIET --js_output_file #{jq_min}"
min = File.read( jq_min )
# Equivilent of "head"
File.open(jq_min, 'w') do |f|
f.write File.readlines(jq)[0..14].join()
f.write min
end
end
file selector => [sizzle, :init] do
puts "Building selector code from Sizzle..."
File.open(selector, 'w') do |f|
f.write File.read(sizzle).gsub(
/^.+EXPOSE$\n/,
'\0' + File.read( File.join( src_dir, 'sizzle-jquery.js' ))
).gsub(
/^window.Sizzle.+$\n/, ''
)
end
end
file sizzle do
puts "Retrieving SizzleJS from Github..."
sh "git clone git://github.com/jeresig/sizzle.git #{sizzle_dir}"
end
file qunit do
puts "Retrieving QUnit from Github..."
sh "git clone git://github.com/jquery/qunit.git #{qunit_dir}"
end
def cat( files )
files.map do |file|
File.read(file)
end.join('')
end
<project name="jQuery" default="all" basedir=".">
<loadfile property="version" srcfile="version.txt" />
<property name="PREFIX" value="." />
<property description="Folder for jquery and min target" name="dist" value="${PREFIX}/dist" />
<property name="JQ" value="${dist}/jquery.js" />
<property name="JQ_MIN" value="${dist}/jquery.min.js" />
<loadfile property="sizzle-exports" srcfile="src/sizzle-jquery.js" />
<available property="qunit" file="test/qunit" />
<available property="sizzle" file="src/sizzle" />
<target name="all" depends="jquery,lint,min" />
<target name="qunit-clone" unless="qunit">
<exec executable="git" outputproperty="git-qunit" >
<arg line="clone git://github.com/jquery/qunit.git test/qunit" />
</exec>
<echo message="git clone qunit: ${git-qunit}" />
</target>
<target name="qunit-pull" if="qunit">
<exec executable="git" outputproperty="git-qunit" dir="test/qunit" >
<arg line="pull origin master" />
</exec>
<echo message="git pull sizzle: ${git-qunit}" />
</target>
<target name="sizzle-clone" unless="sizzle">
<exec executable="git" outputproperty="git-sizzle" >
<arg line="clone git://github.com/jeresig/sizzle.git src/sizzle" />
</exec>
<echo message="git clone sizzle: ${git-sizzle}" />
</target>
<target name="sizzle-pull" if="sizzle">
<exec executable="git" outputproperty="git-sizzle" dir="src/sizzle" >
<arg line="pull origin master" />
</exec>
<echo message="git pull sizzle: ${git-sizzle}" />
</target>
<target name="init" depends="qunit-clone,qunit-pull,sizzle-clone,sizzle-pull" />
<target name="selector" depends="init" description="Builds the selector library for jQuery from Sizzle.">
<copy file="src/sizzle/sizzle.js" tofile="src/selector.js" overwrite="true" />
<replaceregexp match="// EXPOSE(.*)&#10;" replace="// EXPOSE\1&#10;${sizzle-exports}" file="src/selector.js" />
<replaceregexp match="window.Sizzle(.*)&#10;" replace="" file="src/selector.js" />
</target>
<target name="jquery" depends="init,selector" description="Main jquery build, concatenates source files and replaces @VERSION">
<echo message="Building ${JQ}" />
<mkdir dir="${dist}" />
<concat destfile="${JQ}">
<fileset file="src/intro.js" />
<fileset file="src/core.js" />
<fileset file="src/support.js" />
<fileset file="src/data.js" />
<fileset file="src/queue.js" />
<fileset file="src/attributes.js" />
<fileset file="src/event.js" />
<fileset file="src/selector.js" />
<fileset file="src/traversing.js" />
<fileset file="src/manipulation.js" />
<fileset file="src/css.js" />
<fileset file="src/ajax.js" />
<fileset file="src/ajax/jsonp.js" />
<fileset file="src/ajax/script.js" />
<fileset file="src/ajax/xhr.js" />
<fileset file="src/effects.js" />
<fileset file="src/offset.js" />
<fileset file="src/dimensions.js" />
<fileset file="src/outro.js" />
</concat>
<replaceregexp match="@VERSION" replace="${version}" flags="g" byline="true" file="${JQ}" />
<exec executable="git" outputproperty="date">
<arg line="log -1 --pretty=format:%ad" />
</exec>
<replaceregexp match="(\(\s*function\s*\(\s*jQuery\s*\)\s*\{)|(\}\s*\)\s*\(\s*jQuery\s*\)\s*;)" flags="g" replace="" file="${JQ}" />
<replaceregexp match="@DATE" replace="${date}" file="${JQ}" />
<echo message="${JQ} built." />
</target>
<target name="lint" depends="jquery" description="Check jQuery against JSLint">
<exec executable="java">
<arg line="-jar build/js.jar build/jslint-check.js" />
</exec>
</target>
<target name="min" depends="jquery" description="Remove all comments and whitespace, no compression, great in combination with GZip">
<echo message="Building ${JQ_MIN}" />
<apply executable="java" parallel="false" verbose="true" dest="${dist}">
<fileset dir="${dist}">
<include name="jquery.js" />
</fileset>
<arg line="-jar" />
<arg path="build/google-compiler-20100917.jar" />
<arg value="--warning_level" />
<arg value="QUIET" />
<arg value="--js_output_file" />
<targetfile />
<arg value="--js" />
<mapper type="glob" from="jquery.js" to="tmpmin" />
</apply>
<concat destfile="${JQ_MIN}">
<filelist files="${JQ}, ${dist}/tmpmin" />
<filterchain>
<headfilter lines="15" />
</filterchain>
</concat>
<concat destfile="${JQ_MIN}" append="yes">
<filelist files="${dist}/tmpmin" />
</concat>
<delete file="${dist}/tmpmin" />
<echo message="${JQ_MIN} built." />
</target>
<target name="clean">
<delete dir="${dist}" />
<delete file="src/selector.js" />
<delete dir="test/qunit" />
<delete dir="src/sizzle" />
</target>
<target name="openAjaxMetadata">
<property name="target" value="openAjaxMetadata-jquery-${version}.xml" />
<delete file="${dist}/jquery-*.xml" />
<get src="http://www.exfer.net/jquery/createjQueryXMLDocs.py?version=1.3" dest="${target}" />
<xslt includes="${target}" excludes="build.xml" destdir="./dist" style="build/style.xsl" extension=".xml" />
<delete file="${target}" />
</target>
</project>
load("build/jslint.js");
var src = readFile("dist/jquery.js");
var JSLINT = require("./lib/jslint").JSLINT,
print = require("sys").print,
src = require("fs").readFileSync("dist/jquery.js", "utf8");
JSLINT(src, { evil: true, forin: true, maxerr: 100 });
......@@ -29,8 +29,8 @@ for ( var i = 0; i < e.length; i++ ) {
}
if ( found > 0 ) {
print( "\n" + found + " Error(s) found." );
print( "\n" + found + " Error(s) found.\n" );
} else {
print( "JSLint check passed." );
print( "JSLint check passed.\n" );
}
......@@ -5495,6 +5495,10 @@ loop: for (;;) {
itself.edition = '2010-02-20';
if (typeof exports !== "undefined") {
exports.JSLINT = itself;
}
return itself;
}());
\ No newline at end of file
}());
/***********************************************************************
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
<mihai.bazon@gmail.com>
http://mihai.bazon.net/blog
Distributed under the BSD license:
Copyright 2010 (c) Mihai Bazon <mihai.bazon@gmail.com>
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",
"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",
"debugger",
"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(" \n\r\t"));
var PUNC_BEFORE_EXPRESSION = array_to_hash(characters("[{}(,.;:"));
var PUNC_CHARS = array_to_hash(characters("[]{}(),;:"));
var REGEXP_MODIFIERS = array_to_hash(characters("gmsiy"));
/* -----[ Tokenizer ]----- */
function is_alphanumeric_char(ch) {
ch = ch.charCodeAt(0);
return (ch >= 48 && ch <= 57) ||
(ch >= 65 && ch <= 90) ||
(ch >= 97 && ch <= 122);
};
function is_identifier_char(ch) {
return is_alphanumeric_char(ch) || ch == "$" || ch == "_";
};
function is_digit(ch) {
ch = ch.charCodeAt(0);
return ch >= 48 && ch <= 57;
};
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;
this.col = col;
this.pos = pos;
try {
({})();
} catch(ex) {
this.stack = ex.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, skip_comments) {
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
};
function peek() { return S.text.charAt(S.pos); };
function next(signal_eof) {
var ch = S.text.charAt(S.pos++);
if (signal_eof && !ch)
throw EX_EOF;
if (ch == "\n") {
S.newline_before = true;
++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) {
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,
nlb : S.newline_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)
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() {
var ch = next(true);
switch (ch) {
case "n" : return "\n";
case "r" : return "\r";
case "t" : return "\t";
case "b" : return "\b";
case "v" : return "\v";
case "f" : return "\f";
case "0" : return "\0";
case "x" : return String.fromCharCode(hex_bytes(2));
case "u" : return String.fromCharCode(hex_bytes(4));
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 == "\\") ch = read_escaped_char();
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);
};
function read_multiline_comment() {
next();
return with_eof_error("Unterminated multiline comment", function(){
var i = find("*/", true),
text = S.text.substring(S.pos, i),
tok = token("comment2", text);
S.pos = i + 2;
S.line += text.split("\n").length - 1;
S.newline_before = text.indexOf("\n") >= 0;
return tok;
});
};
function read_regexp() {
return with_eof_error("Unterminated regular expression", function(){
var prev_backslash = false, regexp = "", 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_while(function(ch){
return HOP(REGEXP_MODIFIERS, ch);
});
return token("regexp", [ regexp, mods ]);
});
};
function read_operator(prefix) {
function grow(op) {
var bigger = op + peek();
if (HOP(OPERATORS, bigger)) {
next();
return grow(bigger);
} else {
return op;
}
};
return token("operator", grow(prefix || next()));
};
var handle_slash = skip_comments ? function() {
next();
var regex_allowed = S.regex_allowed;
switch (peek()) {
case "/": read_line_comment(); S.regex_allowed = regex_allowed; return next_token();
case "*": read_multiline_comment(); S.regex_allowed = regex_allowed; return next_token();
}
return S.regex_allowed ? read_regexp() : read_operator("/");
} : function() {
next();
switch (peek()) {
case "/": return read_line_comment();
case "*": return read_multiline_comment();
}
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_while(is_identifier_char);
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)
return read_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 (is_identifier_char(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, strict_mode, embed_tokens) {
var S = {
input: tokenizer($TEXT, true),
token: null,
prev: null,
peeked: null,
in_function: 0,
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();
}
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 !strict_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 new NodeWithToken(str, start, end);
};
var statement = embed_tokens ? function() {
var start = S.token;
var stmt = $statement();
stmt[0] = add_tokens(stmt[0], start, prev());
return stmt;
} : $statement;
function $statement() {
if (is("operator", "/")) {
S.peeked = null;
S.token = S.input(true); // force regexp
}
switch (S.token.type) {
case "num":
case "string":
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":
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 (strict_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 = 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 has_var = is("keyword", "var");
if (has_var)
next();
if (is("name") && is_token(peek(), "operator", "in")) {
// for (i in foo)
var name = S.token.value;
next(); next();
var obj = expression();
expect(")");
return as("for-in", has_var, name, obj, in_loop(statement));
} else {
// classic for
var init = is("punc", ";") ? null : has_var ? var_() : expression();
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 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_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() {
var a = [];
for (;;) {
if (!is("name"))
unexpected();
var name = S.token.value;
next();
if (is("operator", "=")) {
next();
a.push([ name, expression(false) ]);
} else {
a.push([ name ]);
}
if (!is("punc", ","))
break;
next();
}
return a;
};
function var_() {
return as("var", vardefs());
};
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);
};
function expr_atom(allow_calls) {
if (is("operator", "new")) {
next();
return new_();
}
if (is("operator") && HOP(UNARY_PREFIX, S.token.value)) {
return make_unary("unary-prefix",
prog1(S.token.value, next),
expr_atom(allow_calls));
}
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) {
var first = true, a = [];
while (!is("punc", closing)) {
if (first) first = false; else expect(",");
if (allow_trailing_comma && is("punc", closing))
break;
a.push(expression(false));
}
next();
return a;
};
function array_() {
return as("array", expr_list("]", !strict_mode));
};
function object_() {
var first = true, a = [];
while (!is("punc", "}")) {
if (first) first = false; else expect(",");
if (!strict_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);
}
if (allow_calls && is("operator") && HOP(UNARY_POSTFIX, S.token.value)) {
return prog1(curry(make_unary, "unary-postfix", S.token.value, expr),
next);
}
return expr;
};
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) {
var op = is("operator") ? S.token.value : null;
var prec = op != null ? PRECEDENCE[op] : null;
if (prec != null && prec > min_prec) {
next();
var right = expr_op(expr_atom(true), prec);
return expr_op(as("binary", op, left, right), min_prec);
}
return left;
};
function expr_ops() {
return expr_op(expr_atom(true), 0);
};
function maybe_conditional() {
var expr = expr_ops();
if (is("operator", "?")) {
next();
var yes = expression(false);
expect(":");
return as("conditional", expr, yes, expression(false));
}
return expr;
};
function is_assignable(expr) {
switch (expr[0]) {
case "dot":
case "sub":
return true;
case "name":
return expr[1] != "this";
}
};
function maybe_assign() {
var left = maybe_conditional(), val = S.token.value;
if (is("operator") && HOP(ASSIGNMENT, val)) {
if (is_assignable(left)) {
next();
return as("assign", ASSIGNMENT[val], left, maybe_assign());
}
croak("Invalid assignment");
}
return left;
};
function expression(commas) {
if (arguments.length == 0)
commas = true;
var expr = maybe_assign();
if (commas && is("punc", ",")) {
next();
return as("seq", expr, expression());
}
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 == null ? 0 : start);
};
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);
};
/* -----[ 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;
/***********************************************************************
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, include_toplevel) -- mangles the
variable/function names in the AST. Returns an AST. Pass true
as second argument to mangle toplevel names too.
- ast_squeeze(ast) -- employs various optimizations to make the
final generated code even smaller. Returns an AST.
- gen_code(ast, beautify) -- 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
<mihai.bazon@gmail.com>
http://mihai.bazon.net/blog
Distributed under the BSD license:
Copyright 2010 (c) Mihai Bazon <mihai.bazon@gmail.com>
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(ast) {
function _vardefs(defs) {
return MAP(defs, function(def){
var a = [ def[0] ];
if (def.length > 1)
a[1] = walk(def[1]);
return a;
});
};
var walkers = {
"string": function(str) {
return [ "string", str ];
},
"num": function(num) {
return [ "num", num ];
},
"name": function(name) {
return [ "name", name ];
},
"toplevel": function(statements) {
return [ "toplevel", MAP(statements, walk) ];
},
"block": function(statements) {
var out = [ "block" ];
if (statements != null)
out.push(MAP(statements, walk));
return out;
},
"var": function(defs) {
return [ "var", _vardefs(defs) ];
},
"const": function(defs) {
return [ "const", _vardefs(defs) ];
},
"try": function(t, c, f) {
return [
"try",
MAP(t, walk),
c != null ? [ c[0], MAP(c[1], walk) ] : null,
f != null ? MAP(f, walk) : null
];
},
"throw": function(expr) {
return [ "throw", walk(expr) ];
},
"new": function(ctor, args) {
return [ "new", walk(ctor), MAP(args, walk) ];
},
"switch": function(expr, body) {
return [ "switch", walk(expr), MAP(body, function(branch){
return [ branch[0] ? walk(branch[0]) : null,
MAP(branch[1], walk) ];
}) ];
},
"break": function(label) {
return [ "break", label ];
},
"continue": function(label) {
return [ "continue", label ];
},
"conditional": function(cond, t, e) {
return [ "conditional", walk(cond), walk(t), walk(e) ];
},
"assign": function(op, lvalue, rvalue) {
return [ "assign", op, walk(lvalue), walk(rvalue) ];
},
"dot": function(expr) {
return [ "dot", walk(expr) ].concat(slice(arguments, 1));
},
"call": function(expr, args) {
return [ "call", walk(expr), MAP(args, walk) ];
},
"function": function(name, args, body) {
return [ "function", name, args.slice(), MAP(body, walk) ];
},
"defun": function(name, args, body) {
return [ "defun", name, args.slice(), MAP(body, walk) ];
},
"if": function(conditional, t, e) {
return [ "if", walk(conditional), walk(t), walk(e) ];
},
"for": function(init, cond, step, block) {
return [ "for", walk(init), walk(cond), walk(step), walk(block) ];
},
"for-in": function(has_var, key, hash, block) {
return [ "for-in", has_var, key, walk(hash), walk(block) ];
},
"while": function(cond, block) {
return [ "while", walk(cond), walk(block) ];
},
"do": function(cond, block) {
return [ "do", walk(cond), walk(block) ];
},
"return": function(expr) {
return [ "return", walk(expr) ];
},
"binary": function(op, left, right) {
return [ "binary", op, walk(left), walk(right) ];
},
"unary-prefix": function(op, expr) {
return [ "unary-prefix", op, walk(expr) ];
},
"unary-postfix": function(op, expr) {
return [ "unary-postfix", op, walk(expr) ];
},
"sub": function(expr, subscript) {
return [ "sub", walk(expr), walk(subscript) ];
},
"object": function(props) {
return [ "object", 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 [ "regexp", rx, mods ];
},
"array": function(elements) {
return [ "array", MAP(elements, walk) ];
},
"stat": function(stat) {
return [ "stat", walk(stat) ];
},
"seq": function() {
return [ "seq" ].concat(MAP(slice(arguments), walk));
},
"label": function(name, block) {
return [ "label", name, walk(block) ];
},
"with": function(expr, block) {
return [ "with", walk(expr), walk(block) ];
},
"atom": function(name) {
return [ "atom", name ];
}
};
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 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,
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 eval() is detected in this or any subscopes
this.uses_eval = false; // will become TRUE if with() 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$_";
return function(num) {
var ret = "";
do {
ret = DIGITS.charAt(num % 54) + ret;
num = Math.floor(num / 54);
} 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;
}
},
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
var m = s.next_mangled();
s.rev_mangled[m] = name;
return s.mangled[name] = m;
},
define: function(name) {
if (name != null)
return this.names[name] = 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);
var ret = current_scope.body = cont();
ret.scope = current_scope;
current_scope = current_scope.parent;
return ret;
};
function define(name) {
return current_scope.define(name);
};
function reference(name) {
current_scope.refs[name] = true;
};
function _lambda(name, args, body) {
return [ this[0], define(name), args, with_new_scope(function(){
MAP(args, define);
return MAP(body, walk);
})];
};
return with_new_scope(function(){
// process AST
var ret = w.with_walkers({
"function": _lambda,
"defun": _lambda,
"with": function(expr, block) {
for (var s = current_scope; s; s = s.parent)
s.uses_with = true;
},
"var": function(defs) {
MAP(defs, function(d){ define(d[0]) });
},
"const": function(defs) {
MAP(defs, function(d){ define(d[0]) });
},
"try": function(t, c, f) {
if (c != null) return [
"try",
MAP(t, walk),
[ define(c[0]), MAP(c[1], walk) ],
f != null ? MAP(f, walk) : null
];
},
"name": function(name) {
if (name == "eval")
having_eval.push(current_scope);
reference(name);
},
"for-in": function(has_var, name) {
if (has_var) define(name);
else 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, do_toplevel) {
var w = ast_walker(), walk = w.walk, scope;
function get_mangled(name, newMangle) {
if (!do_toplevel && !scope.parent) return name; // don't mangle toplevel
return scope.get_mangled(name, newMangle);
};
function _lambda(name, args, body) {
if (name) name = get_mangled(name);
body = with_scope(body.scope, function(){
args = MAP(args, function(name){ return get_mangled(name) });
return MAP(body, walk);
});
return [ this[0], name, args, body ];
};
function with_scope(s, cont) {
var _scope = scope;
scope = s;
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 MAP(defs, function(d){
return [ get_mangled(d[0]), walk(d[1]) ];
});
};
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;
},
"var": function(defs) {
return [ "var", _vardefs(defs) ];
},
"const": function(defs) {
return [ "const", _vardefs(defs) ];
},
"name": function(name) {
return [ "name", get_mangled(name) ];
},
"try": function(t, c, f) {
return [ "try",
MAP(t, walk),
c != null ? [ get_mangled(c[0]), MAP(c[1], walk) ] : null,
f != null ? MAP(f, walk) : null ];
},
"toplevel": function(body) {
return with_scope(this.scope, function(){
return [ "toplevel", MAP(body, walk) ];
});
},
"for-in": function(has_var, name, obj, stat) {
return [ "for-in", has_var, get_mangled(name), walk(obj), walk(stat) ];
}
}, 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) {
t = last_stat(t);
if (t[0] == "return" || t[0] == "break" || t[0] == "continue" || t[0] == "throw")
return true;
}
};
function negate(c) {
var not_c = [ "unary-prefix", "!", c ];
switch (c[0]) {
case "unary-prefix":
return c[1] == "!" ? c[2] : not_c;
case "binary":
var op = c[1], left = c[2], right = c[3];
switch (op) {
case "<=": return [ "binary", ">", left, right ];
case "<": return [ "binary", ">=", left, right ];
case ">=": return [ "binary", "<", left, right ];
case ">": return [ "binary", "<=", left, right ];
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) {
if (c[0] == "unary-prefix" && c[1] == "!") {
return e ? [ "conditional", c[2], e, t ] : [ "binary", "||", c[2], t ];
} else {
return e ? [ "conditional", c, t, e ] : [ "binary", "&&", c, t ];
}
};
function empty(b) {
return !b || (b[0] == "block" && (!b[1] || b[1].length == 0));
};
function ast_squeeze(ast, options) {
options = defaults(options, {
make_seqs : true,
dead_code : true,
no_warnings : false,
extra : false
});
var w = ast_walker(), walk = w.walk, scope;
function with_scope(s, cont) {
var _scope = scope;
scope = s;
var ret = cont();
ret.scope = s;
scope = _scope;
return ret;
};
function is_constant(node) {
return node[0] == "string" || node[0] == "num";
};
function find_first_execute(node) {
if (!node)
return false;
switch (node[0]) {
case "num":
case "string":
case "name":
return node;
case "call":
case "conditional":
case "for":
case "if":
case "new":
case "return":
case "stat":
case "switch":
case "throw":
return find_first_execute(node[1]);
case "binary":
return find_first_execute(node[2]);
case "assign":
if (node[1] === true)
return find_first_execute(node[3]);
break;
case "var":
if (node[1][0].length > 1)
return find_first_execute(node[1][0][1]);
break;
}
return null;
}
function find_assign_recursive(p, v) {
if (p[0] == "assign" && p[1] != true || p[0] == "unary-prefix") {
if (p[2][0] == "name" && v[0] == "name" && p[2][1] == v[1])
return true;
return false;
}
if (p[0] != "assign" || p[1] !== true)
return false;
if ((is_constant(p[3]) && p[3][0] == v[0] && p[3][1] == v[1]) ||
(p[3][0] == "name" && v[0] == "name" && p[3][1] == v[1]) ||
(p[2][0] == "name" && v[0] == "name" && p[2][1] == v[1]))
return true;
return find_assign_recursive(p[3], v);
};
function rmblock(block) {
if (block != null && block[0] == "block" && block[1] && block[1].length == 1)
block = block[1][0];
return block;
};
function clone(obj) {
if (obj && obj.constructor == Array)
return MAP(obj, clone);
return obj;
};
function make_seq_to_statements(node) {
if (node[0] != "seq") {
switch (node[0]) {
case "var":
case "const":
return [ node ];
default:
return [ [ "stat", node ] ];
}
}
var ret = [];
for (var i = 1; i < node.length; i++)
ret.push.apply(ret, make_seq_to_statements(node[i]));
return ret;
};
function _lambda(name, args, body) {
return [ this[0], name, args, with_scope(body.scope, function(){
return tighten(MAP(body, walk), "lambda");
}) ];
};
// we get here for blocks that have been already transformed.
// 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 = statements.reduce(function(a, stat){
if (stat[0] == "block") {
if (stat[1]) {
a.push.apply(a, stat[1]);
}
} else {
a.push(stat);
}
return a;
}, []);
if (options.extra) {
// Detightening things. We do this because then we can assume that the
// statements are structured in a specific way.
statements = (function(a, prev) {
statements.forEach(function(cur) {
switch (cur[0]) {
case "for":
if (cur[1] != null) {
a.push.apply(a, make_seq_to_statements(cur[1]));
cur[1] = null;
}
a.push(cur);
break;
case "stat":
var stats = make_seq_to_statements(cur[1]);
stats.forEach(function(s) {
if (s[1][0] == "unary-postfix")
s[1][0] = "unary-prefix";
});
a.push.apply(a, stats);
break;
default:
a.push(cur);
}
});
return a;
})([]);
statements = (function(a, prev) {
statements.forEach(function(cur) {
if (!(prev && prev[0] == "stat")) {
a.push(cur);
prev = cur;
return;
}
var p = prev[1];
var c = find_first_execute(cur);
if (c && find_assign_recursive(p, c)) {
var old_cur = clone(cur);
c.splice(0, c.length);
c.push.apply(c, p);
var tmp_cur = best_of(cur, [ "toplevel", [ prev, old_cur ] ]);
if (tmp_cur == cur) {
a[a.length -1] = cur;
} else {
cur = old_cur;
a.push(cur);
}
} else {
a.push(cur);
}
prev = cur;
});
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 (member(st[0], [ "function", "defun" , "var", "const" ])) {
a.push(st);
}
else if (!options.no_warnings)
warn("Removing unreachable code: " + gen_code(st, true));
}
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;
}
});
return a;
})([]);
if (options.extra) {
statements = (function(a, prev){
statements.forEach(function(cur){
var replaced = false;
if (prev && cur[0] == "for" && cur[1] == null && (prev[0] == "var" || prev[0] == "const" || prev[0] == "stat")) {
cur[1] = prev;
a[a.length - 1] = cur;
} else {
a.push(cur);
}
prev = cur;
});
return a;
})([]);
}
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) {
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] == "stat") {
if (e) {
if (e[0] == "stat") {
ret = best_of(ret, [ "stat", make_conditional(c, t[1], e[1]) ]);
}
}
else {
ret = best_of(ret, [ "stat", make_conditional(c, t[1]) ]);
}
}
else if (e && t[0] == e[0] && (t[0] == "return" || t[0] == "throw")) {
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 = [ [ "if", negate(c), e ] ];
if (t[0] == "block") {
if (t[1]) ret = ret.concat(t[1]);
} else {
ret.push(t);
}
ret = walk([ "block", ret ]);
}
return ret;
};
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 ];
}
}
},
"if": make_if,
"toplevel": function(body) {
return [ "toplevel", with_scope(this.scope, function(){
return tighten(MAP(body, walk));
}) ];
},
"switch": function(expr, body) {
var last = body.length - 1;
return [ "switch", walk(expr), MAP(body, function(branch, i){
var block = tighten(MAP(branch[1], walk));
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(MAP(body, walk)) ]);
},
"binary": function(op, left, right) {
left = walk(left);
right = walk(right);
var best = [ "binary", op, left, right ];
if (is_constant(right)) {
if (is_constant(left)) {
var val = null;
switch (op) {
case "+": val = left[1] + right[1]; break;
case "*": val = left[1] * right[1]; break;
case "/": val = left[1] / right[1]; break;
case "-": val = left[1] - right[1]; break;
case "<<": val = left[1] << right[1]; break;
case ">>": val = left[1] >> right[1]; break;
case ">>>": val = left[1] >>> right[1]; break;
}
if (val != null) {
best = best_of(best, [ typeof val == "string" ? "string" : "num", val ]);
}
} else if (left[0] == "binary" && left[1] == "+" && left[3][0] == "string") {
best = best_of(best, [ "binary", "+", left[2], [ "string", left[3][1] + right[1] ] ]);
}
}
return best;
},
"conditional": function(c, t, e) {
return make_conditional(walk(c), walk(t), walk(e));
},
"try": function(t, c, f) {
return [
"try",
tighten(MAP(t, walk)),
c != null ? [ c[0], tighten(MAP(c[1], walk)) ] : null,
f != null ? tighten(MAP(f, walk)) : null
];
},
"unary-prefix": function(op, cond) {
if (op == "!") {
cond = walk(cond);
if (cond[0] == "unary-prefix" && cond[1] == "!") {
var p = w.parent();
if (p[0] == "unary-prefix" && p[1] == "!")
return cond[2];
return [ "unary-prefix", "!", cond ];
}
return best_of(this, negate(cond));
}
},
"name": function(name) {
switch (name) {
case "true": return [ "unary-prefix", "!", [ "num", 0 ]];
case "false": return [ "unary-prefix", "!", [ "num", 1 ]];
}
},
"new": function(ctor, args) {
if (ctor[0] == "name" && ctor[1] == "Array" && !scope.has("Array")) {
if (args.length != 1) {
return [ "array", args ];
} else {
return [ "call", [ "name", "Array" ], args ];
}
}
},
"call": function(expr, args) {
if (expr[0] == "name" && expr[1] == "Array" && args.length != 1 && !scope.has("Array")) {
return [ "array", args ];
}
}
}, function() {
return walk(ast_add_scope(ast));
});
};
/* -----[ re-generate code from the AST ]----- */
var DOT_CALL_NO_PARENS = jsp.array_to_hash([
"name",
"array",
"string",
"dot",
"sub",
"call",
"regexp"
]);
function make_string(str) {
var dq = 0, sq = 0;
str = str.replace(/[\\\b\f\n\r\t\x22\x27]/g, function(s){
switch (s) {
case "\\": return "\\\\";
case "\b": return "\\b";
case "\f": return "\\f";
case "\n": return "\\n";
case "\r": return "\\r";
case "\t": return "\\t";
case '"': ++dq; return '"';
case "'": ++sq; return "'";
}
return s;
});
if (dq > sq) {
return "'" + str.replace(/\x27/g, "\\'") + "'";
} else {
return '"' + str.replace(/\x22/g, '\\"') + '"';
}
};
function gen_code(ast, beautify) {
if (beautify) beautify = defaults(beautify, {
indent_start : 0,
indent_level : 4,
quote_keys : false,
space_colon : false
});
var indentation = 0,
newline = beautify ? "\n" : "",
space = beautify ? " " : "";
function indent(line) {
if (line == null)
line = "";
if (beautify)
line = repeat_string(" ", beautify.indent_start + indentation * beautify.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") {
// 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($stack), self = a.pop(), p = a.pop();
while (p) {
if (p[0] == "stat") return true;
if ((p[0] == "seq" && p[1] === self) ||
(p[0] == "call" && p[1] === self) ||
(p[0] == "binary" && 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) {
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 generators = {
"string": make_string,
"num": make_num,
"name": make_name,
"toplevel": function(statements) {
return make_block_statements(statements)
.join(newline + 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, make)) + ")" : "";
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")
out += ".";
else if (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 (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(has_var, key, hash, block) {
var out = add_spaces([ "for", "(" ]);
if (has_var)
out += "var ";
out += add_spaces([ make_name(key) + " in " + make(hash) + ")", make(block) ]);
return out;
},
"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]]) {
left = "(" + left + ")";
}
if (member(rvalue[0], [ "assign", "conditional", "seq" ]) ||
rvalue[0] == "binary" && PRECEDENCE[operator] >= PRECEDENCE[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) {
if (props.length == 0)
return "{}";
return "{" + 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]));
}
var key = p[0], val = make(p[1]);
if (beautify && beautify.quote_keys) {
key = make_string(key);
} else if (typeof key == "number" || !beautify && +key + "" == key) {
key = make_num(+key);
} else if (!is_identifier(key)) {
key = make_string(key);
}
return indent(add_spaces(beautify && beautify.space_colon
? [ key, ":", val ]
: [ key + ":", val ]));
}).join("," + newline);
}) + newline + indent("}");
},
"regexp": function(rx, mods) {
return "/" + rx + "/" + mods;
},
"array": function(elements) {
if (elements.length == 0) return "[]";
return add_spaces([ "[", add_commas(MAP(elements, function(el){
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);
},
"comment1": function(text) {
return "//" + text + "\n";
},
"comment2": function(text) {
return "/*" + text + "*/";
}
};
// 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[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) {
var out = keyword || "function";
if (name) {
out += " " + make_name(name);
}
out += "(" + add_commas(MAP(args, make_name)) + ")";
return add_spaces([ out, make_block(body) ]);
};
function make_name(name) {
return name.toString();
};
function make_block_statements(statements) {
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)
code = code.replace(/;+\s*$/, "");
a.push(code);
}
}
return 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([ name, "=", make(val) ]);
return name;
};
var $stack = [];
function make(node) {
var type = node[0];
var gen = generators[type];
if (!gen)
throw new Error("Can't find generator for \"" + type + "\"");
$stack.push(node);
var ret = gen.apply(type, node.slice(1));
$stack.pop();
return ret;
};
return make(ast);
};
/* -----[ 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 = [];
for (var i = 0; i < a.length; ++i) {
var val = f.call(o, a[i], i);
if (val instanceof AtTop) ret.unshift(val.v);
else ret.push(val);
}
return ret;
};
MAP.at_top = function(val) { return new AtTop(val) };
function AtTop(val) { this.v = val };
})();
/* -----[ Exports ]----- */
exports.ast_walker = ast_walker;
exports.ast_mangle = ast_mangle;
exports.ast_squeeze = ast_squeeze;
exports.gen_code = gen_code;
exports.ast_add_scope = ast_add_scope;
exports.ast_squeeze_more = require("./squeeze-more").ast_squeeze_more;
exports.set_logger = function(logger) { warn = logger };
var jsp = require("./parse-js"),
pro = require("./process"),
slice = jsp.slice,
member = jsp.member,
PRECEDENCE = jsp.PRECEDENCE,
OPERATORS = jsp.OPERATORS;
function ast_squeeze_more(ast) {
var w = pro.ast_walker(), walk = w.walk;
return w.with_walkers({
"call": function(expr, args) {
if (expr[0] == "dot" && expr[2] == "toString" && args.length == 0) {
// foo.toString() ==> foo+""
return [ "binary", "+", expr[1], [ "string", "" ]];
}
}
}, function() {
return walk(ast);
});
};
exports.ast_squeeze_more = ast_squeeze_more;
#! /usr/bin/env node
// -*- js2 -*-
global.sys = require(/^v0\.[012]/.test(process.version) ? "sys" : "util");
var fs = require("fs"),
jsp = require("./lib/parse-js"),
pro = require("./lib/process");
pro.set_logger(function(msg){
sys.debug(msg);
});
var options = {
ast: false,
mangle: true,
mangle_toplevel: false,
squeeze: true,
make_seqs: true,
dead_code: true,
beautify: false,
verbose: false,
show_copyright: true,
out_same_file: false,
extra: false,
unsafe: false, // XXX: extra & unsafe? but maybe we don't want both, so....
beautify_options: {
indent_level: 4,
indent_start: 0,
quote_keys: false,
space_colon: 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.beautify = true;
break;
case "-i":
case "--indent":
options.beautify_options.indent_level = args.shift();
break;
case "-q":
case "--quote-keys":
options.beautify_options.quote_keys = true;
break;
case "-mt":
case "--mangle-toplevel":
options.mangle_toplevel = 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 "--extra":
options.extra = true;
break;
case "--unsafe":
options.unsafe = true;
break;
default:
filename = v;
break out;
}
}
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);
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 + "*/\n";
}
}
return ret;
};
function squeeze_it(code) {
var result = "";
if (options.show_copyright) {
var initial_comments = [];
// keep first comment
var tok = jsp.tokenizer(code, false), c;
c = tok();
var prev = null;
while (/^comment/.test(c.type) && (!prev || prev == c.type)) {
initial_comments.push(c);
prev = c.type;
c = tok();
}
result += show_copyright(initial_comments);
}
try {
var ast = time_it("parse", function(){ return jsp.parse(code); });
if (options.mangle)
ast = time_it("mangle", function(){ return pro.ast_mangle(ast, options.mangle_toplevel); });
if (options.squeeze)
ast = time_it("squeeze", function(){
ast = pro.ast_squeeze(ast, {
make_seqs : options.make_seqs,
dead_code : options.dead_code,
extra : options.extra
});
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.beautify && options.beautify_options) }) + ";";
return result;
} catch(ex) {
sys.debug(ex.stack);
sys.debug(sys.inspect(ex));
sys.debug(JSON.stringify(ex));
}
};
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."); }
};
......@@ -175,11 +175,12 @@ jQuery.extend({
timeout: 0,
data: null,
dataType: null,
dataTypes: null,
username: null,
password: null,
cache: null,
traditional: false,
headers: {},
crossDomain: null,
*/
xhr: function() {
return new window.XMLHttpRequest();
......@@ -306,30 +307,35 @@ jQuery.extend({
// (match is used internally)
getResponseHeader: function( key , match ) {
if ( state !== 2 ) {
return null;
}
if ( state === 2 ) {
if ( responseHeaders === undefined ) {
if ( responseHeaders === undefined ) {
responseHeaders = {};
responseHeaders = {};
if ( typeof responseHeadersString === "string" ) {
if ( typeof responseHeadersString === "string" ) {
while( ( match = rheaders.exec( responseHeadersString ) ) ) {
responseHeaders[ match[ 1 ].toLowerCase() ] = match[ 2 ];
while( ( match = rheaders.exec( responseHeadersString ) ) ) {
responseHeaders[ match[ 1 ].toLowerCase() ] = match[ 2 ];
}
}
}
match = responseHeaders[ key.toLowerCase() ];
} else {
match = null;
}
return responseHeaders[ key.toLowerCase() ];
return match;
},
// Cancel the request
abort: function( statusText ) {
if ( transport && state !== 2 ) {
if ( transport ) {
transport.abort( statusText || "abort" );
done( 0 , statusText );
}
done( 0 , statusText );
return this;
}
};
......@@ -347,6 +353,10 @@ jQuery.extend({
// State is "done" now
state = 2;
// Dereference transport for early garbage collection
// (no matter how long the jXHR transport will be used
transport = 0;
// Set readyState
jXHR.readyState = status ? 4 : 0;
......@@ -574,12 +584,6 @@ jQuery.extend({
// Remove hash character (#7531: and string promotion)
s.url = ( "" + s.url ).replace( rhash , "" );
// Uppercase the type
s.type = s.type.toUpperCase();
// Determine if request has content
s.hasContent = ! rnoContent.test( s.type );
// Extract dataTypes list
s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().split( /\s+/ );
......@@ -595,88 +599,97 @@ jQuery.extend({
}
// Convert data if not already a string
if ( s.data && s.processData && typeof s.data != "string" ) {
if ( s.data && s.processData && typeof s.data !== "string" ) {
s.data = jQuery.param( s.data , s.traditional );
}
// Get transport
transport = jQuery.ajaxPrefilter( s , options ).ajaxTransport( s );
// Apply prefilters
jQuery.ajaxPrefilter( s , options );
// Uppercase the type
s.type = s.type.toUpperCase();
// Determine if request has content
s.hasContent = ! rnoContent.test( s.type );
// Watch for a new set of requests
if ( s.global && jQuery.active++ === 0 ) {
jQuery.event.trigger( "ajaxStart" );
}
// If no transport, we auto-abort
if ( ! transport ) {
// More options handling for requests with no content
if ( ! s.hasContent ) {
done( 0 , "transport not found" );
jXHR = false;
} else {
// If data is available, append data to url
if ( s.data ) {
s.url += ( rquery.test( s.url ) ? "&" : "?" ) + s.data;
}
// More options handling for requests with no content
if ( ! s.hasContent ) {
// Add anti-cache in url if needed
if ( s.cache === false ) {
// If data is available, append data to url
if ( s.data ) {
s.url += ( rquery.test( s.url ) ? "&" : "?" ) + s.data;
}
var ts = jQuery.now(),
// try replacing _= if it is there
ret = s.url.replace( rts , "$1_=" + ts );
// Add anti-cache in url if needed
if ( s.cache === false ) {
// if nothing was replaced, add timestamp to the end
s.url = ret + ( (ret == s.url ) ? ( rquery.test( s.url ) ? "&" : "?" ) + "_=" + ts : "");
}
}
var ts = jQuery.now(),
// try replacing _= if it is there
ret = s.url.replace( rts , "$1_=" + ts );
// Set the correct header, if data is being sent
if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) {
requestHeaders[ "content-type" ] = s.contentType;
}
// if nothing was replaced, add timestamp to the end
s.url = ret + ( (ret == s.url ) ? ( rquery.test( s.url ) ? "&" : "?" ) + "_=" + ts : "");
}
// Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
if ( s.ifModified ) {
if ( jQuery_lastModified[ s.url ] ) {
requestHeaders[ "if-modified-since" ] = jQuery_lastModified[ s.url ];
}
// Set the correct header, if data is being sent
if ( s.data && s.hasContent && s.contentType !== false || options.contentType ) {
requestHeaders[ "content-type" ] = s.contentType;
if ( jQuery_etag[ s.url ] ) {
requestHeaders[ "if-none-match" ] = jQuery_etag[ s.url ];
}
}
// Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
if ( s.ifModified ) {
if ( jQuery_lastModified[ s.url ] ) {
requestHeaders[ "if-modified-since" ] = jQuery_lastModified[ s.url ];
}
if ( jQuery_etag[ s.url ] ) {
requestHeaders[ "if-none-match" ] = jQuery_etag[ s.url ];
}
}
// Set the Accepts header for the server, depending on the dataType
requestHeaders.accept = s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[ 0 ] ] ?
s.accepts[ s.dataTypes[ 0 ] ] + ( s.dataTypes[ 0 ] !== "*" ? ", */*; q=0.01" : "" ) :
s.accepts[ "*" ];
// Check for headers option
for ( i in s.headers ) {
requestHeaders[ i.toLowerCase() ] = s.headers[ i ];
}
// Allow custom headers/mimetypes and early abort
if ( s.beforeSend && ( s.beforeSend.call( callbackContext , jXHR , s ) === false || state === 2 ) ) {
// Abort if not done already
done( 0 , "abort" );
// Set the Accepts header for the server, depending on the dataType
requestHeaders.accept = s.dataTypes[ 0 ] && s.accepts[ s.dataTypes[ 0 ] ] ?
s.accepts[ s.dataTypes[ 0 ] ] + ( s.dataTypes[ 0 ] !== "*" ? ", */*; q=0.01" : "" ) :
s.accepts[ "*" ];
// Return false
jXHR = false;
// Check for headers option
for ( i in s.headers ) {
requestHeaders[ i.toLowerCase() ] = s.headers[ i ];
} else {
// Install callbacks on deferreds
for ( i in { success:1, error:1, complete:1 } ) {
jXHR[ i ]( s[ i ] );
}
// Allow custom headers/mimetypes and early abort
if ( s.beforeSend && ( s.beforeSend.call( callbackContext , jXHR , s ) === false || state === 2 ) ) {
// Get transport
transport = jQuery.ajaxTransport( s );
// If no transport, we auto-abort
if ( ! transport ) {
// Abort if not done already
done( 0 , "abort" );
jXHR = false;
done( 0 , "notransport" );
} else {
// Set state as sending
state = 1;
jXHR.readyState = 1;
// Install callbacks on deferreds
for ( i in { success:1, error:1, complete:1 } ) {
jXHR[ i ]( s[ i ] );
}
state = jXHR.readyState = 1;
// Send global event
if ( s.global ) {
......
(function( jQuery ) {
var jsc = jQuery.now(),
jsre = /(\=)(?:\?|%3F)(&|$)|()(?:\?\?|%3F%3F)()/i,
rquery_jsonp = /\?/;
jsre = /(\=)(?:\?|%3F)(&|$)|()(?:\?\?|%3F%3F)()/i;
// Default jsonp settings
jQuery.ajaxSetup({
......@@ -11,73 +10,77 @@ jQuery.ajaxSetup({
return "jsonp" + jsc++;
}
// Normalize jsonp queries
// 1) put callback parameter in url or data
// 2) sneakily ensure transportDataType is always jsonp for jsonp requests
}).ajaxPrefilter("json jsonp", function(s, originalSettings) {
// Detect, normalize options and install callbacks for jsonp requests
// (dataIsString is used internally)
}).ajaxPrefilter("json jsonp", function(s, originalSettings, dataIsString) {
dataIsString = ( typeof(s.data) === "string" );
if ( s.dataTypes[ 0 ] === "jsonp" ||
originalSettings.jsonp ||
originalSettings.jsonpCallback ||
jsre.test(s.url) ||
typeof(s.data) === "string" && jsre.test(s.data) ) {
originalSettings.jsonp != null ||
s.jsonp !== false && ( jsre.test( s.url ) ||
dataIsString && jsre.test( s.data ) ) ) {
var jsonpCallback = s.jsonpCallback =
var responseContainer,
jsonpCallback = s.jsonpCallback =
jQuery.isFunction( s.jsonpCallback ) ? s.jsonpCallback() : s.jsonpCallback,
url = s.url.replace(jsre, "$1" + jsonpCallback + "$2"),
data = s.url === url && typeof(s.data) === "string" ? s.data.replace(jsre, "$1" + jsonpCallback + "$2") : s.data;
if ( url === s.url && data === s.data ) {
url += (rquery_jsonp.test( url ) ? "&" : "?") + s.jsonp + "=" + jsonpCallback;
previous = window[ jsonpCallback ],
url = s.url,
data = s.data,
replace = "$1" + jsonpCallback + "$2";
if ( s.jsonp !== false ) {
url = url.replace( jsre, replace );
if ( s.url === url ) {
if ( dataIsString ) {
data = data.replace( jsre, replace );
}
if ( s.data === data ) {
// Add callback manually
url += (/\?/.test( url ) ? "&" : "?") + s.jsonp + "=" + jsonpCallback;
}
}
}
s.url = url;
s.data = data;
s.dataTypes[ 0 ] = "jsonp";
}
// Bind transport to jsonp dataType
}).ajaxTransport("jsonp", function(s) {
// Put callback in place
var responseContainer,
jsonpCallback = s.jsonpCallback,
previous = window[ jsonpCallback ];
window [ jsonpCallback ] = function( response ) {
responseContainer = [response];
};
window [ jsonpCallback ] = function( response ) {
responseContainer = [response];
};
s.complete = [function() {
s.complete = [function() {
// Set callback back to previous value
window[ jsonpCallback ] = previous;
// Set callback back to previous value
window[ jsonpCallback ] = previous;
// Call if it was a function and we have a response
if ( previous) {
if ( responseContainer && jQuery.isFunction ( previous ) ) {
window[ jsonpCallback ] ( responseContainer[0] );
// Call if it was a function and we have a response
if ( previous) {
if ( responseContainer && jQuery.isFunction ( previous ) ) {
window[ jsonpCallback ] ( responseContainer[0] );
}
} else {
// else, more memory leak avoidance
try{ delete window[ jsonpCallback ]; } catch(e){}
}
} else {
// else, more memory leak avoidance
try{ delete window[ jsonpCallback ]; } catch(e){}
}
}, s.complete ];
}, s.complete ];
// Sneakily ensure this will be handled as json
s.dataTypes[ 0 ] = "json";
// Use data converter to retrieve json after script execution
s.converters["script json"] = function() {
if ( ! responseContainer ) {
jQuery.error( jsonpCallback + " was not called" );
}
return responseContainer[ 0 ];
};
// Use data converter to retrieve json after script execution
s.converters["script json"] = function() {
if ( ! responseContainer ) {
jQuery.error( jsonpCallback + " was not called" );
}
return responseContainer[ 0 ];
};
// force json dataType
s.dataTypes[ 0 ] = "json";
// Delegate to script transport
return "script";
// Delegate to script
return "script";
}
});
})( jQuery );
......@@ -15,18 +15,23 @@ jQuery.ajaxSetup({
"text script": jQuery.globalEval
}
// Bind script tag hack transport
}).ajaxTransport("script", function(s) {
// Handle cache's special case and global
}).ajaxPrefilter("script", function(s) {
// Handle cache special case
if ( s.cache === undefined ) {
s.cache = false;
}
// This transport only deals with cross domain get requests
if ( s.crossDomain && s.async && ( s.type === "GET" || ! s.data ) ) {
if ( s.crossDomain ) {
s.type = "GET";
s.global = false;
}
// Bind script tag hack transport
}).ajaxTransport("script", function(s) {
// This transport only deals with cross domain requests
if ( s.crossDomain ) {
var script,
head = document.getElementsByTagName("head")[0] || document.documentElement;
......
......@@ -133,11 +133,11 @@ jQuery.fn.extend({
} else if ( type === "undefined" || type === "boolean" ) {
if ( this.className ) {
// store className if set
jQuery.data( this, "__className__", this.className );
jQuery._data( this, "__className__", this.className );
}
// toggle whole className
this.className = this.className || value === false ? "" : jQuery.data( this, "__className__" ) || "";
this.className = this.className || value === false ? "" : jQuery._data( this, "__className__" ) || "";
}
});
},
......
......@@ -3,7 +3,7 @@ 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 );
return new jQuery.fn.init( selector, context, rootjQuery );
},
// Map over jQuery in case of overwrite
......@@ -19,12 +19,8 @@ var jQuery = function( selector, context ) {
// (both of which we optimize for)
quickExpr = /^(?:[^<]*(<[\w\W]+>)[^>]*$|#([\w\-]+)$)/,
// Is it a simple selector
isSimple = /^.[^:#\[\.,]*$/,
// Check if a string has a non-whitespace character in it
rnotwhite = /\S/,
rwhite = /\s/,
// Used for trimming whitespace
trimLeft = /^\s+/,
......@@ -63,6 +59,9 @@ var jQuery = function( selector, context ) {
// The deferred used on DOM ready
readyList,
// Promise methods
promiseMethods = "then done fail isResolved isRejected promise".split( " " ),
// The ready event handler
DOMContentLoaded,
......@@ -78,7 +77,8 @@ var jQuery = function( selector, context ) {
class2type = {};
jQuery.fn = jQuery.prototype = {
init: function( selector, context ) {
constructor: jQuery,
init: function( selector, context, rootjQuery ) {
var match, elem, ret, doc;
// Handle $(""), $(null), or $(undefined)
......@@ -112,6 +112,7 @@ jQuery.fn = jQuery.prototype = {
// 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
......@@ -171,7 +172,7 @@ jQuery.fn = jQuery.prototype = {
// HANDLE: $(expr, context)
// (which is just equivalent to: $(context).find(expr)
} else {
return jQuery( context ).find( selector );
return this.constructor( context ).find( selector );
}
// HANDLE: $(function)
......@@ -222,7 +223,7 @@ jQuery.fn = jQuery.prototype = {
// (returning the new matched element set)
pushStack: function( elems, name, selector ) {
// Build a new jQuery matched element set
var ret = jQuery();
var ret = this.constructor();
if ( jQuery.isArray( elems ) ) {
push.apply( ret, elems );
......@@ -287,7 +288,7 @@ jQuery.fn = jQuery.prototype = {
},
end: function() {
return this.prevObject || jQuery(null);
return this.prevObject || this.constructor(null);
},
// For internal use only.
......@@ -578,7 +579,7 @@ jQuery.extend({
script.type = "text/javascript";
if ( jQuery.support.scriptEval ) {
if ( jQuery.support.scriptEval() ) {
script.appendChild( document.createTextNode( data ) );
} else {
script.text = data;
......@@ -896,9 +897,10 @@ jQuery.extend({
Deferred: function( func ) {
var deferred = jQuery._Deferred(),
failDeferred = jQuery._Deferred();
failDeferred = jQuery._Deferred(),
promise;
// Add errorDeferred methods and redefine cancel
// Add errorDeferred methods, then and promise
jQuery.extend( deferred , {
then: function( doneCallbacks , failCallbacks ) {
......@@ -911,14 +913,18 @@ jQuery.extend({
isRejected: failDeferred.isResolved,
// Get a promise for this deferred
// If obj is provided, the promise aspect is added to the object
promise: function( obj ) {
obj = obj || {};
jQuery.each( "then done fail isResolved isRejected".split( " " ) , function( _ , method ) {
obj[ method ] = deferred[ method ];
});
obj.promise = function() {
return obj;
};
// (i is used internally)
promise: function( obj , i ) {
if ( obj == null ) {
if ( promise ) {
return promise;
}
promise = obj = {};
}
i = promiseMethods.length;
while( i-- ) {
obj[ promiseMethods[ i ] ] = deferred[ promiseMethods[ i ] ];
}
return obj;
}
......@@ -940,10 +946,32 @@ jQuery.extend({
// Deferred helper
when: function( object ) {
object = object && jQuery.isFunction( object.promise ) ?
object :
jQuery.Deferred().resolve( object );
return object.promise();
var args = arguments,
length = args.length,
deferred = length <= 1 && object && jQuery.isFunction( object.promise ) ?
object :
jQuery.Deferred(),
promise = deferred.promise(),
resolveArray;
if ( length > 1 ) {
resolveArray = new Array( length );
jQuery.each( args, function( index, element, args ) {
jQuery.when( element ).done( function( value ) {
args = arguments;
resolveArray[ index ] = args.length > 1 ? slice.call( args , 0 ) : value;
if( ! --length ) {
deferred.fire( promise, resolveArray );
}
}).fail( function() {
deferred.fireReject( promise, arguments );
});
return !deferred.isRejected();
});
} else if ( deferred !== object ) {
deferred.resolve( object );
}
return promise;
},
// Use of jQuery.browser is frowned upon.
......@@ -960,6 +988,25 @@ jQuery.extend({
return { browser: match[1] || "", version: match[2] || "0" };
},
subclass: function(){
function jQuerySubclass( selector, context ) {
return new jQuerySubclass.fn.init( selector, context );
}
jQuerySubclass.superclass = this;
jQuerySubclass.fn = jQuerySubclass.prototype = this();
jQuerySubclass.fn.constructor = jQuerySubclass;
jQuerySubclass.subclass = this.subclass;
jQuerySubclass.fn.init = function init( selector, context ) {
if (context && context instanceof jQuery && !(context instanceof jQuerySubclass)){
context = jQuerySubclass(context);
}
return jQuery.fn.init.call( this, selector, context, rootjQuerySubclass );
};
jQuerySubclass.fn.init.prototype = jQuerySubclass.fn;
var rootjQuerySubclass = jQuerySubclass(document);
return jQuerySubclass;
},
browser: {}
});
......@@ -988,9 +1035,8 @@ if ( indexOf ) {
};
}
// Verify that \s matches non-breaking spaces
// (IE fails on this test)
if ( !rwhite.test( "\xA0" ) ) {
// IE doesn't match non-breaking spaces with \s
if ( rnotwhite.test( "\xA0" ) ) {
trimLeft = /^[\s\xA0]+/;
trimRight = /[\s\xA0]+$/;
}
......
......@@ -12,6 +12,9 @@ var ralpha = /alpha\([^)]*\)/i,
cssHeight = [ "Top", "Bottom" ],
curCSS,
getComputedStyle,
currentStyle,
fcamelCase = function( all, letter ) {
return letter.toUpperCase();
};
......@@ -169,6 +172,10 @@ jQuery.each(["height", "width"], function( i, name ) {
if ( val <= 0 ) {
val = curCSS( elem, name, name );
if ( val === "0px" && currentStyle ) {
val = currentStyle( elem, name, name );
}
if ( val != null ) {
// Should return "auto" instead of 0, use 0 for
// temporary backwards-compat
......@@ -234,7 +241,7 @@ if ( !jQuery.support.opacity ) {
}
if ( document.defaultView && document.defaultView.getComputedStyle ) {
curCSS = function( elem, newName, name ) {
getComputedStyle = function( elem, newName, name ) {
var ret, defaultView, computedStyle;
name = name.replace( rupper, "-$1" ).toLowerCase();
......@@ -252,10 +259,13 @@ if ( document.defaultView && document.defaultView.getComputedStyle ) {
return ret;
};
} else if ( document.documentElement.currentStyle ) {
curCSS = function( elem, name ) {
var left, rsLeft,
}
if ( document.documentElement.currentStyle ) {
currentStyle = function( elem, name ) {
var left,
ret = elem.currentStyle && elem.currentStyle[ name ],
rsLeft = elem.runtimeStyle && elem.runtimeStyle[ name ],
style = elem.style;
// From the awesome hack by Dean Edwards
......@@ -266,22 +276,27 @@ if ( document.defaultView && document.defaultView.getComputedStyle ) {
if ( !rnumpx.test( ret ) && rnum.test( ret ) ) {
// Remember the original values
left = style.left;
rsLeft = elem.runtimeStyle.left;
// Put in the new values to get a computed value out
elem.runtimeStyle.left = elem.currentStyle.left;
if ( rsLeft ) {
elem.runtimeStyle.left = elem.currentStyle.left;
}
style.left = name === "fontSize" ? "1em" : (ret || 0);
ret = style.pixelLeft + "px";
// Revert the changed values
style.left = left;
elem.runtimeStyle.left = rsLeft;
if ( rsLeft ) {
elem.runtimeStyle.left = rsLeft;
}
}
return ret === "" ? "auto" : ret;
};
}
curCSS = getComputedStyle || currentStyle;
function getWH( elem, name, extra ) {
var which = name === "width" ? cssWidth : cssHeight,
val = name === "width" ? elem.offsetWidth : elem.offsetHeight;
......
(function( jQuery ) {
var windowData = {},
rbrace = /^(?:\{.*\}|\[.*\])$/;
var rbrace = /^(?:\{.*\}|\[.*\])$/;
jQuery.extend({
cache: {},
......@@ -23,110 +22,170 @@ jQuery.extend({
},
hasData: function( elem ) {
if ( elem.nodeType ) {
elem = jQuery.cache[ elem[jQuery.expando] ];
}
elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ];
return !!elem && !jQuery.isEmptyObject(elem);
},
data: function( elem, name, data ) {
data: function( elem, name, data, pvt /* Internal Use Only */ ) {
if ( !jQuery.acceptData( elem ) ) {
return;
}
elem = elem == window ?
windowData :
elem;
var internalKey = jQuery.expando, getByName = typeof name === "string", thisCache,
// 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,
var isNode = elem.nodeType,
id = isNode ? elem[ jQuery.expando ] : null,
cache = jQuery.cache, thisCache;
// 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[ jQuery.expando ] : elem[ jQuery.expando ] && jQuery.expando;
if ( isNode && !id && typeof name === "string" && data === undefined ) {
// 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 || (pvt && id && !cache[ id ][ internalKey ])) && getByName && data === undefined ) {
return;
}
// Get the data from the object directly
if ( !isNode ) {
cache = elem;
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[ jQuery.expando ] = id = ++jQuery.uuid;
} else {
id = jQuery.expando;
}
}
// Compute a unique ID for the element
} else if ( !id ) {
elem[ jQuery.expando ] = id = ++jQuery.uuid;
if ( !cache[ id ] ) {
cache[ id ] = {};
}
// Avoid generating a new cache unless none exists and we
// want to manipulate it.
// 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" ) {
if ( isNode ) {
if ( pvt ) {
cache[ id ][ internalKey ] = jQuery.extend(cache[ id ][ internalKey ], name);
} else {
cache[ id ] = jQuery.extend(cache[ id ], name);
}
}
} else {
jQuery.extend( cache, name );
thisCache = cache[ id ];
// Internal jQuery data is stored in a separate object inside the object's data
// cache in order to avoid key collisions between internal data and user-defined
// data
if ( pvt ) {
if ( !thisCache[ internalKey ] ) {
thisCache[ internalKey ] = {};
}
} else if ( isNode && !cache[ id ] ) {
cache[ id ] = {};
thisCache = thisCache[ internalKey ];
}
thisCache = isNode ? cache[ id ] : cache;
// Prevent overriding the named cache with undefined values
if ( data !== undefined ) {
thisCache[ name ] = data;
}
return typeof name === "string" ? thisCache[ name ] : thisCache;
// TODO: This is a hack for 1.5 ONLY. It will be removed in 1.6. Users should
// not attempt to inspect the internal events object using jQuery.data, as this
// internal data object is undocumented and subject to change.
if ( name === "events" && !thisCache[name] ) {
return thisCache[ internalKey ] && thisCache[ internalKey ].events;
}
return getByName ? thisCache[ name ] : thisCache;
},
removeData: function( elem, name ) {
removeData: function( elem, name, pvt /* Internal Use Only */ ) {
if ( !jQuery.acceptData( elem ) ) {
return;
}
elem = elem == window ?
windowData :
elem;
var internalKey = jQuery.expando, isNode = elem.nodeType,
// See jQuery.data for more information
cache = isNode ? jQuery.cache : elem,
var isNode = elem.nodeType,
id = isNode ? elem[ jQuery.expando ] : elem,
cache = jQuery.cache,
thisCache = isNode ? cache[ id ] : id;
// See jQuery.data for more information
id = isNode ? elem[ jQuery.expando ] : jQuery.expando;
// If there is already no cache entry for this object, there is no
// purpose in continuing
if ( !cache[ id ] ) {
return;
}
// If we want to remove a specific section of the element's data
if ( name ) {
var thisCache = pvt ? cache[ id ][ internalKey ] : cache[ id ];
if ( thisCache ) {
// Remove the section of cache data
delete thisCache[ name ];
// If we've removed all the data, remove the element's cache
if ( isNode && jQuery.isEmptyObject(thisCache) ) {
jQuery.removeData( elem );
// If there is no data left in the cache, we want to continue
// and let the cache object itself get destroyed
if ( !jQuery.isEmptyObject(thisCache) ) {
return;
}
}
}
// See jQuery.data for more information
if ( pvt ) {
delete cache[ id ][ internalKey ];
// Don't destroy the parent cache unless the internal data object
// had been the only thing left in it
if ( !jQuery.isEmptyObject(cache[ id ]) ) {
return;
}
}
var internalCache = cache[ id ][ internalKey ];
// Otherwise, we want to remove all of the element's data
// 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
if ( jQuery.support.deleteExpando || cache != window ) {
delete cache[ id ];
} else {
if ( isNode && jQuery.support.deleteExpando ) {
delete elem[ jQuery.expando ];
cache[ id ] = null;
}
// We destroyed the entire user cache at once because it's faster than
// iterating through each key, but we need to continue to persist internal
// data if it existed
if ( internalCache ) {
cache[ id ] = {};
cache[ id ][ internalKey ] = internalCache;
// Otherwise, we need to eliminate the expando on the node to avoid
// false lookups in the cache for entries that no longer exist
} else 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[ jQuery.expando ];
} else if ( elem.removeAttribute ) {
elem.removeAttribute( jQuery.expando );
// Completely remove the data cache
} else if ( isNode ) {
delete cache[ id ];
// Remove all fields from the object
} else {
for ( var n in elem ) {
delete elem[ n ];
}
elem[ jQuery.expando ] = 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 ) {
......
......@@ -27,7 +27,7 @@ jQuery.fn.extend({
// Reset the inline display of this element to learn if it is
// being hidden by cascaded rules or not
if ( !jQuery.data(elem, "olddisplay") && display === "none" ) {
if ( !jQuery._data(elem, "olddisplay") && display === "none" ) {
display = elem.style.display = "";
}
......@@ -35,7 +35,7 @@ jQuery.fn.extend({
// in a stylesheet to whatever the default browser style is
// for such an element
if ( display === "" && jQuery.css( elem, "display" ) === "none" ) {
jQuery.data(elem, "olddisplay", defaultDisplay(elem.nodeName));
jQuery._data(elem, "olddisplay", defaultDisplay(elem.nodeName));
}
}
......@@ -46,7 +46,7 @@ jQuery.fn.extend({
display = elem.style.display;
if ( display === "" || display === "none" ) {
elem.style.display = jQuery.data(elem, "olddisplay") || "";
elem.style.display = jQuery._data(elem, "olddisplay") || "";
}
}
......@@ -62,8 +62,8 @@ jQuery.fn.extend({
for ( var i = 0, j = this.length; i < j; i++ ) {
var display = jQuery.css( this[i], "display" );
if ( display !== "none" && !jQuery.data( this[i], "olddisplay" ) ) {
jQuery.data( this[i], "olddisplay", display );
if ( display !== "none" && !jQuery._data( this[i], "olddisplay" ) ) {
jQuery._data( this[i], "olddisplay", display );
}
}
......
......@@ -8,7 +8,8 @@ var rnamespaces = /\.(.*)$/,
fcleanup = function( nm ) {
return nm.replace(rescape, "\\$&");
},
focusCounts = { focusin: 0, focusout: 0 };
focusCounts = { focusin: 0, focusout: 0 },
eventKey = "events";
/*
* A number of helper functions used for managing events.
......@@ -50,7 +51,7 @@ jQuery.event = {
}
// Init the element's event structure
var elemData = jQuery.data( elem );
var elemData = jQuery._data( elem );
// If no elemData is found then we must be trying to bind to one of the
// banned noData elements
......@@ -58,10 +59,7 @@ jQuery.event = {
return;
}
// Use a key less likely to result in collisions for plain JS objects.
// Fixes bug #7150.
var eventKey = elem.nodeType ? "events" : "__events__",
events = elemData[ eventKey ],
var events = elemData[ eventKey ],
eventHandle = elemData.handle;
if ( typeof events === "function" ) {
......@@ -177,8 +175,7 @@ jQuery.event = {
}
var ret, type, fn, j, i = 0, all, namespaces, namespace, special, eventType, handleObj, origType,
eventKey = elem.nodeType ? "events" : "__events__",
elemData = jQuery.data( elem ),
elemData = jQuery.hasData( elem ) && jQuery._data( elem ),
events = elemData && elemData[ eventKey ];
if ( !elemData || !events ) {
......@@ -290,10 +287,10 @@ jQuery.event = {
delete elemData.handle;
if ( typeof elemData === "function" ) {
jQuery.removeData( elem, eventKey );
jQuery.removeData( elem, eventKey, true );
} else if ( jQuery.isEmptyObject( elemData ) ) {
jQuery.removeData( elem );
jQuery.removeData( elem, undefined, true );
}
}
},
......@@ -325,9 +322,16 @@ jQuery.event = {
// Only trigger if we've ever bound an event for it
if ( jQuery.event.global[ type ] ) {
// XXX This code smells terrible. event.js should not be directly
// inspecting the data cache
jQuery.each( jQuery.cache, function() {
if ( this.events && this.events[type] ) {
jQuery.event.trigger( event, data, this.handle.elem );
// internalKey variable is just used to make it easier to find
// and potentially change this stuff later; currently it just
// points to jQuery.expando
var internalKey = jQuery.expando,
internalCache = this[ internalKey ];
if ( internalCache && internalCache.events && internalCache.events[type] ) {
jQuery.event.trigger( event, data, internalCache.handle.elem );
}
});
}
......@@ -353,8 +357,8 @@ jQuery.event = {
// Trigger the event, it is assumed that "handle" is a function
var handle = elem.nodeType ?
jQuery.data( elem, "handle" ) :
(jQuery.data( elem, "__events__" ) || {}).handle;
jQuery._data( elem, "handle" ) :
(jQuery._data( elem, eventKey ) || {}).handle;
if ( handle ) {
handle.apply( elem, data );
......@@ -432,7 +436,7 @@ jQuery.event = {
event.namespace = event.namespace || namespace_sort.join(".");
events = jQuery.data(this, this.nodeType ? "events" : "__events__");
events = jQuery._data(this, eventKey);
if ( typeof events === "function" ) {
events = events.events;
......@@ -603,7 +607,7 @@ jQuery.Event = function( src ) {
// 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 ||
this.isDefaultPrevented = (src.defaultPrevented || src.returnValue === false ||
src.getPreventDefault && src.getPreventDefault()) ? returnTrue : returnFalse;
// Event type
......@@ -787,12 +791,12 @@ if ( !jQuery.support.changeBubbles ) {
return;
}
data = jQuery.data( elem, "_change_data" );
data = jQuery._data( elem, "_change_data" );
val = getVal(elem);
// the current data will be also retrieved by beforeactivate
if ( e.type !== "focusout" || elem.type !== "radio" ) {
jQuery.data( elem, "_change_data", val );
jQuery._data( elem, "_change_data", val );
}
if ( data === undefined || val === data ) {
......@@ -837,7 +841,7 @@ if ( !jQuery.support.changeBubbles ) {
// information
beforeactivate: function( e ) {
var elem = e.target;
jQuery.data( elem, "_change_data", getVal(elem) );
jQuery._data( elem, "_change_data", getVal(elem) );
}
},
......@@ -986,8 +990,8 @@ jQuery.fn.extend({
return this.click( jQuery.proxy( fn, 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 );
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();
......@@ -1075,7 +1079,7 @@ function liveHandler( event ) {
var stop, maxLevel, related, match, handleObj, elem, j, i, l, data, close, namespace, ret,
elems = [],
selectors = [],
events = jQuery.data( this, this.nodeType ? "events" : "__events__" );
events = jQuery._data( this, eventKey );
if ( typeof events === "function" ) {
events = events.events;
......
......@@ -346,7 +346,7 @@ jQuery.fn.extend({
table ?
root(this[i], first) :
this[i],
i > 0 || results.cacheable || this.length > 1 ?
i > 0 || results.cacheable || (this.length > 1 && i > 0) ?
jQuery(fragment).clone(true)[0] :
fragment
);
......@@ -381,17 +381,24 @@ function cloneCopyEvent(orig, ret) {
throw "Cloned data mismatch";
}
var oldData = jQuery.data( orig[nodeIndex] ),
curData = jQuery.data( this, oldData ),
events = oldData && oldData.events;
var internalKey = jQuery.expando,
oldData = jQuery.data( orig[nodeIndex] ),
curData = jQuery.data( this, oldData );
if ( events ) {
delete curData.handle;
curData.events = {};
// Switch to use the internal data object, if it exists, for the next
// stage of data copying
if ( (oldData = oldData[ internalKey ]) ) {
var events = oldData.events;
curData = curData[ internalKey ] = jQuery.extend({}, oldData);
for ( var type in events ) {
for ( var i = 0, l = events[ type ].length; i < l; i++ ) {
jQuery.event.add( this, type, events[ type ][ i ], events[ type ][ i ].data );
if ( events ) {
delete curData.handle;
curData.events = {};
for ( var type in events ) {
for ( var i = 0, l = events[ type ].length; i < l; i++ ) {
jQuery.event.add( this, type, events[ type ][ i ], events[ type ][ i ].data );
}
}
}
}
......@@ -608,8 +615,7 @@ jQuery.extend({
},
cleanData: function( elems ) {
var data, id, cache = jQuery.cache,
special = jQuery.event.special,
var data, id, cache = jQuery.cache, internalKey = jQuery.expando, special = jQuery.event.special,
deleteExpando = jQuery.support.deleteExpando;
for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
......@@ -620,13 +626,14 @@ jQuery.extend({
id = elem[ jQuery.expando ];
if ( id ) {
data = cache[ id ];
data = cache[ id ] && cache[ id ][ internalKey ];
if ( data && data.events ) {
for ( var type in data.events ) {
if ( special[ type ] ) {
jQuery.event.remove( elem, type );
// This is a shortcut to avoid jQuery.event.remove's overhead
} else {
jQuery.removeEvent( elem, type, data.handle );
}
......
......@@ -30,7 +30,7 @@ if ( "getBoundingClientRect" in document.documentElement ) {
// Make sure we're not dealing with a disconnected DOM node
if ( !box || !jQuery.contains( docElem, elem ) ) {
return box || { top: 0, left: 0 };
return box ? { top: box.top, left: box.left } : { top: 0, left: 0 };
}
var body = doc.body,
......
......@@ -7,7 +7,7 @@ jQuery.extend({
}
type = (type || "fx") + "queue";
var q = jQuery.data( elem, type );
var q = jQuery._data( elem, type );
// Speed up dequeue by getting out quickly if this is just a lookup
if ( !data ) {
......@@ -15,7 +15,7 @@ jQuery.extend({
}
if ( !q || jQuery.isArray(data) ) {
q = jQuery.data( elem, type, jQuery.makeArray(data) );
q = jQuery._data( elem, type, jQuery.makeArray(data) );
} else {
q.push( data );
......@@ -46,6 +46,10 @@ jQuery.extend({
jQuery.dequeue(elem, type);
});
}
if ( !queue.length ) {
jQuery.removeData( elem, type + "queue", true );
}
}
});
......
......@@ -4,10 +4,7 @@
jQuery.support = {};
var root = document.documentElement,
script = document.createElement("script"),
div = document.createElement("div"),
id = "script" + jQuery.now();
var div = document.createElement("div");
div.style.display = "none";
div.innerHTML = " <link/><table></table><a href='/a' style='color:red;float:left;opacity:.55;'>a</a><input type='checkbox'/>";
......@@ -64,7 +61,7 @@
deleteExpando: true,
optDisabled: false,
checkClone: false,
scriptEval: false,
_scriptEval: null,
noCloneEvent: true,
boxModel: null,
inlineBlockNeedsLayout: false,
......@@ -77,32 +74,46 @@
select.disabled = true;
jQuery.support.optDisabled = !opt.disabled;
script.type = "text/javascript";
try {
script.appendChild( document.createTextNode( "window." + id + "=1;" ) );
} catch(e) {}
root.insertBefore( script, root.firstChild );
jQuery.support.scriptEval = function() {
if ( jQuery.support._scriptEval === null ) {
var root = document.documentElement,
script = document.createElement("script"),
id = "script" + jQuery.now();
script.type = "text/javascript";
try {
script.appendChild( document.createTextNode( "window." + id + "=1;" ) );
} catch(e) {}
root.insertBefore( script, root.firstChild );
// Make sure that the execution of code works by injecting a script
// tag with appendChild/createTextNode
// (IE doesn't support this, fails, and uses .text instead)
if ( window[ id ] ) {
jQuery.support._scriptEval = true;
delete window[ id ];
} else {
jQuery.support._scriptEval = false;
}
root.removeChild( script );
// release memory in IE
root = script = id = null;
}
// Make sure that the execution of code works by injecting a script
// tag with appendChild/createTextNode
// (IE doesn't support this, fails, and uses .text instead)
if ( window[ id ] ) {
jQuery.support.scriptEval = true;
delete window[ id ];
}
return jQuery.support._scriptEval;
};
// Test to see if it's possible to delete an expando from an element
// Fails in Internet Explorer
try {
delete script.test;
delete div.test;
} catch(e) {
jQuery.support.deleteExpando = false;
}
root.removeChild( script );
if ( div.attachEvent && div.fireEvent ) {
div.attachEvent("onclick", function click() {
// Cloning a node shouldn't copy over any
......@@ -177,6 +188,14 @@
var el = document.createElement("div");
eventName = "on" + eventName;
// 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 ( !el.attachEvent ) {
return true;
}
var isSupported = (eventName in el);
if ( !isSupported ) {
el.setAttribute(eventName, "return;");
......@@ -191,6 +210,6 @@
jQuery.support.changeBubbles = eventSupported("change");
// release memory in IE
root = script = div = all = a = null;
div = all = a = null;
})();
})( jQuery );
......@@ -6,7 +6,14 @@ var runtil = /Until$/,
rmultiselector = /,/,
isSimple = /^.[^:#\[\.,]*$/,
slice = Array.prototype.slice,
POS = jQuery.expr.match.POS;
POS = jQuery.expr.match.POS,
// 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 ) {
......@@ -196,7 +203,12 @@ jQuery.each({
}
}, function( name, fn ) {
jQuery.fn[ name ] = function( until, selector ) {
var ret = jQuery.map( this, fn, until );
var ret = jQuery.map( this, fn, until ),
// The variable 'args' was introduced in
// https://github.com/jquery/jquery/commit/52a0238
// to work around a bug in Chrome 10 (Dev) and should be removed when the bug is fixed.
// http://code.google.com/p/v8/issues/detail?id=1050
args = slice.call(arguments);
if ( !runtil.test( name ) ) {
selector = until;
......@@ -206,13 +218,13 @@ jQuery.each({
ret = jQuery.filter( selector, ret );
}
ret = this.length > 1 ? jQuery.unique( ret ) : 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(",") );
return this.pushStack( ret, name, args.join(",") );
};
});
......
<?php header("X-Content-Security-Policy-Report-Only: allow *"); ?>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>CSP Test Page</title>
<script src="../src/core.js"></script>
<script src="../src/support.js"></script>
<script src="../src/data.js"></script>
<script src="../src/queue.js"></script>
<script src="../src/attributes.js"></script>
<script src="../src/event.js"></script>
<script src="../src/sizzle/sizzle.js"></script>
<script src="../src/sizzle-jquery.js"></script>
<script src="../src/traversing.js"></script>
<script src="../src/manipulation.js"></script>
<script src="../src/css.js"></script>
<script src="../src/ajax.js"></script>
<script src="../src/ajax/jsonp.js"></script>
<script src="../src/ajax/script.js"></script>
<script src="../src/ajax/xhr.js"></script>
<script src="../src/effects.js"></script>
<script src="../src/offset.js"></script>
<script src="../src/dimensions.js"></script>
</head>
<body>
<p>CSP Test Page</p>
</body>
</html>
......@@ -45,3 +45,52 @@ function t(a,b,c) {
function url(value) {
return value + (/\?/.test(value) ? "&" : "?") + new Date().getTime() + "" + parseInt(Math.random()*100000);
}
(function () {
// Store the old counts so that we only assert on tests that have actually leaked,
// instead of asserting every time a test has leaked sometime in the past
var oldCacheLength = 0,
oldFragmentsLength = 0,
oldTimersLength = 0,
oldActive = 0;
/**
* Ensures that tests have cleaned up properly after themselves. Should be passed as the
* teardown function on all modules' lifecycle object.
*/
this.moduleTeardown = function () {
var i, fragmentsLength = 0, cacheLength = 0;
// Allow QUnit.reset to clean up any attached elements before checking for leaks
QUnit.reset();
for ( i in jQuery.cache ) {
++cacheLength;
}
jQuery.fragments = {};
for ( i in jQuery.fragments ) {
++fragmentsLength;
}
// Because QUnit doesn't have a mechanism for retrieving the number of expected assertions for a test,
// if we unconditionally assert any of these, the test will fail with too many assertions :|
if ( cacheLength !== oldCacheLength ) {
equals( cacheLength, oldCacheLength, "No unit tests leak memory in jQuery.cache" );
oldCacheLength = cacheLength;
}
if ( fragmentsLength !== oldFragmentsLength ) {
equals( fragmentsLength, oldFragmentsLength, "No unit tests leak memory in jQuery.fragments" );
oldFragmentsLength = fragmentsLength;
}
if ( jQuery.timers.length !== oldTimersLength ) {
equals( jQuery.timers.length, oldTimersLength, "No timers are still running" );
oldTimersLength = jQuery.timers.length;
}
if ( jQuery.active !== oldActive ) {
equals( jQuery.active, 0, "No AJAX requests are still active" );
oldActive = jQuery.active;
}
}
}());
\ No newline at end of file
......@@ -264,7 +264,7 @@ Z</textarea>
<div id="slidetoggleout" class='chain test out'>slideToggleOut<div>slideToggleOut</div></div>
<div id="fadetogglein" class='chain test'>fadeToggleIn<div>fadeToggleIn</div></div>
<div id="fadetoggleout" class='chain test out'>fadeToggleOut<div>fadeToggleOut</div></div>
<div id="fadetoggleout" class='chain test out'>fadeToggleOut<div>fadeToggleOut</div></div>
<div id="fadeto" class='chain test'>fadeTo<div>fadeTo</div></div>
</div>
......
module("ajax");
module("ajax", { teardown: moduleTeardown });
// Safari 3 randomly crashes when running these tests,
// but only in the full suite - you can run just the Ajax
......@@ -1114,286 +1114,249 @@ test("jQuery.getScript(String, Function) - no callback", function() {
});
});
test("jQuery.ajax() - JSONP, Local", function() {
expect(14);
var count = 0;
function plus(){ if ( ++count == 14 ) start(); }
stop();
jQuery.each( [ "Same Domain", "Cross Domain" ] , function( crossDomain , label ) {
jQuery.ajax({
url: "data/jsonp.php",
dataType: "jsonp",
success: function(data){
ok( data.data, "JSON results returned (GET, no callback)" );
plus();
},
error: function(data){
ok( false, "Ajax error JSON (GET, no callback)" );
plus();
}
});
jQuery.ajax({
url: "data/jsonp.php?callback=?",
dataType: "jsonp",
success: function(data){
ok( data.data, "JSON results returned (GET, url callback)" );
plus();
},
error: function(data){
ok( false, "Ajax error JSON (GET, url callback)" );
plus();
}
});
test("jQuery.ajax() - JSONP, " + label, function() {
expect(17);
jQuery.ajax({
url: "data/jsonp.php",
dataType: "jsonp",
data: "callback=?",
success: function(data){
ok( data.data, "JSON results returned (GET, data callback)" );
plus();
},
error: function(data){
ok( false, "Ajax error JSON (GET, data callback)" );
plus();
}
});
var count = 0;
function plus(){ if ( ++count == 17 ) start(); }
jQuery.ajax({
url: "data/jsonp.php?callback=??",
dataType: "jsonp",
success: function(data){
ok( data.data, "JSON results returned (GET, url context-free callback)" );
plus();
},
error: function(data){
ok( false, "Ajax error JSON (GET, url context-free callback)" );
plus();
}
});
stop();
jQuery.ajax({
url: "data/jsonp.php",
dataType: "jsonp",
data: "callback=??",
success: function(data){
ok( data.data, "JSON results returned (GET, data context-free callback)" );
plus();
},
error: function(data){
ok( false, "Ajax error JSON (GET, data context-free callback)" );
plus();
}
});
jQuery.ajax({
url: "data/jsonp.php",
dataType: "jsonp",
crossDomain: crossDomain,
success: function(data){
ok( data.data, "JSON results returned (GET, no callback)" );
plus();
},
error: function(data){
ok( false, "Ajax error JSON (GET, no callback)" );
plus();
}
});
jQuery.ajax({
url: "data/jsonp.php/??",
dataType: "jsonp",
success: function(data){
ok( data.data, "JSON results returned (GET, REST-like)" );
plus();
},
error: function(data){
ok( false, "Ajax error JSON (GET, REST-like)" );
plus();
}
});
jQuery.ajax({
url: "data/jsonp.php?callback=?",
dataType: "jsonp",
crossDomain: crossDomain,
success: function(data){
ok( data.data, "JSON results returned (GET, url callback)" );
plus();
},
error: function(data){
ok( false, "Ajax error JSON (GET, url callback)" );
plus();
}
});
jQuery.ajax({
url: "data/jsonp.php/???json=1",
dataType: "jsonp",
success: function(data){
strictEqual( jQuery.type(data), "array", "JSON results returned (GET, REST-like with param)" );
plus();
},
error: function(data){
ok( false, "Ajax error JSON (GET, REST-like with param)" );
plus();
}
});
jQuery.ajax({
url: "data/jsonp.php",
dataType: "jsonp",
crossDomain: crossDomain,
data: "callback=?",
success: function(data){
ok( data.data, "JSON results returned (GET, data callback)" );
plus();
},
error: function(data){
ok( false, "Ajax error JSON (GET, data callback)" );
plus();
}
});
jQuery.ajax({
url: "data/jsonp.php",
dataType: "jsonp",
data: {
callback: "?"
},
success: function(data){
ok( data.data, "JSON results returned (GET, processed data callback)" );
plus();
},
error: function(data){
ok( false, "Ajax error JSON (GET, processed data callback)" );
plus();
}
});
jQuery.ajax({
url: "data/jsonp.php?callback=??",
dataType: "jsonp",
crossDomain: crossDomain,
success: function(data){
ok( data.data, "JSON results returned (GET, url context-free callback)" );
plus();
},
error: function(data){
ok( false, "Ajax error JSON (GET, url context-free callback)" );
plus();
}
});
jQuery.ajax({
url: "data/jsonp.php",
dataType: "jsonp",
jsonp: "callback",
success: function(data){
ok( data.data, "JSON results returned (GET, data obj callback)" );
plus();
},
error: function(data){
ok( false, "Ajax error JSON (GET, data obj callback)" );
plus();
}
});
jQuery.ajax({
url: "data/jsonp.php",
dataType: "jsonp",
crossDomain: crossDomain,
data: "callback=??",
success: function(data){
ok( data.data, "JSON results returned (GET, data context-free callback)" );
plus();
},
error: function(data){
ok( false, "Ajax error JSON (GET, data context-free callback)" );
plus();
}
});
jQuery.ajax({
url: "data/jsonp.php",
dataType: "jsonp",
jsonpCallback: "jsonpResults",
success: function(data){
ok( data.data, "JSON results returned (GET, custom callback name)" );
plus();
},
error: function(data){
ok( false, "Ajax error JSON (GET, custom callback name)" );
plus();
}
});
jQuery.ajax({
url: "data/jsonp.php/??",
dataType: "jsonp",
crossDomain: crossDomain,
success: function(data){
ok( data.data, "JSON results returned (GET, REST-like)" );
plus();
},
error: function(data){
ok( false, "Ajax error JSON (GET, REST-like)" );
plus();
}
});
jQuery.ajax({
type: "POST",
url: "data/jsonp.php",
dataType: "jsonp",
success: function(data){
ok( data.data, "JSON results returned (POST, no callback)" );
plus();
},
error: function(data){
ok( false, "Ajax error JSON (GET, data obj callback)" );
plus();
}
});
jQuery.ajax({
url: "data/jsonp.php/???json=1",
dataType: "jsonp",
crossDomain: crossDomain,
success: function(data){
strictEqual( jQuery.type(data), "array", "JSON results returned (GET, REST-like with param)" );
plus();
},
error: function(data){
ok( false, "Ajax error JSON (GET, REST-like with param)" );
plus();
}
});
jQuery.ajax({
type: "POST",
url: "data/jsonp.php",
data: "callback=?",
dataType: "jsonp",
success: function(data){
ok( data.data, "JSON results returned (POST, data callback)" );
plus();
},
error: function(data){
ok( false, "Ajax error JSON (POST, data callback)" );
plus();
}
});
jQuery.ajax({
url: "data/jsonp.php",
dataType: "jsonp",
crossDomain: crossDomain,
data: {
callback: "?"
},
success: function(data){
ok( data.data, "JSON results returned (GET, processed data callback)" );
plus();
},
error: function(data){
ok( false, "Ajax error JSON (GET, processed data callback)" );
plus();
}
});
jQuery.ajax({
type: "POST",
url: "data/jsonp.php",
jsonp: "callback",
dataType: "jsonp",
success: function(data){
ok( data.data, "JSON results returned (POST, data obj callback)" );
plus();
},
error: function(data){
ok( false, "Ajax error JSON (POST, data obj callback)" );
plus();
}
});
jQuery.ajax({
url: "data/jsonp.php",
dataType: "jsonp",
crossDomain: crossDomain,
jsonp: "callback",
success: function(data){
ok( data.data, "JSON results returned (GET, data obj callback)" );
plus();
},
error: function(data){
ok( false, "Ajax error JSON (GET, data obj callback)" );
plus();
}
});
//#7578
jQuery.ajax({
url: "data/jsonp.php",
dataType: "jsonp",
beforeSend: function(){
strictEqual( this.cache, false, "cache must be false on JSON request" );
window.jsonpResults = function(data) {
ok( data.data, "JSON results returned (GET, custom callback function)" );
window.jsonpResults = undefined;
plus();
return false;
}
});
});
test("jQuery.ajax() - JSONP - Custom JSONP Callback", function() {
expect(1);
stop();
window.jsonpResults = function(data) {
ok( data.data, "JSON results returned (GET, custom callback function)" );
window.jsonpResults = undefined;
start();
};
jQuery.ajax({
url: "data/jsonp.php",
dataType: "jsonp",
jsonpCallback: "jsonpResults"
});
});
test("jQuery.ajax() - JSONP, Remote", function() {
expect(4);
};
var count = 0;
function plus(){ if ( ++count == 4 ) start(); }
jQuery.ajax({
url: "data/jsonp.php",
dataType: "jsonp",
crossDomain: crossDomain,
jsonpCallback: "jsonpResults",
success: function(data){
ok( data.data, "JSON results returned (GET, custom callback name)" );
plus();
},
error: function(data){
ok( false, "Ajax error JSON (GET, custom callback name)" );
plus();
}
});
var base = window.location.href.replace(/[^\/]*$/, "");
jQuery.ajax({
type: "POST",
url: "data/jsonp.php",
dataType: "jsonp",
crossDomain: crossDomain,
success: function(data){
ok( data.data, "JSON results returned (POST, no callback)" );
plus();
},
error: function(data){
ok( false, "Ajax error JSON (GET, data obj callback)" );
plus();
}
});
stop();
jQuery.ajax({
type: "POST",
url: "data/jsonp.php",
data: "callback=?",
dataType: "jsonp",
crossDomain: crossDomain,
success: function(data){
ok( data.data, "JSON results returned (POST, data callback)" );
plus();
},
error: function(data){
ok( false, "Ajax error JSON (POST, data callback)" );
plus();
}
});
jQuery.ajax({
url: base + "data/jsonp.php",
dataType: "jsonp",
success: function(data){
ok( data.data, "JSON results returned (GET, no callback)" );
plus();
},
error: function(data){
ok( false, "Ajax error JSON (GET, no callback)" );
plus();
}
});
jQuery.ajax({
type: "POST",
url: "data/jsonp.php",
jsonp: "callback",
dataType: "jsonp",
crossDomain: crossDomain,
success: function(data){
ok( data.data, "JSON results returned (POST, data obj callback)" );
plus();
},
error: function(data){
ok( false, "Ajax error JSON (POST, data obj callback)" );
plus();
}
});
jQuery.ajax({
url: base + "data/jsonp.php?callback=?",
dataType: "jsonp",
success: function(data){
ok( data.data, "JSON results returned (GET, url callback)" );
plus();
},
error: function(data){
ok( false, "Ajax error JSON (GET, url callback)" );
plus();
}
});
//#7578
jQuery.ajax({
url: "data/jsonp.php",
dataType: "jsonp",
crossDomain: crossDomain,
beforeSend: function(){
strictEqual( this.cache, false, "cache must be false on JSON request" );
plus();
return false;
}
});
jQuery.ajax({
url: base + "data/jsonp.php",
dataType: "jsonp",
data: "callback=?",
success: function(data){
ok( data.data, "JSON results returned (GET, data callback)" );
plus();
},
error: function(data){
ok( false, "Ajax error JSON (GET, data callback)" );
plus();
}
});
jQuery.ajax({
url: "data/jsonp.php?callback=XXX",
dataType: "jsonp",
jsonp: false,
jsonpCallback: "XXX",
crossDomain: crossDomain,
beforeSend: function() {
ok( /^data\/jsonp.php\?callback=XXX&_=\d+$/.test( this.url ) ,
"The URL wasn't messed with (GET, custom callback name with no url manipulation)" );
plus();
},
success: function(data){
ok( data.data, "JSON results returned (GET, custom callback name with no url manipulation)" );
plus();
},
error: function(data){
ok( false, "Ajax error JSON (GET, custom callback name with no url manipulation)" );
plus();
}
});
jQuery.ajax({
url: base + "data/jsonp.php",
dataType: "jsonp",
jsonp: "callback",
success: function(data){
ok( data.data, "JSON results returned (GET, data obj callback)" );
plus();
},
error: function(data){
ok( false, "Ajax error JSON (GET, data obj callback)" );
plus();
}
});
});
......@@ -1865,25 +1828,19 @@ test("jQuery ajax - failing cross-domain", function() {
var i = 2;
if ( jQuery.ajax({
jQuery.ajax({
url: 'http://somewebsitethatdoesnotexist-67864863574657654.com',
success: function(){ ok( false , "success" ); },
error: function(xhr,_,e){ ok( true , "file not found: " + xhr.status + " => " + e ); },
complete: function() { if ( ! --i ) start(); }
}) === false ) {
ok( true , "no transport" );
if ( ! --i ) start();
}
});
if ( jQuery.ajax({
jQuery.ajax({
url: 'http://www.google.com',
success: function(){ ok( false , "success" ); },
error: function(xhr,_,e){ ok( true , "access denied: " + xhr.status + " => " + e ); },
complete: function() { if ( ! --i ) start(); }
}) === false ) {
ok( true , "no transport" );
if ( ! --i ) start();
}
});
});
......@@ -1937,7 +1894,7 @@ test( "jQuery.ajax - statusCode" , function() {
404: function() {
ok( ! isSuccess , name );
}
}
};
}
jQuery.each( {
......
module("attributes");
module("attributes", { teardown: moduleTeardown });
var bareObj = function(value) { return value; };
var functionReturningObj = function(value) { return (function() { return value; }); };
......@@ -703,12 +703,12 @@ var testToggleClass = function(valueObj) {
// toggleClass storage
e.toggleClass(true);
ok( e.get(0).className === "", "Assert class is empty (data was empty)" );
ok( e[0].className === "", "Assert class is empty (data was empty)" );
e.addClass("testD testE");
ok( e.is(".testD.testE"), "Assert class present" );
e.toggleClass();
ok( !e.is(".testD.testE"), "Assert class not present" );
ok( e.data('__className__') === 'testD testE', "Assert data was stored" );
ok( jQuery._data(e[0], '__className__') === 'testD testE', "Assert data was stored" );
e.toggleClass();
ok( e.is(".testD.testE"), "Assert class present (restored from data)" );
e.toggleClass(false);
......@@ -720,11 +720,9 @@ var testToggleClass = function(valueObj) {
e.toggleClass();
ok( e.is(".testD.testE"), "Assert class present (restored from data)" );
// Cleanup
e.removeClass("testD");
e.removeData('__className__');
jQuery.removeData(e[0], '__className__', true);
};
test("toggleClass(String|boolean|undefined[, boolean])", function() {
......@@ -785,7 +783,7 @@ test("toggleClass(Fucntion[, boolean]) with incoming value", function() {
// Cleanup
e.removeClass("test");
e.removeData('__className__');
jQuery.removeData(e[0], '__className__', true);
});
test("addClass, removeClass, hasClass", function() {
......
module("core");
module("core", { teardown: moduleTeardown });
test("Basic requirements", function() {
expect(7);
......@@ -21,7 +21,7 @@ test("jQuery()", function() {
equals( jQuery(null).length, 0, "jQuery(null) === jQuery([])" );
equals( jQuery("").length, 0, "jQuery('') === jQuery([])" );
var obj = jQuery("div")
var obj = jQuery("div");
equals( jQuery(obj).selector, "div", "jQuery(jQueryObj) == jQueryObj" );
// can actually yield more than one, when iframes are included, the window is an array as well
......@@ -85,10 +85,16 @@ test("jQuery()", function() {
exec = true;
elem.click();
// manually clean up detached elements
elem.remove();
for ( var i = 0; i < 3; ++i ) {
elem = jQuery("<input type='text' value='TEST' />");
}
equals( elem[0].defaultValue, "TEST", "Ensure cached nodes are cloned properly (Bug #6655)" );
// manually clean up detached elements
elem.remove();
});
test("selector state", function() {
......@@ -1003,7 +1009,7 @@ test("jQuery._Deferred()", function() {
test("jQuery.Deferred()", function() {
expect( 4 );
expect( 10 );
jQuery.Deferred( function( defer ) {
strictEqual( this , defer , "Defer passed as this & first argument" );
......@@ -1023,11 +1029,35 @@ test("jQuery.Deferred()", function() {
}, function() {
ok( true , "Error on reject" );
});
( new jQuery.Deferred( function( defer ) {
strictEqual( this , defer , "Defer passed as this & first argument (new)" );
this.resolve( "done" );
}) ).then( function( value ) {
strictEqual( value , "done" , "Passed function executed (new)" );
});
( new jQuery.Deferred() ).resolve().then( function() {
ok( true , "Success on resolve (new)" );
}, function() {
ok( false , "Error on resolve (new)" );
});
( new jQuery.Deferred() ).reject().then( function() {
ok( false , "Success on reject (new)" );
}, function() {
ok( true , "Error on reject (new)" );
});
var tmp = jQuery.Deferred();
strictEqual( tmp.promise() , tmp.promise() , "Test deferred always return same promise" );
strictEqual( tmp.promise() , tmp.promise().promise() , "Test deferred's promise always return same promise as deferred" );
});
test("jQuery.when()", function() {
expect( 21 );
expect( 23 );
// Some other objects
jQuery.each( {
......@@ -1050,6 +1080,10 @@ test("jQuery.when()", function() {
} );
ok( jQuery.isFunction( jQuery.when().then( function( resolveValue ) {
strictEqual( resolveValue , undefined , "Test the promise was resolved with no parameter" );
} ).promise ) , "Test calling when with no parameter triggers the creation of a new Promise" );
var cache, i;
for( i = 1 ; i < 4 ; i++ ) {
......@@ -1063,3 +1097,106 @@ test("jQuery.when()", function() {
});
}
});
test("jQuery.when() - joined", function() {
expect(8);
jQuery.when( 1, 2, 3 ).done( function( a, b, c ) {
strictEqual( a , 1 , "Test first param is first resolved value - non-observables" );
strictEqual( b , 2 , "Test second param is second resolved value - non-observables" );
strictEqual( c , 3 , "Test third param is third resolved value - non-observables" );
}).fail( function() {
ok( false , "Test the created deferred was resolved - non-observables");
});
var successDeferred = jQuery.Deferred().resolve( 1 , 2 , 3 ),
errorDeferred = jQuery.Deferred().reject( "error" , "errorParam" );
jQuery.when( 1 , successDeferred , 3 ).done( function( a, b, c ) {
strictEqual( a , 1 , "Test first param is first resolved value - resolved observable" );
same( b , [ 1 , 2 , 3 ] , "Test second param is second resolved value - resolved observable" );
strictEqual( c , 3 , "Test third param is third resolved value - resolved observable" );
}).fail( function() {
ok( false , "Test the created deferred was resolved - resolved observable");
});
jQuery.when( 1 , errorDeferred , 3 ).done( function() {
ok( false , "Test the created deferred was rejected - rejected observable");
}).fail( function( error , errorParam ) {
strictEqual( error , "error" , "Test first param is first rejected value - rejected observable" );
strictEqual( errorParam , "errorParam" , "Test second param is second rejected value - rejected observable" );
});
});
test("jQuery.subclass", function(){
expect(378);
var Subclass = jQuery.subclass(),
SubclassSubclass = Subclass.subclass(),
jQueryDocument = jQuery(document),
selectors, contexts, methods, method, arg, description;
jQueryDocument.toString = function(){ return 'jQueryDocument'; };
Subclass.fn.subclassMethod = function(){};
SubclassSubclass.fn.subclassSubclassMethod = function(){};
selectors = [
'body',
'html, body',
'<div></div>'
];
methods = [ // all methods that return a new jQuery instance
['eq', 1],
['add', document],
['end'],
['has'],
['closest', 'div'],
['filter', document],
['find', 'div']
];
contexts = [undefined, document, jQueryDocument];
jQuery.each(selectors, function(i, selector){
jQuery.each(methods, function(){
method = this[0];
arg = this[1];
jQuery.each(contexts, function(i, context){
description = '("'+selector+'", '+context+').'+method+'('+(arg||'')+')';
same(
jQuery(selector, context)[method](arg).subclassMethod, undefined,
'jQuery'+description+' doesnt have Subclass methods'
);
same(
jQuery(selector, context)[method](arg).subclassSubclassMethod, undefined,
'jQuery'+description+' doesnt have SubclassSubclass methods'
);
same(
Subclass(selector, context)[method](arg).subclassMethod, Subclass.fn.subclassMethod,
'Subclass'+description+' has Subclass methods'
);
same(
Subclass(selector, context)[method](arg).subclassSubclassMethod, undefined,
'Subclass'+description+' doesnt have SubclassSubclass methods'
);
same(
SubclassSubclass(selector, context)[method](arg).subclassMethod, Subclass.fn.subclassMethod,
'SubclassSubclass'+description+' has Subclass methods'
);
same(
SubclassSubclass(selector, context)[method](arg).subclassSubclassMethod, SubclassSubclass.fn.subclassSubclassMethod,
'SubclassSubclass'+description+' has SubclassSubclass methods'
);
});
});
});
});
module("css");
module("css", { teardown: moduleTeardown });
test("css(String|Hash)", function() {
expect(41);
......@@ -320,3 +320,16 @@ test(":visible selector works properly on children with a hidden parent (bug #45
jQuery('#table').css('display', 'none').html('<tr><td>cell</td><td>cell</td></tr>');
equals(jQuery('#table td:visible').length, 0, "hidden cell children not perceived as visible");
});
test("internal ref to elem.runtimeStyle (bug #7608)", function () {
expect(1);
var result = true;
try {
jQuery("#foo").css( { width: "0%" } ).css("width");
} catch (e) {
result = false;
}
ok( result, "elem.runtimeStyle does not throw exception" );
});
module("data");
module("data", { teardown: moduleTeardown });
test("expando", function(){
expect(6);
expect(1);
equals("expando" in jQuery, true, "jQuery is exposing the expando");
});
var obj = {};
equals( jQuery.data(obj), obj, "jQuery.data(obj) returns the object");
equals( jQuery.expando in obj, false, "jQuery.data(obj) did not add an expando to the object" );
function dataTests (elem) {
// expect(32)
obj = {};
jQuery.data(obj, 'test');
equals( jQuery.expando in obj, false, "jQuery.data(obj,key) did not add an expando to the object" );
function getCacheLength() {
var cacheLength = 0;
for (var i in jQuery.cache) {
++cacheLength;
}
obj = {};
jQuery.data(obj, "foo", "bar");
equals( jQuery.expando in obj, false, "jQuery.data(obj,key,value) did not add an expando to the object" );
equals( obj.foo, "bar", "jQuery.data(obj,key,value) sets fields directly on the object." );
});
return cacheLength;
}
test("jQuery.acceptData", function() {
expect(7);
equals( jQuery.data(elem, "foo"), undefined, "No data exists initially" );
strictEqual( jQuery.hasData(elem), false, "jQuery.hasData agrees no data exists initially" );
ok( jQuery.acceptData( document ), "document" );
ok( jQuery.acceptData( document.documentElement ), "documentElement" );
ok( jQuery.acceptData( {} ), "object" );
ok( !jQuery.acceptData( document.createElement("embed") ), "embed" );
ok( !jQuery.acceptData( document.createElement("applet") ), "applet" );
var dataObj = jQuery.data(elem);
equals( typeof dataObj, "object", "Calling data with no args gives us a data object reference" );
strictEqual( jQuery.data(elem), dataObj, "Calling jQuery.data returns the same data object when called multiple times" );
var flash = document.createElement("object");
flash.setAttribute("classid", "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000");
ok( jQuery.acceptData( flash ), "flash" );
strictEqual( jQuery.hasData(elem), false, "jQuery.hasData agrees no data exists even when an empty data obj exists" );
var applet = document.createElement("object");
applet.setAttribute("classid", "clsid:8AD9C840-044E-11D1-B3E9-00805F499D93");
ok( !jQuery.acceptData( applet ), "applet" );
});
dataObj.foo = "bar";
equals( jQuery.data(elem, "foo"), "bar", "Data is readable by jQuery.data when set directly on a returned data object" );
test("jQuery.data", function() {
expect(15);
var div = document.createElement("div");
strictEqual( jQuery.hasData(elem), true, "jQuery.hasData agrees data exists when data exists" );
ok( jQuery.data(div, "test") === undefined, "Check for no data exists" );
jQuery.data(elem, "foo", "baz");
equals( jQuery.data(elem, "foo"), "baz", "Data can be changed by jQuery.data" );
equals( dataObj.foo, "baz", "Changes made through jQuery.data propagate to referenced data object" );
jQuery.data(div, "test", "success");
equals( jQuery.data(div, "test"), "success", "Check for added data" );
jQuery.data(elem, "foo", undefined);
equals( jQuery.data(elem, "foo"), "baz", "Data is not unset by passing undefined to jQuery.data" );
ok( jQuery.data(div, "notexist") === undefined, "Check for no data exists" );
jQuery.data(elem, "foo", null);
strictEqual( jQuery.data(elem, "foo"), null, "Setting null using jQuery.data works OK" );
var data = jQuery.data(div);
same( data, { "test": "success" }, "Return complete data set" );
jQuery.data(elem, "foo", "foo1");
jQuery.data(div, "test", "overwritten");
equals( jQuery.data(div, "test"), "overwritten", "Check for overwritten data" );
jQuery.data(elem, { "bar" : "baz", "boom" : "bloz" });
strictEqual( jQuery.data(elem, "foo"), "foo1", "Passing an object extends the data object instead of replacing it" );
equals( jQuery.data(elem, "boom"), "bloz", "Extending the data object works" );
jQuery.data(div, "test", undefined);
equals( jQuery.data(div, "test"), "overwritten", "Check that data wasn't removed");
jQuery._data(elem, "foo", "foo2");
equals( jQuery._data(elem, "foo"), "foo2", "Setting internal data works" );
equals( jQuery.data(elem, "foo"), "foo1", "Setting internal data does not override user data" );
jQuery.data(div, "test", null);
ok( jQuery.data(div, "test") === null, "Check for null data");
var internalDataObj = jQuery.data(elem, jQuery.expando);
strictEqual( jQuery._data(elem), internalDataObj, "Internal data object is accessible via jQuery.expando property" );
notStrictEqual( dataObj, internalDataObj, "Internal data object is not the same as user data object" );
jQuery.data(div, "test3", "orig");
jQuery.data(div, { "test": "in", "test2": "in2" });
equals( jQuery.data(div, "test"), "in", "Verify setting an object in data" );
equals( jQuery.data(div, "test2"), "in2", "Verify setting an object in data" );
equals( jQuery.data(div, "test3"), "orig", "Verify original not overwritten" );
strictEqual( elem.boom, undefined, "Data is never stored directly on the object" );
var obj = {};
jQuery.data( obj, "prop", true );
jQuery.removeData(elem, "foo");
strictEqual( jQuery.data(elem, "foo"), undefined, "jQuery.removeData removes single properties" );
ok( obj.prop, "Data is being stored on the object" );
equals( jQuery.data( obj, "prop" ), true, "Make sure the right value is retrieved" );
jQuery.removeData(elem);
strictEqual( jQuery.data(elem, jQuery.expando), internalDataObj, "jQuery.removeData does not remove internal data if it exists" );
jQuery.data( window, "BAD", true );
ok( !window[ jQuery.expando ], "Make sure there is no expando on the window object." );
ok( !window.BAD, "And make sure that the property wasn't set directly on the window." );
ok( jQuery.data( window, "BAD" ), "Make sure that the value was set." );
});
jQuery.removeData(elem, undefined, true);
test("jQuery.hasData", function() {
expect(6);
strictEqual( jQuery.data(elem, jQuery.expando), undefined, "jQuery.removeData on internal data works" );
strictEqual( jQuery.hasData(elem), false, "jQuery.hasData agrees all data has been removed from object" );
jQuery._data(elem, "foo", "foo2");
strictEqual( jQuery.hasData(elem), true, "jQuery.hasData shows data exists even if it is only internal data" );
jQuery.data(elem, "foo", "foo1");
equals( jQuery._data(elem, "foo"), "foo2", "Setting user data does not override internal data" );
jQuery.removeData(elem, undefined, true);
equals( jQuery.data(elem, "foo"), "foo1", "jQuery.removeData for internal data does not remove user data" );
if (elem.nodeType) {
var oldCacheLength = getCacheLength();
jQuery.removeData(elem, "foo");
equals( getCacheLength(), oldCacheLength - 1, "Removing the last item in the data object destroys it" );
}
else {
jQuery.removeData(elem, "foo");
var expected, actual;
function testData(obj) {
equals( jQuery.hasData(obj), false, "No data exists" );
jQuery.data( obj, "foo", "bar" );
equals( jQuery.hasData(obj), true, "Data exists" );
jQuery.removeData( obj, "foo" );
equals( jQuery.hasData(obj), false, "Data was removed" );
if (jQuery.support.deleteExpando) {
expected = false;
actual = jQuery.expando in elem;
}
else {
expected = null;
actual = elem[ jQuery.expando ];
}
equals( actual, expected, "Removing the last item in the data object destroys it" );
}
testData(document.createElement('div'));
testData({});
jQuery.data(elem, "foo", "foo1");
jQuery._data(elem, "foo", "foo2");
equals( jQuery.data(elem, "foo"), "foo1", "(sanity check) Ensure data is set in user data object" );
equals( jQuery._data(elem, "foo"), "foo2", "(sanity check) Ensure data is set in internal data object" );
jQuery.removeData(elem, "foo", true);
strictEqual( jQuery.data(elem, jQuery.expando), undefined, "Removing the last item in internal data destroys the internal data object" );
jQuery._data(elem, "foo", "foo2");
equals( jQuery._data(elem, "foo"), "foo2", "(sanity check) Ensure data is set in internal data object" );
jQuery.removeData(elem, "foo");
equals( jQuery._data(elem, "foo"), "foo2", "(sanity check) jQuery.removeData for user data does not remove internal data" );
if (elem.nodeType) {
oldCacheLength = getCacheLength();
jQuery.removeData(elem, "foo", true);
equals( getCacheLength(), oldCacheLength - 1, "Removing the last item in the internal data object also destroys the user data object when it is empty" );
}
else {
jQuery.removeData(elem, "foo", true);
if (jQuery.support.deleteExpando) {
expected = false;
actual = jQuery.expando in elem;
}
else {
expected = null;
actual = elem[ jQuery.expando ];
}
equals( actual, expected, "Removing the last item in the internal data object also destroys the user data object when it is empty" );
}
}
test("jQuery.data", function() {
expect(128);
var div = document.createElement("div");
dataTests(div);
dataTests({});
// remove bound handlers from window object to stop potential false positives caused by fix for #5280 in
// transports/xhr.js
jQuery(window).unbind("unload");
dataTests(window);
dataTests(document);
// clean up unattached element
jQuery(div).remove();
});
test("jQuery.acceptData", function() {
expect(7);
ok( jQuery.acceptData( document ), "document" );
ok( jQuery.acceptData( document.documentElement ), "documentElement" );
ok( jQuery.acceptData( {} ), "object" );
ok( !jQuery.acceptData( document.createElement("embed") ), "embed" );
ok( !jQuery.acceptData( document.createElement("applet") ), "applet" );
var flash = document.createElement("object");
flash.setAttribute("classid", "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000");
ok( jQuery.acceptData( flash ), "flash" );
var applet = document.createElement("object");
applet.setAttribute("classid", "clsid:8AD9C840-044E-11D1-B3E9-00805F499D93");
ok( !jQuery.acceptData( applet ), "applet" );
});
test(".data()", function() {
......@@ -98,7 +179,6 @@ test(".data()", function() {
var div = jQuery("#foo");
strictEqual( div.data("foo"), undefined, "Make sure that missing result is undefined" );
div.data("test", "success");
same( div.data(), {test: "success"}, "data() get the entire data object" );
strictEqual( div.data("foo"), undefined, "Make sure that missing result is still undefined" );
......@@ -107,7 +187,7 @@ test(".data()", function() {
equals( nodiv.data(), null, "data() on empty set returns null" );
var obj = { foo: "bar" };
equals( jQuery(obj).data(), obj, "Retrieve data object from a wrapped JS object (#7524)" );
deepEqual( jQuery(obj).data(), {}, "Retrieve data object from a wrapped JS object (#7524)" );
})
test(".data(String) and .data(String, Object)", function() {
......@@ -194,11 +274,14 @@ test(".data(String) and .data(String, Object)", function() {
equals( $elem.data('null',null).data('null'), null, "null's are preserved");
equals( $elem.data('emptyString','').data('emptyString'), '', "Empty strings are preserved");
equals( $elem.data('false',false).data('false'), false, "false's are preserved");
equals( $elem.data('exists'), true, "Existing data is returned" );
equals( $elem.data('exists'), undefined, "Existing data is not returned" );
// Clean up
$elem.removeData();
ok( jQuery.isEmptyObject( $elem[0] ), "removeData clears the object" );
deepEqual( $elem[0], {exists:true}, "removeData does not clear the object" );
// manually clean up detached elements
parent.remove();
});
test("data-* attributes", function() {
......@@ -218,6 +301,8 @@ test("data-* attributes", function() {
div.data("attr", "internal").attr("data-attr", "external");
equals( div.data("attr"), "internal", "Check for .data('attr') precedence (internal > external data-* attribute)" );
div.remove();
child.appendTo('#main');
equals( child.data("myobj"), "old data", "Value accessed from data-* attribute");
......@@ -229,6 +314,8 @@ test("data-* attributes", function() {
var obj = child.data(), obj2 = dummy.data(), check = [ "myobj", "ignored", "other" ], num = 0, num2 = 0;
dummy.remove();
for ( var i = 0, l = check.length; i < l; i++ ) {
ok( obj[ check[i] ], "Make sure data- property exists when calling data-." );
ok( obj2[ check[i] ], "Make sure data- property exists when calling data-." );
......@@ -323,13 +410,17 @@ test(".data(Object)", function() {
var obj = {test:"unset"},
jqobj = jQuery(obj);
jqobj.data("test", "unset");
jqobj.data({ "test": "in", "test2": "in2" });
equals( obj.test, "in", "Verify setting an object on an object extends the object" );
equals( obj.test2, "in2", "Verify setting an object on an object extends the object" );
equals( jQuery.data(obj).test, "in", "Verify setting an object on an object extends the data object" );
equals( obj.test2, undefined, "Verify setting an object on an object does not extend the object" );
// manually clean up detached elements
div.remove();
});
test("jQuery.removeData", function() {
expect(7);
expect(6);
var div = jQuery("#foo")[0];
jQuery.data(div, "test", "testing");
jQuery.removeData(div, "test");
......@@ -342,10 +433,9 @@ test("jQuery.removeData", function() {
var obj = {};
jQuery.data(obj, "test", "testing");
equals( obj.test, "testing", "verify data on plain object");
equals( jQuery(obj).data("test"), "testing", "verify data on plain object");
jQuery.removeData(obj, "test");
equals( jQuery.data(obj, "test"), undefined, "Check removal of data on plain object" );
equals( obj.test, undefined, "Check removal of data directly from plain object" );
jQuery.data( window, "BAD", true );
jQuery.removeData( window, "BAD" );
......@@ -371,4 +461,4 @@ test(".removeData()", function() {
div.removeData("test.foo");
equals( div.data("test.foo"), undefined, "Make sure data is intact" );
});
});
\ No newline at end of file
module("dimensions");
module("dimensions", { teardown: moduleTeardown });
function pass( val ) {
return val;
......@@ -33,6 +33,8 @@ function testWidth( val ) {
var blah = jQuery("blah");
equals( blah.width( val(10) ), blah, "Make sure that setting a width on an empty set returns the set." );
equals( blah.width(), null, "Make sure 'null' is returned on an empty set");
jQuery.removeData($div[0], 'olddisplay', true);
}
test("width()", function() {
......@@ -80,6 +82,8 @@ function testHeight( val ) {
var blah = jQuery("blah");
equals( blah.height( val(10) ), blah, "Make sure that setting a height on an empty set returns the set." );
equals( blah.height(), null, "Make sure 'null' is returned on an empty set");
jQuery.removeData($div[0], 'olddisplay', true);
}
test("height()", function() {
......@@ -126,6 +130,9 @@ test("innerWidth()", function() {
// Temporarily require 0 for backwards compat - should be auto
equals( div.innerWidth(), 0, "Make sure that disconnected nodes are handled." );
div.remove();
jQuery.removeData($div[0], 'olddisplay', true);
});
test("innerHeight()", function() {
......@@ -152,6 +159,9 @@ test("innerHeight()", function() {
// Temporarily require 0 for backwards compat - should be auto
equals( div.innerHeight(), 0, "Make sure that disconnected nodes are handled." );
div.remove();
jQuery.removeData($div[0], 'olddisplay', true);
});
test("outerWidth()", function() {
......@@ -179,6 +189,9 @@ test("outerWidth()", function() {
// Temporarily require 0 for backwards compat - should be auto
equals( div.outerWidth(), 0, "Make sure that disconnected nodes are handled." );
div.remove();
jQuery.removeData($div[0], 'olddisplay', true);
});
test("outerHeight()", function() {
......@@ -205,4 +218,7 @@ test("outerHeight()", function() {
// Temporarily require 0 for backwards compat - should be auto
equals( div.outerHeight(), 0, "Make sure that disconnected nodes are handled." );
div.remove();
jQuery.removeData($div[0], 'olddisplay', true);
});
module("effects");
module("effects", { teardown: moduleTeardown });
test("sanity check", function() {
expect(1);
......@@ -14,7 +14,7 @@ test("show()", function() {
equals( hiddendiv.css("display"), "block", "Make sure a pre-hidden div is visible." );
var div = jQuery("<div>").hide().appendTo("body").show();
var div = jQuery("<div>").hide().appendTo("#main").show();
equal( div.css("display"), "block", "Make sure pre-hidden divs show" );
......@@ -403,13 +403,16 @@ test("animate duration 0", function() {
$elem.hide(0, function(){
ok(true, "Hide callback with no duration");
});
// manually clean up detached elements
$elem.remove();
});
test("animate hyphenated properties", function(){
expect(1);
stop();
jQuery("#nothiddendiv")
jQuery("#foo")
.css("font-size", 10)
.animate({"font-size": 20}, 200, function(){
equals( this.style.fontSize, "20px", "The font-size property was animated." );
......@@ -433,7 +436,7 @@ test("stop()", function() {
expect(3);
stop();
var $foo = jQuery("#nothiddendiv");
var $foo = jQuery("#foo");
var w = 0;
$foo.hide().width(200).width();
......@@ -446,6 +449,8 @@ test("stop()", function() {
nw = $foo.width();
notEqual( nw, w, "Stop didn't reset the animation " + nw + "px " + w + "px");
setTimeout(function(){
$foo.removeData();
$foo.removeData(undefined, true);
equals( nw, $foo.width(), "The animation didn't continue" );
start();
}, 100);
......@@ -456,7 +461,7 @@ test("stop() - several in queue", function() {
expect(3);
stop();
var $foo = jQuery("#nothiddendivchild");
var $foo = jQuery("#foo");
var w = 0;
$foo.hide().width(200).width();
......@@ -481,7 +486,7 @@ test("stop(clearQueue)", function() {
expect(4);
stop();
var $foo = jQuery("#nothiddendiv");
var $foo = jQuery("#foo");
var w = 0;
$foo.hide().width(200).width();
......@@ -508,7 +513,7 @@ test("stop(clearQueue, gotoEnd)", function() {
expect(1);
stop();
var $foo = jQuery("#nothiddendivchild");
var $foo = jQuery("#foo");
var w = 0;
$foo.hide().width(200).width();
......@@ -536,7 +541,7 @@ test("stop(clearQueue, gotoEnd)", function() {
test("toggle()", function() {
expect(6);
var x = jQuery("#nothiddendiv");
var x = jQuery("#foo");
ok( x.is(":visible"), "is visible" );
x.toggle();
ok( x.is(":hidden"), "is hidden" );
......@@ -737,6 +742,9 @@ jQuery.each( {
}
}
// manually remove generated element
jQuery(this).remove();
start();
});
});
......@@ -763,6 +771,10 @@ jQuery.checkState = function(){
var cur = self.style[ c ] || jQuery.css(self, c);
equals( cur, v, "Make sure that " + c + " is reset (Old: " + v + " Cur: " + cur + ")");
});
// manually clean data on modified element
jQuery.removeData(this, 'olddisplay', true);
start();
}
......@@ -829,9 +841,6 @@ jQuery.makeTest = function( text ){
jQuery("<h4></h4>")
.text( text )
.appendTo("#fx-tests")
.click(function(){
jQuery(this).next().toggle();
})
.after( elem );
return elem;
......@@ -895,7 +904,7 @@ test("hide hidden elements (bug #7141)", function() {
var div = jQuery("<div style='display:none'></div>").appendTo("#main");
equals( div.css("display"), "none", "Element is hidden by default" );
div.hide();
ok( !div.data("olddisplay"), "olddisplay is undefined after hiding an already-hidden element" );
ok( !jQuery._data(div, "olddisplay"), "olddisplay is undefined after hiding an already-hidden element" );
div.show();
equals( div.css("display"), "block", "Show a double-hidden element" );
......@@ -910,7 +919,7 @@ test("hide hidden elements, with animation (bug #7141)", function() {
var div = jQuery("<div style='display:none'></div>").appendTo("#main");
equals( div.css("display"), "none", "Element is hidden by default" );
div.hide(1, function () {
ok( !div.data("olddisplay"), "olddisplay is undefined after hiding an already-hidden element" );
ok( !jQuery._data(div, "olddisplay"), "olddisplay is undefined after hiding an already-hidden element" );
div.show(1, function () {
equals( div.css("display"), "block", "Show a double-hidden element" );
start();
......
module("event");
module("event", { teardown: moduleTeardown });
test("null or undefined handler", function() {
expect(2);
......@@ -28,7 +28,7 @@ test("bind(), with data", function() {
};
jQuery("#firstp").bind("click", {foo: "bar"}, handler).click().unbind("click", handler);
ok( !jQuery.data(jQuery("#firstp")[0], "events"), "Event handler unbound when using data." );
ok( !jQuery._data(jQuery("#firstp")[0], "events"), "Event handler unbound when using data." );
});
test("click(), with data", function() {
......@@ -39,7 +39,7 @@ test("click(), with data", function() {
};
jQuery("#firstp").click({foo: "bar"}, handler).click().unbind("click", handler);
ok( !jQuery.data(jQuery("#firstp")[0], "events"), "Event handler unbound when using data." );
ok( !jQuery._data(jQuery("#firstp")[0], "events"), "Event handler unbound when using data." );
});
test("bind(), with data, trigger with data", function() {
......@@ -80,6 +80,9 @@ test("bind(), multiple events at once and namespaces", function() {
cur = "focusin";
div.trigger("focusin.a");
// manually clean up detached elements
div.remove();
div = jQuery("<div/>").bind("click mouseover", obj, function(e) {
equals( e.type, cur, "Verify right multi event was fired." );
equals( e.data, obj, "Make sure the data came in correctly." );
......@@ -91,6 +94,9 @@ test("bind(), multiple events at once and namespaces", function() {
cur = "mouseover";
div.trigger("mouseover");
// manually clean up detached elements
div.remove();
div = jQuery("<div/>").bind("focusin.a focusout.b", function(e) {
equals( e.type, cur, "Verify right multi event was fired." );
});
......@@ -100,6 +106,9 @@ test("bind(), multiple events at once and namespaces", function() {
cur = "focusout";
div.trigger("focusout.b");
// manually clean up detached elements
div.remove();
});
test("bind(), namespace with special add", function() {
......@@ -295,15 +304,15 @@ test("live/delegate immediate propagation", function() {
$p.undelegate( "click" );
});
test("bind/delegate bubbling, isDefaultPrevented (Bug #7793)", function() {
test("bind/delegate bubbling, isDefaultPrevented", function() {
expect(2);
var $anchor2 = jQuery( "#anchor2" ),
$main = jQuery( "#main" ),
fakeClick = function($jq) {
// Use a native click so we don't get jQuery simulated bubbling
if ( document.createEvent ) {
var e = document.createEvent( "MouseEvents" );
e.initEvent( "click", true, true );
var e = document.createEvent( 'MouseEvents' );
e.initEvent( "click", true, true );
$jq[0].dispatchEvent(e);
}
else if ( $jq[0].click ) {
......@@ -314,7 +323,15 @@ test("bind/delegate bubbling, isDefaultPrevented (Bug #7793)", function() {
e.preventDefault();
});
$main.delegate("#foo", "click", function(e) {
equals( e.isDefaultPrevented(), true, "isDefaultPrevented true passed to bubbled event" );
var orig = e.originalEvent;
if ( typeof(orig.defaultPrevented) === "boolean" || typeof(orig.returnValue) === "boolean" || orig.getPreventDefault ) {
equals( e.isDefaultPrevented(), true, "isDefaultPrevented true passed to bubbled event" );
} else {
// Opera < 11 doesn't implement any interface we can use, so give it a pass
ok( true, "isDefaultPrevented not supported by this browser, test skipped" );
}
});
fakeClick( $anchor2 );
$anchor2.unbind( "click" );
......@@ -505,7 +522,7 @@ test("bind(), with different this object", function() {
.bind("click", jQuery.proxy(handler1, thisObject)).click().unbind("click", handler1)
.bind("click", data, jQuery.proxy(handler2, thisObject)).click().unbind("click", handler2);
ok( !jQuery.data(jQuery("#firstp")[0], "events"), "Event handler unbound when using different this object and data." );
ok( !jQuery._data(jQuery("#firstp")[0], "events"), "Event handler unbound when using different this object and data." );
});
test("bind(name, false), unbind(name, false)", function() {
......@@ -525,6 +542,9 @@ test("bind(name, false), unbind(name, false)", function() {
jQuery("#ap").unbind("click", false);
jQuery("#ap").trigger("click");
equals( main, 1, "Verify that the trigger happened correctly." );
// manually clean up events from elements outside the fixture
jQuery("#main").unbind("click");
});
test("bind()/trigger()/unbind() on plain object", function() {
......@@ -547,7 +567,7 @@ test("bind()/trigger()/unbind() on plain object", function() {
}
});
var events = jQuery(obj).data("__events__");
var events = jQuery._data(obj, "events");
ok( events, "Object has events bound." );
equals( obj.events, undefined, "Events object on plain objects is not events" );
equals( typeof events, "function", "'events' expando is a function on plain objects." );
......@@ -567,7 +587,9 @@ test("bind()/trigger()/unbind() on plain object", function() {
// Make sure it doesn't complain when no events are found
jQuery(obj).unbind("test");
equals( obj.__events__, undefined, "Make sure events object is removed" );
equals( obj && obj[ jQuery.expando ] &&
obj[ jQuery.expando ][ jQuery.expando ] &&
obj[ jQuery.expando ][ jQuery.expando ].events, undefined, "Make sure events object is removed" );
});
test("unbind(type)", function() {
......@@ -661,13 +683,18 @@ test("hover()", function() {
test("trigger() shortcuts", function() {
expect(6);
jQuery('<li><a href="#">Change location</a></li>').prependTo('#firstUL').find('a').bind('click', function() {
var elem = jQuery('<li><a href="#">Change location</a></li>').prependTo('#firstUL');
elem.find('a').bind('click', function() {
var close = jQuery('spanx', this); // same with jQuery(this).find('span');
equals( close.length, 0, "Context element does not exist, length must be zero" );
ok( !close[0], "Context element does not exist, direct access to element must return undefined" );
return false;
}).click();
// manually clean up detached elements
elem.remove();
jQuery("#check1").click(function() {
ok( true, "click event handler for checkbox gets fired twice, see #815" );
}).click();
......@@ -686,9 +713,12 @@ test("trigger() shortcuts", function() {
jQuery('#simon1').click();
equals( clickCounter, 1, "Check that click, triggers onclick event handler on an a tag also" );
jQuery('<img />').load(function(){
elem = jQuery('<img />').load(function(){
ok( true, "Trigger the load event, using the shortcut .load() (#2819)");
}).load();
// manually clean up detached elements
elem.remove();
});
test("trigger() bubbling", function() {
......@@ -723,6 +753,10 @@ test("trigger() bubbling", function() {
equals( body, 2, "ap bubble" );
equals( main, 1, "ap bubble" );
equals( ap, 1, "ap bubble" );
// manually clean up events from elements outside the fixture
jQuery(document).unbind("click");
jQuery("html, body, #main").unbind("click");
});
test("trigger(type, [data], [fn])", function() {
......@@ -766,7 +800,7 @@ test("trigger(type, [data], [fn])", function() {
pass = true;
try {
jQuery('table:first').bind('test:test', function(){}).trigger('test:test');
jQuery('#main table:first').bind('test:test', function(){}).trigger('test:test');
} catch (e) {
pass = false;
}
......@@ -947,9 +981,12 @@ test("toggle(Function, Function, ...)", function() {
equals( turn, 2, "Trying toggle with 3 functions, attempt 5 yields 2");
$div.unbind('click',fns[0]);
var data = jQuery.data( $div[0], 'events' );
var data = jQuery._data( $div[0], 'events' );
ok( !data, "Unbinding one function from toggle unbinds them all");
// manually clean up detached elements
$div.remove();
// Test Multi-Toggles
var a = [], b = [];
$div = jQuery("<div/>");
......@@ -965,6 +1002,9 @@ test("toggle(Function, Function, ...)", function() {
$div.click();
same( a, [1,2,1], "Check that a click worked with a second toggle, second click." );
same( b, [1,2], "Check that a click worked with a second toggle, second click." );
// manually clean up detached elements
$div.remove();
});
test(".live()/.die()", function() {
......@@ -1065,7 +1105,7 @@ test(".live()/.die()", function() {
equals( clicked, 2, "live with a context" );
// Make sure the event is actually stored on the context
ok( jQuery.data(container, "events").live, "live with a context" );
ok( jQuery._data(container, "events").live, "live with a context" );
// Test unbinding with a different context
jQuery("#foo", container).die("click");
......@@ -1275,6 +1315,9 @@ test("live with multiple events", function(){
div.trigger("submit");
equals( count, 2, "Make sure both the click and submit were triggered." );
// manually clean up events from elements outside the fixture
div.die();
});
test("live with namespaces", function(){
......@@ -1578,7 +1621,7 @@ test(".delegate()/.undelegate()", function() {
equals( clicked, 2, "delegate with a context" );
// Make sure the event is actually stored on the context
ok( jQuery.data(container, "events").live, "delegate with a context" );
ok( jQuery._data(container, "events").live, "delegate with a context" );
// Test unbinding with a different context
jQuery("#main").undelegate("#foo", "click");
......@@ -1907,7 +1950,7 @@ test("window resize", function() {
ok( true, "Resize event fired." );
}).resize().unbind("resize");
ok( !jQuery(window).data("__events__"), "Make sure all the events are gone." );
ok( !jQuery._data(window, "__events__"), "Make sure all the events are gone." );
});
test("focusin bubbles", function() {
......
module("manipulation");
module("manipulation", { teardown: moduleTeardown });
// Ensure that an extended Array prototype doesn't break jQuery
Array.prototype.arrayProtoFn = function(arg) { throw("arrayProtoFn should not be called"); };
......@@ -115,12 +115,19 @@ var testWrap = function(val) {
// Wrap an element with a jQuery set and event
result = jQuery("<div></div>").click(function(){
ok(true, "Event triggered.");
// Remove handlers on detached elements
result.unbind();
jQuery(this).unbind();
});
j = jQuery("<span/>").wrap(result);
equals( j[0].parentNode.nodeName.toLowerCase(), "div", "Wrapping works." );
j.parent().trigger("click");
// clean up attached elements
QUnit.reset();
}
test("wrap(String|Element)", function() {
......@@ -408,8 +415,12 @@ test("append the same fragment with events (Bug #6997, 5566)", function () {
ok(true, "Event exists on original after being unbound on clone");
jQuery(this).unbind('click');
});
element.clone(true).unbind('click')[0].fireEvent('onclick');
var clone = element.clone(true).unbind('click');
clone[0].fireEvent('onclick');
element[0].fireEvent('onclick');
// manually clean up detached elements
clone.remove();
}
element = jQuery("<a class='test6997'></a>").click(function () {
......@@ -886,20 +897,36 @@ test("clone()", function() {
ok( true, "Bound event still exists." );
});
div = div.clone(true).clone(true);
clone = div.clone(true);
// manually clean up detached elements
div.remove();
div = clone.clone(true);
// manually clean up detached elements
clone.remove();
equals( div.length, 1, "One element cloned" );
equals( div[0].nodeName.toUpperCase(), "DIV", "DIV element cloned" );
div.trigger("click");
// manually clean up detached elements
div.remove();
div = jQuery("<div/>").append([ document.createElement("table"), document.createElement("table") ]);
div.find("table").click(function(){
ok( true, "Bound event still exists." );
});
div = div.clone(true);
equals( div.length, 1, "One element cloned" );
equals( div[0].nodeName.toUpperCase(), "DIV", "DIV element cloned" );
div.find("table:last").trigger("click");
clone = div.clone(true);
equals( clone.length, 1, "One element cloned" );
equals( clone[0].nodeName.toUpperCase(), "DIV", "DIV element cloned" );
clone.find("table:last").trigger("click");
// manually clean up detached elements
div.remove();
clone.remove();
// this is technically an invalid object, but because of the special
// classid instantiation it is the only kind that IE has trouble with,
......@@ -920,12 +947,16 @@ test("clone()", function() {
equals( clone[0].nodeName.toUpperCase(), "DIV", "DIV element cloned" );
div = jQuery("<div/>").data({ a: true });
var div2 = div.clone(true);
equals( div2.data("a"), true, "Data cloned." );
div2.data("a", false);
equals( div2.data("a"), false, "Ensure cloned element data object was correctly modified" );
clone = div.clone(true);
equals( clone.data("a"), true, "Data cloned." );
clone.data("a", false);
equals( clone.data("a"), false, "Ensure cloned element data object was correctly modified" );
equals( div.data("a"), true, "Ensure cloned element data object is copied, not referenced" );
// manually clean up detached elements
div.remove();
clone.remove();
var form = document.createElement("form");
form.action = "/test/";
var div = document.createElement("div");
......@@ -1155,15 +1186,21 @@ var testRemove = function(method) {
jQuery("#nonnodes").contents()[method]();
equals( jQuery("#nonnodes").contents().length, 0, "Check node,textnode,comment remove works" );
// manually clean up detached elements
if (method === "detach") {
first.remove();
}
QUnit.reset();
var count = 0;
var first = jQuery("#ap").children(":first");
var cleanUp = first.click(function() { count++ })[method]().appendTo("body").click();
var cleanUp = first.click(function() { count++ })[method]().appendTo("#main").click();
equals( method == "remove" ? 0 : 1, count );
cleanUp.detach();
// manually clean up detached elements
cleanUp.remove();
};
test("remove()", function() {
......
module("offset");
module("offset", { teardown: moduleTeardown });
test("disconnected node", function() {
expect(2);
......
module("queue");
module("queue", { teardown: moduleTeardown });
test("queue() with other types",function() {
expect(9);
......
module("selector");
module("selector", { teardown: moduleTeardown });
test("element", function() {
expect(21);
......@@ -58,13 +58,14 @@ if ( location.protocol != "file:" ) {
}
test("broken", function() {
expect(8);
expect(19);
function broken(name, selector) {
try {
jQuery(selector);
ok( false, name + ": " + selector );
} catch(e){
ok( typeof e === "string" && e.indexOf("Syntax error") >= 0,
ok( typeof e === "string" && e.indexOf("Syntax error") >= 0,
name + ": " + selector );
}
}
......@@ -77,10 +78,31 @@ test("broken", function() {
broken( "Broken Selector", "<>", [] );
broken( "Broken Selector", "{}", [] );
broken( "Doesn't exist", ":visble", [] );
broken( "Nth-child", ":nth-child", [] );
broken( "Nth-child", ":nth-child(-)", [] );
// Sigh. WebKit thinks this is a real selector in qSA
// They've already fixed this and it'll be coming into
// current browsers soon.
//broken( "Nth-child", ":nth-child(asdf)", [] );
broken( "Nth-child", ":nth-child(2n+-0)", [] );
broken( "Nth-child", ":nth-child(2+0)", [] );
broken( "Nth-child", ":nth-child(- 1n)", [] );
broken( "Nth-child", ":nth-child(-1 n)", [] );
broken( "First-child", ":first-child(n)", [] );
broken( "Last-child", ":last-child(n)", [] );
broken( "Only-child", ":only-child(n)", [] );
// Make sure attribute value quoting works correctly. See: #6093
var attrbad = jQuery('<input type="hidden" value="2" name="foo.baz" id="attrbad1"/><input type="hidden" value="2" name="foo[baz]" id="attrbad2"/>').appendTo("body");
broken( "Attribute not escaped", "input[name=foo.baz]", [] );
broken( "Attribute not escaped", "input[name=foo[baz]]", [] );
attrbad.remove();
});
test("id", function() {
expect(28);
expect(29);
t( "ID Selector", "#body", ["body"] );
t( "ID Selector w/ Element", "body#body", ["body"] );
t( "ID Selector w/ Element", "ul#first", [] );
......@@ -116,6 +138,9 @@ test("id", function() {
same( jQuery("body").find("div#form").get(), [], "ID selector within the context of another element" );
//#7533
equal( jQuery("<div id=\"A'B~C.D[E]\"><p>foo</p></div>").find("p").length, 1, "Find where context root is a node and has an ID with CSS3 meta characters" );
t( "Underscore ID", "#types_all", ["types_all"] );
t( "Dash ID", "#fx-queue", ["fx-queue"] );
......@@ -150,7 +175,7 @@ test("class", function() {
t( "Child escaped Class", "form > .test\\.foo\\[5\\]bar", ["test.foo[5]bar"] );
var div = document.createElement("div");
div.innerHTML = "<div class='test e'></div><div class='test'></div>";
div.innerHTML = "<div class='test e'></div><div class='test'></div>";
same( jQuery(".e", div).get(), [ div.firstChild ], "Finding a second class." );
div.lastChild.className = "e";
......@@ -202,7 +227,7 @@ test("multiple", function() {
});
test("child and adjacent", function() {
expect(27);
expect(31);
t( "Child", "p > a", ["simon1","google","groups","mark","yahoo","simon"] );
t( "Child", "p> a", ["simon1","google","groups","mark","yahoo","simon"] );
t( "Child", "p >a", ["simon1","google","groups","mark","yahoo","simon"] );
......@@ -210,20 +235,25 @@ test("child and adjacent", function() {
t( "Child w/ Class", "p > a.blog", ["mark","simon"] );
t( "All Children", "code > *", ["anchor1","anchor2"] );
t( "All Grandchildren", "p > * > *", ["anchor1","anchor2"] );
t( "Adjacent", "#main a + a", ["groups"] );
t( "Adjacent", "#main a +a", ["groups"] );
t( "Adjacent", "#main a+ a", ["groups"] );
t( "Adjacent", "#main a+a", ["groups"] );
t( "Adjacent", "a + a", ["groups"] );
t( "Adjacent", "a +a", ["groups"] );
t( "Adjacent", "a+ a", ["groups"] );
t( "Adjacent", "a+a", ["groups"] );
t( "Adjacent", "p + p", ["ap","en","sap"] );
t( "Adjacent", "p#firstp + p", ["ap"] );
t( "Adjacent", "p[lang=en] + p", ["sap"] );
t( "Adjacent", "a.GROUPS + code + a", ["mark"] );
t( "Comma, Child, and Adjacent", "#main a + a, code > a", ["groups","anchor1","anchor2"] );
t( "Comma, Child, and Adjacent", "a + a, code > a", ["groups","anchor1","anchor2"] );
t( "Element Preceded By", "p ~ div", ["foo", "moretests","tabindex-tests", "liveHandlerOrder", "siblingTest"] );
t( "Element Preceded By", "#first ~ div", ["moretests","tabindex-tests", "liveHandlerOrder", "siblingTest"] );
t( "Element Preceded By", "#groups ~ a", ["mark"] );
t( "Element Preceded By", "#length ~ input", ["idTest"] );
t( "Element Preceded By", "#siblingfirst ~ em", ["siblingnext"] );
same( jQuery("#siblingfirst").find("~ em").get(), q("siblingnext"), "Element Preceded By with a context." );
same( jQuery("#siblingfirst").find("+ em").get(), q("siblingnext"), "Element Directly Preceded By with a context." );
var a = jQuery("<div id='foo'></div><p id='bar'></p><p id='bar2'></p>");
same( jQuery("~ p", a[0]).get(), [a[1], a[2]], "Detached Element Directly Preceded By with a context." );
same( jQuery("+ p", a[0]).get(), [a[1]], "Detached Element Preceded By with a context." );
t( "Verify deep class selector", "div.blah > p > a", [] );
......@@ -237,7 +267,8 @@ test("child and adjacent", function() {
});
test("attributes", function() {
expect(39);
expect(41);
t( "Attribute Exists", "a[title]", ["google"] );
t( "Attribute Exists", "*[title]", ["google"] );
t( "Attribute Exists", "[title]", ["google"] );
......@@ -294,10 +325,18 @@ test("attributes", function() {
t("Select options via :selected", "#select3 option:selected", ["option3b", "option3c"] );
t( "Grouped Form Elements", "input[name='foo[bar]']", ["hidden2"] );
// Make sure attribute value quoting works correctly. See: #6093
var attrbad = jQuery('<input type="hidden" value="2" name="foo.baz" id="attrbad1"/><input type="hidden" value="2" name="foo[baz]" id="attrbad2"/>').appendTo("body");
t("Find escaped attribute value", "input[name=foo\\.baz]", ["attrbad1"]);
t("Find escaped attribute value", "input[name=foo\\[baz\\]]", ["attrbad2"]);
attrbad.remove();
});
test("pseudo - child", function() {
expect(31);
expect(38);
t( "First Child", "p:first-child", ["firstp","sndp"] );
t( "Last Child", "p:last-child", ["sap"] );
t( "Only Child", "#main a:only-child", ["simon1","anchor1","yahoo","anchor2","liveLink1","liveLink2"] );
......@@ -306,6 +345,7 @@ test("pseudo - child", function() {
t( "First Child", "p:first-child", ["firstp","sndp"] );
t( "Nth Child", "p:nth-child(1)", ["firstp","sndp"] );
t( "Nth Child With Whitespace", "p:nth-child( 1 )", ["firstp","sndp"] );
t( "Not Nth Child", "p:not(:nth-child(1))", ["ap","en","sap","first"] );
// Verify that the child position isn't being cached improperly
......@@ -322,15 +362,19 @@ test("pseudo - child", function() {
t( "Nth-child", "#main form#form > *:nth-child(2)", ["text1"] );
t( "Nth-child", "#main form#form > :nth-child(2)", ["text1"] );
t( "Nth-child", "#form select:first option:nth-child(-1)", [] );
t( "Nth-child", "#form select:first option:nth-child(3)", ["option1c"] );
t( "Nth-child", "#form select:first option:nth-child(0n+3)", ["option1c"] );
t( "Nth-child", "#form select:first option:nth-child(1n+0)", ["option1a", "option1b", "option1c", "option1d"] );
t( "Nth-child", "#form select:first option:nth-child(1n)", ["option1a", "option1b", "option1c", "option1d"] );
t( "Nth-child", "#form select:first option:nth-child(n)", ["option1a", "option1b", "option1c", "option1d"] );
t( "Nth-child", "#form select:first option:nth-child(+n)", ["option1a", "option1b", "option1c", "option1d"] );
t( "Nth-child", "#form select:first option:nth-child(even)", ["option1b", "option1d"] );
t( "Nth-child", "#form select:first option:nth-child(odd)", ["option1a", "option1c"] );
t( "Nth-child", "#form select:first option:nth-child(2n)", ["option1b", "option1d"] );
t( "Nth-child", "#form select:first option:nth-child(2n+1)", ["option1a", "option1c"] );
t( "Nth-child", "#form select:first option:nth-child(2n + 1)", ["option1a", "option1c"] );
t( "Nth-child", "#form select:first option:nth-child(+2n + 1)", ["option1a", "option1c"] );
t( "Nth-child", "#form select:first option:nth-child(3n)", ["option1c"] );
t( "Nth-child", "#form select:first option:nth-child(3n+1)", ["option1a", "option1d"] );
t( "Nth-child", "#form select:first option:nth-child(3n+2)", ["option1b"] );
......@@ -339,7 +383,9 @@ test("pseudo - child", function() {
t( "Nth-child", "#form select:first option:nth-child(3n-2)", ["option1a", "option1d"] );
t( "Nth-child", "#form select:first option:nth-child(3n-3)", ["option1c"] );
t( "Nth-child", "#form select:first option:nth-child(3n+0)", ["option1c"] );
t( "Nth-child", "#form select:first option:nth-child(-1n+3)", ["option1a", "option1b", "option1c"] );
t( "Nth-child", "#form select:first option:nth-child(-n+3)", ["option1a", "option1b", "option1c"] );
t( "Nth-child", "#form select:first option:nth-child(-1n + 3)", ["option1a", "option1b", "option1c"] );
});
test("pseudo - misc", function() {
......
module("traversing");
module("traversing", { teardown: moduleTeardown });
test("find(String)", function() {
expect(5);
......
1.4.5pre
\ No newline at end of file
1.5pre
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment