Merge branch 'fix-7853-add-context' of https://github.com/dmethvin/jquery into dmethv...
authorJohn Resig <jeresig@gmail.com>
Thu, 20 Jan 2011 19:51:30 +0000 (14:51 -0500)
committerJohn Resig <jeresig@gmail.com>
Thu, 20 Jan 2011 19:51:30 +0000 (14:51 -0500)
56 files changed:
MIT-LICENSE.txt
Makefile
README.md
Rakefile [deleted file]
build.xml [deleted file]
build/google-compiler-20100917.jar [deleted file]
build/js.jar [deleted file]
build/jslint-check.js
build/lib/jslint.js [moved from build/jslint.js with 99% similarity]
build/lib/parse-js.js [new file with mode: 0644]
build/lib/process.js [new file with mode: 0644]
build/lib/squeeze-more.js [new file with mode: 0644]
build/uglify.js [new file with mode: 0644]
src/ajax.js
src/ajax/jsonp.js [new file with mode: 0644]
src/ajax/script.js [moved from src/transports/script.js with 56% similarity]
src/ajax/xhr.js [new file with mode: 0644]
src/attributes.js
src/core.js
src/css.js
src/data.js
src/dimensions.js
src/effects.js
src/event.js
src/intro.js
src/manipulation.js
src/offset.js
src/queue.js
src/support.js
src/transports/jsonp.js [deleted file]
src/transports/xhr.js [deleted file]
src/traversing.js
src/xhr.js [deleted file]
test/csp.php [new file with mode: 0644]
test/data/errorWithText.php [new file with mode: 0644]
test/data/headers.php
test/data/jsonp.php
test/data/params_html.php
test/data/testinit.js
test/delegatetest.html
test/index.html
test/polluted.php
test/unit/ajax.js
test/unit/attributes.js
test/unit/core.js
test/unit/css.js
test/unit/data.js
test/unit/dimensions.js
test/unit/effects.js
test/unit/event.js
test/unit/manipulation.js
test/unit/offset.js
test/unit/queue.js
test/unit/selector.js
test/unit/traversing.js
version.txt

index ea33691..5327046 100644 (file)
@@ -1,4 +1,4 @@
-Copyright (c) 2010 John Resig, http://jquery.com/
+Copyright (c) 2011 John Resig, http://jquery.com/
 
 Permission is hereby granted, free of charge, to any person obtaining
 a copy of this software and associated documentation files (the
index 935f69c..c708db5 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -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\
@@ -24,10 +21,9 @@ BASE_FILES = ${SRC_DIR}/core.js\
        ${SRC_DIR}/manipulation.js\
        ${SRC_DIR}/css.js\
        ${SRC_DIR}/ajax.js\
-       ${SRC_DIR}/xhr.js\
-       ${SRC_DIR}/transports/jsonp.js\
-       ${SRC_DIR}/transports/script.js\
-       ${SRC_DIR}/transports/xhr.js\
+       ${SRC_DIR}/ajax/jsonp.js\
+       ${SRC_DIR}/ajax/script.js\
+       ${SRC_DIR}/ajax/xhr.js\
        ${SRC_DIR}/effects.js\
        ${SRC_DIR}/offset.js\
        ${SRC_DIR}/dimensions.js
@@ -79,13 +75,13 @@ init:
 jquery: ${JQ}
 jq: ${JQ}
 
-${JQ}: ${MODULES} ${DIST_DIR}
+${JQ}: ${MODULES} | ${DIST_DIR}
        @@echo "Building" ${JQ}
 
        @@cat ${MODULES} | \
                sed 's/.function..jQuery...{//' | \
                sed 's/}...jQuery..;//' | \
-               sed 's/Date:./&'"${DATE}"'/' | \
+               sed 's/@DATE/'"${DATE}"'/' | \
                ${VER} > ${JQ};
 
 ${SRC_DIR}/selector.js: ${SIZZLE_DIR}/sizzle.js
@@ -94,17 +90,16 @@ ${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}.tmp
+       @@echo ";" >> ${JQ_MIN}.tmp
+       @@sed 's/\*\/(/*\/ʩ(/' ${JQ_MIN}.tmp | tr "ʩ" "\n" > ${JQ_MIN}
+       @@rm -rf ${JQ_MIN}.tmp
 
 clean:
        @@echo "Removing Distribution directory:" ${DIST_DIR}
index 384e2d1..d56576c 100644 (file)
--- a/README.md
+++ b/README.md
@@ -1,85 +1,64 @@
 [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.
diff --git a/Rakefile b/Rakefile
deleted file mode 100644 (file)
index 5ea143b..0000000
--- a/Rakefile
+++ /dev/null
@@ -1,138 +0,0 @@
-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 xhr transports/jsonp transports/script transports/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:.)/, "\\1#{date}" ).gsub(/@VERSION/, version)
-  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
diff --git a/build.xml b/build.xml
deleted file mode 100644 (file)
index f6650f4..0000000
--- a/build.xml
+++ /dev/null
@@ -1,133 +0,0 @@
-<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/xhr.js" />
-                       <fileset file="src/transports/jsonp.js" />
-                       <fileset file="src/transports/script.js" />
-                       <fileset file="src/transports/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: ${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>
diff --git a/build/google-compiler-20100917.jar b/build/google-compiler-20100917.jar
deleted file mode 100644 (file)
index 4dfa5ad..0000000
Binary files a/build/google-compiler-20100917.jar and /dev/null differ
diff --git a/build/js.jar b/build/js.jar
deleted file mode 100644 (file)
index a76cc7c..0000000
Binary files a/build/js.jar and /dev/null differ
index 976975a..72d6701 100644 (file)
@@ -1,6 +1,6 @@
-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" );
 }
