Added test for #174; Modified failing (IE) tests for find
[jquery.git] / src / jquery / jquery.js
index 5c69caa..7a59a4e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * jQuery - New Wave Javascript
+ * jQuery @VERSION - New Wave Javascript
  *
  * Copyright (c) 2006 John Resig (jquery.com)
  * Dual licensed under the MIT (MIT-LICENSE.txt)
@@ -50,8 +50,10 @@ jQuery = function(a,c) {
                return new jQuery(a,c);
 
        // Handle HTML strings
-       var m = /^[^<]*(<.+>)[^>]*$/.exec(a);
-       if ( m ) a = jQuery.clean( [ m[1] ] );
+       if ( a.constructor == String ) {
+               var m = /^[^<]*(<.+>)[^>]*$/.exec(a);
+               if ( m ) a = jQuery.clean( [ m[1] ] );
+       }
 
        // Watch for when an array is passed in
        this.get( a.constructor == Array || a.length && !a.nodeType && a[0] != undefined && a[0].nodeType ?
@@ -95,7 +97,10 @@ if ( typeof $ != "undefined" )
  * @result [ <p>two</p> ]
  *
  * @example $("<div><p>Hello</p></div>").appendTo("#body")
- * @desc Creates a div element (and all of its contents) dynamically, and appends it to the element with the ID of body.
+ * @desc Creates a div element (and all of its contents) dynamically, 
+ * and appends it to the element with the ID of body. Internally, an
+ * element is created and it's innerHTML property set to the given markup.
+ * It is therefore both quite flexible and limited. 
  *
  * @name $
  * @param String expr An expression to search with, or a string of HTML to create on the fly.
@@ -154,6 +159,7 @@ if ( typeof $ != "undefined" )
  * behaves just like $(document).ready(), in that it should be used to wrap
  * all of the other $() operations on your page. While this function is,
  * technically, chainable - there really isn't much use for chaining against it.
+ * You can have as many $(document).ready events on your page as you like.
  *
  * @example $(function(){
  *   // Document is ready
@@ -186,7 +192,7 @@ var $ = jQuery;
 
 jQuery.fn = jQuery.prototype = {
        /**
-        * The current SVN version of jQuery.
+        * The current version of jQuery.
         *
         * @private
         * @property
@@ -194,7 +200,7 @@ jQuery.fn = jQuery.prototype = {
         * @type String
         * @cat Core
         */
-       jquery: "$Rev$",
+       jquery: "@VERSION",
 
        /**
         * The number of elements currently matched.
@@ -391,8 +397,8 @@ jQuery.fn = jQuery.prototype = {
         * ok( $('#simon').attr('class') == "blog link", 'Check for class attribute' );
         * ok( $('#name').attr('name') == "name", 'Check for name attribute' );
         * ok( $('#text1').attr('name') == "action", 'Check for name attribute' );
-        * ok( $('#form').attr('action') == "formaction", 'Check for action attribute' );
-        * 
+        * ok( $('#form').attr('action').indexOf("formaction") >= 0, 'Check for action attribute' );
+        *
         * @name attr
         * @type Object
         * @param String name The name of the property to access.
@@ -442,6 +448,17 @@ jQuery.fn = jQuery.prototype = {
         * $("#check2").attr('checked', false);
         * ok( document.getElementById('check2').checked == false, 'Set checked attribute' );
         *
+        * @test stop();
+        * $.get('data/dashboard.xml', function(xml) { 
+        *   var titles = [];
+        *   $('tab', xml).each(function() {
+        *     titles.push($(this).attr('title'));
+        *   });
+        *   ok( titles[0] == 'Location', 'attr() in XML context: Check first title' );
+        *   ok( titles[1] == 'Users', 'attr() in XML context: Check second title' );
+        *   start();
+        * });
+        *
         * @name attr
         * @type jQuery
         * @param String key The name of the property to set.
@@ -493,7 +510,7 @@ jQuery.fn = jQuery.prototype = {
         * representation of itself. Eg. fontWeight, fontSize, fontFamily, borderWidth,
         * borderStyle, borderBottomWidth etc.
         *
-        * @test ok( $('#foo').css("display") == 'block', 'Check for css property "display"');
+        * @test ok( $('#main').css("display") == 'none', 'Check for css property "display"');
         *
         * @name css
         * @type Object
@@ -644,7 +661,7 @@ jQuery.fn = jQuery.prototype = {
                        // Insert it before the element to be wrapped
                        this.parentNode.insertBefore( b, this );
 
-                       // Find he deepest point in the wrap structure
+                       // Find the deepest point in the wrap structure
                        while ( b.firstChild )
                                b = b.firstChild;
 
@@ -976,6 +993,12 @@ jQuery.fn = jQuery.prototype = {
         * @result $("p").filter(".selected") == [ <p class="selected">Hello</p> ]
         *
         * @test isSet( $("input").filter(":checked").get(), q("radio2", "check1"), "Filter elements" );
+        * @test $("input").filter(":checked",function(i){ 
+        *   ok( this == q("radio2", "check1")[i], "Filter elements, context" );
+        * });
+        * @test $("#main > p#ap > a").filter("#foobar",function(){},function(i){
+        *   ok( this == q("google","groups", "mark")[i], "Filter elements, else context" );
+        * });
         *
         * @name filter
         * @type jQuery
@@ -1154,52 +1177,6 @@ jQuery.fn = jQuery.prototype = {
        },
        
        /**
-        * Executes the first callback for every element that fits the expression
-        * and executes the second callback for every element that does not fit
-        * the expression.
-        *
-        * @example $('div').ifelse(':visible',
-     *   function() { $(this).slideUp(); },
-         function() { $(this).slideDown(); }
-     * );
-        * @desc Slides down all visible div elements and slides down all others
-        * 
-        * @test var checked = 0, notChecked = 0;
-        * var inputChecked = $(':input').ifelse(':checked',
-        *   function() { checked++; },
-        *   function() { notChecked++ }
-        * );
-        * ok( checked == 2, 'Check is if/else: Count checked elements' );
-        * ok( notChecked == 12, 'Check is if/else: Count unchecked elements' );
-        *
-        * $('#first, #foo, #ap').ifelse('p',
-        *   function() { $(this).html('me == p') },
-        *   function() { $(this).html('me != p') }
-        * );
-        * ok( $('#first').text() == 'me == p', 'Check filter-if-clause' );
-        * ok( $('#foo').text() == 'me != p', 'Check else-clause' );
-        * ok( $('#ap').text() == 'me == p', 'Check filter-if-clause' );
-        *
-        * @name ifelse
-        * @type jQuery
-        * @param String expression The expression with which to filter
-        * @param Function ifCallback Called for elements that fit the expression
-        * @param Function elseCallback Called for elements that don't fit the expression
-        * @cat DOM/Traversing
-        */
-       ifelse: function(expr, ifCallback, elseCallback) {
-               var ifCallback = ifCallback || function() {};
-        var elseCalllback = elseCallback || function() {};
-        return this.each(function() {
-            if($(this).is(expr)) {
-                ifCallback.apply(this);
-            } else {
-                elseCallback.apply(this);
-            }
-        });
-       },
-
-       /**
         *
         *
         * @private
@@ -1247,17 +1224,23 @@ jQuery.fn = jQuery.prototype = {
         */
        pushStack: function(a,args) {
                var fn = args && args[args.length-1];
+               var fn2 = args && args[args.length-2];
+               
+               if ( fn && fn.constructor != Function ) fn = null;
+               if ( fn2 && fn2.constructor != Function ) fn2 = null;
 
-               if ( !fn || fn.constructor != Function ) {
+               if ( !fn ) {
                        if ( !this.stack ) this.stack = [];
                        this.stack.push( this.get() );
                        this.get( a );
                } else {
                        var old = this.get();
                        this.get( a );
-                       if ( typeof fn == "function" )
-                               this.each( fn );
-                       this.get( old );
+
+                       if ( fn2 && a.length || !fn2 )
+                               this.each( fn2 || fn ).get( old );
+                       else
+                               this.get( old ).each( fn );
                }
 
                return this;
@@ -1404,7 +1387,7 @@ jQuery.extend({
                                fn.apply( obj[i], args || [i, obj[i]] );
                else
                        for ( var i = 0; i < obj.length; i++ )
-                               fn.apply( obj[i], args || [i, obj[i]] );
+                               if ( fn.apply( obj[i], args || [i, obj[i]] ) === false ) break;
                return obj;
        },
 
@@ -1414,10 +1397,6 @@ jQuery.extend({
                        o.className += ( o.className ? " " : "" ) + c;
                },
                remove: function(o,c){
-                       /*
-                       o.className = !c ? "" :
-                               o.className.replace(
-                                       new RegExp("(^|\\s*\\b[^-])"+c+"($|\\b(?=[^-]))", "g"), "");*/
                        if( !c ) {
                                o.className = "";
                        } else {
@@ -1485,14 +1464,19 @@ jQuery.extend({
                        });
 
                        return p == "height" ? oHeight : oWidth;
-               } else if ( p == "opacity" && jQuery.browser.msie )
-                       return parseFloat( jQuery.curCSS(e,"filter").replace(/[^0-9.]/,"") ) || 1;
+               }
 
                return jQuery.curCSS( e, p );
        },
 
        curCSS: function(elem, prop, force) {
                var ret;
+               
+               if (prop == 'opacity' && jQuery.browser.msie)
+                       return jQuery.attr(elem.style, 'opacity');
+                       
+               if (prop == "float" || prop == "cssFloat")
+                   prop = jQuery.browser.msie ? "styleFloat" : "cssFloat";
 
                if (!force && elem.style[prop]) {
 
@@ -1526,7 +1510,9 @@ jQuery.extend({
                var r = [];
                for ( var i = 0; i < a.length; i++ ) {
                        if ( a[i].constructor == String ) {
-
+                               // trim whitespace, otherwise indexOf won't work as expected
+                               a[i] = jQuery.trim(a[i]);
+                               
                                var table = "";
 
                                if ( !a[i].indexOf("<thead") || !a[i].indexOf("<tbody") ) {
@@ -1585,7 +1571,7 @@ jQuery.extend({
                        empty: "!a.childNodes.length",
 
                        // Text Check
-                       contains: "(a.innerText||a.innerHTML).indexOf(m[3])>=0",
+                       contains: "jQuery.fn.text.apply([a]).indexOf(m[3])>=0",
 
                        // Visibility
                        visible: "a.type!='hidden'&&jQuery.css(a,'display')!='none'&&jQuery.css(a,'visibility')!='hidden'",
@@ -1595,7 +1581,7 @@ jQuery.extend({
                        enabled: "!a.disabled",
                        disabled: "a.disabled",
                        checked: "a.checked",
-                       selected: "a.selected",
+                       selected: "a.selected || jQuery.attr(a, 'selected')",
 
                        // Form elements
                        text: "a.type=='text'",
@@ -1675,9 +1661,9 @@ jQuery.extend({
         * t( "Attribute Exists", "*[@title]", ["google"] );
         * t( "Attribute Exists", "[@title]", ["google"] );
         * 
-        * t( "Non-existing part of attribute [@name*=bla]", "[@name*=bla]", [] ); 
-        * t( "Non-existing start of attribute [@name^=bla]", "[@name^=bla]", [] ); 
-        * t( "Non-existing end of attribute [@name$=bla]", "[@name$=bla]", [] ); 
+        * t( "Non-existing part of attribute", "[@name*=bla]", [] ); 
+        * t( "Non-existing start of attribute", "[@name^=bla]", [] ); 
+        * t( "Non-existing end of attribute", "[@name$=bla]", [] ); 
         *
         * t( "Attribute Equals", "a[@rel='bookmark']", ["simon1"] );
         * t( "Attribute Equals", 'a[@rel="bookmark"]', ["simon1"] );
@@ -1854,22 +1840,39 @@ jQuery.extend({
                var fix = {
                        "for": "htmlFor",
                        "class": "className",
-                       "float": "cssFloat",
+                       "float": jQuery.browser.msie ? "styleFloat" : "cssFloat",
+                       cssFloat: jQuery.browser.msie ? "styleFloat" : "cssFloat",
                        innerHTML: "innerHTML",
                        className: "className",
                        value: "value",
                        disabled: "disabled",
                        checked: "checked"
                };
+               
+               // IE actually uses filters for opacity ... elem is actually elem.style
+               if (name == "opacity" && jQuery.browser.msie && value != undefined) {
+                       // IE has trouble with opacity if it does not have layout
+                       // Would prefer to check element.hasLayout first but don't have access to the element here
+                       elem['zoom'] = 1; 
+                       if (value == 1) // Remove filter to avoid more IE weirdness
+                               return elem["filter"] = elem["filter"].replace(/alpha\([^\)]*\)/gi,"");
+                       else
+                               return elem["filter"] = elem["filter"].replace(/alpha\([^\)]*\)/gi,"") + "alpha(opacity=" + value * 100 + ")";
+               } else if (name == "opacity" && jQuery.browser.msie) {
+                       return elem["filter"] ? parseFloat( elem["filter"].match(/alpha\(opacity=(.*)\)/)[1] )/100 : 1;
+               }
+               
+               // Mozilla doesn't play well with opacity 1
+               if (name == "opacity" && jQuery.browser.mozilla && value == 1) value = 0.9999;
 
                if ( fix[name] ) {
                        if ( value != undefined ) elem[fix[name]] = value;
                        return elem[fix[name]];
-               } else if( value == undefined && $.browser.msie && elem.nodeName && elem.nodeName.toUpperCase() == 'FORM' && (name == 'action' || name == 'method') ) {
+               } else if( value == undefined && jQuery.browser.msie && elem.nodeName && elem.nodeName.toUpperCase() == 'FORM' && (name == 'action' || name == 'method') ) {
                        return elem.getAttributeNode(name).nodeValue;
-               } else if ( elem.getAttribute != undefined ) {
+               } else if ( elem.getAttribute != undefined && elem.tagName ) { // IE elem.getAttribute passes even for style
                        if ( value != undefined ) elem.setAttribute( name, value );
-                       return elem.getAttribute( name, 2 );
+                       return elem.getAttribute( name );
                } else {
                        name = name.replace(/-([a-z])/ig,function(z,b){return b.toUpperCase();});
                        if ( value != undefined ) elem[name] = value;
@@ -1880,13 +1883,13 @@ jQuery.extend({
        // The regular expressions that power the parsing engine
        parse: [
                // Match: [@value='test'], [@foo]
-               "\\[ *(@)S *([!*$^=]*)Q\\]",
+               "\\[ *(@)S *([!*$^=]*) *('?\"?)(.*?)\\4 *\\]",
 
                // Match: [div], [div p]
-               "(\\[)Q\\]",
+               "(\\[)\s*(.*?)\s*\\]",
 
                // Match: :contains('foo')
-               "(:)S\\(Q\\)",
+               "(:)S\\(\"?'?([^\\)]*?)\"?'?\\)",
 
                // Match: :even, :last-chlid
                "([:.#]*)S"
@@ -1902,28 +1905,19 @@ jQuery.extend({
                        var p = jQuery.parse;
 
                        for ( var i = 0; i < p.length; i++ ) {
-                               // get number for backreference
-                               var br = 0;
-                               if(p[i].indexOf('Q') != -1){
-                                       br = p[i].replace(/\\\(/g,'').match(/\(|S/g).length+1;
-                               }
-                               var re = new RegExp( "^" + p[i]
-                       
-                                       // Look for a string-like sequence
-                                       .replace( 'S', "([a-z*_-][a-z0-9_-]*)" )
-
-                                       // Look for something (optionally) enclosed with quotes
-                                       .replace( 'Q', " *('|\"|)([^'\"]*?)\\"+br+" *" ), "i" );
+               
+                               // Look for, and replace, string-like sequences
+                               // and finally build a regexp out of it
+                               var re = new RegExp(
+                                       "^" + p[i].replace("S", "([a-z*_-][a-z0-9_-]*)"), "i" );
 
                                var m = re.exec( t );
 
                                if ( m ) {
-                                       // Re-organize the match
-                                       if(br == 4){
+                                       // Re-organize the first match
+                                       if ( !i )
                                                m = ["",m[1], m[3], m[2], m[5]];
-                                       } else if(br != 0) {
-                                               m.splice(br,1);
-                                       }
+
                                        // Remove what we just matched
                                        t = t.replace( re, "" );
 
@@ -2700,7 +2694,7 @@ jQuery.macros = {
                /**
                 * Set the value of every matched element.
                 *
-                * @example $("input").value("test");
+                * @example $("input").val("test");
                 * @before <input type="text" value="some text"/>
                 * @result <input type="text" value="test"/>
                 *