* $Rev$
*/
-// Global undefined variable
-window.undefined = window.undefined;
-
/**
* Create a new jQuery Object
*
* @param jQuery|Element|Array<Element> c context
* @cat Core
*/
-var jQuery = function(a,c) {
+window.jQuery = function(a,c) {
// If the context is global, return a new object
- if ( window == this )
+ if ( window == this || !this.init )
return new jQuery(a,c);
-
- // Make sure that a selection was provided
- a = a || document;
-
- // HANDLE: $(function)
- // Shortcut for document ready
- if ( jQuery.isFunction(a) )
- return new jQuery(document)[ jQuery.fn.ready ? "ready" : "load" ]( a );
-
- // Handle HTML strings
- if ( typeof a == "string" ) {
- // HANDLE: $(html) -> $(array)
- var m = /^[^<]*(<(.|\s)+>)[^>]*$/.exec(a);
- if ( m )
- a = jQuery.clean( [ m[1] ] );
-
- // HANDLE: $(expr)
- else
- return new jQuery( c ).find( a );
- }
- return this.setArray(
- // HANDLE: $(array)
- a.constructor == Array && a ||
-
- // HANDLE: $(arraylike)
- // Watch for when an array-like object is passed as the selector
- (a.jquery || a.length && a != window && !a.nodeType && a[0] != undefined && a[0].nodeType) && jQuery.makeArray( a ) ||
-
- // HANDLE: $(*)
- [ a ] );
+ return this.init(a,c);
};
// Map over the $ in case of overwrite
jQuery._$ = $;
// Map the jQuery namespace to the '$' one
-var $ = jQuery;
+window.$ = jQuery;
/**
* This function accepts a string containing a CSS or
jQuery.fn = jQuery.prototype = {
/**
+ * Initialize a new jQuery object
+ *
+ * @private
+ * @name init
+ * @param String|Function|Element|Array<Element>|jQuery a selector
+ * @param jQuery|Element|Array<Element> c context
+ * @cat Core
+ */
+ init: function(a,c) {
+ // Make sure that a selection was provided
+ a = a || document;
+
+ // HANDLE: $(function)
+ // Shortcut for document ready
+ if ( jQuery.isFunction(a) )
+ return new jQuery(document)[ jQuery.fn.ready ? "ready" : "load" ]( a );
+
+ // Handle HTML strings
+ if ( typeof a == "string" ) {
+ // HANDLE: $(html) -> $(array)
+ var m = /^[^<]*(<(.|\s)+>)[^>]*$/.exec(a);
+ if ( m )
+ a = jQuery.clean( [ m[1] ] );
+
+ // HANDLE: $(expr)
+ else
+ return new jQuery( c ).find( a );
+ }
+
+ return this.setArray(
+ // HANDLE: $(array)
+ a.constructor == Array && a ||
+
+ // HANDLE: $(arraylike)
+ // Watch for when an array-like object is passed as the selector
+ (a.jquery || a.length && a != window && !a.nodeType && a[0] != undefined && a[0].nodeType) && jQuery.makeArray( a ) ||
+
+ // HANDLE: $(*)
+ [ a ] );
+ },
+
+ /**
* The current version of jQuery.
*
* @private
*/
setArray: function( a ) {
this.length = 0;
- [].push.apply( this, a );
+ Array.prototype.push.apply( this, a );
return this;
},
* @cat DOM/Attributes
*/
text: function(e) {
- if ( typeof e == "string" )
+ if ( typeof e != "object" && e != null )
return this.empty().append( document.createTextNode( e ) );
var t = "";
/**
* Searches for all elements that match the specified expression.
-
+ *
* This method is a good way to find additional descendant
* elements with which to process.
*
* @cat DOM/Traversing
*/
find: function(t) {
- return this.pushStack( jQuery.unique( jQuery.map( this, function(a){
- return jQuery.find(t,a);
- }) ), t );
+ var data = jQuery.map(this, function(a){ return jQuery.find(t,a); });
+ return this.pushStack( /[^+>] [^+>]/.test( t ) || t.indexOf("..") > -1 ?
+ jQuery.unique( data ) : data );
},
/**
* @cat DOM/Manipulation
*/
clone: function(deep) {
- return this.pushStack( jQuery.map( this, function(a){
- var a = a.cloneNode( deep != undefined ? deep : true );
- a.$events = null; // drop $events expando to avoid firing incorrect events
- return a;
+ deep = deep != undefined ? deep : true;
+ var $this = this.add(this.find("*"));
+ if (jQuery.browser.msie) {
+ // Need to remove events on the element and its descendants
+ $this.each(function() {
+ this._$events = {};
+ for (var type in this.$events)
+ this._$events[type] = jQuery.extend({},this.$events[type]);
+ }).unbind();
+ }
+
+ // Do the clone
+ var r = this.pushStack( jQuery.map( this, function(a){
+ return a.cloneNode( deep );
}) );
+
+ if (jQuery.browser.msie) {
+ $this.each(function() {
+ // Add the events back to the original and its descendants
+ var events = this._$events;
+ for (var type in events)
+ for (var handler in events[type])
+ jQuery.event.add(this, type, events[type][handler], events[type][handler].data);
+ this._$events = null;
+ });
+ }
+
+ // copy form values over
+ if (deep) {
+ var inputs = r.add(r.find('*')).filter('select,input[@type=checkbox]');
+ $this.filter('select,input[@type=checkbox]').each(function(i) {
+ if (this.selectedIndex)
+ inputs[i].selectedIndex = this.selectedIndex;
+ if (this.checked)
+ inputs[i].checked = true;
+ });
+ }
+
+ // Return the cloned set
+ return r;
},
/**
return this.pushStack(
jQuery.isFunction( t ) &&
jQuery.grep(this, function(el, index){
- return t.apply(el, [index])
+ return t.apply(el, [index]);
}) ||
jQuery.multiFilter(t,this) );
( this.length ? this[0].innerHTML : null ) :
this.empty().append( val );
},
+
+ slice: function() {
+ return this.pushStack( Array.prototype.slice.apply( this, arguments ) );
+ },
/**
* @private
obj = this.getElementsByTagName("tbody")[0] || this.appendChild(document.createElement("tbody"));
jQuery.each( a, function(){
- fn.apply( obj, [ clone ? this.cloneNode(true) : this ] );
+ if ( jQuery.nodeName(this, "script") ) {
+ if ( this.src )
+ jQuery.ajax({ url: this.src, async: false, dataType: "script" });
+ else
+ jQuery.globalEval( this.text || this.textContent || this.innerHTML || "" );
+ } else
+ fn.apply( obj, [ clone ? this.cloneNode(true) : this ] );
});
-
});
}
};
/**
* Extend one object with one or more others, returning the original,
* modified, object. This is a great utility for simple inheritance.
- *
- * There is also an optional collision resolution function. Any time the target and
- * merged object both contain a key this function is called. This may be used to create
- * a recursive merge. (See example) Unless you know what this is, you probably don't care.
*
* @example var settings = { validate: false, limit: 5, name: "foo" };
* var options = { validate: true, name: "bar" };
* @result settings == { validate: true, limit: 5, name: "bar" }
* @desc Merge defaults and options, without modifying the defaults
*
- * @example var defaults = { validate: false, limit: 5, name: "foo", nested: {depth: false} };
- * var options = { validate: true, name: "bar", nested: {depth: true} };
- * var collision_resolver_fn = function(target, mergee) {
- * // combine nested Objects, in this case the object being merged takes priority
- * return jQuery.extend({}, target, mergee);
- * }
- * var settings = jQuery.extend({}, collision_resolver_fn, defaults, options);
- * @result settings == { validate: true, limit: 5, name: "bar" }
- * @desc Recursively merge defaults and options, without modifying the defaults
- *
* @name $.extend
* @param Object target The object to extend
- * @param Function (optional) collision resolution function. Hook to extend the merging behavior of $.extend(). See example.
* @param Object prop1 The object that will be merged into the first.
* @param Object propN (optional) More objects to merge into the first
* @type Object
*/
jQuery.extend = jQuery.fn.extend = function() {
// copy reference to target object
- var resolver, prop, target = arguments[0],
- a = 1;
-
+ var target = arguments[0] || {}, a = 1, al = arguments.length, deep = false;
+
+ // Handle a deep copy situation
+ if ( target.constructor == Boolean ) {
+ deep = target;
+ target = arguments[1] || {};
+ }
+
// extend jQuery itself if only one argument is passed
- if ( arguments.length == 1 ) {
+ if ( al == 1 ) {
target = this;
a = 0;
- } else if (jQuery.isFunction(arguments[a])) {
- resolver = arguments[a++];
}
- while (prop = arguments[a++])
- // Extend the base object
- for ( var i in prop ) {
- if (resolver && target[i] && prop[i]) {
- target[i] = resolver(target[i], prop[i]);
- } else {
- target[i] = prop[i];
- }
- }
+ var prop;
+
+ for ( ; a < al; a++ )
+ // Only deal with non-null/undefined values
+ if ( (prop = arguments[a]) != null )
+ // Extend the base object
+ for ( var i in prop ) {
+ // Prevent never-ending loop
+ if ( target == prop[i] )
+ continue;
+
+ // Recurse if we're merging object values
+ if ( deep && typeof prop[i] == 'object' && target[i] )
+ jQuery.extend( target[i], prop[i] );
+
+ // Don't bring in undefined values
+ else if ( prop[i] != undefined )
+ target[i] = prop[i];
+ }
// Return the modified object
return target;
// check if an element is in a XML document
isXMLDoc: function(elem) {
- return elem.tagName && elem.ownerDocument && !elem.ownerDocument.body;
+ return elem.documentElement && !elem.body ||
+ elem.tagName && elem.ownerDocument && !elem.ownerDocument.body;
+ },
+
+ // Evalulates a script in a global context
+ // Evaluates Async. in Safari 2 :-(
+ globalEval: function( data ) {
+ data = jQuery.trim( data );
+ if ( data ) {
+ if ( window.execScript )
+ window.execScript( data );
+ else if ( jQuery.browser.safari )
+ // safari doesn't provide a synchronous global eval
+ window.setTimeout( data, 0 );
+ else
+ eval.call( window, data );
+ }
},
nodeName: function( elem, name ) {
*/
// args is for internal usage only
each: function( obj, fn, args ) {
- if ( obj.length == undefined )
- for ( var i in obj )
- fn.apply( obj[i], args || [i, obj[i]] );
- else
- for ( var i = 0, ol = obj.length; i < ol; i++ )
- if ( fn.apply( obj[i], args || [i, obj[i]] ) === false ) break;
+ if ( args ) {
+ if ( obj.length == undefined )
+ for ( var i in obj )
+ fn.apply( obj[i], args );
+ else
+ for ( var i = 0, ol = obj.length; i < ol; i++ )
+ if ( fn.apply( obj[i], args ) === false ) break;
+
+ // A special, fast, case for the most common use of each
+ } else {
+ if ( obj.length == undefined )
+ for ( var i in obj )
+ fn.call( obj[i], i, obj[i] );
+ else
+ for ( var i = 0, ol = obj.length, val = obj[0];
+ i < ol && fn.call(val,i,val) !== false; val = obj[++i] );
+ }
+
return obj;
},
className: {
// internal only, use addClass("class")
add: function( elem, c ){
- jQuery.each( c.split(/\s+/), function(i, cur){
+ jQuery.each( (c || "").split(/\s+/), function(i, cur){
if ( !jQuery.className.has( elem.className, cur ) )
elem.className += ( elem.className ? " " : "" ) + cur;
});
// internal only, use removeClass("class")
remove: function( elem, c ){
- elem.className = c ?
+ elem.className = c != undefined ?
jQuery.grep( elem.className.split(/\s+/), function(cur){
return !jQuery.className.has( c, cur );
}).join(" ") : "";
},
curCSS: function(elem, prop, force) {
- var ret;
+ var ret, stack = [], swap = [];
+
+ // A helper method for determining if an element's values are broken
+ function color(a){
+ if ( !jQuery.browser.safari )
+ return false;
+
+ var ret = document.defaultView.getComputedStyle(a,null);
+ return !ret || ret.getPropertyValue("color") == "";
+ }
if (prop == "opacity" && jQuery.browser.msie) {
ret = jQuery.attr(elem.style, "opacity");
return ret == "" ? "1" : ret;
}
- if (prop == "float" || prop == "cssFloat")
- prop = jQuery.browser.msie ? "styleFloat" : "cssFloat";
+ if (prop.match(/float/i))
+ prop = styleFloat;
if (!force && elem.style[prop])
ret = elem.style[prop];
else if (document.defaultView && document.defaultView.getComputedStyle) {
- if (prop == "cssFloat" || prop == "styleFloat")
+ if (prop.match(/float/i))
prop = "float";
prop = prop.replace(/([A-Z])/g,"-$1").toLowerCase();
var cur = document.defaultView.getComputedStyle(elem, null);
- if ( cur )
+ if ( cur && !color(elem) )
ret = cur.getPropertyValue(prop);
- else if ( prop == "display" )
- ret = "none";
- else
- jQuery.swap(elem, { display: "block" }, function() {
- var c = document.defaultView.getComputedStyle(this, "");
- ret = c && c.getPropertyValue(prop) || "";
- });
+
+ // If the element isn't reporting its values properly in Safari
+ // then some display: none elements are involved
+ else {
+ // Locate all of the parent display: none elements
+ for ( var a = elem; a && color(a); a = a.parentNode )
+ stack.unshift(a);
+
+ // Go through and make them visible, but in reverse
+ // (It would be better if we knew the exact display type that they had)
+ for ( a = 0; a < stack.length; a++ )
+ if ( color(stack[a]) ) {
+ swap[a] = stack[a].style.display;
+ stack[a].style.display = "block";
+ }
+
+ // Since we flip the display style, we have to handle that
+ // one special, otherwise get the value
+ ret = prop == "display" && swap[stack.length-1] != null ?
+ "none" :
+ document.defaultView.getComputedStyle(elem,null).getPropertyValue(prop) || "";
+
+ // Finally, revert the display styles back
+ for ( a = 0; a < swap.length; a++ )
+ if ( swap[a] != null )
+ stack[a].style.display = swap[a];
+ }
+
+ if ( prop == "opacity" && ret == "" )
+ ret = "1";
} else if (elem.currentStyle) {
var newProp = prop.replace(/\-(\w)/g,function(m,c){return c.toUpperCase();});
if ( arg.constructor == Number )
arg = arg.toString();
- // Convert html string into DOM nodes
+ // Convert html string into DOM nodes
if ( typeof arg == "string" ) {
// Trim whitespace, otherwise indexOf won't work as expected
- var s = jQuery.trim(arg), div = doc.createElement("div"), tb = [];
+ var s = jQuery.trim(arg).toLowerCase(), div = doc.createElement("div"), tb = [];
var wrap =
- // option or optgroup
+ // option or optgroup
!s.indexOf("<opt") &&
[1, "<select>", "</select>"] ||
- (!s.indexOf("<thead") || !s.indexOf("<tbody") || !s.indexOf("<tfoot")) &&
+ !s.indexOf("<leg") &&
+ [1, "<fieldset>", "</fieldset>"] ||
+
+ s.match(/^<(thead|tbody|tfoot|colg|cap)/) &&
[1, "<table>", "</table>"] ||
!s.indexOf("<tr") &&
(!s.indexOf("<td") || !s.indexOf("<th")) &&
[3, "<table><tbody><tr>", "</tr></tbody></table>"] ||
+ !s.indexOf("<col") &&
+ [2, "<table><tbody></tbody><colgroup>", "</colgroup></table>"] ||
+
+ // IE can't serialize <link> and <script> tags normally
+ jQuery.browser.msie &&
+ [1, "div<div>", "</div>"] ||
+
[0,"",""];
// Go to html and back, then peel off extra wrappers
// Move to the right depth
while ( wrap[0]-- )
- div = div.firstChild;
+ div = div.lastChild;
// Remove IE's autoinserted <tbody> from table fragments
if ( jQuery.browser.msie ) {
for ( var n = tb.length-1; n >= 0 ; --n )
if ( jQuery.nodeName(tb[n], "tbody") && !tb[n].childNodes.length )
tb[n].parentNode.removeChild(tb[n]);
-
+
+ // IE completely kills leading whitespace when innerHTML is used
+ if ( /^\s/.test(arg) )
+ div.insertBefore( doc.createTextNode( arg.match(/^\s*/)[0] ), div.firstChild );
+
}
arg = jQuery.makeArray( div.childNodes );
}
- if ( arg.length === 0 && !jQuery(arg).is("form, select") )
+ if ( 0 === arg.length && (!jQuery.nodeName(arg, "form") && !jQuery.nodeName(arg, "select")) )
return;
- if ( arg[0] == undefined || jQuery(arg).is("form, select") )
+ if ( arg[0] == undefined || jQuery.nodeName(arg, "form") || arg.options )
r.push( arg );
else
r = jQuery.merge( r, arg );
},
attr: function(elem, name, value){
- var fix = jQuery.isXMLDoc(elem) ? {} : {
- "for": "htmlFor",
- "class": "className",
- "float": jQuery.browser.msie ? "styleFloat" : "cssFloat",
- cssFloat: jQuery.browser.msie ? "styleFloat" : "cssFloat",
- innerHTML: "innerHTML",
- className: "className",
- value: "value",
- disabled: "disabled",
- checked: "checked",
- readonly: "readOnly",
- selected: "selected"
- };
-
- // IE actually uses filters for opacity ... elem is actually elem.style
- if ( name == "opacity" && jQuery.browser.msie ) {
- if ( value != undefined ) {
- // IE has trouble with opacity if it does not have layout
- // Force it by setting the zoom level
- elem.zoom = 1;
-
- // Set the alpha filter to set the opacity
- elem.filter = (elem.filter || "").replace(/alpha\([^)]*\)/,"") +
- (parseFloat(value).toString() == "NaN" ? "" : "alpha(opacity=" + value * 100 + ")");
- }
+ var fix = jQuery.isXMLDoc(elem) ? {} : jQuery.props;
- return elem.filter ?
- (parseFloat( elem.filter.match(/opacity=([^)]*)/)[1] ) / 100).toString() : "";
- }
+ // Safari mis-reports the default selected property of a hidden option
+ // Accessing the parent's selectedIndex property fixes it
+ if ( name == "selected" && jQuery.browser.safari )
+ elem.parentNode.selectedIndex;
// Certain attributes only work when accessed via the old DOM 0 way
if ( fix[name] ) {
if ( value != undefined ) elem[fix[name]] = value;
return elem[fix[name]];
+ } else if ( jQuery.browser.msie && name == "style" )
+ return jQuery.attr( elem.style, "cssText", value );
- } else if ( value == undefined && jQuery.browser.msie && jQuery.nodeName(elem, "form") && (name == "action" || name == "method") )
+ else if ( value == undefined && jQuery.browser.msie && jQuery.nodeName(elem, "form") && (name == "action" || name == "method") )
return elem.getAttributeNode(name).nodeValue;
// IE elem.getAttribute passes even for style
else if ( elem.tagName ) {
+
if ( value != undefined ) elem.setAttribute( name, value );
if ( jQuery.browser.msie && /href|src/.test(name) && !jQuery.isXMLDoc(elem) )
return elem.getAttribute( name, 2 );
// elem is actually elem.style ... set the style
} else {
+ // IE actually uses filters for opacity
+ if ( name == "opacity" && jQuery.browser.msie ) {
+ if ( value != undefined ) {
+ // IE has trouble with opacity if it does not have layout
+ // Force it by setting the zoom level
+ elem.zoom = 1;
+
+ // Set the alpha filter to set the opacity
+ elem.filter = (elem.filter || "").replace(/alpha\([^)]*\)/,"") +
+ (parseFloat(value).toString() == "NaN" ? "" : "alpha(opacity=" + value * 100 + ")");
+ }
+
+ return elem.filter ?
+ (parseFloat( elem.filter.match(/opacity=([^)]*)/)[1] ) / 100).toString() : "";
+ }
name = name.replace(/-([a-z])/ig,function(z,b){return b.toUpperCase();});
if ( value != undefined ) elem[name] = value;
return elem[name];
* @cat JavaScript
*/
trim: function(t){
- return t.replace(/^\s+|\s+$/g, "");
+ return (t||"").replace(/^\s+|\s+$/g, "");
},
makeArray: function( a ) {
},
/**
- * Merge two arrays together, removing all duplicates.
- *
- * The result is the altered first argument with
- * the unique elements from the second array added.
+ * Merge two arrays together by concatenating them.
*
* @example $.merge( [0,1,2], [2,3,4] )
- * @result [0,1,2,3,4]
- * @desc Merges two arrays, removing the duplicate 2
- *
- * @example var array = [3,2,1];
- * $.merge( array, [4,3,2] )
- * @result array == [3,2,1,4]
- * @desc Merges two arrays, removing the duplicates 3 and 2
+ * @result [0,1,2,2,3,4]
+ * @desc Merges two arrays.
*
* @name $.merge
* @type Array
- * @param Array first The first array to merge, the unique elements of second added.
- * @param Array second The second array to merge into the first, unaltered.
+ * @param Array first The first array to merge, the elements of second are added.
+ * @param Array second The second array to append to the first, unaltered.
* @cat JavaScript
*/
merge: function(first, second) {
// We have to loop this way because IE & Opera overwrite the length
// expando of getElementsByTagName
- for ( var i = 0; second[i]; i++ )
- first.push(second[i]);
+
+ // Also, we need to make sure that the correct elements are being returned
+ // (IE returns comment nodes in a '*' query)
+ if ( jQuery.browser.msie ) {
+ for ( var i = 0; second[i]; i++ )
+ if ( second[i].nodeType != 8 )
+ first.push(second[i]);
+ } else
+ for ( var i = 0; second[i]; i++ )
+ first.push(second[i]);
+
return first;
},
+ /**
+ * Reduce an array (of jQuery objects only) to its unique elements.
+ *
+ * @example $.unique( [x1, x2, x3, x2, x3] )
+ * @result [x1, x2, x3]
+ * @desc Reduces the arrays of jQuery objects to unique elements by removing the duplicates of x2 and x3
+ *
+ * @name $.unique
+ * @type Array
+ * @param Array array The array to reduce to its unique jQuery objects.
+ * @cat JavaScript
+ */
unique: function(first) {
var r = [], num = jQuery.mergeNum++;
- for ( var i = 0, fl = first.length; i < fl; i++ )
- if ( first[i].mergeNum != num ) {
- first[i].mergeNum = num;
- r.push(first[i]);
- }
+ try {
+ for ( var i = 0, fl = first.length; i < fl; i++ )
+ if ( num != first[i].mergeNum ) {
+ first[i].mergeNum = num;
+ r.push(first[i]);
+ }
+ } catch(e) {
+ r = first;
+ }
return r;
},
if ( typeof fn == "string" )
fn = new Function("a","return " + fn);
- var result = [], r = [];
+ var result = [];
// Go through the array, translating each of the items to their
// new value (or values).
* @type Boolean
* @cat JavaScript
*/
-new function() {
- var b = navigator.userAgent.toLowerCase();
-
- // Figure out what browser is being used
- jQuery.browser = {
- safari: /webkit/.test(b),
- opera: /opera/.test(b),
- msie: /msie/.test(b) && !/opera/.test(b),
- mozilla: /mozilla/.test(b) && !/(compatible|webkit)/.test(b)
- };
+var userAgent = navigator.userAgent.toLowerCase();
+
+// Figure out what browser is being used
+jQuery.browser = {
+ version: (userAgent.match(/.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/) || [])[1],
+ safari: /webkit/.test(userAgent),
+ opera: /opera/.test(userAgent),
+ msie: /msie/.test(userAgent) && !/opera/.test(userAgent),
+ mozilla: /mozilla/.test(userAgent) && !/(compatible|webkit)/.test(userAgent)
+};
+var styleFloat = jQuery.browser.msie ? "styleFloat" : "cssFloat";
+
+jQuery.extend({
// Check to see if the W3C box model is being used
- jQuery.boxModel = !jQuery.browser.msie || document.compatMode == "CSS1Compat";
-};
+ boxModel: !jQuery.browser.msie || document.compatMode == "CSS1Compat",
+
+ styleFloat: jQuery.browser.msie ? "styleFloat" : "cssFloat",
+
+ props: {
+ "for": "htmlFor",
+ "class": "className",
+ "float": styleFloat,
+ cssFloat: styleFloat,
+ styleFloat: styleFloat,
+ innerHTML: "innerHTML",
+ className: "className",
+ value: "value",
+ disabled: "disabled",
+ checked: "checked",
+ readonly: "readOnly",
+ selected: "selected",
+ maxlength: "maxLength"
+ }
+});
/**
* Get a set of elements containing the unique parents of the matched
var ret = jQuery.map(this,n);
if ( a && typeof a == "string" )
ret = jQuery.multiFilter(a,ret);
- return this.pushStack( ret );
+ return this.pushStack( jQuery.unique(ret) );
};
});