similarity index 99%
rename from build/jslint.js
rename to build/lib/jslint.js
index f629fec..f563292 100644 (file)
@@ -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
+}());
diff --git a/build/lib/parse-js.js b/build/lib/parse-js.js
new file mode 100644 (file)
index 0000000..7e4fd0e
--- /dev/null
@@ -0,0 +1,1239 @@
+/***********************************************************************
+
+  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;
diff --git a/build/lib/process.js b/build/lib/process.js
new file mode 100644 (file)
index 0000000..edcf599
--- /dev/null
@@ -0,0 +1,1562 @@
+/***********************************************************************
+
+  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 };
diff --git a/build/lib/squeeze-more.js b/build/lib/squeeze-more.js
new file mode 100644 (file)
index 0000000..12380af
--- /dev/null
@@ -0,0 +1,22 @@
+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;
diff --git a/build/uglify.js b/build/uglify.js
new file mode 100644 (file)
index 0000000..943ddd8
--- /dev/null
@@ -0,0 +1,199 @@
+#! /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 + "*/";
+                }
+        }
+        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."); }
+};
index 18ea203..bf0259c 100644 (file)
@@ -1,11 +1,20 @@
 (function( jQuery ) {
-       
-var rscript = /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,
-       rselectTextarea = /^(?:select|textarea)/i,
-       rinput = /^(?:color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,
+
+var r20 = /%20/g,
        rbracket = /\[\]$/,
+       rhash = /#.*$/,
+       rheaders = /^(.*?):\s*(.*?)\r?$/mg, // IE leaves an \r character at EOL
+       rinput = /^(?:color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,
+       rnoContent = /^(?:GET|HEAD)$/,
        rquery = /\?/,
-       r20 = /%20/g,
+       rscript = /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,
+       rselectTextarea = /^(?:select|textarea)/i,
+       rts = /([?&])_=[^&]*/,
+       rurl = /^(\w+:)?\/\/([^\/?#:]+)(?::(\d+))?/,
+       rCRLF = /\r?\n/g,
+
+       // Slice function
+       sliceFunc = Array.prototype.slice,
 
        // Keep a copy of the old load method
        _load = jQuery.fn.load;
@@ -43,35 +52,43 @@ jQuery.fn.extend({
                                type = "POST";
                        }
                }
-               
+
                var self = this;
-               
+
                // Request the remote document
                jQuery.ajax({
                        url: url,
                        type: type,
                        dataType: "html",
                        data: params,
-                       complete: function( res, status ) {
+                       // Complete callback (responseText is used internally)
+                       complete: function( jXHR, status, responseText ) {
+                               // Store the response as specified by the jXHR object
+                               responseText = jXHR.responseText;
                                // If successful, inject the HTML into all the matched elements
-                               if ( status === "success" || status === "notmodified" ) {
+                               if ( jXHR.isResolved() ) {
+                                       // #4825: Get the actual response in case
+                                       // a dataFilter is present in ajaxSettings
+                                       jXHR.done(function( r ) {
+                                               responseText = r;
+                                       });
                                        // See if a selector was specified
                                        self.html( selector ?
                                                // Create a dummy div to hold the results
                                                jQuery("<div>")
                                                        // inject the contents of the document in, removing the scripts
                                                        // to avoid any 'Permission Denied' errors in IE
-                                                       .append(res.responseText.replace(rscript, ""))
+                                                       .append(responseText.replace(rscript, ""))
 
                                                        // Locate the specified elements
                                                        .find(selector) :
 
                                                // If not, just inject the full result
-                                               res.responseText );
+                                               responseText );
                                }
 
                                if ( callback ) {
-                                       self.each( callback, [res.responseText, status, res] );
+                                       self.each( callback, [responseText, status, jXHR] );
                                }
                        }
                });
@@ -99,9 +116,9 @@ jQuery.fn.extend({
                                null :
                                jQuery.isArray(val) ?
                                        jQuery.map( val, function(val, i){
-                                               return {name: elem.name, value: val};
+                                               return { name: elem.name, value: val.replace(rCRLF, "\r\n") };
                                        }) :
-                                       {name: elem.name, value: val};
+                                       { name: elem.name, value: val.replace(rCRLF, "\r\n") };
                }).get();
        }
 });
@@ -143,7 +160,10 @@ jQuery.extend({
        },
 
        ajaxSetup: function( settings ) {
-               jQuery.extend( jQuery.ajaxSettings, settings );
+               jQuery.extend( true, jQuery.ajaxSettings, settings );
+               if ( settings.context ) {
+                       jQuery.ajaxSettings.context = settings.context;
+               }
        },
 
        ajaxSettings: {
@@ -157,21 +177,14 @@ 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();
-               },
-               xhrResponseFields: {
-                       xml: "XML",
-                       text: "Text",
-                       json: "JSON"
-               },
-                       
+
                accepts: {
                        xml: "application/xml, text/xml",
                        html: "text/html",
@@ -179,97 +192,579 @@ jQuery.extend({
                        json: "application/json, text/javascript",
                        "*": "*/*"
                },
-               
-               autoDataType: {
+
+               contents: {
                        xml: /xml/,
                        html: /html/,
                        json: /json/
                },
-               
+
+               responseFields: {
+                       xml: "responseXML",
+                       text: "responseText"
+               },
+
                // Prefilters
                // 1) They are useful to introduce custom dataTypes (see transport/jsonp for an example)
                // 2) These are called:
                //    * BEFORE asking for a transport
                //    * AFTER param serialization (s.data is a string if s.processData is true)
-               // 3) They MUST be order agnostic
-               prefilters: [],
-               
+               // 3) key is the dataType
+               // 4) the catchall symbol "*" can be used
+               // 5) execution will start with transport dataType and THEN continue down to "*" if needed
+               prefilters: {},
+
                // Transports bindings
                // 1) key is the dataType
                // 2) the catchall symbol "*" can be used
                // 3) selection will start with transport dataType and THEN go to "*" if needed
-               transports: {
-               },
-               
-               // Checkers
-               // 1) key is dataType
-               // 2) they are called to control successful response
-               // 3) error throws is used as error data
-               dataCheckers: {
-       
-                       // Check if data is a string
-                       "text": function(data) {
-                               if ( typeof data != "string" ) {
-                                       jQuery.error("typeerror");
-                               }
-                       },
-       
-                       // Check if xml has been properly parsed
-                       "xml": function(data) {
-                               var documentElement = data ? data.documentElement : data;
-                               if ( ! documentElement || ! documentElement.nodeName ) {
-                                       jQuery.error("typeerror");
-                               }
-                               if ( documentElement.nodeName == "parsererror" ) {
-                                       jQuery.error("parsererror");
-                               }
-                       }
-               },
-               
+               transports: {},
+
                // List of data converters
-               // 1) key format is "source_type => destination_type" (spaces required)
+               // 1) key format is "source_type destination_type" (a single space in-between)
                // 2) the catchall symbol "*" can be used for source_type
-               dataConverters: {
-               
+               converters: {
+
                        // Convert anything to text
-                       "* => text": function(data) {
-                               return "" + data;
-                       },
-                       
-                       // Text to html (no transformation)
-                       "text => html": function(data) {
-                               return data;
-                       },
-                       
+                       "* text": window.String,
+
+                       // Text to html (true = no transformation)
+                       "text html": true,
+
                        // Evaluate text as a json expression
-                       "text => json": jQuery.parseJSON,
-                       
+                       "text json": jQuery.parseJSON,
+
                        // Parse text as xml
-                       "text => xml": function(data) {
-                               var xml, parser;
-                               if ( window.DOMParser ) { // Standard
-                                       parser = new DOMParser();
-                                       xml = parser.parseFromString(data,"text/xml");
-                               } else { // IE
-                                       xml = new ActiveXObject("Microsoft.XMLDOM");
-                                       xml.async="false";
-                                       xml.loadXML(data);
-                               }
-                               return xml;
-                       }
+                       "text xml": jQuery.parseXML
                }
        },
 
+       ajaxPrefilter: function( a , b ) {
+               ajaxPrefilterOrTransport( "prefilters" , a , b );
+       },
+
+       ajaxTransport: function( a , b ) {
+               return ajaxPrefilterOrTransport( "transports" , a , b );
+       },
+
        // Main method
-       ajax: function( url , s ) {
-               
-               if ( arguments.length === 1 ) {
-                       s = url;
-                       url = s ? s.url : undefined;
+       ajax: function( url , options ) {
+
+               // If options is not an object,
+               // we simulate pre-1.5 signature
+               if ( typeof( options ) !== "object" ) {
+                       options = url;
+                       url = undefined;
                }
-               
-               return jQuery.xhr().open( s ? s.type : undefined , url ).send( undefined , s );
-               
+
+               // Force options to be an object
+               options = options || {};
+
+               var // Create the final options object
+                       s = jQuery.extend( true , {} , jQuery.ajaxSettings , options ),
+                       // jQuery lists
+                       jQuery_lastModified = jQuery.lastModified,
+                       jQuery_etag = jQuery.etag,
+                       // Callbacks contexts
+                       // We force the original context if it exists
+                       // or take it from jQuery.ajaxSettings otherwise
+                       // (plain objects used as context get extended)
+                       callbackContext =
+                               ( s.context = ( "context" in options ? options : jQuery.ajaxSettings ).context ) || s,
+                       globalEventContext = callbackContext === s ? jQuery.event : jQuery( callbackContext ),
+                       // Deferreds
+                       deferred = jQuery.Deferred(),
+                       completeDeferred = jQuery._Deferred(),
+                       // Status-dependent callbacks
+                       statusCode = s.statusCode || {},
+                       // Headers (they are sent all at once)
+                       requestHeaders = {},
+                       // Response headers
+                       responseHeadersString,
+                       responseHeaders,
+                       // transport
+                       transport,
+                       // timeout handle
+                       timeoutTimer,
+                       // Cross-domain detection vars
+                       loc = document.location,
+                       protocol = loc.protocol || "http:",
+                       parts,
+                       // The jXHR state
+                       state = 0,
+                       // Loop variable
+                       i,
+                       // Fake xhr
+                       jXHR = {
+
+                               readyState: 0,
+
+                               // Caches the header
+                               setRequestHeader: function(name,value) {
+                                       if ( state === 0 ) {
+                                               requestHeaders[ name.toLowerCase() ] = value;
+                                       }
+                                       return this;
+                               },
+
+                               // Raw string
+                               getAllResponseHeaders: function() {
+                                       return state === 2 ? responseHeadersString : null;
+                               },
+
+                               // Builds headers hashtable if needed
+                               getResponseHeader: function( key ) {
+
+                                       var match;
+
+                                       if ( state === 2 ) {
+
+                                               if ( !responseHeaders ) {
+
+                                                       responseHeaders = {};
+
+                                                       while( ( match = rheaders.exec( responseHeadersString ) ) ) {
+                                                               responseHeaders[ match[ 1 ].toLowerCase() ] = match[ 2 ];
+                                                       }
+                                               }
+                                               match = responseHeaders[ key.toLowerCase() ];
+
+                                       }
+
+                                       return match || null;
+                               },
+
+                               // Cancel the request
+                               abort: function( statusText ) {
+                                       if ( transport ) {
+                                               transport.abort( statusText || "abort" );
+                                       }
+                                       done( 0 , statusText );
+                                       return this;
+                               }
+                       };
+
+               // Callback for when everything is done
+               // It is defined here because jslint complains if it is declared
+               // at the end of the function (which would be more logical and readable)
+               function done( status , statusText , responses , headers) {
+
+                       // Called once
+                       if ( state === 2 ) {
+                               return;
+                       }
+
+                       // State is "done" now
+                       state = 2;
+
+                       // Dereference transport for early garbage collection
+                       // (no matter how long the jXHR object will be used)
+                       transport = undefined;
+
+                       // Set readyState
+                       jXHR.readyState = status ? 4 : 0;
+
+                       // Cache response headers
+                       responseHeadersString = headers || "";
+
+                       // Clear timeout if it exists
+                       if ( timeoutTimer ) {
+                               clearTimeout(timeoutTimer);
+                       }
+
+                       var // Reference dataTypes and responseFields
+                               dataTypes = s.dataTypes,
+                               responseFields = s.responseFields,
+                               responseField,
+
+                               // Flag to mark as success
+                               isSuccess,
+                               // Stored success
+                               success,
+                               // Stored error
+                               error,
+
+                               // To keep track of statusCode based callbacks
+                               oldStatusCode,
+
+                               // Actual response
+                               response;
+
+                       // If we got responses:
+                       // - find the right one
+                       // - update dataTypes accordingly
+                       // - set responseXXX accordingly too
+                       if ( responses ) {
+
+                               var contents = s.contents,
+                                       transportDataType = dataTypes[0],
+                                       ct,
+                                       type,
+                                       finalDataType;
+
+                               // Auto (xml, json, script or text determined given headers)
+                               if ( transportDataType === "*" && ( ct = jXHR.getResponseHeader( "content-type" ) ) ) {
+
+                                       for ( type in contents ) {
+                                               if ( contents[ type ] && contents[ type ].test( ct ) ) {
+                                                       transportDataType = dataTypes[0] = type;
+                                                       break;
+                                               }
+                                       }
+
+                                       type = undefined;
+                               }
+
+                               // Get final dataType
+                               for( type in responses ) {
+                                       if ( ! finalDataType && type === transportDataType ) {
+                                               finalDataType = type;
+                                       }
+                                       responseField = responseFields[ type ];
+                                       if ( responseField && ! ( responseField in jXHR ) ) {
+                                               jXHR[ responseField ] = responses[ type ];
+                                       }
+                               }
+
+                               // If no response with the expected dataType was provided
+                               // Take the last response as a default if it exists
+                               if ( ! finalDataType && type ) {
+                                       finalDataType = type;
+                                       if ( transportDataType === "*" ) {
+                                               dataTypes.shift();
+                                       }
+                                       dataTypes.unshift( finalDataType );
+                               }
+
+                               // Get final response
+                               response = responses[ finalDataType ];
+                       }
+
+                       // If successful, handle type chaining
+                       if ( status >= 200 && status < 300 || status === 304 ) {
+
+                               // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
+                               if ( s.ifModified ) {
+
+                                       var lastModified = jXHR.getResponseHeader("Last-Modified"),
+                                               etag = jXHR.getResponseHeader("Etag");
+
+                                       if (lastModified) {
+                                               jQuery_lastModified[ s.url ] = lastModified;
+                                       }
+                                       if (etag) {
+                                               jQuery_etag[ s.url ] = etag;
+                                       }
+                               }
+
+                               // If not modified
+                               if ( status === 304 ) {
+
+                                       statusText = "notmodified";
+                                       isSuccess = 1;
+
+                               // If we have data
+                               } else {
+
+                                       statusText = "success";
+
+                                       // Chain data conversions and determine the final value
+                                       // (if an exception is thrown in the process, it'll be notified as an error)
+                                       try {
+
+                                               var i,
+                                                       // Current dataType
+                                                       current,
+                                                       // Previous dataType
+                                                       prev,
+                                                       // Conversion expression
+                                                       conversion,
+                                                       // Conversion function
+                                                       conv,
+                                                       // Conversion functions (when text is used in-between)
+                                                       conv1,
+                                                       conv2,
+                                                       // Local references to converters
+                                                       converters = s.converters;
+
+                                               // For each dataType in the chain
+                                               for( i = 0 ; i < dataTypes.length ; i++ ) {
+
+                                                       current = dataTypes[ i ];
+
+                                                       // If a responseXXX field for this dataType exists
+                                                       // and if it hasn't been set yet
+                                                       responseField = responseFields[ current ];
+                                                       if ( responseField && ! ( responseField in jXHR ) ) {
+                                                               jXHR[ responseField ] = response;
+                                                       }
+
+                                                       // If this is not the first element
+                                                       if ( i ) {
+
+                                                               // Get the dataType to convert from
+                                                               prev = dataTypes[ i - 1 ];
+
+                                                               // If no catch-all and dataTypes are actually different
+                                                               if ( prev !== "*" && current !== "*" && prev !== current ) {
+
+                                                                       // Get the converter
+                                                                       conversion = prev + " " + current;
+                                                                       conv = converters[ conversion ] || converters[ "* " + current ];
+
+                                                                       conv1 = conv2 = 0;
+
+                                                                       // If there is no direct converter and none of the dataTypes is text
+                                                                       if ( ! conv && prev !== "text" && current !== "text" ) {
+                                                                               // Try with text in-between
+                                                                               conv1 = converters[ prev + " text" ] || converters[ "* text" ];
+                                                                               conv2 = converters[ "text " + current ];
+                                                                               // Revert back to a single converter
+                                                                               // if one of the converter is an equivalence
+                                                                               if ( conv1 === true ) {
+                                                                                       conv = conv2;
+                                                                               } else if ( conv2 === true ) {
+                                                                                       conv = conv1;
+                                                                               }
+                                                                       }
+                                                                       // If we found no converter, dispatch an error
+                                                                       if ( ! ( conv || conv1 && conv2 ) ) {
+                                                                               throw conversion;
+                                                                       }
+                                                                       // If found converter is not an equivalence
+                                                                       if ( conv !== true ) {
+                                                                               // Convert with 1 or 2 converters accordingly
+                                                                               response = conv ? conv( response ) : conv2( conv1( response ) );
+                                                                       }
+                                                               }
+                                                       // If it is the first element of the chain
+                                                       // and we have a dataFilter
+                                                       } else if ( s.dataFilter ) {
+                                                               // Apply the dataFilter
+                                                               response = s.dataFilter( response , current );
+                                                               // Get dataTypes again in case the filter changed them
+                                                               dataTypes = s.dataTypes;
+                                                       }
+                                               }
+                                               // End of loop
+
+                                               // We have a real success
+                                               success = response;
+                                               isSuccess = 1;
+
+                                       // If an exception was thrown
+                                       } catch(e) {
+
+                                               // We have a parsererror
+                                               statusText = "parsererror";
+                                               error = "" + e;
+
+                                       }
+                               }
+
+                       // if not success, mark it as an error
+                       } else {
+                                       error = statusText = statusText || "error";
+                       }
+
+                       // Set data for the fake xhr object
+                       jXHR.status = status;
+                       jXHR.statusText = statusText;
+
+                       // Success/Error
+                       if ( isSuccess ) {
+                               deferred.resolveWith( callbackContext , [ success , statusText , jXHR ] );
+                       } else {
+                               deferred.rejectWith( callbackContext , [ jXHR , statusText , error ] );
+                       }
+
+                       // Status-dependent callbacks
+                       oldStatusCode = statusCode;
+                       statusCode = undefined;
+                       jXHR.statusCode( oldStatusCode );
+
+                       if ( s.global ) {
+                               globalEventContext.trigger( "ajax" + ( isSuccess ? "Success" : "Error" ) ,
+                                               [ jXHR , s , isSuccess ? success : error ] );
+                       }
+
+                       // Complete
+                       completeDeferred.resolveWith( callbackContext, [ jXHR , statusText ] );
+
+                       if ( s.global ) {
+                               globalEventContext.trigger( "ajaxComplete" , [ jXHR , s] );
+                               // Handle the global AJAX counter
+                               if ( ! --jQuery.active ) {
+                                       jQuery.event.trigger( "ajaxStop" );
+                               }
+                       }
+               }
+
+               // Attach deferreds
+               deferred.promise( jXHR );
+               jXHR.success = jXHR.done;
+               jXHR.error = jXHR.fail;
+               jXHR.complete = completeDeferred.done;
+
+               // Status-dependent callbacks
+               jXHR.statusCode = function( map ) {
+                       if ( map ) {
+                               var tmp;
+                               if ( statusCode ) {
+                                       for( tmp in map ) {
+                                               statusCode[ tmp ] = [ statusCode[ tmp ] , map[ tmp ] ];
+                                       }
+                               } else {
+                                       tmp = map[ jXHR.status ];
+                                       jXHR.done( tmp ).fail( tmp );
+                               }
+                       }
+                       return this;
+               };
+
+               // Remove hash character (#7531: and string promotion)
+               // We also use the url parameter if available
+               s.url = ( "" + ( url || s.url ) ).replace( rhash , "" );
+
+               // Extract dataTypes list
+               s.dataTypes = jQuery.trim( s.dataType || "*" ).toLowerCase().split( /\s+/ );
+
+               // Determine if a cross-domain request is in order
+               if ( ! s.crossDomain ) {
+                       parts = rurl.exec( s.url.toLowerCase() );
+                       s.crossDomain = !!(
+                                       parts &&
+                                       ( parts[ 1 ] && parts[ 1 ] != protocol ||
+                                               parts[ 2 ] != loc.hostname ||
+                                               ( parts[ 3 ] || ( ( parts[ 1 ] || protocol ) === "http:" ? 80 : 443 ) ) !=
+                                                       ( loc.port || ( protocol === "http:" ? 80 : 443 ) ) )
+                       );
+               }
+
+               // Convert data if not already a string
+               if ( s.data && s.processData && typeof s.data !== "string" ) {
+                       s.data = jQuery.param( s.data , s.traditional );
+               }
+
+               // 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" );
+               }
+
+               // More options handling for requests with no content
+               if ( ! s.hasContent ) {
+
+                       // If data is available, append data to url
+                       if ( s.data ) {
+                               s.url += ( rquery.test( s.url ) ? "&" : "?" ) + s.data;
+                       }
+
+                       // Add anti-cache in url if needed
+                       if ( s.cache === false ) {
+
+                               var ts = jQuery.now(),
+                                       // try replacing _= if it is there
+                                       ret = s.url.replace( rts , "$1_=" + ts );
+
+                               // if nothing was replaced, add timestamp to the end
+                               s.url = ret + ( (ret == s.url ) ? ( rquery.test( s.url ) ? "&" : "?" ) + "_=" + 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;
+               }
+
+               // 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" );
+
+                               // Return false
+                               jXHR = false;
+
+               } else {
+
+                       // Install callbacks on deferreds
+                       for ( i in { success:1, error:1, complete:1 } ) {
+                               jXHR[ i ]( s[ i ] );
+                       }
+
+                       // Get transport
+                       transport = jQuery.ajaxTransport( s , options );
+
+                       // If no transport, we auto-abort
+                       if ( ! transport ) {
+
+                               done( 0 , "notransport" );
+
+                       } else {
+
+                               // Set state as sending
+                               state = jXHR.readyState = 1;
+
+                               // Send global event
+                               if ( s.global ) {
+                                       globalEventContext.trigger( "ajaxSend" , [ jXHR , s ] );
+                               }
+
+                               // Timeout
+                               if ( s.async && s.timeout > 0 ) {
+                                       timeoutTimer = setTimeout(function(){
+                                               jXHR.abort( "timeout" );
+                                       }, s.timeout);
+                               }
+
+                               // Try to send
+                               try {
+                                       transport.send(requestHeaders, done);
+                               } catch (e) {
+                                       // Propagate exception as error if not done
+                                       if ( status === 1 ) {
+
+                                               done(0, "error", "" + e);
+                                               jXHR = false;
+
+                                       // Simply rethrow otherwise
+                                       } else {
+                                               jQuery.error(e);
+                                       }
+                               }
+                       }
+               }
+
+               return jXHR;
        },
 
        // Serialize an array of form elements or a set of
@@ -281,19 +776,19 @@ jQuery.extend({
                                value = jQuery.isFunction(value) ? value() : value;
                                s[ s.length ] = encodeURIComponent(key) + "=" + encodeURIComponent(value);
                        };
-               
+
                // Set traditional to true for jQuery <= 1.3.2 behavior.
                if ( traditional === undefined ) {
                        traditional = jQuery.ajaxSettings.traditional;
                }
-               
+
                // If an array was passed in, assume that it is an array of form elements.
                if ( jQuery.isArray(a) || a.jquery ) {
                        // Serialize the form elements
                        jQuery.each( a, function() {
                                add( this.name, this.value );
                        });
-                       
+
                } else {
                        // If traditional, encode the "old" way (the way 1.3.2 or older
                        // did it), otherwise encode params recursively.
@@ -326,7 +821,7 @@ function buildParams( prefix, obj, traditional, add ) {
                                buildParams( prefix + "[" + ( typeof v === "object" || jQuery.isArray(v) ? i : "" ) + "]", v, traditional, add );
                        }
                });
-                       
+
        } else if ( !traditional && obj != null && typeof obj === "object" ) {
                // If we see an array here, it is empty and should be treated as an empty
                // object
@@ -339,7 +834,7 @@ function buildParams( prefix, obj, traditional, add ) {
                                buildParams( prefix + "[" + k + "]", v, traditional, add );
                        });
                }
-                                       
+
        } else {
                // Serialize scalar item.
                add( prefix, obj );
@@ -359,33 +854,97 @@ jQuery.extend({
 
 });
 
-/*
- * Create the request object; Microsoft failed to properly
- * implement the XMLHttpRequest in IE7 (can't request local files),
- * so we use the ActiveXObject when it is available
- * Additionally XMLHttpRequest can be disabled in IE7/IE8 so
- * we need a fallback.
- */
-if ( window.ActiveXObject ) {
-       jQuery.ajaxSettings.xhr = function() {
-       if ( window.location.protocol !== "file:" ) {
-               try {
-                       return new window.XMLHttpRequest();
-               } catch( xhrError ) {}
-       }
-       
-       try {
-               return new window.ActiveXObject("Microsoft.XMLHTTP");
-       } catch( activeError ) {}
-       };
-}
+// Base function for both ajaxPrefilter and ajaxTransport
+function ajaxPrefilterOrTransport( arg0 , arg1 , arg2 ) {
+
+       var type = jQuery.type( arg1 ),
+               structure = jQuery.ajaxSettings[ arg0 ],
+               i,
+               length;
+
+       // We have an options map so we have to inspect the structure
+       if ( type === "object" ) {
+
+               var options = arg1,
+                       originalOptions = arg2,
+                       // When dealing with prefilters, we execute only
+                       // (no selection so we never stop when a function
+                       // returns a non-falsy, non-string value)
+                       executeOnly = ( arg0 === "prefilters" ),
+                       inspect = function( dataType, tested ) {
+
+                               if ( ! tested[ dataType ] ) {
+
+                                       tested[ dataType ] = true;
+
+                                       var list = structure[ dataType ],
+                                               selected;
+
+                                       for( i = 0, length = list ? list.length : 0 ; ( executeOnly || ! selected ) && i < length ; i++ ) {
+                                               selected = list[ i ]( options , originalOptions );
+                                               // If we got redirected to a different dataType,
+                                               // we add it and switch to the corresponding list
+                                               if ( typeof( selected ) === "string" && selected !== dataType ) {
+                                                       options.dataTypes.unshift( selected );
+                                                       selected = inspect( selected , tested );
+                                                       // We always break in order not to continue
+                                                       // to iterate in previous list
+                                                       break;
+                                               }
+                                       }
+                                       // If we're only executing or nothing was selected
+                                       // we try the catchall dataType
+                                       if ( executeOnly || ! selected ) {
+                                               selected = inspect( "*" , tested );
+                                       }
+                                       // This will be ignored by ajaxPrefilter
+                                       // so it's safe to return no matter what
+                                       return selected;
+                               }
+
+                       };
+
+               // Start inspection with current transport dataType
+               return inspect( options.dataTypes[ 0 ] , {} );
 
-var testXHR = jQuery.ajaxSettings.xhr();
+       } else {
 
-// Does this browser support XHR requests?
-jQuery.support.ajax = !!testXHR;
+               // We're requested to add to the structure
+               // Signature is ( dataTypeExpression , function )
+               // with dataTypeExpression being optional and
+               // defaulting to catchAll (*)
+               type = type === "function";
 
-// Does this browser support crossDomain XHR requests
-jQuery.support.cors = testXHR && "withCredentials" in testXHR;
+               if ( type ) {
+                       arg2 = arg1;
+                       arg1 = undefined;
+               }
+               arg1 = arg1 || "*";
+
+               // We control that the second argument is really a function
+               if ( type || jQuery.isFunction( arg2 ) ) {
+
+                       var dataTypes = arg1.split( /\s+/ ),
+                               functor = arg2,
+                               dataType,
+                               list,
+                               placeBefore;
+
+                       // For each dataType in the dataTypeExpression
+                       for( i = 0 , length = dataTypes.length ; i < length ; i++ ) {
+                               dataType = dataTypes[ i ];
+                               // We control if we're asked to add before
+                               // any existing element
+                               placeBefore = /^\+/.test( dataType );
+                               if ( placeBefore ) {
+                                       dataType = dataType.substr( 1 );
+                               }
+                               list = structure[ dataType ] = structure[ dataType ] || [];
+                               // then we add to the structure accordingly
+                               list[ placeBefore ? "unshift" : "push" ]( functor );
+                       }
+               }
+       }
+}
 
 })( jQuery );
diff --git a/src/ajax/jsonp.js b/src/ajax/jsonp.js
new file mode 100644 (file)
index 0000000..ff8d1f1
--- /dev/null
@@ -0,0 +1,87 @@
+(function( jQuery ) {
+
+var jsc = jQuery.now(),
+       jsre = /(\=)(?:\?|%3F)(&|$)|()(?:\?\?|%3F%3F)()/i;
+
+// Default jsonp settings
+jQuery.ajaxSetup({
+       jsonp: "callback",
+       jsonpCallback: function() {
+               return "jsonp" + jsc++;
+       }
+});
+
+// Detect, normalize options and install callbacks for jsonp requests
+// (dataIsString is used internally)
+jQuery.ajaxPrefilter("json jsonp", function(s, originalSettings, dataIsString) {
+
+       dataIsString = ( typeof(s.data) === "string" );
+
+       if ( s.dataTypes[ 0 ] === "jsonp" ||
+               originalSettings.jsonpCallback ||
+               originalSettings.jsonp != null ||
+               s.jsonp !== false && ( jsre.test( s.url ) ||
+                               dataIsString && jsre.test( s.data ) ) ) {
+
+               var responseContainer,
+                       jsonpCallback = s.jsonpCallback =
+                               jQuery.isFunction( s.jsonpCallback ) ? s.jsonpCallback() : s.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;
+
+               window [ jsonpCallback ] = function( response ) {
+                       responseContainer = [response];
+               };
+
+               s.complete = [function() {
+
+                       // 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] );
+                               }
+                       } else {
+                               // else, more memory leak avoidance
+                               try{ delete window[ jsonpCallback ]; } catch(e){}
+                       }
+
+               }, s.complete ];
+
+               // 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
+               return "script";
+       }
+});
+
+})( jQuery );
similarity index 56%
rename from src/transports/script.js
rename to src/ajax/script.js
index fe38735..bdf69dd 100644 (file)
@@ -1,39 +1,45 @@
 (function( jQuery ) {
 
-// Install text to script executor
-jQuery.extend( true, jQuery.ajaxSettings , {
+// Install script dataType
+jQuery.ajaxSetup({
 
        accepts: {
                script: "text/javascript, application/javascript"
        },
-       
-       autoDataType: {
+
+       contents: {
                script: /javascript/
        },
-               
-       dataConverters: {
-               "text => script": jQuery.globalEval
+
+       converters: {
+               "text script": jQuery.globalEval
        }
-} );
+});
+
+// Handle cache's special case and global
+jQuery.ajaxPrefilter("script", function(s) {
 
-// Bind script tag hack transport
-jQuery.xhr.bindTransport("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
+jQuery.ajaxTransport("script", function(s) {
+
+       // This transport only deals with cross domain requests
+       if ( s.crossDomain ) {
+
                var script,
                        head = document.getElementsByTagName("head")[0] || document.documentElement;
-               
+
                return {
-                       
+
                        send: function(_, callback) {
 
                                script = document.createElement("script");
@@ -43,37 +49,39 @@ jQuery.xhr.bindTransport("script", function(s) {
                                if ( s.scriptCharset ) {
                                        script.charset = s.scriptCharset;
                                }
-                               
+
                                script.src = s.url;
-                               
+
                                // Attach handlers for all browsers
-                               script.onload = script.onreadystatechange = function(statusText) {
-                                       
-                                       if ( (!script.readyState ||
-                                                       script.readyState === "loaded" || script.readyState === "complete") ) {
-                                                               
+                               script.onload = script.onreadystatechange = function( _ , isAbort ) {
+
+                                       if ( ! script.readyState || /loaded|complete/.test( script.readyState ) ) {
+
                                                // Handle memory leak in IE
                                                script.onload = script.onreadystatechange = null;
-                                               
+
                                                // Remove the script
                                                if ( head && script.parentNode ) {
                                                        head.removeChild( script );
                                                }
-                                               
-                                               script = undefined;
-                                               
-                                               // Callback & dereference
-                                               callback(statusText ? 0 : 200, statusText || "success");
+
+                                               // Dereference the script
+                                               script = 0;
+
+                                               // Callback if not abort
+                                               if ( ! isAbort ) {
+                                                       callback( 200, "success" );
+                                               }
                                        }
                                };
                                // Use insertBefore instead of appendChild  to circumvent an IE6 bug.
                                // This arises when a base node is used (#2709 and #4378).
                                head.insertBefore( script, head.firstChild );
                        },
-                       
-                       abort: function(statusText) {
+
+                       abort: function() {
                                if ( script ) {
-                                       script.onload(statusText);
+                                       script.onload(0,1);
                                }
                        }
                };
diff --git a/src/ajax/xhr.js b/src/ajax/xhr.js
new file mode 100644 (file)
index 0000000..7a4da2d
--- /dev/null
@@ -0,0 +1,225 @@
+(function( jQuery ) {
+
+var // Next active xhr id
+       xhrId = jQuery.now(),
+
+       // active xhrs
+       xhrs = {},
+
+       // #5280: see below
+       xhrUnloadAbortInstalled,
+
+       // XHR used to determine supports properties
+       testXHR;
+
+// Create the request object
+// (This is still attached to ajaxSettings for backward compatibility)
+jQuery.ajaxSettings.xhr = window.ActiveXObject ?
+       /* Microsoft failed to properly
+        * implement the XMLHttpRequest in IE7 (can't request local files),
+        * so we use the ActiveXObject when it is available
+        * Additionally XMLHttpRequest can be disabled in IE7/IE8 so
+        * we need a fallback.
+        */
+       function() {
+               if ( window.location.protocol !== "file:" ) {
+                       try {
+                               return new window.XMLHttpRequest();
+                       } catch( xhrError ) {}
+               }
+
+               try {
+                       return new window.ActiveXObject("Microsoft.XMLHTTP");
+               } catch( activeError ) {}
+       } :
+       // For all other browsers, use the standard XMLHttpRequest object
+       function() {
+               return new window.XMLHttpRequest();
+       };
+
+// Test if we can create an xhr object
+try {
+       testXHR = jQuery.ajaxSettings.xhr();
+} catch( xhrCreationException ) {}
+
+//Does this browser support XHR requests?
+jQuery.support.ajax = !!testXHR;
+
+// Does this browser support crossDomain XHR requests
+jQuery.support.cors = testXHR && "withCredentials" in testXHR;
+
+// No need for the temporary xhr anymore
+testXHR = undefined;
+
+// Create transport if the browser can provide an xhr
+if ( jQuery.support.ajax ) {
+       jQuery.ajaxTransport( function( s ) {
+
+               // Cross domain only allowed if supported through XMLHttpRequest
+               if ( ! s.crossDomain || jQuery.support.cors ) {
+
+                       var callback;
+
+                       return {
+
+                               send: function(headers, complete) {
+
+                                       // #5280: we need to abort on unload or IE will keep connections alive
+                                       if ( ! xhrUnloadAbortInstalled ) {
+
+                                               xhrUnloadAbortInstalled = 1;
+
+                                               jQuery(window).bind( "unload" , function() {
+
+                                                       // Abort all pending requests
+                                                       jQuery.each(xhrs, function(_, xhr) {
+                                                               if ( xhr.onreadystatechange ) {
+                                                                       xhr.onreadystatechange( 1 );
+                                                               }
+                                                       });
+
+                                               });
+                                       }
+
+                                       // Get a new xhr
+                                       var xhr = s.xhr(),
+                                               handle;
+
+                                       // Open the socket
+                                       // Passing null username, generates a login popup on Opera (#2865)
+                                       if ( s.username ) {
+                                               xhr.open(s.type, s.url, s.async, s.username, s.password);
+                                       } else {
+                                               xhr.open(s.type, s.url, s.async);
+                                       }
+
+                                       // Requested-With header
+                                       // Not set for crossDomain requests with no content
+                                       // (see why at http://trac.dojotoolkit.org/ticket/9486)
+                                       // Won't change header if already provided
+                                       if ( ! ( s.crossDomain && ! s.hasContent ) && ! headers["x-requested-with"] ) {
+                                               headers["x-requested-with"] = "XMLHttpRequest";
+                                       }
+
+                                       // Need an extra try/catch for cross domain requests in Firefox 3
+                                       try {
+
+                                               jQuery.each(headers, function(key,value) {
+                                                       xhr.setRequestHeader(key,value);
+                                               });
+
+                                       } catch(_) {}
+
+                                       // Do send the request
+                                       try {
+                                               xhr.send( ( s.hasContent && s.data ) || null );
+                                       } catch(e) {
+                                               complete(0, "error", "" + e);
+                                               return;
+                                       }
+
+                                       // Listener
+                                       callback = function( _ , isAbort ) {
+
+                                               // Was never called and is aborted or complete
+                                               if ( callback && ( isAbort || xhr.readyState === 4 ) ) {
+
+                                                       // Only called once
+                                                       callback = 0;
+
+                                                       // Do not keep as active anymore
+                                                       if (handle) {
+                                                               xhr.onreadystatechange = jQuery.noop;
+                                                               delete xhrs[ handle ];
+                                                       }
+
+                                                       // If it's an abort
+                                                       if ( isAbort ) {
+
+                                                               // Abort it manually if needed
+                                                               if ( xhr.readyState !== 4 ) {
+                                                                       xhr.abort();
+                                                               }
+                                                       } else {
+
+                                                               // Get info
+                                                               var status = xhr.status,
+                                                                       statusText,
+                                                                       responseHeaders = xhr.getAllResponseHeaders(),
+                                                                       responses = {},
+                                                                       xml = xhr.responseXML;
+
+                                                               // Construct response list
+                                                               if ( xml && xml.documentElement /* #4958 */ ) {
+                                                                       responses.xml = xml;
+                                                               }
+                                                               responses.text = xhr.responseText;
+
+                                                               try { // Firefox throws an exception when accessing statusText for faulty cross-domain requests
+
+                                                                       statusText = xhr.statusText;
+
+                                                               } catch( e ) {
+
+                                                                       statusText = ""; // We normalize with Webkit giving an empty statusText
+
+                                                               }
+
+                                                               // Filter status for non standard behaviours
+                                                               // (so many they seem to be the actual "standard")
+                                                               status =
+                                                                       // Opera returns 0 when it should be 304
+                                                                       // Webkit returns 0 for failing cross-domain no matter the real status
+                                                                       status === 0 ?
+                                                                               (
+                                                                                       ! s.crossDomain || statusText ? // Webkit, Firefox: filter out faulty cross-domain requests
+                                                                                       (
+                                                                                               responseHeaders ? // Opera: filter out real aborts #6060
+                                                                                               304
+                                                                                               :
+                                                                                               0
+                                                                                       )
+                                                                                       :
+                                                                                       302 // We assume 302 but could be anything cross-domain related
+                                                                               )
+                                                                               :
+                                                                               (
+                                                                                       status == 1223 ?        // IE sometimes returns 1223 when it should be 204 (see #1450)
+                                                                                               204
+                                                                                               :
+                                                                                               status
+                                                                               );
+
+                                                               // Call complete
+                                                               complete(status,statusText,responses,responseHeaders);
+                                                       }
+                                               }
+                                       };
+
+                                       // if we're in sync mode
+                                       // or it's in cache and has been retrieved directly (IE6 & IE7)
+                                       // we need to manually fire the callback
+                                       if ( ! s.async || xhr.readyState === 4 ) {
+
+                                               callback();
+
+                                       } else {
+
+                                               // Add to list of active xhrs
+                                               handle = xhrId++;
+                                               xhrs[ handle ] = xhr;
+                                               xhr.onreadystatechange = callback;
+                                       }
+                               },
+
+                               abort: function() {
+                                       if ( callback ) {
+                                               callback(0,1);
+                                       }
+                               }
+                       };
+               }
+       });
+}
+
+})( jQuery );
index 78b1bfd..d37400a 100644 (file)
@@ -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__" ) || "";
                        }
                });
        },
@@ -182,7 +182,7 @@ jQuery.fn.extend({
                                                var option = options[ i ];
 
                                                // Don't return options that are disabled or in a disabled optgroup
-                                               if ( option.selected && (jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null) && 
+                                               if ( option.selected && (jQuery.support.optDisabled ? !option.disabled : option.getAttribute("disabled") === null) &&
                                                                (!option.parentNode.disabled || !jQuery.nodeName( option.parentNode, "optgroup" )) ) {
 
                                                        // Get the specific value for the option
index e75c86f..236f84d 100644 (file)
@@ -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+/,
@@ -56,12 +52,15 @@ var jQuery = function( selector, context ) {
 
        // For matching the engine and version of the browser
        browserMatch,
-       
+
        // Has the ready events already been bound?
        readyBound = false,
-       
-       // The functions to execute on DOM ready
-       readyList = [],
+
+       // The deferred used on DOM ready
+       readyList,
+
+       // Promise methods
+       promiseMethods = "then done fail isResolved isRejected promise".split( " " ),
 
        // The ready event handler
        DOMContentLoaded,
@@ -73,12 +72,13 @@ var jQuery = function( selector, context ) {
        slice = Array.prototype.slice,
        trim = String.prototype.trim,
        indexOf = Array.prototype.indexOf,
-       
+
        // [[Class]] -> type pairs
        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)
@@ -92,7 +92,7 @@ jQuery.fn = jQuery.prototype = {
                        this.length = 1;
                        return this;
                }
-               
+
                // The body element only exists once, optimize finding it
                if ( selector === "body" && !context && document.body ) {
                        this.context = document;
@@ -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
@@ -129,9 +130,9 @@ jQuery.fn = jQuery.prototype = {
 
                                        } else {
                                                ret = jQuery.buildFragment( [ match[1] ], [ doc ] );
-                                               selector = (ret.cacheable ? ret.fragment.cloneNode(true) : ret.fragment).childNodes;
+                                               selector = (ret.cacheable ? jQuery(ret.fragment).clone()[0] : ret.fragment).childNodes;
                                        }
-                                       
+
                                        return jQuery.merge( this, selector );
                                        
                                // HANDLE: $("#id")
@@ -157,13 +158,6 @@ jQuery.fn = jQuery.prototype = {
                                        return this;
                                }
 
-                       // HANDLE: $("TAG")
-                       } else if ( !context && !rnonword.test( selector ) ) {
-                               this.selector = selector;
-                               this.context = document;
-                               selector = document.getElementsByTagName( selector );
-                               return jQuery.merge( this, selector );
-
                        // HANDLE: $(expr, $(...))
                        } else if ( !context || context.jquery ) {
                                return (context || rootjQuery).find( selector );
@@ -171,7 +165,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,11 +216,11 @@ 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 );
-               
+
                } else {
                        jQuery.merge( ret, elems );
                }
@@ -252,25 +246,15 @@ jQuery.fn = jQuery.prototype = {
        each: function( callback, args ) {
                return jQuery.each( this, callback, args );
        },
-       
-       ready: function( fn ) {
+
+       ready: function() {
                // Attach the listeners
                jQuery.bindReady();
 
-               // If the DOM is already ready
-               if ( jQuery.isReady ) {
-                       // Execute the function immediately
-                       fn.call( document, jQuery );
-
-               // Otherwise, remember the function for later
-               } else if ( readyList ) {
-                       // Add the function to the wait list
-                       readyList.push( fn );
-               }
-
-               return this;
+               // Change ready & apply
+               return ( jQuery.fn.ready = readyList.done ).apply( this , arguments );
        },
-       
+
        eq: function( i ) {
                return i === -1 ?
                        this.slice( i ) :
@@ -295,9 +279,9 @@ jQuery.fn = jQuery.prototype = {
                        return callback.call( elem, i, elem );
                }));
        },
-       
+
        end: function() {
-               return this.prevObject || jQuery(null);
+               return this.prevObject || this.constructor(null);
        },
 
        // For internal use only.
@@ -384,14 +368,14 @@ jQuery.extend({
 
                return jQuery;
        },
-       
+
        // Is the DOM ready to be used? Set to true once it occurs.
        isReady: false,
 
        // A counter to track how many items to wait for before
        // the ready event fires. See #6781
        readyWait: 1,
-       
+
        // Handle when the DOM is ready
        ready: function( wait ) {
                // A third-party is pushing the ready event forwards
@@ -415,27 +399,15 @@ jQuery.extend({
                        }
 
                        // If there are functions bound, to execute
-                       if ( readyList ) {
-                               // Execute all of them
-                               var fn,
-                                       i = 0,
-                                       ready = readyList;
+                       readyList.resolveWith( document , [ jQuery ] );
 
-                               // Reset the list of functions
-                               readyList = null;
-
-                               while ( (fn = ready[ i++ ]) ) {
-                                       fn.call( document, jQuery );
-                               }
-
-                               // Trigger any bound ready events
-                               if ( jQuery.fn.trigger ) {
-                                       jQuery( document ).trigger( "ready" ).unbind( "ready" );
-                               }
+                       // Trigger any bound ready events
+                       if ( jQuery.fn.trigger ) {
+                               jQuery( document ).trigger( "ready" ).unbind( "ready" );
                        }
                }
        },
-       
+
        bindReady: function() {
                if ( readyBound ) {
                        return;
@@ -454,7 +426,7 @@ jQuery.extend({
                if ( document.addEventListener ) {
                        // Use the handy event callback
                        document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
-                       
+
                        // A fallback to window.onload, that will always work
                        window.addEventListener( "load", jQuery.ready, false );
 
@@ -463,7 +435,7 @@ jQuery.extend({
                        // ensure firing before onload,
                        // maybe late but safe also for iframes
                        document.attachEvent("onreadystatechange", DOMContentLoaded);
-                       
+
                        // A fallback to window.onload, that will always work
                        window.attachEvent( "onload", jQuery.ready );
 
@@ -514,20 +486,20 @@ jQuery.extend({
                if ( !obj || jQuery.type(obj) !== "object" || obj.nodeType || jQuery.isWindow( obj ) ) {
                        return false;
                }
-               
+
                // Not own constructor property must be Object
                if ( obj.constructor &&
                        !hasOwn.call(obj, "constructor") &&
                        !hasOwn.call(obj.constructor.prototype, "isPrototypeOf") ) {
                        return false;
                }
-               
+
                // Own properties are enumerated firstly, so to speed up,
                // if last one is own, then all properties are own.
-       
+
                var key;
                for ( key in obj ) {}
-               
+
                return key === undefined || hasOwn.call( obj, key );
        },
 
@@ -537,11 +509,11 @@ jQuery.extend({
                }
                return true;
        },
-       
+
        error: function( msg ) {
                throw msg;
        },
-       
+
        parseJSON: function( data ) {
                if ( typeof data !== "string" || !data ) {
                        return null;
@@ -549,7 +521,7 @@ jQuery.extend({
 
                // Make sure leading/trailing whitespace is removed (IE can't handle it)
                data = jQuery.trim( data );
-               
+
                // Make sure the incoming data is actual JSON
                // Logic borrowed from http://json.org/json2.js
                if ( rvalidchars.test(data.replace(rvalidescape, "@")
@@ -566,6 +538,28 @@ jQuery.extend({
                }
        },
 
+       // Cross-browser xml parsing
+       // (xml & tmp used internally)
+       parseXML: function( data , xml , tmp ) {
+
+               if ( window.DOMParser ) { // Standard
+                       tmp = new DOMParser();
+                       xml = tmp.parseFromString( data , "text/xml" );
+               } else { // IE
+                       xml = new ActiveXObject( "Microsoft.XMLDOM" );
+                       xml.async = "false";
+                       xml.loadXML( data );
+               }
+
+               tmp = xml.documentElement;
+
+               if ( ! tmp || ! tmp.nodeName || tmp.nodeName === "parsererror" ) {
+                       jQuery.error( "Invalid XML: " + data );
+               }
+
+               return xml;
+       },
+
        noop: function() {},
 
        // Evalulates a script in a global context
@@ -578,7 +572,7 @@ jQuery.extend({
 
                        script.type = "text/javascript";
 
-                       if ( jQuery.support.scriptEval ) {
+                       if ( jQuery.support.scriptEval() ) {
                                script.appendChild( document.createTextNode( data ) );
                        } else {
                                script.text = data;
@@ -691,7 +685,7 @@ jQuery.extend({
                        for ( var l = second.length; j < l; j++ ) {
                                first[ i++ ] = second[ j ];
                        }
-               
+
                } else {
                        while ( second[j] !== undefined ) {
                                first[ i++ ] = second[ j++ ];
@@ -772,7 +766,7 @@ jQuery.extend({
        // The value/s can be optionally by executed if its a function
        access: function( elems, key, value, exec, fn, pass ) {
                var length = elems.length;
-       
+
                // Setting many attributes
                if ( typeof key === "object" ) {
                        for ( var k in key ) {
@@ -780,19 +774,19 @@ jQuery.extend({
                        }
                        return elems;
                }
-       
+
                // Setting one attribute
                if ( value !== undefined ) {
                        // Optionally, function values get executed if exec is true
                        exec = !pass && exec && jQuery.isFunction(value);
-               
+
                        for ( var i = 0; i < length; i++ ) {
                                fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass );
                        }
-               
+
                        return elems;
                }
-       
+
                // Getting an attribute
                return length ? fn( elems[0], key ) : undefined;
        },
@@ -801,6 +795,178 @@ jQuery.extend({
                return (new Date()).getTime();
        },
 
+       // Create a simple deferred (one callbacks list)
+       _Deferred: function() {
+
+               var // callbacks list
+                       callbacks = [],
+                       // stored [ context , args ]
+                       fired,
+                       // to avoid firing when already doing so
+                       firing,
+                       // flag to know if the deferred has been cancelled
+                       cancelled,
+                       // the deferred itself
+                       deferred  = {
+
+                               // done( f1, f2, ...)
+                               done: function () {
+
+                                       if ( ! cancelled ) {
+
+                                               var args = arguments,
+                                                       i,
+                                                       length,
+                                                       elem,
+                                                       type,
+                                                       _fired;
+
+                                               if ( fired ) {
+                                                       _fired = fired;
+                                                       fired = 0;
+                                               }
+
+                                               for ( i = 0, length = args.length ; i < length ; i++ ) {
+                                                       elem = args[ i ];
+                                                       type = jQuery.type( elem );
+                                                       if ( type === "array" ) {
+                                                               deferred.done.apply( deferred , elem );
+                                                       } else if ( type === "function" ) {
+                                                               callbacks.push( elem );
+                                                       }
+                                               }
+
+                                               if ( _fired ) {
+                                                       deferred.resolveWith( _fired[ 0 ] , _fired[ 1 ] );
+                                               }
+                                       }
+
+                                       return this;
+                               },
+
+                               // resolve with given context and args
+                               resolveWith: function( context , args ) {
+                                       if ( ! cancelled && ! fired && ! firing ) {
+
+                                               firing = 1;
+
+                                               try {
+                                                       while( callbacks[ 0 ] ) {
+                                                               callbacks.shift().apply( context , args );
+                                                       }
+                                               }
+                                               finally {
+                                                       fired = [ context , args ];
+                                                       firing = 0;
+                                               }
+                                       }
+                                       return this;
+                               },
+
+                               // resolve with this as context and given arguments
+                               resolve: function() {
+                                       deferred.resolveWith( jQuery.isFunction( this.promise ) ? this.promise() : this , arguments );
+                                       return this;
+                               },
+
+                               // Has this deferred been resolved?
+                               isResolved: function() {
+                                       return !!( firing || fired );
+                               },
+
+                               // Cancel
+                               cancel: function() {
+                                       cancelled = 1;
+                                       callbacks = [];
+                                       return this;
+                               }
+                       };
+
+               return deferred;
+       },
+
+       // Full fledged deferred (two callbacks list)
+       // Typical success/error system
+       Deferred: function( func ) {
+
+               var deferred = jQuery._Deferred(),
+                       failDeferred = jQuery._Deferred(),
+                       promise;
+
+               // Add errorDeferred methods, then and promise
+               jQuery.extend( deferred , {
+
+                       then: function( doneCallbacks , failCallbacks ) {
+                               deferred.done( doneCallbacks ).fail( failCallbacks );
+                               return this;
+                       },
+                       fail: failDeferred.done,
+                       rejectWith: failDeferred.resolveWith,
+                       reject: failDeferred.resolve,
+                       isRejected: failDeferred.isResolved,
+                       // Get a promise for this deferred
+                       // If obj is provided, the promise aspect is added to the object
+                       // (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;
+                       }
+
+               } );
+
+               // Make sure only one callback list will be used
+               deferred.then( failDeferred.cancel , deferred.cancel );
+
+               // Unexpose cancel
+               delete deferred.cancel;
+
+               // Call given func if any
+               if ( func ) {
+                       func.call( deferred , deferred );
+               }
+
+               return deferred;
+       },
+
+       // Deferred helper
+       when: function( object ) {
+               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.resolveWith( promise, resolveArray );
+                                       }
+                               }).fail( function() {
+                                       deferred.rejectWith( promise, arguments );
+                               });
+                               return !deferred.isRejected();
+                       });
+               } else if ( deferred !== object ) {
+                       deferred.resolve( object );
+               }
+               return promise;
+       },
+
        // Use of jQuery.browser is frowned upon.
        // More details: http://docs.jquery.com/Utilities/jQuery.browser
        uaMatch: function( ua ) {
@@ -815,9 +981,31 @@ 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: {}
 });
 
+// Create readyList deferred
+readyList = jQuery._Deferred();
+
 // Populate the class2type map
 jQuery.each("Boolean Number String Function Array Date RegExp Object".split(" "), function(i, name) {
        class2type[ "[object " + name + "]" ] = name.toLowerCase();
@@ -840,9 +1028,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]+$/;
 }
@@ -886,11 +1073,6 @@ function doScrollCheck() {
        jQuery.ready();
 }
 
-// Expose jQuery as an Asynchronous Module
-if ( typeof define !== "undefined" ) {
-       define( "jquery", [], function () { return jQuery; } );
-}
-
 // Expose jQuery to the global object
 return (window.jQuery = window.$ = jQuery);
 
index 8a83c60..19c6342 100644 (file)
@@ -263,8 +263,9 @@ if ( document.defaultView && document.defaultView.getComputedStyle ) {
 
 if ( document.documentElement.currentStyle ) {
        currentStyle = function( elem, name ) {
-               var left, rsLeft,
+               var left, 
                        ret = elem.currentStyle && elem.currentStyle[ name ],
+                       rsLeft = elem.runtimeStyle && elem.runtimeStyle[ name ],
                        style = elem.style;
 
                // From the awesome hack by Dean Edwards
@@ -275,16 +276,19 @@ if ( document.documentElement.currentStyle ) {
                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;
index 4d1d1bd..21f0e3a 100644 (file)
@@ -1,7 +1,6 @@
 (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 ) {
index f35b0ac..e2d411d 100644 (file)
@@ -25,7 +25,7 @@ jQuery.each([ "Height", "Width" ], function( i, name ) {
                if ( !elem ) {
                        return size == null ? null : this;
                }
-               
+
                if ( jQuery.isFunction( size ) ) {
                        return this.each(function( i ) {
                                var self = jQuery( this );
@@ -35,8 +35,10 @@ jQuery.each([ "Height", "Width" ], function( i, name ) {
 
                if ( jQuery.isWindow( elem ) ) {
                        // Everyone else use document.documentElement or document.body depending on Quirks vs Standards mode
-                       return elem.document.compatMode === "CSS1Compat" && elem.document.documentElement[ "client" + name ] ||
-                               elem.document.body[ "client" + name ];
+                       // 3rd condition allows Nokia support, as it supports the docElem prop but not CSS1Compat
+                       var docElemProp = elem.document.documentElement[ "client" + name ];
+                       return elem.document.compatMode === "CSS1Compat" && docElemProp ||
+                               elem.document.body[ "client" + name ] || docElemProp;
 
                // Get document width or height
                } else if ( elem.nodeType === 9 ) {
index 6007074..b067539 100644 (file)
@@ -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") || "";
                                }
                        }
 
@@ -61,9 +61,9 @@ jQuery.fn.extend({
                } else {
                        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 );
                                }
                        }
 
@@ -337,7 +337,7 @@ jQuery.fx.prototype = {
                }
 
                var r = parseFloat( jQuery.css( this.elem, this.prop ) );
-               return r && r > -10000 ? r : 0;
+               return r || 0;
        },
 
        // Start an animation from one number to another
index c904734..2ddf288 100644 (file)
@@ -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,12 +59,9 @@ 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" ) {
                        // On plain objects events is a fn that holds the the data
                        // which prevents this data from being JSON serialized
@@ -143,9 +141,9 @@ jQuery.event = {
                                        }
                                }
                        }
-                       
-                       if ( special.add ) { 
-                               special.add.call( elem, handleObj ); 
+
+                       if ( special.add ) {
+                               special.add.call( elem, handleObj );
 
                                if ( !handleObj.handler.guid ) {
                                        handleObj.handler.guid = handler.guid;
@@ -177,14 +175,13 @@ 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 ) {
                        return;
                }
-               
+
                if ( typeof events === "function" ) {
                        elemData = events;
                        events = events.events;
@@ -222,7 +219,7 @@ jQuery.event = {
                                namespaces = type.split(".");
                                type = namespaces.shift();
 
-                               namespace = new RegExp("(^|\\.)" + 
+                               namespace = new RegExp("(^|\\.)" +
                                        jQuery.map( namespaces.slice(0).sort(), fcleanup ).join("\\.(?:.*\\.)?") + "(\\.|$)");
                        }
 
@@ -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 );
@@ -384,7 +388,7 @@ jQuery.event = {
                                isClick = jQuery.nodeName( target, "a" ) && targetType === "click",
                                special = jQuery.event.special[ targetType ] || {};
 
-                       if ( (!special._default || special._default.call( elem, event ) === false) && 
+                       if ( (!special._default || special._default.call( elem, event ) === false) &&
                                !isClick && !(target && target.nodeName && jQuery.noData[target.nodeName.toLowerCase()]) ) {
 
                                try {
@@ -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;
@@ -454,7 +458,7 @@ jQuery.event = {
                                        event.handler = handleObj.handler;
                                        event.data = handleObj.data;
                                        event.handleObj = handleObj;
-       
+
                                        var ret = handleObj.handler.apply( this, args );
 
                                        if ( ret !== undefined ) {
@@ -553,7 +557,7 @@ jQuery.event = {
                        add: function( handleObj ) {
                                jQuery.event.add( this,
                                        liveConvert( handleObj.origType, handleObj.selector ),
-                                       jQuery.extend({}, handleObj, {handler: liveHandler, guid: handleObj.handler.guid}) ); 
+                                       jQuery.extend({}, handleObj, {handler: liveHandler, guid: handleObj.handler.guid}) );
                        },
 
                        remove: function( handleObj ) {
@@ -583,7 +587,7 @@ jQuery.removeEvent = document.removeEventListener ?
                if ( elem.removeEventListener ) {
                        elem.removeEventListener( type, handle, false );
                }
-       } : 
+       } :
        function( elem, type, handle ) {
                if ( elem.detachEvent ) {
                        elem.detachEvent( "on" + type, handle );
@@ -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
@@ -636,7 +640,7 @@ jQuery.Event.prototype = {
                if ( !e ) {
                        return;
                }
-               
+
                // if preventDefault exists run it on the original event
                if ( e.preventDefault ) {
                        e.preventDefault();
@@ -732,7 +736,7 @@ if ( !jQuery.support.submitBubbles ) {
                                                return trigger( "submit", this, arguments );
                                        }
                                });
-        
+
                                jQuery.event.add(this, "keypress.specialSubmit", function( e ) {
                                        var elem = e.target,
                                                type = elem.type;
@@ -787,14 +791,14 @@ 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 ) {
                        return;
                }
@@ -808,7 +812,7 @@ if ( !jQuery.support.changeBubbles ) {
 
        jQuery.event.special.change = {
                filters: {
-                       focusout: testChange, 
+                       focusout: testChange,
 
                        beforedeactivate: testChange,
 
@@ -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) );
                        }
                },
 
@@ -879,15 +883,15 @@ if ( document.addEventListener ) {
                                if ( focusCounts[fix]++ === 0 ) {
                                        document.addEventListener( orig, handler, true );
                                }
-                       }, 
-                       teardown: function() { 
+                       },
+                       teardown: function() {
                                if ( --focusCounts[fix] === 0 ) {
                                        document.removeEventListener( orig, handler, true );
                                }
                        }
                };
 
-               function handler( e ) { 
+               function handler( e ) {
                        e = jQuery.event.fix( e );
                        e.type = fix;
                        return jQuery.event.trigger( e, null, e.target );
@@ -904,7 +908,7 @@ jQuery.each(["bind", "one"], function( i, name ) {
                        }
                        return this;
                }
-               
+
                if ( jQuery.isFunction( data ) || data === false ) {
                        fn = data;
                        data = undefined;
@@ -944,20 +948,20 @@ jQuery.fn.extend({
 
                return this;
        },
-       
+
        delegate: function( selector, types, data, fn ) {
                return this.live( types, data, fn, selector );
        },
-       
+
        undelegate: function( selector, types, fn ) {
                if ( arguments.length === 0 ) {
                                return this.unbind( "live" );
-               
+
                } else {
                        return this.die( types, null, fn, selector );
                }
        },
-       
+
        trigger: function( type, data ) {
                return this.each(function() {
                        jQuery.event.trigger( type, data, this );
@@ -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();
@@ -1014,12 +1018,12 @@ jQuery.each(["live", "die"], function( i, name ) {
                var type, i = 0, match, namespaces, preType,
                        selector = origSelector || this.selector,
                        context = origSelector ? this : jQuery( this.context );
-               
+
                if ( typeof types === "object" && !types.preventDefault ) {
                        for ( var key in types ) {
                                context[ name ]( key, data, types[key], selector );
                        }
-                       
+
                        return this;
                }
 
@@ -1066,7 +1070,7 @@ jQuery.each(["live", "die"], function( i, name ) {
                                context.unbind( "live." + liveConvert( type, selector ), fn );
                        }
                }
-               
+
                return this;
        };
 });
@@ -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;
@@ -1085,7 +1089,7 @@ function liveHandler( event ) {
        if ( event.liveFired === this || !events || !events.live || event.target.disabled || event.button && event.type === "click" ) {
                return;
        }
-       
+
        if ( event.namespace ) {
                namespace = new RegExp("(^|\\.)" + event.namespace.split(".").join("\\.(?:.*\\.)?") + "(\\.|$)");
        }
@@ -1183,21 +1187,4 @@ jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblcl
        }
 });
 
-// Prevent memory leaks in IE
-// Window isn't included so as not to unbind existing unload events
-// More info:
-//  - http://isaacschlueter.com/2006/10/msie-memory-leaks/
-if ( window.attachEvent && !window.addEventListener ) {
-       jQuery(window).bind("unload", function() {
-               for ( var id in jQuery.cache ) {
-                       if ( jQuery.cache[ id ].handle ) {
-                               // Try/Catch is to handle iframes being unloaded, see #4280
-                               try {
-                                       jQuery.event.remove( jQuery.cache[ id ].handle.elem );
-                               } catch(e) {}
-                       }
-               }
-       });
-}
-
 })( jQuery );
index cb15705..a75f311 100644 (file)
@@ -11,7 +11,7 @@
  * Copyright 2010, The Dojo Foundation
  * Released under the MIT, BSD, and GPL Licenses.
  *
- * Date: 
+ * Date: @DATE
  */
 (function( window, undefined ) {
 
index 96caa02..596a457 100644 (file)
@@ -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 );
+                                       }
                                }
                        }
                }
@@ -420,15 +427,29 @@ function cloneFixAttributes(src, dest) {
        if ( nodeName === "object" ) {
                dest.outerHTML = src.outerHTML;
 
-       // IE6-8 fails to persist the checked state of a cloned checkbox
-       // or radio button
-       } else if ( nodeName === "input" && src.checked ) {
-               dest.defaultChecked = dest.checked = src.checked;
+       } else if ( nodeName === "input" && (src.type === "checkbox" || src.type === "radio") ) {
+               // IE6-8 fails to persist the checked state of a cloned checkbox
+               // or radio button. Worse, IE6-7 fail to give the cloned element
+               // a checked appearance if the defaultChecked value isn't also set
+               if ( src.checked ) {
+                       dest.defaultChecked = dest.checked = src.checked;
+               }
+
+               // IE6-7 get confused and end up setting the value of a cloned
+               // checkbox/radio button to an empty string instead of "on"
+               if ( dest.value !== src.value ) {
+                       dest.value = src.value;
+               }
 
        // IE6-8 fails to return the selected option to the default selected
        // state when cloning options
        } else if ( nodeName === "option" ) {
                dest.selected = src.defaultSelected;
+
+       // IE6-8 fails to set the defaultValue to the correct value when
+       // cloning other types of input fields
+       } else if ( nodeName === "input" || nodeName === "textarea" ) {
+               dest.defaultValue = src.defaultValue;
        }
 
        // Event data gets referenced instead of copied if the expando
@@ -594,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++ ) {
@@ -606,17 +626,23 @@ 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 );
                                                }
                                        }
+
+                                       // Null the DOM reference to avoid IE6/7/8 leak (#7054)
+                                       if ( data.handle ) {
+                                               data.handle.elem = null;
+                                       }
                                }
 
                                if ( deleteExpando ) {
index 3fb2917..a10d30a 100644 (file)
@@ -7,7 +7,7 @@ if ( "getBoundingClientRect" in document.documentElement ) {
        jQuery.fn.offset = function( options ) {
                var elem = this[0], box;
 
-               if ( options ) { 
+               if ( options ) {
                        return this.each(function( i ) {
                                jQuery.offset.setOffset( this, options, i );
                        });
@@ -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,
@@ -49,7 +49,7 @@ if ( "getBoundingClientRect" in document.documentElement ) {
        jQuery.fn.offset = function( options ) {
                var elem = this[0];
 
-               if ( options ) { 
+               if ( options ) {
                        return this.each(function( i ) {
                                jQuery.offset.setOffset( this, options, i );
                        });
@@ -168,7 +168,7 @@ jQuery.offset = {
 
                return { top: top, left: left };
        },
-       
+
        setOffset: function( elem, options, i ) {
                var position = jQuery.css( elem, "position" );
 
@@ -202,7 +202,7 @@ jQuery.offset = {
                if (options.left != null) {
                        props.left = (options.left - curOffset.left) + curLeft;
                }
-               
+
                if ( "using" in options ) {
                        options.using.call( elem, props );
                } else {
@@ -262,7 +262,7 @@ jQuery.each( ["Left", "Top"], function( i, name ) {
 
        jQuery.fn[ method ] = function(val) {
                var elem = this[0], win;
-               
+
                if ( !elem ) {
                        return null;
                }
index 735b0e1..9e3e2fb 100644 (file)
@@ -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 );
+               }
        }
 });
 
index e4c3ea9..7be28fd 100644 (file)
@@ -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,
        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
                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;");
        jQuery.support.changeBubbles = eventSupported("change");
 
        // release memory in IE
-       root = script = div = all = a = null;
+       div = all = a = null;
 })();
 })( jQuery );
diff --git a/src/transports/jsonp.js b/src/transports/jsonp.js
deleted file mode 100644 (file)
index d9e77f2..0000000
+++ /dev/null
@@ -1,89 +0,0 @@
-(function( jQuery ) {
-
-var jsc = jQuery.now(),
-       jsre = /\=\?(&|$)/,
-       rquery_jsonp = /\?/;
-
-// Default jsonp callback name
-jQuery.ajaxSettings.jsonpCallback = function() {
-       return "jsonp" + jsc++;
-};
-
-// Normalize jsonp queries
-// 1) put callback parameter in url or data
-// 2) ensure transportDataType is json
-// 3) ensure options jsonp is always provided so that jsonp requests are always
-//    json request with the jsonp option set
-jQuery.xhr.prefilter( function(s) {
-       
-       var transportDataType = s.dataTypes[0];
-       
-       if ( s.jsonp ||
-               transportDataType === "jsonp" ||
-               transportDataType === "json" && ( jsre.test(s.url) || typeof(s.data) === "string" && jsre.test(s.data) ) ) {
-
-               var jsonp = s.jsonp = s.jsonp || "callback",
-                       jsonpCallback = s.jsonpCallback =
-                               jQuery.isFunction( s.jsonpCallback ) ? s.jsonpCallback() : s.jsonpCallback,
-                       url = s.url.replace(jsre, "=" + jsonpCallback + "$1"),
-                       data = s.url == url && typeof(s.data) === "string" ? s.data.replace(jsre, "=" + jsonpCallback + "$1") : s.data;
-                       
-               if ( url == s.url && data == s.data ) {
-                       url = url += (rquery_jsonp.test( url ) ? "&" : "?") + jsonp + "=" + jsonpCallback;
-               }
-               
-               s.url = url;
-               s.data = data;
-               
-               s.dataTypes[0] = "json";
-       }
-       
-});
-
-// Bind transport to json dataType
-jQuery.xhr.bindTransport("json", function(s) {
-
-       if ( s.jsonp ) {
-               
-               // Put callback in place
-               var responseContainer,
-                       jsonpCallback = s.jsonpCallback,
-                       previous = window[ jsonpCallback ];
-                       
-               window [ jsonpCallback ] = function( response ) {
-                       responseContainer = [response];
-               };
-               
-               s.complete = [function() {
-
-                       // 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] );
-                               }
-                       } else {
-                               // else, more memory leak avoidance
-                               try{ delete window[ jsonpCallback ]; } catch(e){}
-                       }
-                       
-               }, s.complete ];
-                               
-               // Use data converter to retrieve json after script execution
-               s.dataConverters["script => json"] = function() {
-                       if ( ! responseContainer ) {
-                               jQuery.error("Callback '" + jsonpCallback + "' was not called");
-                       }
-                       return responseContainer[ 0 ];
-               };
-               
-               // Delegate to script transport
-               return "script";
-               
-       }
-
-});
-
-})( jQuery );
diff --git a/src/transports/xhr.js b/src/transports/xhr.js
deleted file mode 100644 (file)
index 783ee46..0000000
+++ /dev/null
@@ -1,191 +0,0 @@
-(function( jQuery ) {
-
-var // Next fake timer id
-       xhrPollingId = jQuery.now(),
-       
-       // Callbacks hashtable
-       xhrs = {},
-
-       // #5280: see end of file
-       xhrUnloadAbortMarker = [];
-
-       
-jQuery.xhr.bindTransport( function( s , determineDataType ) {
-       
-       // Cross domain only allowed if supported through XMLHttpRequest
-       if ( ! s.crossDomain || jQuery.support.cors ) {
-               
-               var callback;
-               
-               return {
-                       
-                       send: function(headers, complete) {
-                               
-                               var xhr = s.xhr(),
-                                       handle;
-                               
-                               // Open the socket
-                               // Passing null username, generates a login popup on Opera (#2865)
-                               if ( s.username ) {
-                                       xhr.open(s.type, s.url, s.async, s.username, s.password);
-                               } else {
-                                       xhr.open(s.type, s.url, s.async);
-                               }
-                               
-                               // Requested-With header
-                               // Not set for crossDomain requests with no content
-                               // (see why at http://trac.dojotoolkit.org/ticket/9486)
-                               // Won't change header if already provided in beforeSend
-                               if ( ! ( s.crossDomain && ! s.hasContent ) && ! headers["x-requested-with"] ) {
-                                       headers["x-requested-with"] = "XMLHttpRequest";
-                               }
-                               
-                               // Need an extra try/catch for cross domain requests in Firefox 3
-                               try {
-                                       
-                                       jQuery.each(headers, function(key,value) {
-                                               xhr.setRequestHeader(key,value);
-                                       });
-                                       
-                               } catch(_) {}
-                               
-                               // Do send the request
-                               try {
-                                       xhr.send( ( s.hasContent && s.data ) || null );
-                               } catch(e) {
-                                       complete(0, "error", "" + e);
-                                       return;
-                               }
-                               
-                               // Listener
-                               callback = function ( abortStatusText ) {
-                                       
-                                       // Was never called and is aborted or complete
-                                       if ( callback && ( abortStatusText || xhr.readyState === 4 ) ) {
-                                       
-                                               // Do not listen anymore
-                                               if (handle) {
-                                                       xhr.onreadystatechange = jQuery.noop;
-                                                       delete xhrs[ handle ];
-                                                       handle = undefined;
-                                               }
-                                               
-                                               callback = 0;
-                                               
-                                               // Get info
-                                               var status, statusText, response, responseHeaders;
-                                                       
-                                               if ( abortStatusText ) {
-                                                       
-                                                       if ( xhr.readyState !== 4 ) {
-                                                               xhr.abort();
-                                                       }
-                                                       
-                                                       // Stop here if unloadAbort
-                                                       if ( abortStatusText === xhrUnloadAbortMarker ) {
-                                                               return;
-                                                       }
-                                                       
-                                                       status = 0;
-                                                       statusText = abortStatusText;
-                                                       
-                                               } else {
-                                                       
-                                                       status = xhr.status;
-                                                       
-                                                       try { // Firefox throws an exception when accessing statusText for faulty cross-domain requests
-                                                               
-                                                               statusText = xhr.statusText;
-                                                               
-                                                       } catch( e ) {
-                                                               
-                                                               statusText = ""; // We normalize with Webkit giving an empty statusText
-                                                               
-                                                       }
-                                                       
-                                                       responseHeaders = xhr.getAllResponseHeaders();
-                                                       
-                                                       // Filter status for non standard behaviours
-                                                       // (so many they seem to be the actual "standard")
-                                                       status =
-                                                               // Opera returns 0 when it should be 304
-                                                               // Webkit returns 0 for failing cross-domain no matter the real status
-                                                               status === 0 ?
-                                                                       (
-                                                                               ! s.crossDomain || statusText ? // Webkit, Firefox: filter out faulty cross-domain requests
-                                                                               (
-                                                                                       responseHeaders ? // Opera: filter out real aborts #6060
-                                                                                       304
-                                                                                       :
-                                                                                       0
-                                                                               )
-                                                                               :
-                                                                               302 // We assume 302 but could be anything cross-domain related
-                                                                       )
-                                                                       :
-                                                                       (
-                                                                               status == 1223 ?        // IE sometimes returns 1223 when it should be 204 (see #1450)
-                                                                                       204
-                                                                                       :
-                                                                                       status
-                                                                       );
-                                                                       
-                                                       // Guess response if needed & update datatype accordingly
-                                                       if ( status >= 200 && status < 300 ) {
-                                                               response = 
-                                                                       determineDataType(
-                                                                               s,
-                                                                               xhr.getResponseHeader("content-type"),
-                                                                               xhr.responseText,
-                                                                               xhr.responseXML );
-                                                       }
-                                               }
-                                               
-                                               // Call complete
-                                               complete(status,statusText,response,responseHeaders);
-                                       }
-                               };
-                               
-                               // if we're in sync mode
-                               // or it's in cache and has been retrieved directly (IE6 & IE7)
-                               // we need to manually fire the callback
-                               if ( ! s.async || xhr.readyState === 4 ) {
-                                       
-                                       callback();
-                                       
-                               } else {
-                                       
-                                       // Listener is externalized to handle abort on unload
-                                       handle = xhrPollingId++;
-                                       xhrs[ handle ] = xhr;
-                                       xhr.onreadystatechange = function() {
-                                               callback();
-                                       };
-                               }                                       
-                       },
-                       
-                       abort: function(statusText) {
-                               if ( callback ) {
-                                       callback(statusText);
-                               }
-                       }
-               };
-       }
-});
-
-// #5280: we need to abort on unload or IE will keep connections alive
-jQuery(window).bind( "unload" , function() {
-       
-       // Abort all pending requests
-       jQuery.each(xhrs, function(_, xhr) {
-               if ( xhr.onreadystatechange ) {
-                       xhr.onreadystatechange( xhrUnloadAbortMarker );
-               }
-       });
-       
-       // Resest polling structure to be safe
-       xhrs = {};
-       
-});
-
-})( jQuery );
index e86883d..90601df 100644 (file)
@@ -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 ) {
@@ -51,7 +58,7 @@ jQuery.fn.extend({
        filter: function( selector ) {
                return this.pushStack( winnow(this, selector, true), "filter", selector );
        },
-       
+
        is: function( selector ) {
                return !!selector && jQuery.filter( selector, this ).length > 0;
        },
@@ -69,7 +76,7 @@ jQuery.fn.extend({
                                        selector = selectors[i];
 
                                        if ( !matches[selector] ) {
-                                               matches[selector] = jQuery.expr.match.POS.test( selector ) ? 
+                                               matches[selector] = jQuery.expr.match.POS.test( selector ) ?
                                                        jQuery( selector, context || this.context ) :
                                                        selector;
                                        }
@@ -92,7 +99,7 @@ jQuery.fn.extend({
                        return ret;
                }
 
-               var pos = POS.test( selectors ) ? 
+               var pos = POS.test( selectors ) ?
                        jQuery( selectors, context || this.context ) : null;
 
                for ( i = 0, l = this.length; i < l; i++ ) {
@@ -113,10 +120,10 @@ jQuery.fn.extend({
                }
 
                ret = ret.length > 1 ? jQuery.unique(ret) : ret;
-               
+
                return this.pushStack( ret, "closest", selectors );
        },
-       
+
        // Determine the position of an element within
        // the matched set of elements
        index: function( elem ) {
@@ -196,8 +203,13 @@ 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(",") );
        };
 });
 
@@ -226,7 +238,7 @@ jQuery.extend({
                        jQuery.find.matchesSelector(elems[0], expr) ? [ elems[0] ] : [] :
                        jQuery.find.matches(expr, elems);
        },
-       
+
        dir: function( elem, dir, until ) {
                var matched = [],
                        cur = elem[ dir ];
diff --git a/src/xhr.js b/src/xhr.js
deleted file mode 100644 (file)
index 4896e6c..0000000
+++ /dev/null
@@ -1,909 +0,0 @@
-(function( jQuery ) {
-
-var rquery_xhr = /\?/,
-       rhash = /#.*$/,
-       rheaders = /^(.*?):\s*(.*?)\r?$/mg, // IE leaves an \r character at EOL
-       rnoContent = /^(?:GET|HEAD)$/,
-       rts = /([?&])_=[^&]*/,
-       rurl = /^(\w+:)?\/\/([^\/?#]+)/,
-       
-       sliceFunc = Array.prototype.slice,
-       
-       isFunction = jQuery.isFunction;
-       
-// Creates a jQuery xhr object
-jQuery.xhr = function( _native ) {
-       
-       if ( _native ) {
-               return jQuery.ajaxSettings.xhr();
-       }
-       
-       function reset(force) {
-               
-               // We only need to reset if we went through the init phase
-               // (with the exception of object creation)
-               if ( force || internal ) {
-               
-                       // Reset callbacks lists
-                       callbacksLists = {
-                               success: createCBList(),
-                               error: createCBList(),
-                               complete: createCBList()
-                       };
-                       
-                       // Reset private variables
-                       requestHeaders = {};
-                       responseHeadersString = responseHeaders = internal = done = timeoutTimer = s = undefined;
-                       
-                       // Reset state
-                       xhr.readyState = 0;
-                       sendFlag = 0;
-                       
-                       // Remove responseX fields
-                       for ( var name in xhr ) {
-                               if ( /^response/.test(name) ) {
-                                       delete xhr[name];
-                               }
-                       }
-               }
-       }
-       
-       function init() {
-               
-               var // Options extraction
-               
-                       // Remove hash character (#7531: first for string promotion)
-                       url = s.url = ( "" + s.url ).replace( rhash , "" ),
-                       
-                       // Uppercase the type
-                       type = s.type = s.type.toUpperCase(),
-                       
-                       // Determine if request has content
-                       hasContent = s.hasContent = ! rnoContent.test( type ),
-                       
-                       // Extract dataTypes list
-                       dataType = s.dataType,
-                       dataTypes = s.dataTypes = dataType ? jQuery.trim(dataType).toLowerCase().split(/\s+/) : ["*"],
-                       
-                       // Determine if a cross-domain request is in order
-                       parts = rurl.exec( url.toLowerCase() ),
-                       loc = location,
-                       crossDomain = s.crossDomain = !!( parts && ( parts[1] && parts[1] != loc.protocol || parts[2] != loc.host ) ),
-                       
-                       // Get other options locally
-                       data = s.data,
-                       originalContentType = s.contentType,
-                       prefilters = s.prefilters,
-                       accepts = s.accepts,
-                       headers = s.headers,
-                       
-                       // Other Variables
-                       transportDataType,
-                       i;
-                       
-               // Convert data if not already a string
-               if ( data && s.processData && typeof data != "string" ) {
-                       data = s.data = jQuery.param( data , s.traditional );
-               }
-               
-               // Apply option prefilters
-               for ( i = 0; i < prefilters.length; i++ ) {
-                       prefilters[i](s);
-               }
-               
-               // Get internal
-               internal = selectTransport( s );
-               
-               // Re-actualize url & data
-               url = s.url;
-               data = s.data;
-               
-               // If internal was found
-               if ( internal ) {
-                       
-                       // Get transportDataType
-                       transportDataType = dataTypes[0];
-                       
-                       // More options handling for requests with no content
-                       if ( ! hasContent ) {
-                               
-                               // If data is available, append data to url
-                               if ( data ) {
-                                       url += (rquery_xhr.test(url) ? "&" : "?") + data;
-                               }
-                                                               
-                               // Add anti-cache in url if needed
-                               if ( s.cache === false ) {
-                                       
-                                       var ts = jQuery.now(),
-                                               // try replacing _= if it is there
-                                               ret = url.replace(rts, "$1_=" + ts );
-                                               
-                                       // if nothing was replaced, add timestamp to the end
-                                       url = ret + ((ret == url) ? (rquery_xhr.test(url) ? "&" : "?") + "_=" + ts : "");
-                               }
-                               
-                               s.url = url;
-                       }
-                       
-                       // Set the correct header, if data is being sent
-                       if ( ( data && hasContent ) || originalContentType ) {
-                               requestHeaders["content-type"] = s.contentType;
-                       }
-               
-                       // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
-                       if ( s.ifModified ) {
-                               if ( jQuery_lastModified[url] ) { 
-                                       requestHeaders["if-modified-since"] = jQuery_lastModified[url];
-                               }
-                               if ( jQuery_etag[url] ) {
-                                       requestHeaders["if-none-match"] = jQuery_etag[url];
-                               }
-                       }
-               
-                       // Set the Accepts header for the server, depending on the dataType
-                       requestHeaders.accept = transportDataType && accepts[ transportDataType ] ?
-                               accepts[ transportDataType ] + ( transportDataType !== "*" ? ", */*; q=0.01" : "" ) :
-                               accepts[ "*" ];
-                               
-                       // Check for headers option
-                       for ( i in headers ) {
-                               requestHeaders[ i.toLowerCase() ] = headers[ i ];
-                       }                       
-               }
-                       
-               callbackContext = s.context || s;
-               globalEventContext = s.context ? jQuery(s.context) : jQuery.event;
-               
-               for ( i in callbacksLists ) {
-                       callbacksLists[i].bind(s[i]);
-               }
-               
-               // Watch for a new set of requests
-               if ( s.global && jQuery.active++ === 0 ) {
-                       jQuery.event.trigger( "ajaxStart" );
-               }
-               
-               done = whenDone;
-       }
-       
-       function whenDone(status, statusText, response, headers) {
-               
-               // Called once
-               done = undefined;
-               
-               // Reset sendFlag
-               sendFlag = 0;
-               
-               // Cache response headers
-               responseHeadersString = headers || "";
-
-               // Clear timeout if it exists
-               if ( timeoutTimer ) {
-                       clearTimeout(timeoutTimer);
-               }
-               
-               var // Reference url
-                       url = s.url,
-                       // and ifModified status
-                       ifModified = s.ifModified,
-                       
-                       // Is it a success?
-                       isSuccess = 0,
-                       // Stored success
-                       success,
-                       // Stored error
-                       error = statusText;
-
-               // If not timeout, force a jQuery-compliant status text
-               if ( statusText != "timeout" ) {
-                       statusText = ( status >= 200 && status < 300 ) ? 
-                               "success" :
-                               ( status === 304 ? "notmodified" : "error" );
-               }
-               
-               // If successful, handle type chaining
-               if ( statusText === "success" || statusText === "notmodified" ) {
-                       
-                       // Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
-                       if ( ifModified ) {
-                               var lastModified = xhr.getResponseHeader("Last-Modified"),
-                                       etag = xhr.getResponseHeader("Etag");
-                                       
-                               if (lastModified) {
-                                       jQuery_lastModified[url] = lastModified;
-                               }
-                               if (etag) {
-                                       jQuery_etag[url] = etag;
-                               }
-                       }
-                       
-                       if ( ifModified && statusText === "notmodified" ) {
-                               
-                               success = null;
-                               isSuccess = 1;
-                               
-                       } else {
-                               // Chain data conversions and determine the final value
-                               // (if an exception is thrown in the process, it'll be notified as an error)
-                               try {
-                                       
-                                       function checkData(data) {
-                                               if ( data !== undefined ) {
-                                                       var testFunction = s.dataCheckers[srcDataType];
-                                                       if ( isFunction( testFunction ) ) {
-                                                               testFunction(data);
-                                                       }
-                                               }
-                                       }
-                                       
-                                       function convertData (data) {
-                                               var conversionFunction = dataConverters[srcDataType+" => "+destDataType] ||
-                                                               dataConverters["* => "+destDataType],
-                                                       noFunction = ! isFunction( conversionFunction );
-                                               if ( noFunction ) {
-                                                       if ( srcDataType != "text" && destDataType != "text" ) {
-                                                               // We try to put text inbetween
-                                                               var first = dataConverters[srcDataType+" => text"] ||
-                                                                               dataConverters["* => text"],
-                                                                       second = dataConverters["text => "+destDataType] ||
-                                                                               dataConverters["* => "+destDataType],
-                                                                       areFunctions = isFunction( first ) && isFunction( second );
-                                                               if ( areFunctions ) {
-                                                                       conversionFunction = function (data) {
-                                                                               return second( first ( data ) );
-                                                                       };
-                                                               }
-                                                               noFunction = ! areFunctions;
-                                                       }
-                                                       if ( noFunction ) {
-                                                               jQuery.error( "no data converter between " + srcDataType + " and " + destDataType );
-                                                       }
-                                                       
-                                               }
-                                               return conversionFunction(data);
-                                       }
-                                       
-                                       var dataTypes = s.dataTypes,
-                                               i,
-                                               length,
-                                               data = response,
-                                               dataConverters = s.dataConverters,
-                                               srcDataType,
-                                               destDataType,
-                                               responseTypes = s.xhrResponseFields;
-                                               
-                                       for ( i = 0, length = dataTypes.length ; i < length ; i++ ) {
-       
-                                               destDataType = dataTypes[i];
-                                               
-                                               if ( !srcDataType ) { // First time
-                                                       
-                                                       // Copy type
-                                                       srcDataType = destDataType;
-                                                       // Check
-                                                       checkData(data);
-                                                       // Apply dataFilter
-                                                       if ( isFunction( s.dataFilter ) ) {
-                                                               data = s.dataFilter(data, s.dataType);
-                                                               // Recheck data
-                                                               checkData(data);
-                                                       }
-                                                       
-                                               } else { // Subsequent times
-                                                       
-                                                       // handle auto
-                                                       // JULIAN: for reasons unknown to me === doesn't work here
-                                                       if (destDataType == "*") {
-       
-                                                               destDataType = srcDataType;
-                                                               
-                                                       } else if ( srcDataType != destDataType ) {
-                                                               
-                                                               // Convert
-                                                               data = convertData(data);
-                                                               // Copy type & check
-                                                               srcDataType = destDataType;
-                                                               checkData(data);
-                                                               
-                                                       }
-                                                       
-                                               }
-       
-                                               // Copy response into the xhr if it hasn't been already
-                                               var responseDataType,
-                                                       responseType = responseTypes[srcDataType];
-                                               
-                                               if ( responseType ) {
-                                                       
-                                                       responseDataType = srcDataType;
-                                                       
-                                               } else {
-                                                       
-                                                       responseType = responseTypes[ responseDataType = "text" ];
-                                                       
-                                               }
-                                                       
-                                               if ( responseType !== 1 ) {
-                                                       xhr[ "response" + responseType ] = data;
-                                                       responseTypes[ responseType ] = 1;
-                                               }
-                                               
-                                       }
-       
-                                       // We have a real success
-                                       success = data;
-                                       isSuccess = 1;
-                                       
-                               } catch(e) {
-                                       
-                                       statusText = "parsererror";
-                                       error = "" + e;
-                                       
-                               }
-                       }
-                       
-               } else { // if not success, mark it as an error
-                       
-                               error = error || statusText;
-                               
-               }
-                       
-               // Set data for the fake xhr object
-               xhr.status = status;
-               xhr.statusText = statusText;
-               
-               // Keep local copies of vars in case callbacks re-use the xhr
-               var _s = s,
-                       _callbacksLists = callbacksLists,
-                       _callbackContext = callbackContext,
-                       _globalEventContext = globalEventContext;
-                       
-               // Set state if the xhr hasn't been re-used
-               function _setState( value ) {
-                       if ( xhr.readyState && s === _s ) {
-                               setState( value );
-                       }
-               }
-                               
-               // Really completed?
-               if ( status && s.async ) {
-                       setState( 2 );
-                       _setState( 3 );
-               }
-               
-               // We're done
-               _setState( 4 );
-               
-               // Success
-               _callbacksLists.success.fire( isSuccess , _callbackContext , success, statusText, xhr);
-               if ( isSuccess && _s.global ) {
-                       _globalEventContext.trigger( "ajaxSuccess", [xhr, _s, success] );
-               }
-               // Error
-               _callbacksLists.error.fire( ! isSuccess , _callbackContext , xhr, statusText, error);
-               if ( !isSuccess && _s.global ) {
-                       _globalEventContext.trigger( "ajaxError", [xhr, _s, error] );   
-               }
-               // Complete
-               _callbacksLists.complete.fire( 1 , _callbackContext, xhr, statusText);
-               if ( _s.global ) {
-                       _globalEventContext.trigger( "ajaxComplete", [xhr, _s] );
-                       // Handle the global AJAX counter
-                       if ( ! --jQuery.active ) {
-                               jQuery.event.trigger( "ajaxStop" );
-                       }
-               }
-       }
-       
-       // Ready state control
-       function checkState( expected , test ) {
-               if ( expected !== true && ( expected === false || test === false || xhr.readyState !== expected ) ) {
-                       jQuery.error("INVALID_STATE_ERR");
-               }
-       }
-       
-       // Ready state change
-       function setState( value ) {
-               xhr.readyState = value;
-               if ( isFunction( xhr.onreadystatechange ) ) {
-                       xhr.onreadystatechange();
-               }
-       }
-       
-       var // jQuery lists
-               jQuery_lastModified = jQuery.lastModified,
-               jQuery_etag = jQuery.etag,
-               // Options object
-               s,
-               // Callback stuff
-               callbackContext,
-               globalEventContext,
-               callbacksLists,
-               // Headers (they are sent all at once)
-               requestHeaders,
-               // Response headers
-               responseHeadersString,
-               responseHeaders,
-               // Done callback
-               done,
-               // transport
-               internal,
-               // timeout handle
-               timeoutTimer,
-               // The send flag
-               sendFlag,
-               // Fake xhr
-               xhr = {
-                       // state
-                       readyState: 0,
-                       
-                       // Callback
-                       onreadystatechange: null,
-                       
-                       // Open
-                       open: function(type, url, async, username, password) {
-                               
-                               xhr.abort();
-                               reset();
-                               
-                               s = {
-                                       type: type,
-                                       url: url,
-                                       async: async,
-                                       username: username,
-                                       password: password
-                               };
-                               
-                               setState(1);
-                               
-                               return xhr;
-                       },
-                       
-                       // Send
-                       send: function(data, moreOptions) {
-                               
-                               checkState(1 , !sendFlag);
-                               
-                               s.data = data;
-                               
-                               s = jQuery.extend( true,
-                                       {},
-                                       jQuery.ajaxSettings,
-                                       s,
-                                       moreOptions || ( moreOptions === false ? { global: false } : {} ) );
-                                       
-                               if ( moreOptions ) {
-                                       // We force the original context
-                                       // (plain objects used as context get extended)
-                                       s.context = moreOptions.context;
-                               }
-                               
-                               init();
-                               
-                               // If not internal, abort
-                               if ( ! internal ) {
-                                       done( 0 , "transport not found" );
-                                       return false;
-                               }
-                               
-                               // Allow custom headers/mimetypes and early abort
-                               if ( s.beforeSend ) {
-                                       
-                                       var _s = s;
-                                       
-                                       if ( s.beforeSend.call(callbackContext, xhr, s) === false || ! xhr.readyState || _s !== s ) {
-                                               
-                                               // Abort if not done
-                                               if ( xhr.readyState && _s === s ) {
-                                                       xhr.abort();
-                                               }
-       
-                                               // Handle the global AJAX counter
-                                               if ( _s.global && ! --jQuery.active ) {
-                                                       jQuery.event.trigger( "ajaxStop" );
-                                               }
-                                               
-                                               return false;
-                                       }
-                               }
-                               
-                               sendFlag = 1;
-                               
-                               // Send global event
-                               if ( s.global ) {
-                                       globalEventContext.trigger("ajaxSend", [xhr, s]);
-                               }
-                               
-                               // Timeout
-                               if ( s.async && s.timeout > 0 ) {
-                                       timeoutTimer = setTimeout(function(){
-                                               xhr.abort("timeout");
-                                       }, s.timeout);
-                               }
-                               
-                               if ( s.async ) {
-                                       setState(1);
-                               }
-                               
-                               try {
-                                       
-                                       internal.send(requestHeaders, done);
-                                       return xhr;
-                                                                                       
-                               } catch (e) {
-                                       
-                                       if ( done ) {
-                                               
-                                               done(0, "error", "" + e);
-                                               
-                                       } else {
-                                               
-                                               jQuery.error(e);
-                                               
-                                       }
-                               }
-                               
-                               return false;
-                       },
-                       
-                       // Caches the header
-                       setRequestHeader: function(name,value) {
-                               checkState(1, !sendFlag);
-                               requestHeaders[ name.toLowerCase() ] = value;
-                               return xhr;
-                       },
-                       
-                       // Raw string
-                       getAllResponseHeaders: function() {
-                               return xhr.readyState <= 1 ? "" : responseHeadersString;
-                       },
-                       
-                       // Builds headers hashtable if needed
-                       getResponseHeader: function( key ) {
-                               
-                               if ( xhr.readyState <= 1 ) {
-                                       
-                                       return null;
-                                       
-                               }
-                               
-                               if ( responseHeaders === undefined ) {
-                                       
-                                       responseHeaders = {};
-                                       
-                                       if ( typeof responseHeadersString === "string" ) {
-                                               
-                                               var match;
-                                               
-                                               while( ( match = rheaders.exec( responseHeadersString ) ) ) {
-                                                       responseHeaders[ match[ 1 ].toLowerCase() ] = match[ 2 ];
-                                               }
-                                       }
-                               }
-                               return responseHeaders[ key.toLowerCase() ];
-                       },
-                       
-                       // Cancel the request
-                       abort: function(statusText) {
-                               if (internal) {
-                                       internal.abort( statusText || "abort" );
-                               }
-                               xhr.readyState = 0;
-                       }
-               };
-               
-       // Init data (so that we can bind callbacks early
-       reset(1);
-
-       // Install callbacks related methods
-       jQuery.each(callbacksLists, function(name) {
-               var list;
-               xhr[name] = function() {
-                       list = callbacksLists[name];
-                       if ( list ) {
-                               list.bind.apply(list, arguments );
-                       }
-                       return this;
-               };
-       });
-       
-       // Return the xhr emulation
-       return xhr;
-};
-
-// Create a callback list
-function createCBList() {
-       
-       var functors = [],
-               autoFire = 0,
-               fireArgs,
-               list = {
-               
-                       fire: function( flag , context ) {
-                               
-                               // Save info for later bindings
-                               fireArgs = arguments;
-                               
-                               // Remove autoFire to keep bindings in order
-                               autoFire = 0;
-                                       
-                               var args = sliceFunc.call( fireArgs , 2 );
-                                       
-                               // Execute callbacks
-                               while ( flag && functors.length ) {
-                                       flag = functors.shift().apply( context , args ) !== false;
-                               }
-                                       
-                               // Clean if asked to stop
-                               if ( ! flag ) {
-                                       clean();
-                               }
-                                               
-                               // Set autoFire
-                               autoFire = 1;                                   
-                       },
-                       
-                       bind: function() {
-                               
-                               var args = arguments,
-                                       i = 0,
-                                       length = args.length,
-                                       func;
-                               
-                               for ( ; i < length ; i++ ) {
-                                       
-                                       func = args[ i ];
-                                       
-                                       if ( jQuery.isArray(func) ) {
-                                               
-                                               list.bind.apply( list , func );
-                                               
-                                       } else if ( isFunction(func) ) {
-                                               
-                                               // Add if not already in
-                                               if ( ! pos( func ) ) {
-                                                       functors.push( func );
-                                               }
-                                       }
-                               }
-                               
-                               if ( autoFire ) {
-                                       list.fire.apply( list , fireArgs );
-                               }
-                       },
-                       
-                       unbind: function() {
-                               
-                               var i = 0,
-                                       args = arguments,
-                                       length = args.length,
-                                       func,                                   
-                                       position;
-                                       
-                               if ( length ) {
-                                               
-                                       for( ; i < length ; i++ ) {
-                                               func = args[i];
-                                               if ( jQuery.isArray(func) ) {
-                                                       list.unbind.apply(list,func);
-                                               } else if ( isFunction(func) ) {
-                                                       position = pos(func);
-                                                       if ( position ) {
-                                                               functors.splice(position-1,1);
-                                                       }
-                                               }
-                                       }
-                               
-                               } else {
-                                       
-                                       functors = [];
-                               
-                               }
-
-                       }
-                       
-               };
-
-       // Get the index of the functor in the list (1-based)
-       function pos( func ) {
-               for (var i = 0, length = functors.length; i < length && functors[i] !== func; i++) {
-               }
-               return i < length ? ( i + 1 ) : 0;
-       }
-               
-       // Clean the object
-       function clean() {
-               // Empty callbacks list
-               functors = [];
-               // Inhibit methods
-               for (var i in list) {
-                       list[i] = jQuery.noop;
-               }
-       }
-                               
-       return list;
-}
-
-jQuery.extend(jQuery.xhr, {
-       
-       // Add new prefilter
-       prefilter: function (functor) {
-               if ( isFunction(functor) ) {
-                       jQuery.ajaxSettings.prefilters.push( functor );
-               }
-               return this;
-       },
-       
-       // Bind a transport to one or more dataTypes
-       bindTransport: function () {
-               
-               var args = arguments,
-                       i,
-                       start = 0,
-                       length = args.length,
-                       dataTypes = [ "*" ],
-                       functors = [],
-                       functor,
-                       first,
-                       append,
-                       list,
-                       transports = jQuery.ajaxSettings.transports;
-                       
-               if ( length ) {
-                               
-                       if ( ! isFunction( args[ 0 ] ) ) {
-                               
-                               dataTypes = args[ 0 ].toLowerCase().split(/\s+/);
-                               start = 1;
-                               
-                       }
-                       
-                       if ( dataTypes.length && start < length ) {
-                               
-                               for ( i = start; i < length; i++ ) {
-                                       functor = args[i];
-                                       if ( isFunction(functor) ) {
-                                               functors.push( functor );
-                                       }
-                               }
-                                               
-                               if ( functors.length ) {
-                                                       
-                                       jQuery.each ( dataTypes, function( _ , dataType ) {
-                                               
-                                               first = /^\+/.test( dataType );
-                                               
-                                               if (first) {
-                                                       dataType = dataType.substr(1);
-                                               }
-                                               
-                                               if ( dataType !== "" ) {
-                                               
-                                                       append = Array.prototype[ first ? "unshift" : "push" ];
-                                                       
-                                                       list = transports[ dataType ];
-                                       
-                                                       jQuery.each ( functors, function( _ , functor ) {
-                                                                       
-                                                               if ( ! list ) {
-                                                                       
-                                                                       list = transports[ dataType ] = [ functor ];
-                                                                       
-                                                               } else {
-                                                                       
-                                                                       append.call( list , functor );
-                                                               }
-                                                       } );
-                                               }
-                                                                       
-                                       } );
-                               }
-                       }
-               }
-               
-               return this;
-       }
-
-       
-});
-
-// Select a transport given options
-function selectTransport( s ) {
-
-       var dataTypes = s.dataTypes,
-               transportDataType,
-               transportsList,
-               transport,
-               i,
-               length,
-               checked = {},
-               flag;
-               
-       function initSearch( dataType ) {
-
-               flag = transportDataType !== dataType && ! checked[ dataType ];
-               
-               if ( flag ) {
-                       
-                       checked[ dataType ] = 1;
-                       transportDataType = dataType;
-                       transportsList = s.transports[ dataType ];
-                       i = -1;
-                       length = transportsList ? transportsList.length : 0 ;
-               }
-
-               return flag;
-       }
-       
-       initSearch( dataTypes[ 0 ] );
-
-       for ( i = 0 ; ! transport && i <= length ; i++ ) {
-               
-               if ( i === length ) {
-                       
-                       initSearch( "*" );
-                       
-               } else {
-
-                       transport = transportsList[ i ]( s , determineDataType );
-
-                       // If we got redirected to another dataType
-                       // Search there (if not in progress or already tried)
-                       if ( typeof( transport ) === "string" &&
-                               initSearch( transport ) ) {
-
-                               dataTypes.unshift( transport );
-                               transport = 0;
-                       }
-               }
-       }
-
-       return transport;
-}
-       
-// Utility function that handles dataType when response is received
-// (for those transports that can give text or xml responses)
-function determineDataType( s , ct , text , xml ) {
-       
-       var autoDataType = s.autoDataType,
-               type,
-               regexp,
-               dataTypes = s.dataTypes,
-               transportDataType = dataTypes[0],
-               response;
-       
-       // Auto (xml, json, script or text determined given headers)
-       if ( transportDataType === "*" ) {
-
-               for ( type in autoDataType ) {
-                       if ( ( regexp = autoDataType[ type ] ) && regexp.test( ct ) ) {
-                               transportDataType = dataTypes[0] = type;
-                               break;
-                       }
-               }                       
-       } 
-       
-       // xml and parsed as such
-       if ( transportDataType === "xml" &&
-               xml &&
-               xml.documentElement /* #4958 */ ) {
-               
-               response = xml;
-       
-       // Text response was provided
-       } else {
-               
-               response = text;
-               
-               // If it's not really text, defer to dataConverters
-               if ( transportDataType !== "text" ) {
-                       dataTypes.unshift( "text" );
-               }
-               
-       }
-       
-       return response;
-}      
-
-})( jQuery );
diff --git a/test/csp.php b/test/csp.php
new file mode 100644 (file)
index 0000000..acf8f32
--- /dev/null
@@ -0,0 +1,30 @@
+<?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>
diff --git a/test/data/errorWithText.php b/test/data/errorWithText.php
new file mode 100644 (file)
index 0000000..abd8732
--- /dev/null
@@ -0,0 +1,5 @@
+<?php
+
+header("HTTP/1.0 400 Bad Request");
+
+echo "plain text message";
\ No newline at end of file
index f2c21c0..d500b16 100644 (file)
@@ -4,17 +4,13 @@ header( "Sample-Header: Hello World" );
 
 $headers = array();
 
-foreach( $_SERVER as $key => $value ) { 
-       
-       if ( substr( $key , 0 , 5 ) == "HTTP_" ) { 
-               
-               $key = str_replace( "_" , "-" , substr( $key , 5) );
-               $headers[ $key ] = $value;
+foreach( $_SERVER as $key => $value ) {
 
-       }
-       
-} 
+       $key = str_replace( "_" , "-" , substr( $key , 0 , 5 ) == "HTTP_" ? substr( $key , 5 ) : $key );
+       $headers[ $key ] = $value;
+
+}
 
 foreach( explode( "_" , $_GET[ "keys" ] ) as $key ) {
-       echo "$key: " . $headers[ strtoupper( $key ) ] . "\n";
+       echo "$key: " . @$headers[ strtoupper( $key ) ] . "\n";
 }
index 9ae1d84..6c13d72 100644 (file)
@@ -1,6 +1,10 @@
 <?php
 error_reporting(0);
 $callback = $_REQUEST['callback'];
+if ( ! $callback ) {
+       $callback = explode("?",end(explode("/",$_SERVER['REQUEST_URI'])));
+       $callback = $callback[0];
+}
 $json = $_REQUEST['json'];
 if($json) {
        echo $callback . '([ {"name": "John", "age": 21}, {"name": "Peter", "age": 25 } ])';
index 0bab00f..e88ef15 100644 (file)
@@ -1,12 +1,12 @@
 <div id="post">
-<?php 
+<?php
        foreach( $_POST as $key=>$value )
                echo "<b id='$key'>$value</b>";
-?> 
+?>
 </div>
 <div id="get">
 <?php
        foreach( $_GET as $key=>$value )
                echo "<b id='$key'>$value</b>";
-?> 
+?>
 </div>
\ No newline at end of file
index 8f431fb..c478390 100644 (file)
@@ -1,12 +1,7 @@
 var jQuery = this.jQuery || "jQuery", // For testing .noConflict()
        $ = this.$ || "$",
        originaljQuery = jQuery,
-       original$ = $,
-       commonJSDefined;
-
-function define(module, dependencies, callback) {
-       commonJSDefined = callback();
-}
+       original$ = $;
 
 /**
  * Returns an array of elements with the given IDs, eg.
@@ -50,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
index 327085c..6479d26 100644 (file)
        $(document).bind("focusin", function() {
                jQuery("#boundFocus").blink();
        });
-       
+
        $(document).bind("focusout", function() {
                jQuery("#boundBlur").blink();
        });
        $(document).bind("change", function(){
                jQuery("#boundChange").blink();
        });
-       
+
        $("#text_submit").addSubmitTest("#textSubmit", true);
        $("#password_submit").addSubmitTest("#passwordSubmit", true);
        $("#submit_submit").addSubmitTest("#submitSubmit", true);
        $(document).bind("submit", function(){
                jQuery("#boundSubmit").blink();
        });
-       
+
         </script>
     </body>
 </html>
index 238b7d5..fc5f667 100644 (file)
        <script src="../src/manipulation.js"></script>
        <script src="../src/css.js"></script>
        <script src="../src/ajax.js"></script>
-       <script src="../src/xhr.js"></script>
-       <script src="../src/transports/jsonp.js"></script>
-       <script src="../src/transports/script.js"></script>
-       <script src="../src/transports/xhr.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>
@@ -265,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>
index 3ddb7ac..55df0dd 100644 (file)
@@ -15,7 +15,7 @@
                $suite = file_get_contents('index.html');
                echo str_replace( '<!-- Includes -->', $includes, $suite );
                exit;
-       }       
+       }
 ?>
 
 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
@@ -43,7 +43,7 @@
        <h1 id="header">jQuery Test Suite</h1>
        <h2 id="banner" class="fail"></h2>
        <h2 id="userAgent">Choose other libraries to include</h2>
-       
+
        <form class="otherlibs" action="" method="post">
                <?php
                        $libs = scandir('otherlibs');
index 35c0302..1ed15b5 100644 (file)
@@ -1,4 +1,4 @@
-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
@@ -70,44 +70,6 @@ test("jQuery.ajax() - success callbacks - (url, options) syntax", function() {
        }, 13);
 });
 
-test("jQuery.ajax() - success/error callbacks (remote)", function() {
-       
-       var supports = jQuery.support.cors;
-       
-       expect( supports ? 9 : 6 );
-
-       jQuery.ajaxSetup({ timeout: 0 });
-
-       stop();
-
-       setTimeout(function(){
-               jQuery('#foo').ajaxStart(function(){
-                       ok( true, "ajaxStart" );
-               }).ajaxStop(function(){
-                       ok( true, "ajaxStop" );
-                       start();
-               }).ajaxSend(function(){
-                       ok( supports , "ajaxSend" );
-               }).ajaxComplete(function(){
-                       ok( true, "ajaxComplete" );
-               }).ajaxError(function(){
-                       ok( ! supports, "ajaxError" );
-               }).ajaxSuccess(function(){
-                       ok( supports, "ajaxSuccess" );
-               });
-
-               jQuery.ajax({
-                       // JULIAN TODO: Get an url especially for jQuery
-                       url: "http://rockstarapps.com/test.php",
-                       dataType: "text",
-                       beforeSend: function(){ ok(supports, "beforeSend"); },
-                       success: function( val ){ ok(supports, "success"); ok(supports && val.length, "data received"); },
-                       error: function(_ , a , b ){ ok(!supports, "error"); },
-                       complete: function(){ ok(true, "complete"); }
-               });
-       }, 13);
-});
-
 test("jQuery.ajax() - success callbacks (late binding)", function() {
        expect( 8 );
 
@@ -173,7 +135,7 @@ test("jQuery.ajax() - success callbacks (oncomplete binding)", function() {
                                .error(function(){ ok(false, "error"); })
                                .complete(function(){ start(); });
                        }
-               })
+               });
        }, 13);
 });
 
@@ -211,7 +173,7 @@ test("jQuery.ajax() - success callbacks (very late binding)", function() {
                                        .complete(function(){ start(); });
                                },100);
                        }
-               })
+               });
        }, 13);
 });
 
@@ -221,7 +183,7 @@ test("jQuery.ajax() - success callbacks (order)", function() {
        jQuery.ajaxSetup({ timeout: 0 });
 
        stop();
-       
+
        var testString = "";
 
        setTimeout(function(){
@@ -278,12 +240,52 @@ test("jQuery.ajax() - error callbacks", function() {
        });
 });
 
+test("jQuery.ajax() - responseText on error", function() {
+
+       expect( 1 );
+
+       stop();
+
+       jQuery.ajax({
+               url: url("data/errorWithText.php"),
+               error: function(xhr) {
+                       strictEqual( xhr.responseText , "plain text message" , "Test jXHR.responseText is filled for HTTP errors" );
+               },
+               complete: function() {
+                       start();
+               }
+       });
+});
+
+test(".ajax() - retry with jQuery.ajax( this )", function() {
+
+       expect( 1 );
+
+       stop();
+
+       var firstTime = 1;
+
+       jQuery.ajax({
+               url: url("data/errorWithText.php"),
+               error: function() {
+                       if ( firstTime ) {
+                               firstTime = 0;
+                               jQuery.ajax( this );
+                       } else {
+                               ok( true , "Test retrying with jQuery.ajax(this) works" );
+                               start();
+                       }
+               }
+       });
+
+});
+
 test(".ajax() - headers" , function() {
 
        expect( 2 );
-       
+
        stop();
-       
+
        var requestHeaders = {
                siMPle: "value",
                "SometHing-elsE": "other value",
@@ -291,11 +293,11 @@ test(".ajax() - headers" , function() {
                },
                list = [],
                i;
-               
+
        for( i in requestHeaders ) {
                list.push( i );
        }
-       
+
        jQuery.ajax(url("data/headers.php?keys="+list.join( "_" ) ), {
                headers: requestHeaders,
                success: function( data , _ , xhr ) {
@@ -304,19 +306,77 @@ test(".ajax() - headers" , function() {
                                tmp.push( i , ": " , requestHeaders[ i ] , "\n" );
                        }
                        tmp = tmp.join( "" );
-                       
+
                        equals( data , tmp , "Headers were sent" );
                        equals( xhr.getResponseHeader( "Sample-Header" ) , "Hello World" , "Sample header received" );
                        start();
                },
                error: function(){ ok(false, "error"); }
        });
-               
+
+});
+
+test(".ajax() - Accept header" , function() {
+
+       expect( 1 );
+
+       stop();
+
+       jQuery.ajax(url("data/headers.php?keys=accept"), {
+               headers: {
+                       Accept: "very wrong accept value"
+               },
+               beforeSend: function( xhr ) {
+                       xhr.setRequestHeader( "Accept", "*/*" );
+               },
+               success: function( data ) {
+                       strictEqual( data , "accept: */*\n" , "Test Accept header is set to last value provided" );
+                       start();
+               },
+               error: function(){ ok(false, "error"); }
+       });
+
+});
+
+test(".ajax() - contentType" , function() {
+
+       expect( 2 );
+
+       stop();
+
+       var count = 2;
+
+       function restart() {
+               if ( ! --count ) {
+                       start();
+               }
+       }
+
+       jQuery.ajax(url("data/headers.php?keys=content-type" ), {
+               contentType: "test",
+               success: function( data ) {
+                       strictEqual( data , "content-type: test\n" , "Test content-type is sent when options.contentType is set" );
+               },
+               complete: function() {
+                       restart();
+               }
+       });
+
+       jQuery.ajax(url("data/headers.php?keys=content-type" ), {
+               contentType: false,
+               success: function( data ) {
+                       strictEqual( data , "content-type: \n" , "Test content-type is not sent when options.contentType===false" );
+               },
+               complete: function() {
+                       restart();
+               }
+       });
+
 });
 
 test(".ajax() - hash", function() {
        expect(3);
-       
+
        jQuery.ajax({
                url: "data/name.html#foo",
                beforeSend: function( xhr, settings ) {
@@ -324,7 +384,7 @@ test(".ajax() - hash", function() {
                        return false;
                }
        });
-       
+
        jQuery.ajax({
                url: "data/name.html?abc#foo",
                beforeSend: function( xhr, settings ) {
@@ -332,7 +392,7 @@ test(".ajax() - hash", function() {
                        return false;
                }
        });
-       
+
        jQuery.ajax({
                url: "data/name.html?abc#foo",
                data: { "test": 123 },
@@ -343,10 +403,57 @@ test(".ajax() - hash", function() {
        });
 });
 
+test("jQuery ajax - cross-domain detection", function() {
+
+       expect( 4 );
+
+       var loc = document.location,
+               otherPort = loc.port === 666 ? 667 : 666,
+               otherProtocol = loc.protocol === "http:" ? "https:" : "http:";
+
+       jQuery.ajax({
+               dataType: "jsonp",
+               url: otherProtocol + "//" + loc.host,
+               beforeSend: function( _ , s ) {
+                       ok( s.crossDomain , "Test different protocols are detected as cross-domain" );
+                       return false;
+               }
+       });
+
+       jQuery.ajax({
+               dataType: "jsonp",
+               url: loc.protocol + '//somewebsitethatdoesnotexist-656329477541.com:' + ( loc.port || 80 ),
+               beforeSend: function( _ , s ) {
+                       ok( s.crossDomain , "Test different hostnames are detected as cross-domain" );
+                       return false;
+               }
+       });
+
+       jQuery.ajax({
+               dataType: "jsonp",
+               url: loc.protocol + "//" + loc.hostname + ":" + otherPort,
+               beforeSend: function( _ , s ) {
+                       ok( s.crossDomain , "Test different ports are detected as cross-domain" );
+                       return false;
+               }
+       });
+
+       jQuery.ajax({
+               dataType: "jsonp",
+               url: loc.protocol + "//" + loc.host,
+               crossDomain: true,
+               beforeSend: function( _ , s ) {
+                       ok( s.crossDomain , "Test forced crossDomain is detected as cross-domain" );
+                       return false;
+               }
+       });
+
+});
+
 test(".ajax() - 304", function() {
        expect( 1 );
        stop();
-       
+
        jQuery.ajax({
                url: url("data/notmodified.php"),
                success: function(){ ok(true, "304 ok"); },
@@ -409,142 +516,12 @@ test("jQuery.ajax() - abort", function() {
        equals( xhr.readyState, 0, "XHR readyState indicates successful abortion" );
 });
 
-test("jQuery.ajax() - readyState (success)", function() {
-       expect( 1 );
-
-       jQuery.ajaxSetup({ timeout: 0 });
-
-       stop();
-       
-       var control = "";
-
-       setTimeout(function(){
-               jQuery.ajax({
-                       url: url("data/name.html"),
-                       beforeSend: function( xhr ) {
-                               xhr.onreadystatechange = function() {
-                                       control += xhr.readyState;
-                               }
-                       },
-                       complete: function(){ 
-                               setTimeout( function() {
-                                       equals( control , "1234" , "onreadystatechange was properly called" );
-                               }, 13 );
-                               start();
-                       }
-               });
-       }, 13);
-});
-
-test("jQuery.ajax() - readyState (abort)", function() {
-       expect( 2 );
-
-       jQuery.ajaxSetup({ timeout: 0 });
-
-       stop();
-       
-       var control = "";
-
-       setTimeout(function(){
-
-               jQuery.ajaxSetup({ timeout: 500 });
-
-               jQuery.ajax({
-                       url: url("data/name.php?wait=5"),
-                       beforeSend: function( xhr ) {
-                               xhr.onreadystatechange = function() {
-                                       control += xhr.readyState;
-                               }
-                       },
-                       complete: function( xhr ){ 
-                               setTimeout( function() {
-                                       equals( control , "14" , "onreadystatechange was properly called" );
-                                       equals( xhr.readyState, 0 , "readyState is 0" );
-                               }, 13 );
-                               start();
-                       }
-               });
-       }, 13);
-});
-
-test("jQuery.xhr() - reuse", function() {
-       expect( 15 );
-
-       jQuery.ajaxSetup({ timeout: 0 });
-
-       stop();
-       
-       var number = 0;
-       
-       setTimeout(function(){
-               jQuery('#foo').ajaxStart(function(){
-                       ok( true, "ajaxStart" );
-               }).ajaxStop(function(){
-                       ok( true, "ajaxStop" );
-                       start();
-               }).ajaxSend(function(){
-                       number++;
-                       ok( true, "ajaxSend (" + number +")" );
-               }).ajaxComplete(function(){
-                       ok( true, "ajaxComplete (" + number +")" );
-               }).ajaxError(function(){
-                       ok( false, "ajaxError (" + number +")" );
-               }).ajaxSuccess(function(){
-                       ok( true, "ajaxSuccess (" + number +")" );
-               });
-
-               jQuery.ajax({
-                       url: url("data/name.html"),
-                       beforeSend: function(){ ok(true, "beforeSend (1)"); },
-                       success: function( _1 , _2 , xhr ){
-                               ok(true, "success (1)");
-                               xhr.complete(function() {
-                                       ok(true, "complete (1bis)"); 
-                               });
-                               xhr.open( "GET", url("data/name.html") );
-                               xhr.success( function(){ ok(true, "beforeSend (2)"); } )
-                               xhr.send( null, {
-                                       success: function(){ ok(true, "success (2)"); },
-                                       error: function(){ ok(false, "error (2)"); },
-                                       complete: function(){ ok(true, "complete (2)"); }
-                               } );
-                       },
-                       error: function(){ ok(false, "error (1)"); },
-                       complete: function(){ ok(true, "complete (1)"); }
-               });
-       }, 13);
-});
-
-test("jQuery.xhr() - early binding", function() {
-       expect( 2 );
-
-       jQuery.ajaxSetup({ timeout: 0 });
-
-       stop();
-       
-       jQuery.xhr()
-               .success( function(){ ok(true, "success"); } )
-               .error( function(){ ok(false, "error"); } )
-               .complete( function(){ ok(true, "complete"); start(); } )
-               .open( "GET", url("data/name.html") )
-               .send();
-});
-
-test("jQuery.xhr() - get native implementation", function() {
-       
-       var xhr = jQuery.xhr(true);
-       
-       ok( xhr.readyState !== undefined , "implements XMLHttpRequest" );
-       ok( ! jQuery.isFunction( xhr.success ) , "is not jQuery's abstraction" );
-       
-});
-
 test("Ajax events with context", function() {
        expect(14);
-       
+
        stop();
        var context = document.createElement("div");
-       
+
        function event(e){
                equals( this, context, e.type );
        }
@@ -560,7 +537,7 @@ test("Ajax events with context", function() {
                        equals( typeof this.url, "string", "context is settings on callback " + msg );
                };
        }
-       
+
        jQuery('#foo').add(context)
                        .ajaxSend(event)
                        .ajaxComplete(event)
@@ -606,7 +583,7 @@ test("jQuery.ajax context modification", function() {
 
        stop();
 
-       var obj = {}
+       var obj = {};
 
        jQuery.ajax({
                url: url("data/name.html"),
@@ -622,6 +599,47 @@ test("jQuery.ajax context modification", function() {
        equals( obj.test, "foo", "Make sure the original object is maintained." );
 });
 
+test("jQuery.ajax context modification through ajaxSetup", function() {
+       expect(4);
+
+       stop();
+
+       var obj = {};
+
+       jQuery.ajaxSetup({
+               context: obj
+       });
+
+       strictEqual( jQuery.ajaxSettings.context, obj, "Make sure the context is properly set in ajaxSettings." );
+
+       jQuery.ajax({
+               url: url("data/name.html"),
+               complete: function() {
+                       strictEqual( this, obj, "Make sure the original object is maintained." );
+                       jQuery.ajax({
+                               url: url("data/name.html"),
+                               context: {},
+                               complete: function() {
+                                       ok( this !== obj, "Make sure overidding context is possible." );
+                                       jQuery.ajaxSetup({
+                                               context: false
+                                       });
+                                       jQuery.ajax({
+                                               url: url("data/name.html"),
+                                               beforeSend: function(){
+                                                       this.test = "foo2";
+                                               },
+                                               complete: function() {
+                                                       ok( this !== obj, "Make sure unsetting context is possible." );
+                                                       start();
+                                               }
+                                       });
+                               }
+                       });
+               }
+       });
+});
+
 test("jQuery.ajax() - disabled globals", function() {
        expect( 3 );
        stop();
@@ -653,34 +671,6 @@ test("jQuery.ajax() - disabled globals", function() {
        });
 });
 
-test("jQuery.xhr() - disabled globals through xhr.send(data , false)", function() {
-       expect( 2 );
-       stop();
-
-       jQuery('#foo').ajaxStart(function(){
-               ok( false, "ajaxStart" );
-       }).ajaxStop(function(){
-               ok( false, "ajaxStop" );
-       }).ajaxSend(function(){
-               ok( false, "ajaxSend" );
-       }).ajaxComplete(function(){
-               ok( false, "ajaxComplete" );
-       }).ajaxError(function(){
-               ok( false, "ajaxError" );
-       }).ajaxSuccess(function(){
-               ok( false, "ajaxSuccess" );
-       });
-
-       jQuery.xhr()
-               .success(function(){ ok(true, "success"); })
-               .error(function(){ ok(false, "error"); })
-               .complete(function(){
-                 ok(true, "complete");
-                 setTimeout(function(){ start(); }, 13);
-               })
-               .open("GET", url("data/name.html")).send(undefined, false);
-});
-
 test("jQuery.ajax - xml: non-namespace elements inside namespaced elements", function() {
        expect(3);
        stop();
@@ -707,6 +697,10 @@ test("jQuery.ajax - xml: non-namespace elements inside namespaced elements (over
                equals( jQuery("jsconf", resp).length, 1, 'jsconf in responseXML' );
                equals( jQuery("thing", resp).length, 2, 'things in responseXML' );
                start();
+         },
+         error: function(_1,_2,error) {
+               ok( false, error );
+               start();
          }
        });
 });
@@ -721,7 +715,7 @@ test("jQuery.ajax - HEAD requests", function() {
                success: function(data, status, xhr){
                        var h = xhr.getAllResponseHeaders();
                        ok( /Date/i.test(h), 'No Date in HEAD response' );
-                       
+
                        jQuery.ajax({
                                url: url("data/name.html"),
                                data: { whip_it: "good" },
@@ -842,20 +836,20 @@ test("serialize()", function() {
                'Check input serialization as query string');
 
        equals( jQuery('#testForm').serialize(),
-               'T3=%3F%0AZ&H1=x&H2=&PWD=&T1=&T2=YES&My+Name=me&S1=abc&S3=YES&S4=',
+               'T3=%3F%0D%0AZ&H1=x&H2=&PWD=&T1=&T2=YES&My+Name=me&S1=abc&S3=YES&S4=',
                'Check form serialization as query string');
 
        equals( jQuery('#testForm :input').serialize(),
-               'T3=%3F%0AZ&H1=x&H2=&PWD=&T1=&T2=YES&My+Name=me&S1=abc&S3=YES&S4=',
+               'T3=%3F%0D%0AZ&H1=x&H2=&PWD=&T1=&T2=YES&My+Name=me&S1=abc&S3=YES&S4=',
                'Check input serialization as query string');
 
        equals( jQuery('#form, #testForm').serialize(),
-               "action=Test&radio2=on&check=on&hidden=&foo%5Bbar%5D=&name=name&search=search&email=dave%40jquery.com&number=43&select1=&select2=3&select3=1&select3=2&select5=3&T3=%3F%0AZ&H1=x&H2=&PWD=&T1=&T2=YES&My+Name=me&S1=abc&S3=YES&S4=",
+               "action=Test&radio2=on&check=on&hidden=&foo%5Bbar%5D=&name=name&search=search&email=dave%40jquery.com&number=43&select1=&select2=3&select3=1&select3=2&select5=3&T3=%3F%0D%0AZ&H1=x&H2=&PWD=&T1=&T2=YES&My+Name=me&S1=abc&S3=YES&S4=",
                'Multiple form serialization as query string');
 
   /* Temporarily disabled. Opera 10 has problems with form serialization.
        equals( jQuery('#form, #testForm :input').serialize(),
-               "action=Test&radio2=on&check=on&hidden=&foo%5Bbar%5D=&name=name&search=search&email=dave%40jquery.com&number=43&select1=&select2=3&select3=1&select3=2&T3=%3F%0AZ&H1=x&H2=&PWD=&T1=&T2=YES&My+Name=me&S1=abc&S3=YES&S4=",
+               "action=Test&radio2=on&check=on&hidden=&foo%5Bbar%5D=&name=name&search=search&email=dave%40jquery.com&number=43&select1=&select2=3&select3=1&select3=2&T3=%3F%0D%0AZ&H1=x&H2=&PWD=&T1=&T2=YES&My+Name=me&S1=abc&S3=YES&S4=",
                'Mixed form/input serialization as query string');
        */
        jQuery("#html5email, #html5number").remove();
@@ -863,9 +857,9 @@ test("serialize()", function() {
 
 test("jQuery.param()", function() {
        expect(22);
-       
+
        equals( !jQuery.ajaxSettings.traditional, true, "traditional flag, falsy by default" );
-  
+
        var params = {foo:"bar", baz:42, quux:"All your base are belong to us"};
        equals( jQuery.param(params), "foo=bar&baz=42&quux=All+your+base+are+belong+to+us", "simple" );
 
@@ -880,13 +874,13 @@ test("jQuery.param()", function() {
 
        params = {foo: { bar: 'baz', beep: 42, quux: 'All your base are belong to us' } };
        equals( jQuery.param(params), "foo%5Bbar%5D=baz&foo%5Bbeep%5D=42&foo%5Bquux%5D=All+your+base+are+belong+to+us", "even more arrays" );
-       
+
        params = { a:[1,2], b:{ c:3, d:[4,5], e:{ x:[6], y:7, z:[8,9] }, f:true, g:false, h:undefined }, i:[10,11], j:true, k:false, l:[undefined,0], m:"cowboy hat?" };
        equals( decodeURIComponent( jQuery.param(params) ), "a[]=1&a[]=2&b[c]=3&b[d][]=4&b[d][]=5&b[e][x][]=6&b[e][y]=7&b[e][z][]=8&b[e][z][]=9&b[f]=true&b[g]=false&b[h]=undefined&i[]=10&i[]=11&j=true&k=false&l[]=undefined&l[]=0&m=cowboy+hat?", "huge structure" );
-       
+
        params = { a: [ 0, [ 1, 2 ], [ 3, [ 4, 5 ], [ 6 ] ], { b: [ 7, [ 8, 9 ], [ { c: 10, d: 11 } ], [ [ 12 ] ], [ [ [ 13 ] ] ], { e: { f: { g: [ 14, [ 15 ] ] } } }, 16 ] }, 17 ] };
        equals( decodeURIComponent( jQuery.param(params) ), "a[]=0&a[1][]=1&a[1][]=2&a[2][]=3&a[2][1][]=4&a[2][1][]=5&a[2][2][]=6&a[3][b][]=7&a[3][b][1][]=8&a[3][b][1][]=9&a[3][b][2][0][c]=10&a[3][b][2][0][d]=11&a[3][b][3][0][]=12&a[3][b][4][0][0][]=13&a[3][b][5][e][f][g][]=14&a[3][b][5][e][f][g][1][]=15&a[3][b][]=16&a[]=17", "nested arrays" );
-       
+
        params = { a:[1,2], b:{ c:3, d:[4,5], e:{ x:[6], y:7, z:[8,9] }, f:true, g:false, h:undefined }, i:[10,11], j:true, k:false, l:[undefined,0], m:"cowboy hat?" };
        equals( jQuery.param(params,true), "a=1&a=2&b=%5Bobject+Object%5D&i=10&i=11&j=true&k=false&l=undefined&l=0&m=cowboy+hat%3F", "huge structure, forced traditional" );
 
@@ -898,7 +892,7 @@ test("jQuery.param()", function() {
        equals( jQuery.param({"foo": {"bar": {}} }), "foo%5Bbar%5D=", "Empty object param" );
 
        jQuery.ajaxSetup({ traditional: true });
-       
+
        var params = {foo:"bar", baz:42, quux:"All your base are belong to us"};
        equals( jQuery.param(params), "foo=bar&baz=42&quux=All+your+base+are+belong+to+us", "simple" );
 
@@ -913,16 +907,16 @@ test("jQuery.param()", function() {
 
        params = {"foo[bar]":"baz", "foo[beep]":42, "foo[quux]":"All your base are belong to us"};
        equals( jQuery.param(params), "foo%5Bbar%5D=baz&foo%5Bbeep%5D=42&foo%5Bquux%5D=All+your+base+are+belong+to+us", "even more arrays" );
-       
+
        params = { a:[1,2], b:{ c:3, d:[4,5], e:{ x:[6], y:7, z:[8,9] }, f:true, g:false, h:undefined }, i:[10,11], j:true, k:false, l:[undefined,0], m:"cowboy hat?" };
        equals( jQuery.param(params), "a=1&a=2&b=%5Bobject+Object%5D&i=10&i=11&j=true&k=false&l=undefined&l=0&m=cowboy+hat%3F", "huge structure" );
-       
+
        params = { a: [ 0, [ 1, 2 ], [ 3, [ 4, 5 ], [ 6 ] ], { b: [ 7, [ 8, 9 ], [ { c: 10, d: 11 } ], [ [ 12 ] ], [ [ [ 13 ] ] ], { e: { f: { g: [ 14, [ 15 ] ] } } }, 16 ] }, 17 ] };
        equals( jQuery.param(params), "a=0&a=1%2C2&a=3%2C4%2C5%2C6&a=%5Bobject+Object%5D&a=17", "nested arrays (not possible when jQuery.param.traditional == true)" );
-       
+
        params = { a:[1,2], b:{ c:3, d:[4,5], e:{ x:[6], y:7, z:[8,9] }, f:true, g:false, h:undefined }, i:[10,11], j:true, k:false, l:[undefined,0], m:"cowboy hat?" };
        equals( decodeURIComponent( jQuery.param(params,false) ), "a[]=1&a[]=2&b[c]=3&b[d][]=4&b[d][]=5&b[e][x][]=6&b[e][y]=7&b[e][z][]=8&b[e][z][]=9&b[f]=true&b[g]=false&b[h]=undefined&i[]=10&i[]=11&j=true&k=false&l[]=undefined&l[]=0&m=cowboy+hat?", "huge structure, forced not traditional" );
-       
+
        params = { param1: null };
        equals( jQuery.param(params,false), "param1=null", "Make sure that null params aren't traversed." );
 });
@@ -971,7 +965,7 @@ test("pass-through request object", function() {
 
 test("ajax cache", function () {
        expect(18);
-       
+
        stop();
 
        var count = 0;
@@ -1094,6 +1088,18 @@ test("load(String, Function) - check file with only a script tag", function() {
        });
 });
 
+test("load(String, Function) - dataFilter in ajaxSettings", function() {
+       expect(2);
+       stop();
+       jQuery.ajaxSetup({ dataFilter: function() { return "Hello World"; } });
+       var div = jQuery("<div/>").load(url("data/name.html"), function(responseText) {
+               strictEqual( div.html(), "Hello World" , "Test div was filled with filtered data" );
+               strictEqual( responseText, "Hello World" , "Test callback receives filtered data" );
+               jQuery.ajaxSetup({ dataFilter: 0 });
+               start();
+       });
+});
+
 test("load(String, Object, Function)", function() {
        expect(2);
        stop();
@@ -1149,217 +1155,249 @@ test("jQuery.getScript(String, Function) - no callback", function() {
        });
 });
 
-test("jQuery.ajax() - JSONP, Local", function() {
-       expect(9);
+jQuery.each( [ "Same Domain", "Cross Domain" ] , function( crossDomain , label ) {
 
-       var count = 0;
-       function plus(){ if ( ++count == 9 ) start(); }
-
-       stop();
-
-       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();
-               }
-       });
+       test("jQuery.ajax() - JSONP, " + label, function() {
+               expect(17);
 
-       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();
-               }
-       });
+               var count = 0;
+               function plus(){ if ( ++count == 17 ) start(); }
 
-       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();
-               }
-       });
+               stop();
 
-       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,
+                       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",
-               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?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({
-               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",
+                       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({
-               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?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({
-               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,
+                       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();
+                       }
+               });
 
-       //#7578
-       jQuery.ajax({
-               url: "data/jsonp.php",
-               dataType: "jsonp",
-               beforeSend: function(){
-                       strictEqual( this.cache, false, "cache must be false on JSON request" );
-                       plus();
-                       return false;
-               }
-       });
-});
+               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();
+                       }
+               });
 
-test("jQuery.ajax() - JSONP - Custom JSONP Callback", function() {
-       expect(1);
-       stop();
+               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();
+                       }
+               });
 
-       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",
+                       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({
-               url: "data/jsonp.php",
-               dataType: "jsonp",
-               jsonpCallback: "jsonpResults"
-       });
-});
+               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();
+                       }
+               });
 
-test("jQuery.ajax() - JSONP, Remote", function() {
-       expect(4);
+               window.jsonpResults = function(data) {
+                       ok( data.data, "JSON results returned (GET, custom callback function)" );
+                       window.jsonpResults = undefined;
+                       plus();
+               };
 
-       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();
-               }
        });
 });
 
@@ -1384,7 +1422,7 @@ test("jQuery.ajax() - script, Remote with POST", function() {
        expect(3);
 
        var base = window.location.href.replace(/[^\/]*$/, "");
-       
+
        stop();
 
        jQuery.ajax({
@@ -1482,12 +1520,12 @@ test("jQuery.ajax() - json by content-type disabled with options", function() {
        jQuery.ajax({
                url: url("data/json.php"),
                data: { header: "json", json: "array" },
-               autoDataType: {
+               contents: {
                        json: false
                },
                success: function( text ) {
                        equals( typeof text , "string" , "json wasn't auto-determined" );
-                       var json = this.dataConverters["text => json"]( text );
+                       var json = jQuery.parseJSON( text );
                        ok( json.length >= 2, "Check length");
                        equals( json[0].name, 'John', 'Check JSON: first, name' );
                        equals( json[0].age, 21, 'Check JSON: first, age' );
@@ -1525,7 +1563,7 @@ test("jQuery.getJSON(String, Function) - JSON object", function() {
 
 test("jQuery.getJSON - Using Native JSON", function() {
        expect(2);
-       
+
        var old = window.JSON;
        JSON = {
                parse: function(str){
@@ -1714,7 +1752,7 @@ test("data option: evaluate function values (#2806)", function() {
                        equals( result, "key=value" );
                        start();
                }
-       })
+       });
 });
 
 test("data option: empty bodies for non-GET requests", function() {
@@ -1727,7 +1765,7 @@ test("data option: empty bodies for non-GET requests", function() {
                        equals( result, "" );
                        start();
                }
-       })
+       });
 });
 
 test("jQuery.ajax - If-Modified-Since support", function() {
@@ -1740,19 +1778,19 @@ test("jQuery.ajax - If-Modified-Since support", function() {
        jQuery.ajax({
                url: url,
                ifModified: true,
-               success: function(data, status) { 
+               success: function(data, status) {
                        equals(status, "success");
-                       
+
                        jQuery.ajax({
                                url: url,
                                ifModified: true,
-                               success: function(data, status) { 
+                               success: function(data, status) {
                                        if ( data === "FAIL" ) {
                                                ok(true, "Opera is incapable of doing .setRequestHeader('If-Modified-Since').");
                                                ok(true, "Opera is incapable of doing .setRequestHeader('If-Modified-Since').");
                                        } else {
                                                equals(status, "notmodified");
-                                               ok(data == null, "response body should be empty")
+                                               ok(data == null, "response body should be empty");
                                        }
                                        start();
                        },
@@ -1787,19 +1825,19 @@ test("jQuery.ajax - Etag support", function() {
        jQuery.ajax({
                url: url,
                ifModified: true,
-               success: function(data, status) { 
+               success: function(data, status) {
                        equals(status, "success");
-                       
+
                        jQuery.ajax({
                                url: url,
                                ifModified: true,
-                               success: function(data, status) { 
+                               success: function(data, status) {
                                        if ( data === "FAIL" ) {
                                                ok(true, "Opera is incapable of doing .setRequestHeader('If-None-Match').");
                                                ok(true, "Opera is incapable of doing .setRequestHeader('If-None-Match').");
                                        } else {
                                                equals(status, "notmodified");
-                                               ok(data == null, "response body should be empty")
+                                               ok(data == null, "response body should be empty");
                                        }
                                        start();
                        },
@@ -1826,42 +1864,38 @@ test("jQuery.ajax - Etag support", function() {
 test("jQuery ajax - failing cross-domain", function() {
 
        expect( 2 );
-       
+
        stop();
-       
+
        var i = 2;
-       
+
        jQuery.ajax({
-               url: 'http://somewebsitethatdoesnotexist.com',
+               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(); }
        });
-       
+
        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(); }
        });
-       
+
 });
 
 test("jQuery ajax - atom+xml", function() {
 
        stop();
-       
+
        jQuery.ajax({
                url: url( 'data/atom+xml.php' ),
                success: function(){ ok( true , "success" ); },
                error: function(){ ok( false , "error" ); },
                complete: function() { start(); }
        });
-       
-});
 
-test("jQuery.ajax - active counter", function() {
-    ok( jQuery.active == 0, "ajax active counter should be zero: " + jQuery.active );
 });
 
 test( "jQuery.ajax - Location object as url (#7531)", 1, function () {
@@ -1875,6 +1909,107 @@ test( "jQuery.ajax - Location object as url (#7531)", 1, function () {
        ok( success, "document.location did not generate exception" );
 });
 
+test( "jQuery.ajax - statusCode" , function() {
+
+       var count = 12;
+
+       expect( 20 );
+       stop();
+
+       function countComplete() {
+               if ( ! --count ) {
+                       start();
+               }
+       }
+
+       function createStatusCodes( name , isSuccess ) {
+               name = "Test " + name + " " + ( isSuccess ? "success" : "error" );
+               return {
+                       200: function() {
+                               ok( isSuccess , name );
+                       },
+                       404: function() {
+                               ok( ! isSuccess , name );
+                       }
+               };
+       }
+
+       jQuery.each( {
+               "data/name.html": true,
+               "data/someFileThatDoesNotExist.html": false
+       } , function( uri , isSuccess ) {
+
+               jQuery.ajax( url( uri ) , {
+                       statusCode: createStatusCodes( "in options" , isSuccess ),
+                       complete: countComplete
+               });
+
+               jQuery.ajax( url( uri ) , {
+                       complete: countComplete
+               }).statusCode( createStatusCodes( "immediately with method" , isSuccess ) );
+
+               jQuery.ajax( url( uri ) , {
+                       complete: function(jXHR) {
+                               jXHR.statusCode( createStatusCodes( "on complete" , isSuccess ) );
+                               countComplete();
+                       }
+               });
+
+               jQuery.ajax( url( uri ) , {
+                       complete: function(jXHR) {
+                               setTimeout( function() {
+                                       jXHR.statusCode( createStatusCodes( "very late binding" , isSuccess ) );
+                                       countComplete();
+                               } , 100 );
+                       }
+               });
+
+               jQuery.ajax( url( uri ) , {
+                       statusCode: createStatusCodes( "all (options)" , isSuccess ),
+                       complete: function(jXHR) {
+                               jXHR.statusCode( createStatusCodes( "all (on complete)" , isSuccess ) );
+                               setTimeout( function() {
+                                       jXHR.statusCode( createStatusCodes( "all (very late binding)" , isSuccess ) );
+                                       countComplete();
+                               } , 100 );
+                       }
+               }).statusCode( createStatusCodes( "all (immediately with method)" , isSuccess ) );
+
+               var testString = "";
+
+               jQuery.ajax( url( uri ), {
+                       success: function( a , b , jXHR ) {
+                               ok( isSuccess , "success" );
+                               var statusCode = {};
+                               statusCode[ jXHR.status ] = function() {
+                                       testString += "B";
+                               };
+                               jXHR.statusCode( statusCode );
+                               testString += "A";
+                       },
+                       error: function( jXHR ) {
+                               ok( ! isSuccess , "error" );
+                               var statusCode = {};
+                               statusCode[ jXHR.status ] = function() {
+                                       testString += "B";
+                               };
+                               jXHR.statusCode( statusCode );
+                               testString += "A";
+                       },
+                       complete: function() {
+                               strictEqual( testString , "AB" , "Test statusCode callbacks are ordered like " +
+                                               ( isSuccess ? "success" :  "error" ) + " callbacks" );
+                               countComplete();
+                       }
+               } );
+
+       });
+});
+
+test("jQuery.ajax - active counter", function() {
+    ok( jQuery.active == 0, "ajax active counter should be zero: " + jQuery.active );
+});
+
 }
 
 //}
\ No newline at end of file
index f9506b3..c58111d 100644 (file)
@@ -1,16 +1,16 @@
-module("attributes");
+module("attributes", { teardown: moduleTeardown });
 
 var bareObj = function(value) { return value; };
 var functionReturningObj = function(value) { return (function() { return value; }); };
 
 test("jQuery.props: itegrity test", function() {
-  
+
   expect(1);
-  
+
   //  This must be maintained and equal jQuery.props
-  //  Ensure that accidental or erroneous property 
+  //  Ensure that accidental or erroneous property
   //  overwrites don't occur
-  //  This is simply for better code coverage and future proofing. 
+  //  This is simply for better code coverage and future proofing.
   var propsShouldBe = {
     "for": "htmlFor",
     "class": "className",
@@ -23,7 +23,7 @@ test("jQuery.props: itegrity test", function() {
     usemap: "useMap",
     frameborder: "frameBorder"
   };
-  
+
   same(propsShouldBe, jQuery.props, "jQuery.props passes integrity check");
 
 });
@@ -33,7 +33,7 @@ test("attr(String)", function() {
 
        // This one sometimes fails randomly ?!
        equals( jQuery('#text1').attr('value'), "Test", 'Check for value attribute' );
-       
+
        equals( jQuery('#text1').attr('value', "Test2").attr('defaultValue'), "Test", 'Check for defaultValue attribute' );
        equals( jQuery('#text1').attr('type'), "text", 'Check for type attribute' );
        equals( jQuery('#radio1').attr('type'), "radio", 'Check for type attribute' );
@@ -255,30 +255,30 @@ test("attr(String, Object)", function() {
 
 test("attr(jquery_method)", function(){
        expect(7);
-       
+
        var $elem = jQuery("<div />"),
                elem = $elem[0];
-       
-       // one at a time        
+
+       // one at a time
        $elem.attr({'html': 'foo'}, true);
        equals( elem.innerHTML, 'foo', 'attr(html)');
-       
+
        $elem.attr({'text': 'bar'}, true);
        equals( elem.innerHTML, 'bar', 'attr(text)');
-       
+
        $elem.attr({'css': {color:'red'}}, true);
        ok( /^(#ff0000|red)$/i.test(elem.style.color), 'attr(css)');
-       
+
        $elem.attr({'height': 10}, true);
        equals( elem.style.height, '10px', 'attr(height)');
-       
+
        // Multiple attributes
-       
+
        $elem.attr({
                width:10,
                css:{ paddingLeft:1, paddingRight:1 }
        }, true);
-       
+
        equals( elem.style.width, '10px', 'attr({...})');
        equals( elem.style.paddingLeft, '1px', 'attr({...})');
        equals( elem.style.paddingRight, '1px', 'attr({...})');
@@ -491,7 +491,7 @@ test( "val(Array of Numbers) (Bug #7123)", function() {
        ok( elements[1].checked, "Second element was checked" );
        ok( !elements[2].checked, "Third element was unchecked" );
        ok( !elements[3].checked, "Fourth element remained unchecked" );
-       
+
        elements.remove();
 });
 
@@ -672,7 +672,7 @@ test("removeClass(Function) with incoming value", function() {
 
        ok( !$divs.is('.test'), "Remove Class" );
 
-       QUnit.reset();  
+       QUnit.reset();
 });
 
 var testToggleClass = function(valueObj) {
@@ -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() {
@@ -740,21 +738,21 @@ test("toggleClass(Fucntion[, boolean]) with incoming value", function() {
 
        var e = jQuery("#firstp"), old = e.attr("class");
        ok( !e.is(".test"), "Assert class not present" );
-       
+
        e.toggleClass(function(i, val) {
                equals( val, old, "Make sure the incoming value is correct." );
                return "test";
        });
        ok( e.is(".test"), "Assert class present" );
-       
+
        old = e.attr("class");
-       
+
        e.toggleClass(function(i, val) {
                equals( val, old, "Make sure the incoming value is correct." );
                return "test";
        });
        ok( !e.is(".test"), "Assert class not present" );
-       
+
        old = e.attr("class");
 
        // class name with a boolean
@@ -764,18 +762,18 @@ test("toggleClass(Fucntion[, boolean]) with incoming value", function() {
                return "test";
        }, false );
        ok( !e.is(".test"), "Assert class not present" );
-       
+
        old = e.attr("class");
-       
+
        e.toggleClass(function(i, val, state) {
                equals( val, old, "Make sure the incoming value is correct." );
                equals( state, true, "Make sure that the state is passed in." );
                return "test";
        }, true );
        ok( e.is(".test"), "Assert class present" );
-       
+
        old = e.attr("class");
-       
+
        e.toggleClass(function(i, val, state) {
                equals( val, old, "Make sure the incoming value is correct." );
                equals( state, false, "Make sure that the state is passed in." );
@@ -785,30 +783,30 @@ 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() {
        expect(17);
+
        var jq = jQuery("<p>Hi</p>"), x = jq[0];
+
        jq.addClass("hi");
        equals( x.className, "hi", "Check single added class" );
+
        jq.addClass("foo bar");
        equals( x.className, "hi foo bar", "Check more added classes" );
+
        jq.removeClass();
        equals( x.className, "", "Remove all classes" );
+
        jq.addClass("hi foo bar");
        jq.removeClass("foo");
        equals( x.className, "hi bar", "Check removal of one class" );
+
        ok( jq.hasClass("hi"), "Check has1" );
        ok( jq.hasClass("bar"), "Check has2" );
+
        var jq = jQuery("<p class='class1\nclass2\tcla.ss3\n\rclass4'></p>");
        ok( jq.hasClass("class1"), "Check hasClass with line feed" );
        ok( jq.is(".class1"), "Check is with line feed" );
@@ -817,7 +815,7 @@ test("addClass, removeClass, hasClass", function() {
        ok( jq.hasClass("cla.ss3"), "Check hasClass with dot" );
        ok( jq.hasClass("class4"), "Check hasClass with carriage return" );
        ok( jq.is(".class4"), "Check is with carriage return" );
+
        jq.removeClass("class2");
        ok( jq.hasClass("class2")==false, "Check the class has been properly removed" );
        jq.removeClass("cla");
index 5c80569..30039bf 100644 (file)
@@ -1,4 +1,4 @@
-module("core");
+module("core", { teardown: moduleTeardown });
 
 test("Basic requirements", function() {
        expect(7);
@@ -14,8 +14,6 @@ test("Basic requirements", function() {
 test("jQuery()", function() {
        expect(24);
 
-       strictEqual( commonJSDefined, jQuery, "CommonJS registered (Bug #7102)" );
-
        // Basic constructor's behavior
 
        equals( jQuery().length, 0, "jQuery() === jQuery([])" );
@@ -23,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
@@ -86,6 +84,17 @@ 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() {
@@ -904,3 +913,290 @@ test("jQuery.parseJSON", function(){
                ok( true, "Test malformed JSON string." );
        }
 });
+
+test("jQuery._Deferred()", function() {
+
+       expect( 10 );
+
+       var deferred,
+               object,
+               test;
+
+       deferred = jQuery._Deferred();
+
+       test = false;
+
+       deferred.done( function( value ) {
+               equals( value , "value" , "Test pre-resolve callback" );
+               test = true;
+       } );
+
+       deferred.resolve( "value" );
+
+       ok( test , "Test pre-resolve callbacks called right away" );
+
+       test = false;
+
+       deferred.done( function( value ) {
+               equals( value , "value" , "Test post-resolve callback" );
+               test = true;
+       } );
+
+       ok( test , "Test post-resolve callbacks called right away" );
+
+       deferred.cancel();
+
+       test = true;
+
+       deferred.done( function() {
+               ok( false , "Cancel was ignored" );
+               test = false;
+       } );
+
+       ok( test , "Test cancel" );
+
+       deferred = jQuery._Deferred().resolve();
+
+       try {
+               deferred.done( function() {
+                       throw "Error";
+               } , function() {
+                       ok( true , "Test deferred do not cancel on exception" );
+               } );
+       } catch( e ) {
+               strictEqual( e , "Error" , "Test deferred propagates exceptions");
+               deferred.done();
+       }
+
+       test = "";
+       deferred = jQuery._Deferred().done( function() {
+
+               test += "A";
+
+       }, function() {
+
+               test += "B";
+
+       } ).resolve();
+
+       strictEqual( test , "AB" , "Test multiple done parameters" );
+
+       test = "";
+
+       deferred.done( function() {
+
+               deferred.done( function() {
+
+                       test += "C";
+
+               } );
+
+               test += "A";
+
+       }, function() {
+
+               test += "B";
+       } );
+
+       strictEqual( test , "ABC" , "Test done callbacks order" );
+
+       deferred = jQuery._Deferred();
+
+       deferred.resolveWith( jQuery , [ document ] ).done( function( doc ) {
+               ok( this === jQuery && arguments.length === 1 && doc === document , "Test fire context & args" );
+       });
+});
+
+test("jQuery.Deferred()", function() {
+
+       expect( 10 );
+
+       jQuery.Deferred( function( defer ) {
+               strictEqual( this , defer , "Defer passed as this & first argument" );
+               this.resolve( "done" );
+       }).then( function( value ) {
+               strictEqual( value , "done" , "Passed function executed" );
+       });
+
+       jQuery.Deferred().resolve().then( function() {
+               ok( true , "Success on resolve" );
+       }, function() {
+               ok( false , "Error on resolve" );
+       });
+
+       jQuery.Deferred().reject().then( function() {
+               ok( false , "Success on reject" );
+       }, 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( 23 );
+
+       // Some other objects
+       jQuery.each( {
+
+               "an empty string": "",
+               "a non-empty string": "some string",
+               "zero": 0,
+               "a number other than zero": 1,
+               "true": true,
+               "false": false,
+               "null": null,
+               "undefined": undefined,
+               "a plain object": {}
+
+       } , function( message , value ) {
+
+               ok( jQuery.isFunction( jQuery.when( value ).then( function( resolveValue ) {
+                       strictEqual( resolveValue , value , "Test the promise was resolved with " + message );
+               } ).promise ) , "Test " + message + " triggers the creation of a new Promise" );
+
+       } );
+
+       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++ ) {
+               jQuery.when( cache || jQuery.Deferred( function() {
+                       this.resolve( i );
+               }) ).then( function( value ) {
+                       strictEqual( value , 1 , "Function executed" + ( i > 1 ? " only once" : "" ) );
+                       cache = value;
+               }, function() {
+                       ok( false , "Fail called" );
+               });
+       }
+});
+
+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'
+                               );
+
+                       });
+               });
+       });
+
+});
index cddd902..555f135 100644 (file)
@@ -1,4 +1,4 @@
-module("css");
+module("css", { teardown: moduleTeardown });
 
 test("css(String|Hash)", function() {
        expect(41);
@@ -178,24 +178,24 @@ if ( !jQuery.support.opacity ) {
 
 test("css(String, Function)", function() {
        expect(3);
-       
+
        var sizes = ["10px", "20px", "30px"];
-       
-       jQuery("<div id='cssFunctionTest'><div class='cssFunction'></div>" + 
-                                "<div class='cssFunction'></div>" + 
+
+       jQuery("<div id='cssFunctionTest'><div class='cssFunction'></div>" +
+                                "<div class='cssFunction'></div>" +
                                 "<div class='cssFunction'></div></div>")
                .appendTo("body");
-       
+
        var index = 0;
-       
+
        jQuery("#cssFunctionTest div").css("font-size", function() {
                var size = sizes[index];
                index++;
                return size;
        });
-       
+
        index = 0;
-       
+
        jQuery("#cssFunctionTest div").each(function() {
                var computedSize = jQuery(this).css("font-size")
                var expectedSize = sizes[index]
@@ -208,24 +208,24 @@ test("css(String, Function)", function() {
 
 test("css(String, Function) with incoming value", function() {
        expect(3);
-       
+
        var sizes = ["10px", "20px", "30px"];
-       
-       jQuery("<div id='cssFunctionTest'><div class='cssFunction'></div>" + 
-                                "<div class='cssFunction'></div>" + 
+
+       jQuery("<div id='cssFunctionTest'><div class='cssFunction'></div>" +
+                                "<div class='cssFunction'></div>" +
                                 "<div class='cssFunction'></div></div>")
                .appendTo("body");
-       
+
        var index = 0;
-       
+
        jQuery("#cssFunctionTest div").css("font-size", function() {
                var size = sizes[index];
                index++;
                return size;
        });
-       
+
        index = 0;
-       
+
        jQuery("#cssFunctionTest div").css("font-size", function(i, computedSize) {
                var expectedSize = sizes[index]
                equals( computedSize, expectedSize, "Div #" + index + " should be " + expectedSize );
@@ -238,61 +238,61 @@ test("css(String, Function) with incoming value", function() {
 
 test("css(Object) where values are Functions", function() {
        expect(3);
-       
+
        var sizes = ["10px", "20px", "30px"];
-       
-       jQuery("<div id='cssFunctionTest'><div class='cssFunction'></div>" + 
-                                "<div class='cssFunction'></div>" + 
+
+       jQuery("<div id='cssFunctionTest'><div class='cssFunction'></div>" +
+                                "<div class='cssFunction'></div>" +
                                 "<div class='cssFunction'></div></div>")
                .appendTo("body");
 
        var index = 0;
-       
+
        jQuery("#cssFunctionTest div").css({fontSize: function() {
                var size = sizes[index];
                index++;
                return size;
        }});
-       
+
        index = 0;
-       
+
        jQuery("#cssFunctionTest div").each(function() {
                var computedSize = jQuery(this).css("font-size")
                var expectedSize = sizes[index]
                equals( computedSize, expectedSize, "Div #" + index + " should be " + expectedSize );
                index++;
        });
-       
+
        jQuery("#cssFunctionTest").remove();
 });
 
 test("css(Object) where values are Functions with incoming values", function() {
        expect(3);
-       
+
        var sizes = ["10px", "20px", "30px"];
-       
-       jQuery("<div id='cssFunctionTest'><div class='cssFunction'></div>" + 
-                                "<div class='cssFunction'></div>" + 
+
+       jQuery("<div id='cssFunctionTest'><div class='cssFunction'></div>" +
+                                "<div class='cssFunction'></div>" +
                                 "<div class='cssFunction'></div></div>")
                .appendTo("body");
 
        var index = 0;
-       
+
        jQuery("#cssFunctionTest div").css({fontSize: function() {
                var size = sizes[index];
                index++;
                return size;
        }});
-       
+
        index = 0;
-       
+
        jQuery("#cssFunctionTest div").css({"font-size": function(i, computedSize) {
                var expectedSize = sizes[index]
                equals( computedSize, expectedSize, "Div #" + index + " should be " + expectedSize );
                index++;
                return computedSize;
        }});
-       
+
        jQuery("#cssFunctionTest").remove();
 });
 
@@ -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" );
+});
index 310cd6b..889fc2d 100644 (file)
-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
index 8255bf3..fa59a9f 100644 (file)
@@ -1,4 +1,4 @@
-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() {
@@ -45,13 +47,13 @@ test("width() with function", function() {
 
 test("width() with function args", function() {
        expect( 2 );
-       
+
        var $div = jQuery("#nothiddendiv");
        $div.width( 30 ).width(function(i, width) {
                equals( width, 30, "Make sure previous value is corrrect." );
                return width + 1;
        });
-       
+
        equals( $div.width(), 31, "Make sure value was modified correctly." );
 });
 
@@ -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() {
@@ -92,13 +96,13 @@ test("height() with function", function() {
 
 test("height() with function args", function() {
        expect( 2 );
-       
+
        var $div = jQuery("#nothiddendiv");
        $div.height( 30 ).height(function(i, height) {
                equals( height, 30, "Make sure previous value is corrrect." );
                return height + 1;
        });
-       
+
        equals( $div.height(), 31, "Make sure value was modified correctly." );
 });
 
@@ -112,13 +116,13 @@ test("innerWidth()", function() {
                border: "2px solid #fff",
                width: 30
        });
-       
+
        equals($div.innerWidth(), 30, "Test with margin and border");
        $div.css("padding", "20px");
        equals($div.innerWidth(), 70, "Test with margin, border and padding");
        $div.hide();
        equals($div.innerWidth(), 70, "Test hidden div");
-       
+
        // reset styles
        $div.css({ display: "", border: "", padding: "", width: "", height: "" });
 
@@ -126,11 +130,14 @@ 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() {
        expect(4);
-       
+
        var $div = jQuery("#nothiddendiv");
        // set styles
        $div.css({
@@ -138,13 +145,13 @@ test("innerHeight()", function() {
                border: "2px solid #fff",
                height: 30
        });
-       
+
        equals($div.innerHeight(), 30, "Test with margin and border");
        $div.css("padding", "20px");
        equals($div.innerHeight(), 70, "Test with margin, border and padding");
        $div.hide();
        equals($div.innerHeight(), 70, "Test hidden div");
-       
+
        // reset styles
        $div.css({ display: "", border: "", padding: "", width: "", height: "" });
 
@@ -152,14 +159,17 @@ 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() {
        expect(7);
-       
+
        var $div = jQuery("#nothiddendiv");
        $div.css("width", 30);
-       
+
        equals($div.outerWidth(), 30, "Test with only width set");
        $div.css("padding", "20px");
        equals($div.outerWidth(), 70, "Test with padding");
@@ -171,7 +181,7 @@ test("outerWidth()", function() {
        equals($div.outerWidth(true), 94, "Test with padding, border and margin with margin option");
        $div.hide();
        equals($div.outerWidth(true), 94, "Test hidden div with padding, border and margin with margin option");
-       
+
        // reset styles
        $div.css({ position: "", display: "", border: "", padding: "", width: "", height: "" });
 
@@ -179,14 +189,17 @@ 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() {
        expect(7);
-       
+
        var $div = jQuery("#nothiddendiv");
        $div.css("height", 30);
-       
+
        equals($div.outerHeight(), 30, "Test with only width set");
        $div.css("padding", "20px");
        equals($div.outerHeight(), 70, "Test with padding");
@@ -197,7 +210,7 @@ test("outerHeight()", function() {
        equals($div.outerHeight(true), 94, "Test with padding, border and margin with margin option");
        $div.hide();
        equals($div.outerHeight(true), 94, "Test hidden div with padding, border and margin with margin option");
-       
+
        // reset styles
        $div.css({ display: "", border: "", padding: "", width: "", height: "" });
 
@@ -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);
 });
index 9690796..b1dd288 100644 (file)
@@ -1,4 +1,4 @@
-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" );
 
@@ -64,7 +64,7 @@ test("show()", function() {
 
        // #show-tests * is set display: none in CSS
        jQuery("#main").append('<div id="show-tests"><div><p><a href="#"></a></p><code></code><pre></pre><span></span></div><table><thead><tr><th></th></tr></thead><tbody><tr><td></td></tr></tbody></table><ul><li></li></ul></div><table id="test-table"></table>');
-       
+
        var old = jQuery("#test-table").show().css("display") !== "table";
        jQuery("#test-table").remove();
 
@@ -140,15 +140,15 @@ test("Persist correct display value", function() {
 
        // #show-tests * is set display: none in CSS
        jQuery("#main").append('<div id="show-tests"><span style="position:absolute;">foo</span></div>');
-  
-       var $span = jQuery("#show-tests span"),  
+
+       var $span = jQuery("#show-tests span"),
          displayNone = $span.css("display"),
          display = '', num = 0;
-               
+
   $span.show();
-  
+
   display = $span.css("display");
-  
+
   $span.hide();
 
   $span.fadeIn(100, function() {
@@ -156,13 +156,13 @@ test("Persist correct display value", function() {
     equals($span.css("display"), display, "Expecting display: " + display);
 
     $span.fadeOut(100, function () {
-  
+
       equals($span.css("display"), displayNone, "Expecting display: " + displayNone);
-      
+
       $span.fadeIn(100, function() {
-      
+
         equals($span.css("display"), display, "Expecting display: " + display);
-      
+
         start();
       });
     });
@@ -194,7 +194,7 @@ test("animate block as inline width/height", function() {
 
        var span = jQuery("<span>").css("display", "inline-block").appendTo("body"),
                expected = span.css("display");
-       
+
        span.remove();
 
        if ( jQuery.support.inlineBlockNeedsLayout || expected === "inline-block" ) {
@@ -220,7 +220,7 @@ test("animate native inline width/height", function() {
 
        var span = jQuery("<span>").css("display", "inline-block").appendTo("body"),
                expected = span.css("display");
-       
+
        span.remove();
 
        if ( jQuery.support.inlineBlockNeedsLayout || expected === "inline-block" ) {
@@ -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" );
@@ -560,6 +565,23 @@ jQuery.checkOverflowDisplay = function(){
        start();
 }
 
+test("support negative values < -10000 (bug #7193)", function () {
+       expect(1);
+       stop();
+
+       jQuery.extend(jQuery.fx.step, {
+               "marginBottom": function(fx) {
+                       equals( fx.cur(), -11000, "Element has margin-bottom of -11000" );
+                       delete jQuery.fx.step.marginBottom;
+               }
+    });
+
+       jQuery("#main").css("marginBottom", "-11000px").animate({ marginBottom: "-11001px" }, {
+               duration: 1,
+               complete: start
+       });
+});
+
 test("JS Overflow and Display", function() {
        expect(2);
        stop();
@@ -720,6 +742,9 @@ jQuery.each( {
                                        }
                                }
 
+                               // manually remove generated element
+                               jQuery(this).remove();
+
                                start();
                        });
                });
@@ -746,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();
 }
 
@@ -812,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;
@@ -878,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" );
 
@@ -889,11 +915,11 @@ test("hide hidden elements, with animation (bug #7141)", function() {
        expect(3);
        QUnit.reset();
        stop();
-       
+
        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();
index 83f6096..02824a9 100644 (file)
@@ -1,23 +1,23 @@
-module("event");
+module("event", { teardown: moduleTeardown });
 
 test("null or undefined handler", function() {
        expect(2);
   // Supports Fixes bug #7229
   try {
-  
+
     jQuery("#firstp").click(null);
-  
+
     ok(true, "Passing a null handler will not throw an exception");
 
-  } catch (e) {}  
+  } catch (e) {}
 
   try {
-  
+
     jQuery("#firstp").click(undefined);
-  
+
     ok(true, "Passing an undefined handler will not throw an exception");
 
-  } catch (e) {}  
+  } catch (e) {}
 });
 
 test("bind(), with data", function() {
@@ -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() {
@@ -175,7 +184,7 @@ test("bind(), no data", function() {
 
 test("bind/one/unbind(Object)", function(){
        expect(6);
-       
+
        var clickCounter = 0, mouseoverCounter = 0;
        function handler(event) {
                if (event.type == "click")
@@ -183,18 +192,18 @@ test("bind/one/unbind(Object)", function(){
                else if (event.type == "mouseover")
                        mouseoverCounter++;
        };
-       
+
        function handlerWithData(event) {
                if (event.type == "click")
                        clickCounter += event.data;
                else if (event.type == "mouseover")
                        mouseoverCounter += event.data;
        };
-       
+
        function trigger(){
                $elem.trigger("click").trigger("mouseover");
        }
-       
+
        var $elem = jQuery("#firstp")
                // Regular bind
                .bind({
@@ -206,16 +215,16 @@ test("bind/one/unbind(Object)", function(){
                        click:handlerWithData,
                        mouseover:handlerWithData
                }, 2 );
-       
+
        trigger();
-       
+
        equals( clickCounter, 3, "bind(Object)" );
        equals( mouseoverCounter, 3, "bind(Object)" );
-       
+
        trigger();
        equals( clickCounter, 4, "bind(Object)" );
        equals( mouseoverCounter, 4, "bind(Object)" );
-       
+
        jQuery("#firstp").unbind({
                click:handler,
                mouseover:handler
@@ -228,10 +237,10 @@ test("bind/one/unbind(Object)", function(){
 
 test("live/die(Object), delegate/undelegate(String, Object)", function() {
        expect(6);
-       
+
        var clickCounter = 0, mouseoverCounter = 0,
                $p = jQuery("#firstp"), $a = $p.find("a:first");
-       
+
        var events = {
                click: function( event ) {
                        clickCounter += ( event.data || 1 );
@@ -240,26 +249,26 @@ test("live/die(Object), delegate/undelegate(String, Object)", function() {
                        mouseoverCounter += ( event.data || 1 );
                }
        };
-       
+
        function trigger() {
                $a.trigger("click").trigger("mouseover");
        }
-       
+
        $a.live( events );
        $p.delegate( "a", events, 2 );
-       
+
        trigger();
        equals( clickCounter, 3, "live/delegate" );
        equals( mouseoverCounter, 3, "live/delegate" );
-       
+
        $p.undelegate( "a", events );
-       
+
        trigger();
        equals( clickCounter, 4, "undelegate" );
        equals( mouseoverCounter, 4, "undelegate" );
-       
+
        $a.die( events );
-       
+
        trigger();
        equals( clickCounter, 4, "die" );
        equals( mouseoverCounter, 4, "die" );
@@ -267,12 +276,12 @@ test("live/die(Object), delegate/undelegate(String, Object)", function() {
 
 test("live/delegate immediate propagation", function() {
        expect(2);
-       
+
        var $p = jQuery("#firstp"), $a = $p.find("a:first"), lastClick;
-       
+
        lastClick = "";
-       $a.live( "click", function(e) { 
-               lastClick = "click1"; 
+       $a.live( "click", function(e) {
+               lastClick = "click1";
                e.stopImmediatePropagation();
        });
        $a.live( "click", function(e) {
@@ -281,10 +290,10 @@ test("live/delegate immediate propagation", function() {
        $a.trigger( "click" );
        equals( lastClick, "click1", "live stopImmediatePropagation" );
        $a.die( "click" );
-       
+
        lastClick = "";
-       $p.delegate( "a", "click", function(e) { 
-               lastClick = "click1"; 
+       $p.delegate( "a", "click", function(e) {
+               lastClick = "click1";
                e.stopImmediatePropagation();
        });
        $p.delegate( "a", "click", function(e) {
@@ -295,14 +304,14 @@ 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" );
+                               var e = document.createEvent( 'MouseEvents' );
                                e.initEvent( "click", true, true ); 
                                $jq[0].dispatchEvent(e);
                        }
@@ -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" );
@@ -333,7 +350,7 @@ test("bind/delegate bubbling, isDefaultPrevented (Bug #7793)", function() {
 test("bind(), iframes", function() {
        // events don't work with iframes, see #939 - this test fails in IE because of contentDocument
        var doc = jQuery("#loadediframe").contents();
-       
+
        jQuery("div", doc).bind("click", function() {
                ok( true, "Binding to element inside iframe" );
        }).click().unbind('click');
@@ -395,7 +412,7 @@ test("bind(), namespaced events, cloned events", function() {
 
 test("bind(), multi-namespaced events", function() {
        expect(6);
-       
+
        var order = [
                "click.test.abc",
                "click.test.abc",
@@ -404,7 +421,7 @@ test("bind(), multi-namespaced events", function() {
                "click.test",
                "custom.test2"
        ];
-       
+
        function check(name, msg){
                same(name, order.shift(), msg);
        }
@@ -424,7 +441,7 @@ test("bind(), multi-namespaced events", function() {
        jQuery("#firstp").bind("click.test.abc",function(e){
                check("click.test.abc", "Namespaced click triggered");
        });
-       
+
        // Those would not trigger/unbind (#5303)
        jQuery("#firstp").trigger("click.a.test");
        jQuery("#firstp").unbind("click.a.test");
@@ -488,7 +505,7 @@ test("bind(), make sure order is maintained", function() {
 
        elem.unbind("click");
 });
+
 test("bind(), with different this object", function() {
        expect(4);
        var thisObject = { myThis: true },
@@ -500,12 +517,12 @@ test("bind(), with different this object", function() {
                        equals( this, thisObject, "bind() with different this object and data" );
                        equals( event.data, data, "bind() with different this object and data" );
                };
-       
+
        jQuery("#firstp")
                .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." );
@@ -566,29 +586,31 @@ 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() {
        expect( 0 );
-       
+
        var $elem = jQuery("#firstp"),
                message;
 
        function error(){
                ok( false, message );
        }
-       
+
        message = "unbind passing function";
        $elem.bind('error1', error).unbind('error1',error).triggerHandler('error1');
-       
+
        message = "unbind all from event";
        $elem.bind('error1', error).unbind('error1').triggerHandler('error1');
-       
+
        message = "unbind all";
        $elem.bind('error1', error).unbind().triggerHandler('error1');
-       
+
        message = "unbind many with function";
        $elem.bind('error1 error2',error)
                 .unbind('error1 error2', error )
@@ -598,7 +620,7 @@ test("unbind(type)", function() {
        $elem.bind('error1 error2',error)
                 .unbind('error1 error2')
                 .trigger('error1').triggerHandler('error2');
-       
+
        message = "unbind without a type or handler";
        $elem.bind("error1 error2.test",error)
                 .unbind()
@@ -607,7 +629,7 @@ test("unbind(type)", function() {
 
 test("unbind(eventObject)", function() {
        expect(4);
-       
+
        var $elem = jQuery("#firstp"),
                num;
 
@@ -616,7 +638,7 @@ test("unbind(eventObject)", function() {
                $elem.trigger('foo').triggerHandler('bar');
                equals( num, expected, "Check the right handlers are triggered" );
        }
-       
+
        $elem
                // This handler shouldn't be unbound
                .bind('foo', function(){
@@ -630,14 +652,14 @@ test("unbind(eventObject)", function() {
                .bind('bar', function(){
                        num += 4;
                });
-               
+
        assert( 7 );
        assert( 5 );
-       
+
        $elem.unbind('bar');
        assert( 1 );
-       
-       $elem.unbind(); 
+
+       $elem.unbind();
        assert( 0 );
 });
 
@@ -661,34 +683,42 @@ 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();
-       
+
        var counter = 0;
        jQuery('#firstp')[0].onclick = function(event) {
                counter++;
        };
        jQuery('#firstp').click();
        equals( counter, 1, "Check that click, triggers onclick event handler also" );
-       
+
        var clickCounter = 0;
        jQuery('#simon1')[0].onclick = function(event) {
                clickCounter++;
        };
        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() {
@@ -763,10 +797,10 @@ test("trigger(type, [data], [fn])", function() {
                pass = false;
        }
        ok( pass, "Trigger focus on hidden element" );
-       
+
        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;
        }
@@ -803,28 +837,28 @@ test("jQuery.Event.currentTarget", function(){
 
 test("trigger(eventObject, [data], [fn])", function() {
        expect(25);
-       
+
        var $parent = jQuery('<div id="par" />').hide().appendTo('body'),
                $child = jQuery('<p id="child">foo</p>').appendTo( $parent );
-       
-       var event = jQuery.Event("noNew");      
+
+       var event = jQuery.Event("noNew");
        ok( event != window, "Instantiate jQuery.Event without the 'new' keyword" );
        equals( event.type, "noNew", "Verify its type" );
-       
+
        equals( event.isDefaultPrevented(), false, "Verify isDefaultPrevented" );
        equals( event.isPropagationStopped(), false, "Verify isPropagationStopped" );
        equals( event.isImmediatePropagationStopped(), false, "Verify isImmediatePropagationStopped" );
-       
+
        event.preventDefault();
        equals( event.isDefaultPrevented(), true, "Verify isDefaultPrevented" );
        event.stopPropagation();
        equals( event.isPropagationStopped(), true, "Verify isPropagationStopped" );
-       
+
        event.isPropagationStopped = function(){ return false };
        event.stopImmediatePropagation();
        equals( event.isPropagationStopped(), true, "Verify isPropagationStopped" );
        equals( event.isImmediatePropagationStopped(), true, "Verify isPropagationStopped" );
-       
+
        $parent.bind('foo',function(e){
                // Tries bubbling
                equals( e.type, 'foo', 'Verify event type when passed passing an event object' );
@@ -832,72 +866,72 @@ test("trigger(eventObject, [data], [fn])", function() {
                equals( e.currentTarget.id, 'par', 'Verify event.target when passed passing an event object' );
                equals( e.secret, 'boo!', 'Verify event object\'s custom attribute when passed passing an event object' );
        });
-       
+
        // test with an event object
        event = new jQuery.Event("foo");
        event.secret = 'boo!';
        $child.trigger(event);
-       
+
        // test with a literal object
        $child.trigger({type:'foo', secret:'boo!'});
-       
+
        $parent.unbind();
 
        function error(){
                ok( false, "This assertion shouldn't be reached");
        }
-       
+
        $parent.bind('foo', error );
-       
+
        $child.bind('foo',function(e, a, b, c ){
                equals( arguments.length, 4, "Check arguments length");
                equals( a, 1, "Check first custom argument");
                equals( b, 2, "Check second custom argument");
                equals( c, 3, "Check third custom argument");
-               
+
                equals( e.isDefaultPrevented(), false, "Verify isDefaultPrevented" );
                equals( e.isPropagationStopped(), false, "Verify isPropagationStopped" );
                equals( e.isImmediatePropagationStopped(), false, "Verify isImmediatePropagationStopped" );
-               
+
                // Skips both errors
                e.stopImmediatePropagation();
-               
+
                return "result";
        });
-       
+
        // We should add this back in when we want to test the order
        // in which event handlers are iterated.
        //$child.bind('foo', error );
-       
+
        event = new jQuery.Event("foo");
        $child.trigger( event, [1,2,3] ).unbind();
        equals( event.result, "result", "Check event.result attribute");
-       
+
        // Will error if it bubbles
        $child.triggerHandler('foo');
-       
+
        $child.unbind();
        $parent.unbind().remove();
 });
 
 test("jQuery.Event.currentTarget", function(){
        expect(1);
-       
+
        var counter = 0,
                $elem = jQuery('<button>a</button>').click(function(e){
                equals( e.currentTarget, this, "Check currentTarget on "+(counter++?"native":"fake") +" event" );
        });
-       
+
        // Fake event
        $elem.trigger('click');
-       
+
        // Cleanup
        $elem.unbind();
 });
 
 test("toggle(Function, Function, ...)", function() {
        expect(16);
-       
+
        var count = 0,
                fn1 = function(e) { count++; },
                fn2 = function(e) { count--; },
@@ -920,7 +954,7 @@ test("toggle(Function, Function, ...)", function() {
                });
                return false;
        }).click().click().click();
-       
+
        var turn = 0;
        var fns = [
                function(){
@@ -933,7 +967,7 @@ test("toggle(Function, Function, ...)", function() {
                        turn = 3;
                }
        ];
-       
+
        var $div = jQuery("<div>&nbsp;</div>").toggle( fns[0], fns[1], fns[2] );
        $div.click();
        equals( turn, 1, "Trying toggle with 3 functions, attempt 1 yields 1");
@@ -945,11 +979,14 @@ test("toggle(Function, Function, ...)", function() {
        equals( turn, 1, "Trying toggle with 3 functions, attempt 4 yields 1");
        $div.click();
        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");
@@ -1087,7 +1127,7 @@ test(".live()/.die()", function() {
        // Test binding with different this object, event data, and trigger data
        jQuery("#foo").live("click", true, jQuery.proxy(function(e, data){
                equals( e.data, true, "live with with different this object, event data, and trigger data" );
-               equals( this.foo, "bar", "live with with different this object, event data, and trigger data" ); 
+               equals( this.foo, "bar", "live with with different this object, event data, and trigger data" );
                equals( data, true, "live with with different this object, event data, and trigger data")
        }, { foo: "bar" }));
        jQuery("#foo").trigger("click", true).die("click");
@@ -1148,25 +1188,25 @@ test(".live()/.die()", function() {
 
        // Cleanup
        jQuery("#nothiddendiv").die("foo", callback);
-       
+
        // Make sure we don't loose the target by DOM modifications
        // after the bubble already reached the liveHandler
        var livec = 0, elemDiv = jQuery("#nothiddendivchild").html('<span></span>').get(0);
-       
+
        jQuery("#nothiddendivchild").live("click", function(e){ jQuery("#nothiddendivchild").html(''); });
        jQuery("#nothiddendivchild").live("click", function(e){ if(e.target) {livec++;} });
-       
+
        jQuery("#nothiddendiv span").click();
        equals( jQuery("#nothiddendiv span").length, 0, "Verify that first handler occurred and modified the DOM." );
        equals( livec, 1, "Verify that second handler occurred even with nuked target." );
-       
+
        // Cleanup
        jQuery("#nothiddendivchild").die("click");
 
        // Verify that .live() ocurs and cancel buble in the same order as
        // we would expect .bind() and .click() without delegation
        var lived = 0, livee = 0;
-       
+
        // bind one pair in one order
        jQuery('span#liveSpan1 a').live('click', function(){ lived++; return false; });
        jQuery('span#liveSpan1').live('click', function(){ livee++; });
@@ -1184,22 +1224,22 @@ test(".live()/.die()", function() {
        jQuery('span#liveSpan2 a').click();
        equals( lived, 1, "Verify that only one first handler occurred." );
        equals( livee, 0, "Verify that second handler doesn't." );
-       
+
        // Cleanup
        jQuery("span#liveSpan1 a").die("click")
        jQuery("span#liveSpan1").die("click");
        jQuery("span#liveSpan2 a").die("click");
        jQuery("span#liveSpan2").die("click");
-       
+
        // Test this, target and currentTarget are correct
-       jQuery('span#liveSpan1').live('click', function(e){ 
+       jQuery('span#liveSpan1').live('click', function(e){
                equals( this.id, 'liveSpan1', 'Check the this within a live handler' );
                equals( e.currentTarget.id, 'liveSpan1', 'Check the event.currentTarget within a live handler' );
                equals( e.target.nodeName.toUpperCase(), 'A', 'Check the event.target within a live handler' );
        });
-       
+
        jQuery('span#liveSpan1 a').click();
-       
+
        jQuery('span#liveSpan1').die('click');
 
        // Work with deep selectors
@@ -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(){
@@ -1333,18 +1376,18 @@ test("live with change", function(){
        expect(8);
 
        var selectChange = 0, checkboxChange = 0;
-       
+
        var select = jQuery("select[name='S1']")
        select.live("change", function() {
                selectChange++;
        });
-       
-       var checkbox = jQuery("#check2"), 
+
+       var checkbox = jQuery("#check2"),
                checkboxFunction = function(){
                        checkboxChange++;
                }
        checkbox.live("change", checkboxFunction);
-       
+
        // test click on select
 
        // second click that changed it
@@ -1352,17 +1395,17 @@ test("live with change", function(){
        select[0].selectedIndex = select[0].selectedIndex ? 0 : 1;
        select.trigger("change");
        equals( selectChange, 1, "Change on click." );
-       
+
        // test keys on select
        selectChange = 0;
        select[0].selectedIndex = select[0].selectedIndex ? 0 : 1;
        select.trigger("change");
        equals( selectChange, 1, "Change on keyup." );
-       
+
        // test click on checkbox
        checkbox.trigger("change");
        equals( checkboxChange, 1, "Change on checkbox." );
-       
+
        // test blur/focus on text
        var text = jQuery("#name"), textChange = 0, oldTextVal = text.val();
        text.live("change", function() {
@@ -1375,7 +1418,7 @@ test("live with change", function(){
 
        text.val(oldTextVal);
        text.die("change");
-       
+
        // test blur/focus on password
        var password = jQuery("#name"), passwordChange = 0, oldPasswordVal = password.val();
        password.live("change", function() {
@@ -1388,9 +1431,9 @@ test("live with change", function(){
 
        password.val(oldPasswordVal);
        password.die("change");
-       
+
        // make sure die works
-       
+
        // die all changes
        selectChange = 0;
        select.die("change");
@@ -1402,7 +1445,7 @@ test("live with change", function(){
        select[0].selectedIndex = select[0].selectedIndex ? 0 : 1;
        select.trigger("change");
        equals( selectChange, 0, "Die on keyup works." );
-       
+
        // die specific checkbox
        checkbox.die("change", checkboxFunction);
        checkbox.trigger("change");
@@ -1411,7 +1454,7 @@ test("live with change", function(){
 
 test("live with submit", function() {
        var count1 = 0, count2 = 0;
-       
+
        jQuery("#testForm").live("submit", function(ev) {
                count1++;
                ev.preventDefault();
@@ -1425,7 +1468,7 @@ test("live with submit", function() {
        jQuery("#testForm input[name=sub1]").submit();
        equals( count1, 1, "Verify form submit." );
        equals( count2, 1, "Verify body submit." );
-       
+
        jQuery("#testForm").die("submit");
        jQuery("body").die("submit");
 });
@@ -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");
@@ -1603,7 +1646,7 @@ test(".delegate()/.undelegate()", function() {
        // Test binding with different this object, event data, and trigger data
        jQuery("#body").delegate("#foo", "click", true, jQuery.proxy(function(e, data){
                equals( e.data, true, "delegate with with different this object, event data, and trigger data" );
-               equals( this.foo, "bar", "delegate with with different this object, event data, and trigger data" ); 
+               equals( this.foo, "bar", "delegate with with different this object, event data, and trigger data" );
                equals( data, true, "delegate with with different this object, event data, and trigger data")
        }, { foo: "bar" }));
        jQuery("#foo").trigger("click", true);
@@ -1665,25 +1708,25 @@ test(".delegate()/.undelegate()", function() {
 
        // Cleanup
        jQuery("#body").undelegate("#nothiddendiv", "foo", callback);
-       
+
        // Make sure we don't loose the target by DOM modifications
        // after the bubble already reached the liveHandler
        var livec = 0, elemDiv = jQuery("#nothiddendivchild").html('<span></span>').get(0);
-       
+
        jQuery("#body").delegate("#nothiddendivchild", "click", function(e){ jQuery("#nothiddendivchild").html(''); });
        jQuery("#body").delegate("#nothiddendivchild", "click", function(e){ if(e.target) {livec++;} });
-       
+
        jQuery("#nothiddendiv span").click();
        equals( jQuery("#nothiddendiv span").length, 0, "Verify that first handler occurred and modified the DOM." );
        equals( livec, 1, "Verify that second handler occurred even with nuked target." );
-       
+
        // Cleanup
        jQuery("#body").undelegate("#nothiddendivchild", "click");
 
        // Verify that .live() ocurs and cancel buble in the same order as
        // we would expect .bind() and .click() without delegation
        var lived = 0, livee = 0;
-       
+
        // bind one pair in one order
        jQuery("#body").delegate('span#liveSpan1 a', 'click', function(){ lived++; return false; });
        jQuery("#body").delegate('span#liveSpan1', 'click', function(){ livee++; });
@@ -1701,19 +1744,19 @@ test(".delegate()/.undelegate()", function() {
        jQuery('span#liveSpan2 a').click();
        equals( lived, 1, "Verify that only one first handler occurred." );
        equals( livee, 0, "Verify that second handler doesn't." );
-       
+
        // Cleanup
        jQuery("#body").undelegate("click");
-       
+
        // Test this, target and currentTarget are correct
-       jQuery("#body").delegate('span#liveSpan1', 'click', function(e){ 
+       jQuery("#body").delegate('span#liveSpan1', 'click', function(e){
                equals( this.id, 'liveSpan1', 'Check the this within a delegate handler' );
                equals( e.currentTarget.id, 'liveSpan1', 'Check the event.currentTarget within a delegate handler' );
                equals( e.target.nodeName.toUpperCase(), 'A', 'Check the event.target within a delegate handler' );
        });
-       
+
        jQuery('span#liveSpan1 a').click();
-       
+
        jQuery("#body").undelegate('span#liveSpan1', 'click');
 
        // Work with deep selectors
@@ -1789,18 +1832,18 @@ test("delegate with change", function(){
        expect(8);
 
        var selectChange = 0, checkboxChange = 0;
-       
+
        var select = jQuery("select[name='S1']");
        jQuery("#body").delegate("select[name='S1']", "change", function() {
                selectChange++;
        });
-       
-       var checkbox = jQuery("#check2"), 
+
+       var checkbox = jQuery("#check2"),
                checkboxFunction = function(){
                        checkboxChange++;
                }
        jQuery("#body").delegate("#check2", "change", checkboxFunction);
-       
+
        // test click on select
 
        // second click that changed it
@@ -1808,17 +1851,17 @@ test("delegate with change", function(){
        select[0].selectedIndex = select[0].selectedIndex ? 0 : 1;
        select.trigger("change");
        equals( selectChange, 1, "Change on click." );
-       
+
        // test keys on select
        selectChange = 0;
        select[0].selectedIndex = select[0].selectedIndex ? 0 : 1;
        select.trigger("change");
        equals( selectChange, 1, "Change on keyup." );
-       
+
        // test click on checkbox
        checkbox.trigger("change");
        equals( checkboxChange, 1, "Change on checkbox." );
-       
+
        // test blur/focus on text
        var text = jQuery("#name"), textChange = 0, oldTextVal = text.val();
        jQuery("#body").delegate("#name", "change", function() {
@@ -1831,7 +1874,7 @@ test("delegate with change", function(){
 
        text.val(oldTextVal);
        jQuery("#body").die("change");
-       
+
        // test blur/focus on password
        var password = jQuery("#name"), passwordChange = 0, oldPasswordVal = password.val();
        jQuery("#body").delegate("#name", "change", function() {
@@ -1844,9 +1887,9 @@ test("delegate with change", function(){
 
        password.val(oldPasswordVal);
        jQuery("#body").undelegate("#name", "change");
-       
+
        // make sure die works
-       
+
        // die all changes
        selectChange = 0;
        jQuery("#body").undelegate("select[name='S1']", "change");
@@ -1858,7 +1901,7 @@ test("delegate with change", function(){
        select[0].selectedIndex = select[0].selectedIndex ? 0 : 1;
        select.trigger("change");
        equals( selectChange, 0, "Die on keyup works." );
-       
+
        // die specific checkbox
        jQuery("#body").undelegate("#check2", "change", checkboxFunction);
        checkbox.trigger("change");
@@ -1867,7 +1910,7 @@ test("delegate with change", function(){
 
 test("delegate with submit", function() {
        var count1 = 0, count2 = 0;
-       
+
        jQuery("#body").delegate("#testForm", "submit", function(ev) {
                count1++;
                ev.preventDefault();
@@ -1881,7 +1924,7 @@ test("delegate with submit", function() {
        jQuery("#testForm input[name=sub1]").submit();
        equals( count1, 1, "Verify form submit." );
        equals( count2, 1, "Verify body submit." );
-       
+
        jQuery("#body").undelegate();
        jQuery(document).undelegate();
 });
@@ -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() {
index e273cf0..37234d8 100644 (file)
@@ -1,4 +1,4 @@
-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() {
@@ -395,7 +402,7 @@ test("append(Function) with incoming value", function() {
 });
 
 test("append the same fragment with events (Bug #6997, 5566)", function () {
-       expect(4 + (document.fireEvent ? 1 : 0));
+       expect(2 + (document.fireEvent ? 1 : 0));
        stop(1000);
 
        var element;
@@ -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 () {
@@ -426,14 +437,6 @@ test("append the same fragment with events (Bug #6997, 5566)", function () {
 
        jQuery("#listWithTabIndex li").before(element);
        jQuery("#listWithTabIndex li.test6997").eq(1).click();
-
-       element = jQuery("<select><option>Foo</option><option selected>Bar</option></select>");
-
-       equals( element.clone().find("option:selected").val(), element.find("option:selected").val(), "Selected option cloned correctly" );
-
-       element = jQuery("<input type='checkbox'>").attr('checked', 'checked');
-
-       equals( element.clone().is(":checked"), element.is(":checked"), "Checked input cloned correctly" );
 });
 
 test("appendTo(String|Element|Array&lt;Element&gt;|jQuery)", function() {
@@ -894,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,
@@ -928,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");
@@ -945,6 +968,28 @@ test("clone()", function() {
        equal( jQuery("body").clone().children()[0].id, "qunit-header", "Make sure cloning body works" );
 });
 
+test("clone(form element) (Bug #3879, #6655)", function() {
+       expect(6);
+       element = jQuery("<select><option>Foo</option><option selected>Bar</option></select>");
+
+       equals( element.clone().find("option:selected").val(), element.find("option:selected").val(), "Selected option cloned correctly" );
+
+       element = jQuery("<input type='checkbox' value='foo'>").attr('checked', 'checked');
+       clone = element.clone();
+
+       equals( clone.is(":checked"), element.is(":checked"), "Checked input cloned correctly" );
+       equals( clone[0].defaultValue, "foo", "Checked input defaultValue cloned correctly" );
+       equals( clone[0].defaultChecked, !jQuery.support.noCloneEvent, "Checked input defaultChecked cloned correctly" );
+
+       element = jQuery("<input type='text' value='foo'>");
+       clone = element.clone();
+       equals( clone[0].defaultValue, "foo", "Text input defaultValue cloned correctly" );
+
+       element = jQuery("<textarea>foo</textarea>");
+       clone = element.clone();
+       equals( clone[0].defaultValue, "foo", "Textarea defaultValue cloned correctly" );
+});
+
 if (!isLocal) {
 test("clone() on XML nodes", function() {
        expect(2);
@@ -1141,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() {
index 8797531..329d69f 100644 (file)
@@ -1,4 +1,4 @@
-module("offset");
+module("offset", { teardown: moduleTeardown });
 
 test("disconnected node", function() {
        expect(2);
@@ -13,9 +13,9 @@ var supportsScroll = false;
 
 testoffset("absolute"/* in iframe */, function($, iframe) {
        expect(4);
-       
+
        var doc = iframe.document, tests;
-       
+
        // force a scroll value on the main window
        // this insures that the results will be wrong
        // if the offset method is using the scroll offset
@@ -28,7 +28,7 @@ testoffset("absolute"/* in iframe */, function($, iframe) {
        }
 
        window.scrollTo(1, 1);
-       
+
        // get offset
        tests = [
                { id: '#absolute-1', top: 1, left: 1 }
@@ -47,16 +47,16 @@ testoffset("absolute"/* in iframe */, function($, iframe) {
                equals( jQuery( this.id, doc ).position().top,  this.top,  "jQuery('" + this.id + "').position().top" );
                equals( jQuery( this.id, doc ).position().left, this.left, "jQuery('" + this.id + "').position().left" );
        });
-       
+
        forceScroll.remove();
 });
 
 testoffset("absolute", function( jQuery ) {
        expect(178);
-       
+
        // get offset tests
        var tests = [
-               { id: '#absolute-1',     top:  1, left:  1 }, 
+               { id: '#absolute-1',     top:  1, left:  1 },
                { id: '#absolute-1-1',   top:  5, left:  5 },
                { id: '#absolute-1-1-1', top:  9, left:  9 },
                { id: '#absolute-2',     top: 20, left: 20 }
@@ -65,8 +65,8 @@ testoffset("absolute", function( jQuery ) {
                equals( jQuery( this.id ).offset().top,  this.top,  "jQuery('" + this.id + "').offset().top" );
                equals( jQuery( this.id ).offset().left, this.left, "jQuery('" + this.id + "').offset().left" );
        });
-       
-       
+
+
        // get position
        tests = [
                { id: '#absolute-1',     top:  0, left:  0 },
@@ -78,13 +78,13 @@ testoffset("absolute", function( jQuery ) {
                equals( jQuery( this.id ).position().top,  this.top,  "jQuery('" + this.id + "').position().top" );
                equals( jQuery( this.id ).position().left, this.left, "jQuery('" + this.id + "').position().left" );
        });
-       
+
        // test #5781
        var offset = jQuery( '#positionTest' ).offset({ top: 10, left: 10 }).offset();
        equals( offset.top,  10, "Setting offset on element with position absolute but 'auto' values." )
        equals( offset.left, 10, "Setting offset on element with position absolute but 'auto' values." )
-       
-       
+
+
        // set offset
        tests = [
                { id: '#absolute-2',     top: 30, left: 30 },
@@ -108,9 +108,9 @@ testoffset("absolute", function( jQuery ) {
                jQuery( this.id ).offset({ top: this.top, left: this.left });
                equals( jQuery( this.id ).offset().top,  this.top,  "jQuery('" + this.id + "').offset({ top: "  + this.top  + " })" );
                equals( jQuery( this.id ).offset().left, this.left, "jQuery('" + this.id + "').offset({ left: " + this.left + " })" );
-               
+
                var top = this.top, left = this.left;
-               
+
                jQuery( this.id ).offset(function(i, val){
                        equals( val.top, top, "Verify incoming top position." );
                        equals( val.left, left, "Verify incoming top position." );
@@ -118,13 +118,13 @@ testoffset("absolute", function( jQuery ) {
                });
                equals( jQuery( this.id ).offset().top,  this.top  + 1, "jQuery('" + this.id + "').offset({ top: "  + (this.top  + 1) + " })" );
                equals( jQuery( this.id ).offset().left, this.left + 1, "jQuery('" + this.id + "').offset({ left: " + (this.left + 1) + " })" );
-               
+
                jQuery( this.id )
                        .offset({ left: this.left + 2 })
                        .offset({ top:  this.top  + 2 });
                equals( jQuery( this.id ).offset().top,  this.top  + 2, "Setting one property at a time." );
                equals( jQuery( this.id ).offset().left, this.left + 2, "Setting one property at a time." );
-               
+
                jQuery( this.id ).offset({ top: this.top, left: this.left, using: function( props ) {
                        jQuery( this ).css({
                                top:  props.top  + 1,
@@ -138,10 +138,10 @@ testoffset("absolute", function( jQuery ) {
 
 testoffset("relative", function( jQuery ) {
        expect(60);
-       
+
        // IE is collapsing the top margin of 1px
        var ie = jQuery.browser.msie && parseInt( jQuery.browser.version, 10 ) < 8;
-       
+
        // get offset
        var tests = [
                { id: '#relative-1',   top: ie ?   6 :   7, left:  7 },
@@ -152,8 +152,8 @@ testoffset("relative", function( jQuery ) {
                equals( jQuery( this.id ).offset().top,  this.top,  "jQuery('" + this.id + "').offset().top" );
                equals( jQuery( this.id ).offset().left, this.left, "jQuery('" + this.id + "').offset().left" );
        });
-       
-       
+
+
        // get position
        tests = [
                { id: '#relative-1',   top: ie ?   5 :   6, left:  6 },
@@ -164,8 +164,8 @@ testoffset("relative", function( jQuery ) {
                equals( jQuery( this.id ).position().top,  this.top,  "jQuery('" + this.id + "').position().top" );
                equals( jQuery( this.id ).position().left, this.left, "jQuery('" + this.id + "').position().left" );
        });
-       
-       
+
+
        // set offset
        tests = [
                { id: '#relative-2',   top: 200, left:  50 },
@@ -185,7 +185,7 @@ testoffset("relative", function( jQuery ) {
                jQuery( this.id ).offset({ top: this.top, left: this.left });
                equals( jQuery( this.id ).offset().top,  this.top,  "jQuery('" + this.id + "').offset({ top: "  + this.top  + " })" );
                equals( jQuery( this.id ).offset().left, this.left, "jQuery('" + this.id + "').offset({ left: " + this.left + " })" );
-               
+
                jQuery( this.id ).offset({ top: this.top, left: this.left, using: function( props ) {
                        jQuery( this ).css({
                                top:  props.top  + 1,
@@ -199,10 +199,10 @@ testoffset("relative", function( jQuery ) {
 
 testoffset("static", function( jQuery ) {
        expect(80);
-       
+
        // IE is collapsing the top margin of 1px
        var ie = jQuery.browser.msie && parseInt( jQuery.browser.version, 10 ) < 8;
-       
+
        // get offset
        var tests = [
                { id: '#static-1',     top: ie ?   6 :   7, left:  7 },
@@ -214,8 +214,8 @@ testoffset("static", function( jQuery ) {
                equals( jQuery( this.id ).offset().top,  this.top,  "jQuery('" + this.id + "').offset().top" );
                equals( jQuery( this.id ).offset().left, this.left, "jQuery('" + this.id + "').offset().left" );
        });
-       
-       
+
+
        // get position
        tests = [
                { id: '#static-1',     top: ie ?   5 :   6, left:  6 },
@@ -227,8 +227,8 @@ testoffset("static", function( jQuery ) {
                equals( jQuery( this.id ).position().top,  this.top,  "jQuery('" + this.top  + "').position().top" );
                equals( jQuery( this.id ).position().left, this.left, "jQuery('" + this.left +"').position().left" );
        });
-       
-       
+
+
        // set offset
        tests = [
                { id: '#static-2',     top: 200, left: 200 },
@@ -252,7 +252,7 @@ testoffset("static", function( jQuery ) {
                jQuery( this.id ).offset({ top: this.top, left: this.left });
                equals( jQuery( this.id ).offset().top,  this.top,  "jQuery('" + this.id + "').offset({ top: "  + this.top  + " })" );
                equals( jQuery( this.id ).offset().left, this.left, "jQuery('" + this.id + "').offset({ left: " + this.left + " })" );
-               
+
                jQuery( this.id ).offset({ top: this.top, left: this.left, using: function( props ) {
                        jQuery( this ).css({
                                top:  props.top  + 1,
@@ -266,9 +266,9 @@ testoffset("static", function( jQuery ) {
 
 testoffset("fixed", function( jQuery ) {
        expect(28);
-       
+
        jQuery.offset.initialize();
-       
+
        var tests = [
                { id: '#fixed-1', top: 1001, left: 1001 },
                { id: '#fixed-2', top: 1021, left: 1021 }
@@ -288,7 +288,7 @@ testoffset("fixed", function( jQuery ) {
                        ok( true, 'Fixed position is not supported' );
                }
        });
-       
+
        tests = [
                { id: '#fixed-1', top: 100, left: 100 },
                { id: '#fixed-1', top:   0, left:   0 },
@@ -297,13 +297,13 @@ testoffset("fixed", function( jQuery ) {
                { id: '#fixed-2', top:   0, left:   0 },
                { id: '#fixed-2', top:  -5, left:  -5 }
        ];
-       
+
        jQuery.each( tests, function() {
                if ( jQuery.offset.supportsFixedPosition ) {
                        jQuery( this.id ).offset({ top: this.top, left: this.left });
                        equals( jQuery( this.id ).offset().top,  this.top,  "jQuery('" + this.id + "').offset({ top: "  + this.top  + " })" );
                        equals( jQuery( this.id ).offset().left, this.left, "jQuery('" + this.id + "').offset({ left: " + this.left + " })" );
-               
+
                        jQuery( this.id ).offset({ top: this.top, left: this.left, using: function( props ) {
                                jQuery( this ).css({
                                        top:  props.top  + 1,
@@ -324,38 +324,38 @@ testoffset("fixed", function( jQuery ) {
 
 testoffset("table", function( jQuery ) {
        expect(4);
-       
+
        equals( jQuery('#table-1').offset().top, 6, "jQuery('#table-1').offset().top" );
        equals( jQuery('#table-1').offset().left, 6, "jQuery('#table-1').offset().left" );
-       
+
        equals( jQuery('#th-1').offset().top, 10, "jQuery('#th-1').offset().top" );
        equals( jQuery('#th-1').offset().left, 10, "jQuery('#th-1').offset().left" );
 });
 
 testoffset("scroll", function( jQuery, win ) {
        expect(16);
-       
+
        var ie = jQuery.browser.msie && parseInt( jQuery.browser.version, 10 ) < 8;
-       
+
        // IE is collapsing the top margin of 1px
        equals( jQuery('#scroll-1').offset().top, ie ? 6 : 7, "jQuery('#scroll-1').offset().top" );
        equals( jQuery('#scroll-1').offset().left, 7, "jQuery('#scroll-1').offset().left" );
-       
+
        // IE is collapsing the top margin of 1px
        equals( jQuery('#scroll-1-1').offset().top, ie ? 9 : 11, "jQuery('#scroll-1-1').offset().top" );
        equals( jQuery('#scroll-1-1').offset().left, 11, "jQuery('#scroll-1-1').offset().left" );
-       
-       
+
+
        // scroll offset tests .scrollTop/Left
        equals( jQuery('#scroll-1').scrollTop(), 5, "jQuery('#scroll-1').scrollTop()" );
        equals( jQuery('#scroll-1').scrollLeft(), 5, "jQuery('#scroll-1').scrollLeft()" );
-       
+
        equals( jQuery('#scroll-1-1').scrollTop(), 0, "jQuery('#scroll-1-1').scrollTop()" );
        equals( jQuery('#scroll-1-1').scrollLeft(), 0, "jQuery('#scroll-1-1').scrollLeft()" );
-       
+
        // equals( jQuery('body').scrollTop(), 0, "jQuery('body').scrollTop()" );
        // equals( jQuery('body').scrollLeft(), 0, "jQuery('body').scrollTop()" );
-       
+
        win.name = "test";
 
        if ( !supportsScroll ) {
@@ -367,11 +367,11 @@ testoffset("scroll", function( jQuery, win ) {
        } else {
                equals( jQuery(win).scrollTop(), 1000, "jQuery(window).scrollTop()" );
                equals( jQuery(win).scrollLeft(), 1000, "jQuery(window).scrollLeft()" );
-       
+
                equals( jQuery(win.document).scrollTop(), 1000, "jQuery(document).scrollTop()" );
                equals( jQuery(win.document).scrollLeft(), 1000, "jQuery(document).scrollLeft()" );
        }
-       
+
        // test jQuery using parent window/document
        // jQuery reference here is in the iframe
        window.scrollTo(0,0);
@@ -383,7 +383,7 @@ testoffset("scroll", function( jQuery, win ) {
 
 testoffset("body", function( jQuery ) {
        expect(2);
-       
+
        equals( jQuery('body').offset().top, 1, "jQuery('#body').offset().top" );
        equals( jQuery('body').offset().left, 1, "jQuery('#body').offset().left" );
 });
@@ -423,11 +423,11 @@ test("offsetParent", function(){
 });
 
 function testoffset(name, fn) {
-       
+
        test(name, function() {
                // pause execution for now
                stop();
-               
+
                // load fixture in iframe
                var iframe = loadFixture(),
                        win = iframe.contentWindow,
@@ -443,7 +443,7 @@ function testoffset(name, fn) {
                                }
                        }, 15 );
        });
-       
+
        function loadFixture() {
                var src = './data/offset/' + name + '.html?' + parseInt( Math.random()*1000, 10 ),
                        iframe = jQuery('<iframe />').css({
index 79b753c..31e587d 100644 (file)
@@ -1,11 +1,11 @@
-module("queue");
+module("queue", { teardown: moduleTeardown });
 
 test("queue() with other types",function() {
        expect(9);
        var counter = 0;
-       
+
        var $div = jQuery({});
-       
+
        $div
                .queue('foo',function(){
                        equals( ++counter, 1, "Dequeuing" );
@@ -21,26 +21,26 @@ test("queue() with other types",function() {
                .queue('foo',function(){
                        equals( ++counter, 4, "Dequeuing" );
                });
-               
+
        equals( $div.queue('foo').length, 4, "Testing queue length" );
-       
+
        $div.dequeue('foo');
-       
+
        equals( counter, 3, "Testing previous call to dequeue" );
        equals( $div.queue('foo').length, 1, "Testing queue length" );
-       
+
        $div.dequeue('foo');
-       
+
        equals( counter, 4, "Testing previous call to dequeue" );
        equals( $div.queue('foo').length, 0, "Testing queue length" );
 });
 
 test("queue(name) passes in the next item in the queue as a parameter", function() {
        expect(2);
-       
+
        var div = jQuery({});
        var counter = 0;
-       
+
        div.queue("foo", function(next) {
                equals(++counter, 1, "Dequeueing");
                next();
@@ -50,16 +50,16 @@ test("queue(name) passes in the next item in the queue as a parameter", function
        }).queue("bar", function() {
                equals(++counter, 3, "Other queues are not triggered by next()")
        });
-       
+
        div.dequeue("foo");
 });
 
 test("queue(name) passes in the next item in the queue as a parameter", function() {
        expect(2);
-       
+
        var div = jQuery({});
        var counter = 0;
-       
+
        div.queue("foo", function(next) {
                equals(++counter, 1, "Dequeueing");
                next();
@@ -69,17 +69,17 @@ test("queue(name) passes in the next item in the queue as a parameter", function
        }).queue("bar", function() {
                equals(++counter, 3, "Other queues are not triggered by next()")
        });
-       
+
        div.dequeue("foo");
 });
 
 test("queue() passes in the next item in the queue as a parameter to fx queues", function() {
        expect(2);
        stop();
-       
+
        var div = jQuery({});
        var counter = 0;
-       
+
        div.queue(function(next) {
                equals(++counter, 1, "Dequeueing");
                var self = this;
@@ -111,10 +111,10 @@ test("delay()", function() {
 
 test("clearQueue(name) clears the queue", function() {
        expect(1);
-       
+
        var div = jQuery({});
        var counter = 0;
-       
+
        div.queue("foo", function(next) {
                counter++;
                jQuery(this).clearQueue("foo");
@@ -122,18 +122,18 @@ test("clearQueue(name) clears the queue", function() {
        }).queue("foo", function(next) {
                counter++;
        });
-       
+
        div.dequeue("foo");
-       
+
        equals(counter, 1, "the queue was cleared");
 });
 
 test("clearQueue() clears the fx queue", function() {
        expect(1);
-       
+
        var div = jQuery({});
        var counter = 0;
-       
+
        div.queue(function(next) {
                counter++;
                var self = this;
@@ -141,8 +141,8 @@ test("clearQueue() clears the fx queue", function() {
        }).queue(function(next) {
                counter++;
        });
-       
+
        equals(counter, 1, "the queue was cleared");
-       
+
        div.removeData();
 });
index 6ec20bc..6a38325 100644 (file)
@@ -1,4 +1,4 @@
-module("selector");
+module("selector", { teardown: moduleTeardown });
 
 test("element", function() {
        expect(21);
@@ -22,7 +22,7 @@ test("element", function() {
        same( jQuery("div").find("p").get(), q("firstp","ap","sndp","en","sap","first"), "Finding elements with a context." );
 
        same( jQuery("#form").find("select").get(), q("select1","select2","select3","select4","select5"), "Finding selects with a context." );
-       
+
        ok( jQuery("#length").length, '&lt;input name="length"&gt; cannot be found under IE, see #945' );
        ok( jQuery("#lengthtest input").length, '&lt;input name="length"&gt; cannot be found under IE, see #945' );
 
@@ -58,17 +58,18 @@ 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 );
                }
        }
-       
+
        broken( "Broken Selector", "[", [] );
        broken( "Broken Selector", "(", [] );
        broken( "Broken 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", [] );
@@ -90,32 +112,35 @@ test("id", function() {
        t( "Multiple ID selectors using UTF8", "#台北Táiběi, #台北", ["台北Táiběi","台北"] );
        t( "Descendant ID selector using UTF8", "div #台北", ["台北"] );
        t( "Child ID selector using UTF8", "form > #台北", ["台北"] );
-       
+
        t( "Escaped ID", "#foo\\:bar", ["foo:bar"] );
        t( "Escaped ID", "#test\\.foo\\[5\\]bar", ["test.foo[5]bar"] );
        t( "Descendant escaped ID", "div #foo\\:bar", ["foo:bar"] );
        t( "Descendant escaped ID", "div #test\\.foo\\[5\\]bar", ["test.foo[5]bar"] );
        t( "Child escaped ID", "form > #foo\\:bar", ["foo:bar"] );
        t( "Child escaped ID", "form > #test\\.foo\\[5\\]bar", ["test.foo[5]bar"] );
-       
+
        t( "ID Selector, child ID present", "#form > #radio1", ["radio1"] ); // bug #267
        t( "ID Selector, not an ancestor ID", "#form #first", [] );
        t( "ID Selector, not a child ID", "#form > #option1a", [] );
-       
+
        t( "All Children of ID", "#foo > *", ["sndp", "en", "sap"] );
        t( "All Children of ID with no children", "#firstUL > *", [] );
-       
+
        var a = jQuery('<div><a name="tName1">tName1 A</a><a name="tName2">tName2 A</a><div id="tName1">tName1 Div</div></div>').appendTo('#main');
        equals( jQuery("#tName1")[0].id, 'tName1', "ID selector with same value for a name attribute" );
        equals( jQuery("#tName2").length, 0, "ID selector non-existing but name attribute on an A tag" );
        a.remove();
 
        t( "ID Selector on Form with an input that has a name of 'id'", "#lengthtest", ["lengthtest"] );
-       
+
        t( "ID selector with non-existant ancestor", "#asdfasdf #foobar", [] ); // bug #986
 
        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"] );
 
@@ -134,7 +159,7 @@ test("class", function() {
        same( jQuery(".blog", "p").get(), q("mark", "simon"), "Finding elements with a context." );
        same( jQuery(".blog", jQuery("p")).get(), q("mark", "simon"), "Finding elements with a context." );
        same( jQuery("p").find(".blog").get(), q("mark", "simon"), "Finding elements with a context." );
-       
+
        t( "Class selector using UTF8", ".台北Táiběi", ["utf8class1"] );
        //t( "Class selector using UTF8", ".台北", ["utf8class1","utf8class2"] );
        t( "Class selector using UTF8", ".台北Táiběi.台北", ["utf8class1"] );
@@ -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";
@@ -194,7 +219,7 @@ test("name", function() {
 
 test("multiple", function() {
        expect(4);
-       
+
        t( "Comma Support", "h2, p", ["qunit-banner","qunit-userAgent","firstp","ap","sndp","en","sap","first"]);
        t( "Comma Support", "h2 , p", ["qunit-banner","qunit-userAgent","firstp","ap","sndp","en","sap","first"]);
        t( "Comma Support", "h2 , p", ["qunit-banner","qunit-userAgent","firstp","ap","sndp","en","sap","first"]);
@@ -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,12 +267,13 @@ 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"] );
        t( "Attribute Exists", "a[ title ]", ["google"] );
-       
+
        t( "Attribute Equals", "a[rel='bookmark']", ["simon1"] );
        t( "Attribute Equals", 'a[rel="bookmark"]', ["simon1"] );
        t( "Attribute Equals", "a[rel=bookmark]", ["simon1"] );
@@ -263,13 +294,13 @@ test("attributes", function() {
        t( "Attribute containing []", "input[name$='[bar]']", ["hidden2"] );
        t( "Attribute containing []", "input[name$='foo[bar]']", ["hidden2"] );
        t( "Attribute containing []", "input[name*='foo[bar]']", ["hidden2"] );
-       
+
        t( "Multiple Attribute Equals", "#form input[type='radio'], #form input[type='hidden']", ["radio1", "radio2", "hidden1"] );
        t( "Multiple Attribute Equals", "#form input[type='radio'], #form input[type=\"hidden\"]", ["radio1", "radio2", "hidden1"] );
        t( "Multiple Attribute Equals", "#form input[type='radio'], #form input[type=hidden]", ["radio1", "radio2", "hidden1"] );
-       
+
        t( "Attribute selector using UTF8", "span[lang=中文]", ["台北"] );
-       
+
        t( "Attribute Begins With", "a[href ^= 'http://www']", ["google","yahoo"] );
        t( "Attribute Ends With", "a[href $= 'org/']", ["mark"] );
        t( "Attribute Contains", "a[href *= 'google']", ["google","groups"] );
@@ -288,16 +319,24 @@ test("attributes", function() {
 
        t("Empty values", "#select1 option[value='']", ["option1a"]);
        t("Empty values", "#select1 option[value!='']", ["option1b","option1c","option1d"]);
-       
+
        t("Select options via :selected", "#select1 option:selected", ["option1a"] );
        t("Select options via :selected", "#select2 option:selected", ["option2d"] );
        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
@@ -315,22 +355,26 @@ test("pseudo - child", function() {
        t( "First Child", "p:first-child", [] );
 
        QUnit.reset();
-       
+
        t( "Last Child", "p:last-child", ["sap"] );
        t( "Last Child", "#main a:last-child", ["simon1","anchor1","mark","yahoo","anchor2","simon","liveLink1","liveLink2"] );
-       
+
        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() {
@@ -347,7 +393,7 @@ test("pseudo - misc", function() {
 
        t( "Headers", ":header", ["qunit-header", "qunit-banner", "qunit-userAgent"] );
        t( "Has Children - :has()", "p:has(a)", ["firstp","ap","en","sap"] );
-       
+
        var select = document.getElementById("select1");
        ok( (window.Sizzle || window.jQuery.find).matchesSelector( select, ":has(option)" ), "Has Option Matches" );
 
@@ -393,7 +439,7 @@ test("pseudo - :not", function() {
        t( ":not() Multiple Class", "#foo a:not(.blog.link)", ["yahoo","anchor2"] );
 });
 
-test("pseudo - position", function() { 
+test("pseudo - position", function() {
        expect(25);
        t( "nth Element", "p:nth(1)", ["ap"] );
        t( "First Element", "p:first", ["firstp"] );
index e4b3e95..56fed22 100644 (file)
@@ -1,4 +1,4 @@
-module("traversing");
+module("traversing", { teardown: moduleTeardown });
 
 test("find(String)", function() {
        expect(5);
@@ -138,7 +138,7 @@ test("closest()", function() {
        same( jq.closest("html", document.body).get(), [], "Context limited." );
        same( jq.closest("body", document.body).get(), [], "Context limited." );
        same( jq.closest("#nothiddendiv", document.body).get(), q("nothiddendiv"), "Context not reached." );
-       
+
        //Test that .closest() returns unique'd set
        equals( jQuery('#main p').closest('#main').length, 1, "Closest should return a unique set" );
 
@@ -275,9 +275,9 @@ test("parents([String])", function() {
 
 test("parentsUntil([String])", function() {
        expect(9);
-       
+
        var parents = jQuery("#groups").parents();
-       
+
        same( jQuery("#groups").parentsUntil().get(), parents.get(), "parentsUntil with no selector (nextAll)" );
        same( jQuery("#groups").parentsUntil(".foo").get(), parents.get(), "parentsUntil with invalid selector (nextAll)" );
        same( jQuery("#groups").parentsUntil("#html").get(), parents.not(':last').get(), "Simple parentsUntil check" );
@@ -307,9 +307,9 @@ test("prev([String])", function() {
 
 test("nextAll([String])", function() {
        expect(4);
-       
+
        var elems = jQuery('#form').children();
-       
+
        same( jQuery("#label-for").nextAll().get(), elems.not(':first').get(), "Simple nextAll check" );
        same( jQuery("#label-for").nextAll('input').get(), elems.not(':first').filter('input').get(), "Filtered nextAll check" );
        same( jQuery("#label-for").nextAll('input,select').get(), elems.not(':first').filter('input,select').get(), "Multiple-filtered nextAll check" );
@@ -318,9 +318,9 @@ test("nextAll([String])", function() {
 
 test("prevAll([String])", function() {
        expect(4);
-       
+
        var elems = jQuery( jQuery('#form').children().slice(0, 12).get().reverse() );
-       
+
        same( jQuery("#area1").prevAll().get(), elems.get(), "Simple prevAll check" );
        same( jQuery("#area1").prevAll('input').get(), elems.filter('input').get(), "Filtered prevAll check" );
        same( jQuery("#area1").prevAll('input,select').get(), elems.filter('input,select').get(), "Multiple-filtered prevAll check" );
@@ -329,9 +329,9 @@ test("prevAll([String])", function() {
 
 test("nextUntil([String])", function() {
        expect(11);
-       
+
        var elems = jQuery('#form').children().slice( 2, 12 );
-       
+
        same( jQuery("#text1").nextUntil().get(), jQuery("#text1").nextAll().get(), "nextUntil with no selector (nextAll)" );
        same( jQuery("#text1").nextUntil(".foo").get(), jQuery("#text1").nextAll().get(), "nextUntil with invalid selector (nextAll)" );
        same( jQuery("#text1").nextUntil("#area1").get(), elems.get(), "Simple nextUntil check" );
@@ -342,15 +342,15 @@ test("nextUntil([String])", function() {
        same( jQuery("#text1").nextUntil("#area1", "button,input").get(), elems.get(), "Multiple-filtered nextUntil check" );
        equals( jQuery("#text1").nextUntil("#area1", "div").length, 0, "Filtered nextUntil check, no match" );
        same( jQuery("#text1, #hidden1").nextUntil("#area1", "button,input").get(), elems.get(), "Multi-source, multiple-filtered nextUntil check" );
-       
+
        same( jQuery("#text1").nextUntil("[class=foo]").get(), jQuery("#text1").nextAll().get(), "Non-element nodes must be skipped, since they have no attributes" );
 });
 
 test("prevUntil([String])", function() {
        expect(10);
-       
+
        var elems = jQuery("#area1").prevAll();
-       
+
        same( jQuery("#area1").prevUntil().get(), elems.get(), "prevUntil with no selector (prevAll)" );
        same( jQuery("#area1").prevUntil(".foo").get(), elems.get(), "prevUntil with invalid selector (prevAll)" );
        same( jQuery("#area1").prevUntil("label").get(), elems.not(':last').get(), "Simple prevUntil check" );
index 7810cd6..574f968 100644 (file)
@@ -1 +1 @@
-1.4.5pre
\ No newline at end of file
+1.5pre
\ No newline at end of file