├── test ├── testsuite.css ├── test.js ├── index.html ├── testrunner.js └── jquery.js ├── README ├── META.json └── jquery.metadata.js /test/testsuite.css: -------------------------------------------------------------------------------- 1 | body, div, h1 { font-family: 'trebuchet ms', verdana, arial; margin: 0; padding: 0 } 2 | body {font-size: 10pt; } 3 | h1 { padding: 15px; font-size: large; background-color: #06b; color: white; } 4 | h2 { padding: 10px; background-color: #eee; color: black; margin: 0; font-size: small; font-weight: normal } 5 | 6 | .pass { color: green; } 7 | .fail { color: red; } 8 | p.result { margin-left: 1em; } 9 | 10 | #banner { height: 2em; border-bottom: 1px solid white; } 11 | h2.pass { background-color: green; } 12 | h2.fail { background-color: red; } -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | jQuery Metadata Plugin 2 | 3 | By John Resig 4 | 5 | This plugin is capable of silently, and automatically, extracting metadata 6 | from classes, random attributes, and child elements. 7 | 8 | For example, you can do: 9 |
  • ...
  • 10 | OR 11 |
  • ...
  • 12 | OR 13 |
  • ...
  • 14 | 15 | The default is the first method, but you can always change it by twiddling 16 | the options. This means that there is at least one option here that can 17 | appease you. 18 | 19 | There's also a bunch of options (like loading data into a single property 20 | and the ability to ignore braces {}). 21 | -------------------------------------------------------------------------------- /META.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "jQuery-Metadata", 3 | "version": 2.0, 4 | "author": [ 5 | "John Resig ", 6 | "Jörn Zaefferer ", 7 | "Paul McLanahan " 8 | ], 9 | "abstract": "Quietly extract JSON metadata contained within DOM elements.", 10 | "license": "mit, gpl", 11 | "distribution_type": "extension", 12 | "requires": { 13 | "jQuery": 1.2 14 | }, 15 | "build_requires": { 16 | "jQuery-testrunner": 0 17 | }, 18 | "provides": { 19 | "jQuery.Metadata": { 20 | "version": 2.0, 21 | "file": "jquery.metadata.js" 22 | } 23 | }, 24 | "keywords": [ 25 | "data", 26 | "utility" 27 | ], 28 | "meta-spec": { 29 | "version": 1.3, 30 | "url": "http://module-build.sourceforge.net/META-spec-v1.3.html" 31 | }, 32 | "generated_by": "John Resig" 33 | } 34 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | function testData(index) { 2 | var data = jQuery.metadata.get(this); 3 | switch(index) { 4 | case 0: 5 | ok( data.foo == "bar", "Check foo property" ); 6 | ok( data.bar == "baz", "Check baz property" ); 7 | ok( data.arr[0] == 1, "Check arr[0] property" ); 8 | ok( data.arr[1] == 2, "Check arr[1] property" ); 9 | break; 10 | case 1: 11 | ok( data.test == "bar", "Check test property" ); 12 | ok( data.bar == "baz", "Check bar property" ); 13 | break; 14 | case 2: 15 | ok( data.zoooo == "bar", "Check zoooo property" ); 16 | ok( data.bar.test == "baz", "Check bar.test property" ); 17 | break; 18 | case 3: 19 | ok( data.number, "Check number property" ); 20 | ok( data.stuff[0] == 2, "Check stuff[0] property" ); 21 | ok( data.stuff[1] == 8, "Check stuff[1] property" ); 22 | break; 23 | default: 24 | ok( false, ["Assertion failed on index ", index, ", with data ", data].join('') ); 25 | } 26 | } 27 | 28 | // check if set can be intercepted without breaking metadata plugin 29 | var oldSet = jQuery.fn.set; 30 | jQuery.fn.set = function() { 31 | ok( true, "set was interecepted" ); 32 | oldSet.apply(this, arguments); 33 | }; 34 | 35 | //jQuery.meta.single = ""; 36 | 37 | test("meta: type attr - from data attribute", function() { 38 | expect(11); 39 | jQuery.metadata.setType("attr", "data"); 40 | jQuery("#one li").each(testData); 41 | }); 42 | 43 | test("meta: type class - from className", function() { 44 | expect(11); 45 | jQuery.metadata.setType( "class" ); 46 | jQuery("#two li").each(testData); 47 | }); 48 | 49 | test("meta: children script element - get data from child script element", function() { 50 | expect(11); 51 | jQuery.metadata.setType( "elem", "script" ); 52 | jQuery("#three li").each(testData); 53 | }); 54 | 55 | test("check if window doesn't break anything", function() { 56 | jQuery(window).get(); 57 | }); 58 | 59 | test("meta: default with single data object", function() { 60 | expect(11); 61 | jQuery.metadata.setType("attr","data"); 62 | jQuery.metadata.defaults.single = "data"; 63 | jQuery("#four li").each(testData); 64 | }); 65 | 66 | test("meta with select and class", function() { 67 | expect(2); 68 | jQuery.metadata.setType("class"); 69 | jQuery.metadata.single = "stuff"; 70 | var e = $('#meal').metadata(); 71 | ok( e, "data property" ); 72 | ok( e.required, "property on data property" ); 73 | }); 74 | 75 | test("try to add and remove classes on metadata elements", function() { 76 | $("#two li").addClass("foobar").addClass("foo bar").removeClass("foobar"); 77 | ok( $("#two li").is(".foo"), 'Check class foo was added.' ); 78 | ok( $("#two li").is(".bar"), 'Check class bar was added.' ); 79 | }); -------------------------------------------------------------------------------- /test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |

    jQuery - Metadata Test Suite

    15 | 16 |

    17 | 18 | 54 | 55 |
      56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /jquery.metadata.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Metadata - jQuery plugin for parsing metadata from elements 3 | * 4 | * Copyright (c) 2006 John Resig, Yehuda Katz, Jörn Zaefferer, Paul McLanahan 5 | * 6 | * Dual licensed under the MIT and GPL licenses: 7 | * http://www.opensource.org/licenses/mit-license.php 8 | * http://www.gnu.org/licenses/gpl.html 9 | * 10 | */ 11 | 12 | /** 13 | * Sets the type of metadata to use. Metadata is encoded in JSON, and each property 14 | * in the JSON will become a property of the element itself. 15 | * 16 | * There are three supported types of metadata storage: 17 | * 18 | * attr: Inside an attribute. The name parameter indicates *which* attribute. 19 | * 20 | * class: Inside the class attribute, wrapped in curly braces: { } 21 | * 22 | * elem: Inside a child element (e.g. a script tag). The 23 | * name parameter indicates *which* element. 24 | * 25 | * The metadata for an element is loaded the first time the element is accessed via jQuery. 26 | * 27 | * As a result, you can define the metadata type, use $(expr) to load the metadata into the elements 28 | * matched by expr, then redefine the metadata type and run another $(expr) for other elements. 29 | * 30 | * @name $.metadata.setType 31 | * 32 | * @example

      This is a p

      33 | * @before $.metadata.setType("class") 34 | * @after $("#one").metadata().item_id == 1; $("#one").metadata().item_label == "Label" 35 | * @desc Reads metadata from the class attribute 36 | * 37 | * @example

      This is a p

      38 | * @before $.metadata.setType("attr", "data") 39 | * @after $("#one").metadata().item_id == 1; $("#one").metadata().item_label == "Label" 40 | * @desc Reads metadata from a "data" attribute 41 | * 42 | * @example

      This is a p

      43 | * @before $.metadata.setType("elem", "script") 44 | * @after $("#one").metadata().item_id == 1; $("#one").metadata().item_label == "Label" 45 | * @desc Reads metadata from a nested script element 46 | * 47 | * @param String type The encoding type 48 | * @param String name The name of the attribute to be used to get metadata (optional) 49 | * @cat Plugins/Metadata 50 | * @descr Sets the type of encoding to be used when loading metadata for the first time 51 | * @type undefined 52 | * @see metadata() 53 | */ 54 | 55 | (function($) { 56 | 57 | $.extend({ 58 | metadata : { 59 | defaults : { 60 | type: 'class', 61 | name: 'metadata', 62 | cre: /({.*})/, 63 | single: 'metadata' 64 | }, 65 | setType: function( type, name ){ 66 | this.defaults.type = type; 67 | this.defaults.name = name; 68 | }, 69 | get: function( elem, opts ){ 70 | var settings = $.extend({},this.defaults,opts); 71 | // check for empty string in single property 72 | if ( !settings.single.length ) settings.single = 'metadata'; 73 | 74 | var data = $.data(elem, settings.single); 75 | // returned cached data if it already exists 76 | if ( data ) return data; 77 | 78 | data = "{}"; 79 | 80 | if ( settings.type == "class" ) { 81 | var m = settings.cre.exec( elem.className ); 82 | if ( m ) 83 | data = m[1]; 84 | } else if ( settings.type == "elem" ) { 85 | if( !elem.getElementsByTagName ) 86 | return undefined; 87 | var e = elem.getElementsByTagName(settings.name); 88 | if ( e.length ) 89 | data = $.trim(e[0].innerHTML); 90 | } else if ( elem.getAttribute != undefined ) { 91 | var attr = elem.getAttribute( settings.name ); 92 | if ( attr ) 93 | data = attr; 94 | } 95 | 96 | if ( data.indexOf( '{' ) <0 ) 97 | data = "{" + data + "}"; 98 | 99 | data = eval("(" + data + ")"); 100 | 101 | $.data( elem, settings.single, data ); 102 | return data; 103 | } 104 | } 105 | }); 106 | 107 | /** 108 | * Returns the metadata object for the first member of the jQuery object. 109 | * 110 | * @name metadata 111 | * @descr Returns element's metadata object 112 | * @param Object opts An object contianing settings to override the defaults 113 | * @type jQuery 114 | * @cat Plugins/Metadata 115 | */ 116 | $.fn.metadata = function( opts ){ 117 | return $.metadata.get( this[0], opts ); 118 | }; 119 | 120 | })(jQuery); -------------------------------------------------------------------------------- /test/testrunner.js: -------------------------------------------------------------------------------- 1 | var _config = { 2 | fixture: null, 3 | Test: [], 4 | stats: { 5 | all: 0, 6 | bad: 0 7 | }, 8 | queue: [], 9 | blocking: true, 10 | timeout: null, 11 | expected: null, 12 | currentModule: null, 13 | asyncTimeout: 2 // seconds for async timeout 14 | }; 15 | 16 | $(function() { 17 | $('#userAgent').html(navigator.userAgent); 18 | runTest(); 19 | }); 20 | 21 | function synchronize(callback) { 22 | _config.queue[_config.queue.length] = callback; 23 | if(!_config.blocking) { 24 | process(); 25 | } 26 | } 27 | 28 | function process() { 29 | while(_config.queue.length && !_config.blocking) { 30 | var call = _config.queue[0]; 31 | _config.queue = _config.queue.slice(1); 32 | call(); 33 | } 34 | } 35 | 36 | function stop(allowFailure) { 37 | _config.blocking = true; 38 | var handler = allowFailure ? start : function() { 39 | ok( false, "Test timed out" ); 40 | start(); 41 | }; 42 | _config.timeout = setTimeout(handler, _config.asyncTimeout * 1000); 43 | } 44 | function start() { 45 | if(_config.timeout) 46 | clearTimeout(_config.timeout); 47 | _config.blocking = false; 48 | process(); 49 | } 50 | 51 | function runTest() { 52 | _config.blocking = false; 53 | var time = new Date(); 54 | _config.fixture = document.getElementById('main').innerHTML; 55 | reset(); 56 | synchronize(function() { 57 | time = new Date() - time; 58 | $("
      ").html(['

      Tests completed in ', 59 | time, ' milliseconds.
      ', 60 | _config.stats.bad, ' tests of ', _config.stats.all, ' failed.

      '] 61 | .join('')) 62 | .appendTo("body"); 63 | $("#banner").addClass(_config.stats.bad ? "fail" : "pass"); 64 | }); 65 | } 66 | 67 | function test(name, callback, nowait) { 68 | if(_config.currentModule) 69 | name = _config.currentModule + " module: " + name; 70 | 71 | var filter = location.search.slice(1); 72 | if ( filter && encodeURIComponent(name) != filter ) 73 | return; 74 | 75 | synchronize(function() { 76 | _config.Test = []; 77 | try { 78 | callback(); 79 | } catch(e) { 80 | if( typeof console != "undefined" && console.error && console.warn ) { 81 | console.error("Test " + name + " died, exception and test follows"); 82 | console.error(e); 83 | console.warn(callback.toString()); 84 | } 85 | _config.Test.push( [ false, "Died on test #" + (_config.Test.length+1) + ": " + e ] ); 86 | //throw e; 87 | } 88 | }); 89 | synchronize(function() { 90 | reset(); 91 | 92 | // don't output pause tests 93 | if(nowait) return; 94 | 95 | if(_config.expected && _config.expected != _config.Test.length) { 96 | _config.Test.push( [ false, "Expected " + _config.expected + " assertions, but " + _config.Test.length + " were run" ] ); 97 | } 98 | _config.expected = null; 99 | 100 | var good = 0, bad = 0; 101 | var ol = document.createElement("ol"); 102 | ol.style.display = "none"; 103 | var li = "", state = "pass"; 104 | for ( var i = 0; i < _config.Test.length; i++ ) { 105 | var li = document.createElement("li"); 106 | li.className = _config.Test[i][0] ? "pass" : "fail"; 107 | li.innerHTML = _config.Test[i][1]; 108 | ol.appendChild( li ); 109 | 110 | _config.stats.all++; 111 | if ( !_config.Test[i][0] ) { 112 | state = "fail"; 113 | bad++; 114 | _config.stats.bad++; 115 | } else good++; 116 | } 117 | 118 | var li = document.createElement("li"); 119 | li.className = state; 120 | 121 | var b = document.createElement("strong"); 122 | b.innerHTML = name + " (" + bad + ", " + good + ", " + _config.Test.length + ")"; 123 | b.onclick = function(){ 124 | var n = this.nextSibling; 125 | if ( jQuery.css( n, "display" ) == "none" ) 126 | n.style.display = "block"; 127 | else 128 | n.style.display = "none"; 129 | }; 130 | $(b).dblclick(function(event) { 131 | var target = jQuery(event.target).filter("strong").clone(); 132 | if ( target.length ) { 133 | target.children().remove(); 134 | location.href = location.href.match(/^(.+?)(\?.*)?$/)[1] + "?" + encodeURIComponent($.trim(target.text())); 135 | } 136 | }); 137 | li.appendChild( b ); 138 | li.appendChild( ol ); 139 | 140 | document.getElementById("tests").appendChild( li ); 141 | }); 142 | } 143 | 144 | // call on start of module test to prepend name to all tests 145 | function module(moduleName) { 146 | _config.currentModule = moduleName; 147 | } 148 | 149 | /** 150 | * Specify the number of expected assertions to gurantee that failed test (no assertions are run at all) don't slip through. 151 | */ 152 | function expect(asserts) { 153 | _config.expected = asserts; 154 | } 155 | 156 | /** 157 | * Resets the test setup. Useful for tests that modify the DOM. 158 | */ 159 | function reset() { 160 | document.getElementById('main').innerHTML = _config.fixture; 161 | } 162 | 163 | /** 164 | * Asserts true. 165 | * @example ok( $("a").size() > 5, "There must be at least 5 anchors" ); 166 | */ 167 | function ok(a, msg) { 168 | _config.Test.push( [ !!a, msg ] ); 169 | } 170 | 171 | /** 172 | * Asserts that two arrays are the same 173 | */ 174 | function isSet(a, b, msg) { 175 | var ret = true; 176 | if ( a && b && a.length != undefined && a.length == b.length ) { 177 | for ( var i = 0; i < a.length; i++ ) 178 | if ( a[i] != b[i] ) 179 | ret = false; 180 | } else 181 | ret = false; 182 | if ( !ret ) 183 | _config.Test.push( [ ret, msg + " expected: " + serialArray(b) + " result: " + serialArray(a) ] ); 184 | else 185 | _config.Test.push( [ ret, msg ] ); 186 | } 187 | 188 | /** 189 | * Asserts that two objects are equivalent 190 | */ 191 | function isObj(a, b, msg) { 192 | var ret = true; 193 | 194 | if ( a && b ) { 195 | for ( var i in a ) 196 | if ( a[i] != b[i] ) 197 | ret = false; 198 | 199 | for ( i in b ) 200 | if ( a[i] != b[i] ) 201 | ret = false; 202 | } else 203 | ret = false; 204 | 205 | _config.Test.push( [ ret, msg ] ); 206 | } 207 | 208 | function serialArray( a ) { 209 | var r = []; 210 | 211 | if ( a && a.length ) 212 | for ( var i = 0; i < a.length; i++ ) { 213 | var str = a[i].nodeName; 214 | if ( str ) { 215 | str = str.toLowerCase(); 216 | if ( a[i].id ) 217 | str += "#" + a[i].id; 218 | } else 219 | str = a[i]; 220 | r.push( str ); 221 | } 222 | 223 | return "[ " + r.join(", ") + " ]" 224 | } 225 | 226 | /** 227 | * Returns an array of elements with the given IDs, eg. 228 | * @example q("main", "foo", "bar") 229 | * @result [
      , , ] 230 | */ 231 | function q() { 232 | var r = []; 233 | for ( var i = 0; i < arguments.length; i++ ) 234 | r.push( document.getElementById( arguments[i] ) ); 235 | return r; 236 | } 237 | 238 | /** 239 | * Asserts that a select matches the given IDs 240 | * @example t("Check for something", "//[a]", ["foo", "baar"]); 241 | * @result returns true if "//[a]" return two elements with the IDs 'foo' and 'baar' 242 | */ 243 | function t(a,b,c) { 244 | var f = jQuery(b); 245 | var s = ""; 246 | for ( var i = 0; i < f.length; i++ ) 247 | s += (s && ",") + '"' + f[i].id + '"'; 248 | isSet(f, q.apply(q,c), a + " (" + b + ")"); 249 | } 250 | 251 | /** 252 | * Add random number to url to stop IE from caching 253 | * 254 | * @example url("data/test.html") 255 | * @result "data/test.html?10538358428943" 256 | * 257 | * @example url("data/test.php?foo=bar") 258 | * @result "data/test.php?foo=bar&10538358345554" 259 | */ 260 | function url(value) { 261 | return value + (/\?/.test(value) ? "&" : "?") + new Date().getTime() + "" + parseInt(Math.random()*100000); 262 | } 263 | 264 | /** 265 | * Checks that the first two arguments are equal, with an optional message. 266 | * Prints out both expected and actual values on failure. 267 | * 268 | * Prefered to ok( expected == actual, message ) 269 | * 270 | * @example equals( "Expected 2 characters.", v.formatMessage("Expected {0} characters.", 2) ); 271 | * 272 | * @param Object expected 273 | * @param Object actual 274 | * @param String message (optional) 275 | */ 276 | function equals(expected, actual, message) { 277 | var result = expected == actual; 278 | message = message || (result ? "okay" : "failed"); 279 | _config.Test.push( [ result, result ? message + ": " + expected : message + " expected: " + expected + " actual: " + actual ] ); 280 | } 281 | 282 | /** 283 | * Trigger an event on an element. 284 | * 285 | * @example triggerEvent( document.body, "click" ); 286 | * 287 | * @param DOMElement elem 288 | * @param String type 289 | */ 290 | function triggerEvent( elem, type, event ) { 291 | if ( jQuery.browser.mozilla || jQuery.browser.opera ) { 292 | event = document.createEvent("MouseEvents"); 293 | event.initMouseEvent(type, true, true, elem.ownerDocument.defaultView, 294 | 0, 0, 0, 0, 0, false, false, false, false, 0, null); 295 | elem.dispatchEvent( event ); 296 | } else if ( jQuery.browser.msie ) { 297 | elem.fireEvent("on"+type); 298 | } 299 | } 300 | -------------------------------------------------------------------------------- /test/jquery.js: -------------------------------------------------------------------------------- 1 | (function(){ 2 | /* 3 | * jQuery 1.2.2b2 - New Wave Javascript 4 | * 5 | * Copyright (c) 2007 John Resig (jquery.com) 6 | * Dual licensed under the MIT (MIT-LICENSE.txt) 7 | * and GPL (GPL-LICENSE.txt) licenses. 8 | * 9 | * $Date: 2007-12-20 14:36:56 +0100 (Don, 20 Dez 2007) $ 10 | * $Rev: 4251 $ 11 | */ 12 | 13 | // Map over jQuery in case of overwrite 14 | if ( window.jQuery ) 15 | var _jQuery = window.jQuery; 16 | 17 | var jQuery = window.jQuery = function( selector, context ) { 18 | // The jQuery object is actually just the init constructor 'enhanced' 19 | return new jQuery.prototype.init( selector, context ); 20 | }; 21 | 22 | // Map over the $ in case of overwrite 23 | if ( window.$ ) 24 | var _$ = window.$; 25 | 26 | // Map the jQuery namespace to the '$' one 27 | window.$ = jQuery; 28 | 29 | // A simple way to check for HTML strings or ID strings 30 | // (both of which we optimize for) 31 | var quickExpr = /^[^<]*(<(.|\s)+>)[^>]*$|^#(\w+)$/; 32 | 33 | // Is it a simple selector 34 | var isSimple = /^.[^:#\[\.]*$/; 35 | 36 | jQuery.fn = jQuery.prototype = { 37 | init: function( selector, context ) { 38 | // Make sure that a selection was provided 39 | selector = selector || document; 40 | 41 | // Handle $(DOMElement) 42 | if ( selector.nodeType ) { 43 | this[0] = selector; 44 | this.length = 1; 45 | return this; 46 | 47 | // Handle HTML strings 48 | } else if ( typeof selector == "string" ) { 49 | // Are we dealing with HTML string or an ID? 50 | var match = quickExpr.exec( selector ); 51 | 52 | // Verify a match, and that no context was specified for #id 53 | if ( match && (match[1] || !context) ) { 54 | 55 | // HANDLE: $(html) -> $(array) 56 | if ( match[1] ) 57 | selector = jQuery.clean( [ match[1] ], context ); 58 | 59 | // HANDLE: $("#id") 60 | else { 61 | var elem = document.getElementById( match[3] ); 62 | 63 | // Make sure an element was located 64 | if ( elem ) 65 | // Handle the case where IE and Opera return items 66 | // by name instead of ID 67 | if ( elem.id != match[3] ) 68 | return jQuery().find( selector ); 69 | 70 | // Otherwise, we inject the element directly into the jQuery object 71 | else { 72 | this[0] = elem; 73 | this.length = 1; 74 | return this; 75 | } 76 | 77 | else 78 | selector = []; 79 | } 80 | 81 | // HANDLE: $(expr, [context]) 82 | // (which is just equivalent to: $(content).find(expr) 83 | } else 84 | return new jQuery( context ).find( selector ); 85 | 86 | // HANDLE: $(function) 87 | // Shortcut for document ready 88 | } else if ( jQuery.isFunction( selector ) ) 89 | return new jQuery( document )[ jQuery.fn.ready ? "ready" : "load" ]( selector ); 90 | 91 | return this.setArray( 92 | // HANDLE: $(array) 93 | selector.constructor == Array && selector || 94 | 95 | // HANDLE: $(arraylike) 96 | // Watch for when an array-like object, contains DOM nodes, is passed in as the selector 97 | (selector.jquery || selector.length && selector != window && !selector.nodeType && selector[0] != undefined && selector[0].nodeType) && jQuery.makeArray( selector ) || 98 | 99 | // HANDLE: $(*) 100 | [ selector ] ); 101 | }, 102 | 103 | // The current version of jQuery being used 104 | jquery: "@VERSION", 105 | 106 | // The number of elements contained in the matched element set 107 | size: function() { 108 | return this.length; 109 | }, 110 | 111 | // The number of elements contained in the matched element set 112 | length: 0, 113 | 114 | // Get the Nth element in the matched element set OR 115 | // Get the whole matched element set as a clean array 116 | get: function( num ) { 117 | return num == undefined ? 118 | 119 | // Return a 'clean' array 120 | jQuery.makeArray( this ) : 121 | 122 | // Return just the object 123 | this[ num ]; 124 | }, 125 | 126 | // Take an array of elements and push it onto the stack 127 | // (returning the new matched element set) 128 | pushStack: function( elems ) { 129 | // Build a new jQuery matched element set 130 | var ret = jQuery( elems ); 131 | 132 | // Add the old object onto the stack (as a reference) 133 | ret.prevObject = this; 134 | 135 | // Return the newly-formed element set 136 | return ret; 137 | }, 138 | 139 | // Force the current matched set of elements to become 140 | // the specified array of elements (destroying the stack in the process) 141 | // You should use pushStack() in order to do this, but maintain the stack 142 | setArray: function( elems ) { 143 | // Resetting the length to 0, then using the native Array push 144 | // is a super-fast way to populate an object with array-like properties 145 | this.length = 0; 146 | Array.prototype.push.apply( this, elems ); 147 | 148 | return this; 149 | }, 150 | 151 | // Execute a callback for every element in the matched set. 152 | // (You can seed the arguments with an array of args, but this is 153 | // only used internally.) 154 | each: function( callback, args ) { 155 | return jQuery.each( this, callback, args ); 156 | }, 157 | 158 | // Determine the position of an element within 159 | // the matched set of elements 160 | index: function( elem ) { 161 | var ret = -1; 162 | 163 | // Locate the position of the desired element 164 | this.each(function(i){ 165 | if ( this == elem ) 166 | ret = i; 167 | }); 168 | 169 | return ret; 170 | }, 171 | 172 | attr: function( name, value, type ) { 173 | var options = name; 174 | 175 | // Look for the case where we're accessing a style value 176 | if ( name.constructor == String ) 177 | if ( value == undefined ) 178 | return this.length && jQuery[ type || "attr" ]( this[0], name ) || undefined; 179 | 180 | else { 181 | options = {}; 182 | options[ name ] = value; 183 | } 184 | 185 | // Check to see if we're setting style values 186 | return this.each(function(i){ 187 | // Set all the styles 188 | for ( name in options ) 189 | jQuery.attr( 190 | type ? 191 | this.style : 192 | this, 193 | name, jQuery.prop( this, options[ name ], type, i, name ) 194 | ); 195 | }); 196 | }, 197 | 198 | css: function( key, value ) { 199 | // ignore negative width and height values 200 | if ( (key == 'width' || key == 'height') && parseFloat(value) < 0 ) 201 | value = undefined; 202 | return this.attr( key, value, "curCSS" ); 203 | }, 204 | 205 | text: function( text ) { 206 | if ( typeof text != "object" && text != null ) 207 | return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode( text ) ); 208 | 209 | var ret = ""; 210 | 211 | jQuery.each( text || this, function(){ 212 | jQuery.each( this.childNodes, function(){ 213 | if ( this.nodeType != 8 ) 214 | ret += this.nodeType != 1 ? 215 | this.nodeValue : 216 | jQuery.fn.text( [ this ] ); 217 | }); 218 | }); 219 | 220 | return ret; 221 | }, 222 | 223 | wrapAll: function( html ) { 224 | if ( this[0] ) 225 | // The elements to wrap the target around 226 | jQuery( html, this[0].ownerDocument ) 227 | .clone() 228 | .insertBefore( this[0] ) 229 | .map(function(){ 230 | var elem = this; 231 | 232 | while ( elem.firstChild ) 233 | elem = elem.firstChild; 234 | 235 | return elem; 236 | }) 237 | .append(this); 238 | 239 | return this; 240 | }, 241 | 242 | wrapInner: function( html ) { 243 | return this.each(function(){ 244 | jQuery( this ).contents().wrapAll( html ); 245 | }); 246 | }, 247 | 248 | wrap: function( html ) { 249 | return this.each(function(){ 250 | jQuery( this ).wrapAll( html ); 251 | }); 252 | }, 253 | 254 | append: function() { 255 | return this.domManip(arguments, true, false, function(elem){ 256 | if (this.nodeType == 1) 257 | this.appendChild( elem ); 258 | }); 259 | }, 260 | 261 | prepend: function() { 262 | return this.domManip(arguments, true, true, function(elem){ 263 | if (this.nodeType == 1) 264 | this.insertBefore( elem, this.firstChild ); 265 | }); 266 | }, 267 | 268 | before: function() { 269 | return this.domManip(arguments, false, false, function(elem){ 270 | this.parentNode.insertBefore( elem, this ); 271 | }); 272 | }, 273 | 274 | after: function() { 275 | return this.domManip(arguments, false, true, function(elem){ 276 | this.parentNode.insertBefore( elem, this.nextSibling ); 277 | }); 278 | }, 279 | 280 | end: function() { 281 | return this.prevObject || jQuery( [] ); 282 | }, 283 | 284 | find: function( selector ) { 285 | var elems = jQuery.map(this, function(elem){ 286 | return jQuery.find( selector, elem ); 287 | }); 288 | 289 | return this.pushStack( /[^+>] [^+>]/.test( selector ) || selector.indexOf("..") > -1 ? 290 | jQuery.unique( elems ) : 291 | elems ); 292 | }, 293 | 294 | clone: function( events ) { 295 | // Do the clone 296 | var ret = this.map(function(){ 297 | if ( jQuery.browser.msie && !jQuery.isXMLDoc(this) ) { 298 | // IE copies events bound via attachEvent when 299 | // using cloneNode. Calling detachEvent on the 300 | // clone will also remove the events from the orignal 301 | // In order to get around this, we use innerHTML. 302 | // Unfortunately, this means some modifications to 303 | // attributes in IE that are actually only stored 304 | // as properties will not be copied (such as the 305 | // the name attribute on an input). 306 | var clone = this.cloneNode(true), 307 | container = document.createElement("div"), 308 | container2 = document.createElement("div"); 309 | container.appendChild(clone); 310 | container2.innerHTML = container.innerHTML; 311 | return container2.firstChild; 312 | } else 313 | return this.cloneNode(true); 314 | }); 315 | 316 | // Need to set the expando to null on the cloned set if it exists 317 | // removeData doesn't work here, IE removes it from the original as well 318 | // this is primarily for IE but the data expando shouldn't be copied over in any browser 319 | var clone = ret.find("*").andSelf().each(function(){ 320 | if ( this[ expando ] != undefined ) 321 | this[ expando ] = null; 322 | }); 323 | 324 | // Copy the events from the original to the clone 325 | if ( events === true ) 326 | this.find("*").andSelf().each(function(i){ 327 | var events = jQuery.data( this, "events" ); 328 | 329 | for ( var type in events ) 330 | for ( var handler in events[ type ] ) 331 | jQuery.event.add( clone[ i ], type, events[ type ][ handler ], events[ type ][ handler ].data ); 332 | }); 333 | 334 | // Return the cloned set 335 | return ret; 336 | }, 337 | 338 | filter: function( selector ) { 339 | return this.pushStack( 340 | jQuery.isFunction( selector ) && 341 | jQuery.grep(this, function(elem, i){ 342 | return selector.call( elem, i ); 343 | }) || 344 | 345 | jQuery.multiFilter( selector, this ) ); 346 | }, 347 | 348 | not: function( selector ) { 349 | if ( selector.constructor == String ) 350 | // test special case where just one selector is passed in 351 | if ( isSimple.test( selector ) ) 352 | return this.pushStack( jQuery.multiFilter( selector, this, true ) ); 353 | else 354 | selector = jQuery.multiFilter( selector, this ); 355 | 356 | var isArrayLike = selector.length && selector[selector.length - 1] !== undefined && !selector.nodeType; 357 | return this.filter(function() { 358 | return isArrayLike ? jQuery.inArray( this, selector ) < 0 : this != selector; 359 | }); 360 | }, 361 | 362 | add: function( selector ) { 363 | return !selector ? this : this.pushStack( jQuery.merge( 364 | this.get(), 365 | selector.constructor == String ? 366 | jQuery( selector ).get() : 367 | selector.length != undefined && (!selector.nodeName || jQuery.nodeName(selector, "form")) ? 368 | selector : [selector] ) ); 369 | }, 370 | 371 | is: function( selector ) { 372 | return selector ? 373 | jQuery.multiFilter( selector, this ).length > 0 : 374 | false; 375 | }, 376 | 377 | hasClass: function( selector ) { 378 | return this.is( "." + selector ); 379 | }, 380 | 381 | val: function( value ) { 382 | if ( value == undefined ) { 383 | 384 | if ( this.length ) { 385 | var elem = this[0]; 386 | 387 | // We need to handle select boxes special 388 | if ( jQuery.nodeName( elem, "select" ) ) { 389 | var index = elem.selectedIndex, 390 | values = [], 391 | options = elem.options, 392 | one = elem.type == "select-one"; 393 | 394 | // Nothing was selected 395 | if ( index < 0 ) 396 | return null; 397 | 398 | // Loop through all the selected options 399 | for ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) { 400 | var option = options[ i ]; 401 | 402 | if ( option.selected ) { 403 | // Get the specifc value for the option 404 | value = jQuery.browser.msie && !option.attributes.value.specified ? option.text : option.value; 405 | 406 | // We don't need an array for one selects 407 | if ( one ) 408 | return value; 409 | 410 | // Multi-Selects return an array 411 | values.push( value ); 412 | } 413 | } 414 | 415 | return values; 416 | 417 | // Everything else, we just grab the value 418 | } else 419 | return (this[0].value || "").replace(/\r/g, ""); 420 | 421 | } 422 | 423 | return undefined; 424 | } 425 | 426 | return this.each(function(){ 427 | if ( this.nodeType != 1 ) 428 | return; 429 | 430 | if ( value.constructor == Array && /radio|checkbox/.test( this.type ) ) 431 | this.checked = (jQuery.inArray(this.value, value) >= 0 || 432 | jQuery.inArray(this.name, value) >= 0); 433 | 434 | else if ( jQuery.nodeName( this, "select" ) ) { 435 | var values = value.constructor == Array ? 436 | value : 437 | [ value ]; 438 | 439 | jQuery( "option", this ).each(function(){ 440 | this.selected = (jQuery.inArray( this.value, values ) >= 0 || 441 | jQuery.inArray( this.text, values ) >= 0); 442 | }); 443 | 444 | if ( !values.length ) 445 | this.selectedIndex = -1; 446 | 447 | } else 448 | this.value = value; 449 | }); 450 | }, 451 | 452 | html: function( value ) { 453 | return value == undefined ? 454 | (this.length ? 455 | this[0].innerHTML : 456 | null) : 457 | this.empty().append( value ); 458 | }, 459 | 460 | replaceWith: function( value ) { 461 | return this.after( value ).remove(); 462 | }, 463 | 464 | eq: function( i ) { 465 | return this.slice( i, i + 1 ); 466 | }, 467 | 468 | slice: function() { 469 | return this.pushStack( Array.prototype.slice.apply( this, arguments ) ); 470 | }, 471 | 472 | map: function( callback ) { 473 | return this.pushStack( jQuery.map(this, function(elem, i){ 474 | return callback.call( elem, i, elem ); 475 | })); 476 | }, 477 | 478 | andSelf: function() { 479 | return this.add( this.prevObject ); 480 | }, 481 | 482 | domManip: function( args, table, reverse, callback ) { 483 | var clone = this.length > 1, elems; 484 | 485 | return this.each(function(){ 486 | if ( !elems ) { 487 | elems = jQuery.clean( args, this.ownerDocument ); 488 | 489 | if ( reverse ) 490 | elems.reverse(); 491 | } 492 | 493 | var obj = this; 494 | 495 | if ( table && jQuery.nodeName( this, "table" ) && jQuery.nodeName( elems[0], "tr" ) ) 496 | obj = this.getElementsByTagName("tbody")[0] || this.appendChild( this.ownerDocument.createElement("tbody") ); 497 | 498 | var scripts = jQuery( [] ); 499 | 500 | jQuery.each(elems, function(){ 501 | var elem = clone ? 502 | jQuery( this ).clone( true )[0] : 503 | this; 504 | 505 | // execute all scripts after the elements have been injected 506 | if ( jQuery.nodeName( elem, "script" ) ) { 507 | scripts = scripts.add( elem ); 508 | } else { 509 | // Remove any inner scripts for later evaluation 510 | if ( elem.nodeType == 1 ) 511 | scripts = scripts.add( jQuery( "script", elem ).remove() ); 512 | 513 | // Inject the elements into the document 514 | callback.call( obj, elem ); 515 | } 516 | }); 517 | 518 | scripts.each( evalScript ); 519 | }); 520 | } 521 | }; 522 | 523 | // Give the init function the jQuery prototype for later instantiation 524 | jQuery.prototype.init.prototype = jQuery.prototype; 525 | 526 | function evalScript( i, elem ) { 527 | if ( elem.src ) 528 | jQuery.ajax({ 529 | url: elem.src, 530 | async: false, 531 | dataType: "script" 532 | }); 533 | 534 | else 535 | jQuery.globalEval( elem.text || elem.textContent || elem.innerHTML || "" ); 536 | 537 | if ( elem.parentNode ) 538 | elem.parentNode.removeChild( elem ); 539 | } 540 | 541 | jQuery.extend = jQuery.fn.extend = function() { 542 | // copy reference to target object 543 | var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options; 544 | 545 | // Handle a deep copy situation 546 | if ( target.constructor == Boolean ) { 547 | deep = target; 548 | target = arguments[1] || {}; 549 | // skip the boolean and the target 550 | i = 2; 551 | } 552 | 553 | // Handle case when target is a string or something (possible in deep copy) 554 | if ( typeof target != "object" && typeof target != "function" ) 555 | target = {}; 556 | 557 | // extend jQuery itself if only one argument is passed 558 | if ( length == 1 ) { 559 | target = this; 560 | i = 0; 561 | } 562 | 563 | for ( ; i < length; i++ ) 564 | // Only deal with non-null/undefined values 565 | if ( (options = arguments[ i ]) != null ) 566 | // Extend the base object 567 | for ( var name in options ) { 568 | // Prevent never-ending loop 569 | if ( target === options[ name ] ) 570 | continue; 571 | 572 | // Recurse if we're merging object values 573 | if ( deep && options[ name ] && typeof options[ name ] == "object" && target[ name ] && !options[ name ].nodeType ) 574 | target[ name ] = jQuery.extend( target[ name ], options[ name ] ); 575 | 576 | // Don't bring in undefined values 577 | else if ( options[ name ] != undefined ) 578 | target[ name ] = options[ name ]; 579 | 580 | } 581 | 582 | // Return the modified object 583 | return target; 584 | }; 585 | 586 | var expando = "jQuery" + (new Date()).getTime(), uuid = 0, windowData = {}; 587 | 588 | // exclude the following css properties to add px 589 | var exclude = /z-?index|font-?weight|opacity|zoom|line-?height/i; 590 | 591 | jQuery.extend({ 592 | noConflict: function( deep ) { 593 | window.$ = _$; 594 | 595 | if ( deep ) 596 | window.jQuery = _jQuery; 597 | 598 | return jQuery; 599 | }, 600 | 601 | // This may seem like some crazy code, but trust me when I say that this 602 | // is the only cross-browser way to do this. --John 603 | isFunction: function( fn ) { 604 | return !!fn && typeof fn != "string" && !fn.nodeName && 605 | fn.constructor != Array && /function/i.test( fn + "" ); 606 | }, 607 | 608 | // check if an element is in a (or is an) XML document 609 | isXMLDoc: function( elem ) { 610 | return elem.documentElement && !elem.body || 611 | elem.tagName && elem.ownerDocument && !elem.ownerDocument.body; 612 | }, 613 | 614 | // Evalulates a script in a global context 615 | globalEval: function( data ) { 616 | data = jQuery.trim( data ); 617 | 618 | if ( data ) { 619 | // Inspired by code by Andrea Giammarchi 620 | // http://webreflection.blogspot.com/2007/08/global-scope-evaluation-and-dom.html 621 | var head = document.getElementsByTagName("head")[0] || document.documentElement, 622 | script = document.createElement("script"); 623 | 624 | script.type = "text/javascript"; 625 | if ( jQuery.browser.msie ) 626 | script.text = data; 627 | else 628 | script.appendChild( document.createTextNode( data ) ); 629 | 630 | head.appendChild( script ); 631 | head.removeChild( script ); 632 | } 633 | }, 634 | 635 | nodeName: function( elem, name ) { 636 | return elem.nodeName && elem.nodeName.toUpperCase() == name.toUpperCase(); 637 | }, 638 | 639 | cache: {}, 640 | 641 | data: function( elem, name, data ) { 642 | elem = elem == window ? 643 | windowData : 644 | elem; 645 | 646 | var id = elem[ expando ]; 647 | 648 | // Compute a unique ID for the element 649 | if ( !id ) 650 | id = elem[ expando ] = ++uuid; 651 | 652 | // Only generate the data cache if we're 653 | // trying to access or manipulate it 654 | if ( name && !jQuery.cache[ id ] ) 655 | jQuery.cache[ id ] = {}; 656 | 657 | // Prevent overriding the named cache with undefined values 658 | if ( data != undefined ) 659 | jQuery.cache[ id ][ name ] = data; 660 | 661 | // Return the named cache data, or the ID for the element 662 | return name ? 663 | jQuery.cache[ id ][ name ] : 664 | id; 665 | }, 666 | 667 | removeData: function( elem, name ) { 668 | elem = elem == window ? 669 | windowData : 670 | elem; 671 | 672 | var id = elem[ expando ]; 673 | 674 | // If we want to remove a specific section of the element's data 675 | if ( name ) { 676 | if ( jQuery.cache[ id ] ) { 677 | // Remove the section of cache data 678 | delete jQuery.cache[ id ][ name ]; 679 | 680 | // If we've removed all the data, remove the element's cache 681 | name = ""; 682 | 683 | for ( name in jQuery.cache[ id ] ) 684 | break; 685 | 686 | if ( !name ) 687 | jQuery.removeData( elem ); 688 | } 689 | 690 | // Otherwise, we want to remove all of the element's data 691 | } else { 692 | // Clean up the element expando 693 | try { 694 | delete elem[ expando ]; 695 | } catch(e){ 696 | // IE has trouble directly removing the expando 697 | // but it's ok with using removeAttribute 698 | if ( elem.removeAttribute ) 699 | elem.removeAttribute( expando ); 700 | } 701 | 702 | // Completely remove the data cache 703 | delete jQuery.cache[ id ]; 704 | } 705 | }, 706 | 707 | // args is for internal usage only 708 | each: function( object, callback, args ) { 709 | if ( args ) { 710 | if ( object.length == undefined ) 711 | for ( var name in object ) 712 | callback.apply( object[ name ], args ); 713 | else 714 | for ( var i = 0, length = object.length; i < length; i++ ) 715 | if ( callback.apply( object[ i ], args ) === false ) 716 | break; 717 | 718 | // A special, fast, case for the most common use of each 719 | } else { 720 | if ( object.length == undefined ) 721 | for ( var name in object ) 722 | callback.call( object[ name ], name, object[ name ] ); 723 | else 724 | for ( var i = 0, length = object.length, value = object[0]; 725 | i < length && callback.call( value, i, value ) !== false; value = object[++i] ){} 726 | } 727 | 728 | return object; 729 | }, 730 | 731 | prop: function( elem, value, type, i, name ) { 732 | // Handle executable functions 733 | if ( jQuery.isFunction( value ) ) 734 | value = value.call( elem, i ); 735 | 736 | // Handle passing in a number to a CSS property 737 | return value && value.constructor == Number && type == "curCSS" && !exclude.test( name ) ? 738 | value + "px" : 739 | value; 740 | }, 741 | 742 | className: { 743 | // internal only, use addClass("class") 744 | add: function( elem, classNames ) { 745 | jQuery.each((classNames || "").split(/\s+/), function(i, className){ 746 | if ( elem.nodeType == 1 && !jQuery.className.has( elem.className, className ) ) 747 | elem.className += (elem.className ? " " : "") + className; 748 | }); 749 | }, 750 | 751 | // internal only, use removeClass("class") 752 | remove: function( elem, classNames ) { 753 | if (elem.nodeType == 1) 754 | elem.className = classNames != undefined ? 755 | jQuery.grep(elem.className.split(/\s+/), function(className){ 756 | return !jQuery.className.has( classNames, className ); 757 | }).join(" ") : 758 | ""; 759 | }, 760 | 761 | // internal only, use is(".class") 762 | has: function( elem, className ) { 763 | return jQuery.inArray( className, (elem.className || elem).toString().split(/\s+/) ) > -1; 764 | } 765 | }, 766 | 767 | // A method for quickly swapping in/out CSS properties to get correct calculations 768 | swap: function( elem, options, callback ) { 769 | var old = {}; 770 | // Remember the old values, and insert the new ones 771 | for ( var name in options ) { 772 | old[ name ] = elem.style[ name ]; 773 | elem.style[ name ] = options[ name ]; 774 | } 775 | 776 | callback.call( elem ); 777 | 778 | // Revert the old values 779 | for ( var name in options ) 780 | elem.style[ name ] = old[ name ]; 781 | }, 782 | 783 | css: function( elem, name, force ) { 784 | if ( name == "width" || name == "height" ) { 785 | var val, props = { position: "absolute", visibility: "hidden", display:"block" }, which = name == "width" ? [ "Left", "Right" ] : [ "Top", "Bottom" ]; 786 | 787 | function getWH() { 788 | val = name == "width" ? elem.offsetWidth : elem.offsetHeight; 789 | var padding = 0, border = 0; 790 | jQuery.each( which, function() { 791 | padding += parseFloat(jQuery.curCSS( elem, "padding" + this, true)) || 0; 792 | border += parseFloat(jQuery.curCSS( elem, "border" + this + "Width", true)) || 0; 793 | }); 794 | val -= Math.round(padding + border); 795 | } 796 | 797 | if ( jQuery(elem).is(":visible") ) 798 | getWH(); 799 | else 800 | jQuery.swap( elem, props, getWH ); 801 | 802 | return Math.max(0, val); 803 | } 804 | 805 | return jQuery.curCSS( elem, name, force ); 806 | }, 807 | 808 | curCSS: function( elem, name, force ) { 809 | var ret; 810 | 811 | // A helper method for determining if an element's values are broken 812 | function color( elem ) { 813 | if ( !jQuery.browser.safari ) 814 | return false; 815 | 816 | var ret = document.defaultView.getComputedStyle( elem, null ); 817 | return !ret || ret.getPropertyValue("color") == ""; 818 | } 819 | 820 | // We need to handle opacity special in IE 821 | if ( name == "opacity" && jQuery.browser.msie ) { 822 | ret = jQuery.attr( elem.style, "opacity" ); 823 | 824 | return ret == "" ? 825 | "1" : 826 | ret; 827 | } 828 | // Opera sometimes will give the wrong display answer, this fixes it, see #2037 829 | if ( jQuery.browser.opera && name == "display" ) { 830 | var save = elem.style.display; 831 | elem.style.display = "block"; 832 | elem.style.display = save; 833 | } 834 | 835 | // Make sure we're using the right name for getting the float value 836 | if ( name.match( /float/i ) ) 837 | name = styleFloat; 838 | 839 | if ( !force && elem.style[ name ] ) 840 | ret = elem.style[ name ]; 841 | 842 | else if ( document.defaultView && document.defaultView.getComputedStyle ) { 843 | 844 | // Only "float" is needed here 845 | if ( name.match( /float/i ) ) 846 | name = "float"; 847 | 848 | name = name.replace( /([A-Z])/g, "-$1" ).toLowerCase(); 849 | 850 | var getComputedStyle = document.defaultView.getComputedStyle( elem, null ); 851 | 852 | if ( getComputedStyle && !color( elem ) ) 853 | ret = getComputedStyle.getPropertyValue( name ); 854 | 855 | // If the element isn't reporting its values properly in Safari 856 | // then some display: none elements are involved 857 | else { 858 | var swap = [], stack = []; 859 | 860 | // Locate all of the parent display: none elements 861 | for ( var a = elem; a && color(a); a = a.parentNode ) 862 | stack.unshift(a); 863 | 864 | // Go through and make them visible, but in reverse 865 | // (It would be better if we knew the exact display type that they had) 866 | for ( var i = 0; i < stack.length; i++ ) 867 | if ( color( stack[ i ] ) ) { 868 | swap[ i ] = stack[ i ].style.display; 869 | stack[ i ].style.display = "block"; 870 | } 871 | 872 | // Since we flip the display style, we have to handle that 873 | // one special, otherwise get the value 874 | ret = name == "display" && swap[ stack.length - 1 ] != null ? 875 | "none" : 876 | ( getComputedStyle && getComputedStyle.getPropertyValue( name ) ) || ""; 877 | 878 | // Finally, revert the display styles back 879 | for ( var i = 0; i < swap.length; i++ ) 880 | if ( swap[ i ] != null ) 881 | stack[ i ].style.display = swap[ i ]; 882 | } 883 | 884 | // We should always get a number back from opacity 885 | if ( name == "opacity" && ret == "" ) 886 | ret = "1"; 887 | 888 | } else if ( elem.currentStyle ) { 889 | var camelCase = name.replace(/\-(\w)/g, function(all, letter){ 890 | return letter.toUpperCase(); 891 | }); 892 | 893 | ret = elem.currentStyle[ name ] || elem.currentStyle[ camelCase ]; 894 | 895 | // From the awesome hack by Dean Edwards 896 | // http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291 897 | 898 | // If we're not dealing with a regular pixel number 899 | // but a number that has a weird ending, we need to convert it to pixels 900 | if ( !/^\d+(px)?$/i.test( ret ) && /^\d/.test( ret ) ) { 901 | // Remember the original values 902 | var style = elem.style.left, runtimeStyle = elem.runtimeStyle.left; 903 | 904 | // Put in the new values to get a computed value out 905 | elem.runtimeStyle.left = elem.currentStyle.left; 906 | elem.style.left = ret || 0; 907 | ret = elem.style.pixelLeft + "px"; 908 | 909 | // Revert the changed values 910 | elem.style.left = style; 911 | elem.runtimeStyle.left = runtimeStyle; 912 | } 913 | } 914 | 915 | return ret; 916 | }, 917 | 918 | clean: function( elems, context ) { 919 | var ret = []; 920 | context = context || document; 921 | // !context.createElement fails in IE with an error but returns typeof 'object' 922 | if (typeof context.createElement == 'undefined') 923 | context = context.ownerDocument || context[0] && context[0].ownerDocument || document; 924 | 925 | jQuery.each(elems, function(i, elem){ 926 | if ( !elem ) 927 | return; 928 | 929 | if ( elem.constructor == Number ) 930 | elem = elem.toString(); 931 | 932 | // Convert html string into DOM nodes 933 | if ( typeof elem == "string" ) { 934 | // Fix "XHTML"-style tags in all browsers 935 | elem = elem.replace(/(<(\w+)[^>]*?)\/>/g, function(all, front, tag){ 936 | return tag.match(/^(abbr|br|col|img|input|link|meta|param|hr|area)$/i) ? 937 | all : 938 | front + ">"; 939 | }); 940 | 941 | // Trim whitespace, otherwise indexOf won't work as expected 942 | var tags = jQuery.trim( elem ).toLowerCase(), div = context.createElement("div"); 943 | 944 | var wrap = 945 | // option or optgroup 946 | !tags.indexOf("", "" ] || 948 | 949 | !tags.indexOf("", "" ] || 951 | 952 | tags.match(/^<(thead|tbody|tfoot|colg|cap)/) && 953 | [ 1, "", "
      " ] || 954 | 955 | !tags.indexOf("", "" ] || 957 | 958 | // matched above 959 | (!tags.indexOf("", "" ] || 961 | 962 | !tags.indexOf("", "" ] || 964 | 965 | // IE can't serialize and