├── README.md ├── jquery.querystring.js └── tests ├── index.html ├── jquery.min.js ├── qunit.css ├── qunit.js └── tests.js /README.md: -------------------------------------------------------------------------------- 1 | This plugin attempts to make working with querystrings easier. The primary function exposed is `$.querystring`, which handles both parsing & serialization (depending on the type of argument passed in). A `$.fn.querystring` method is also provided, which makes getting & setting the querystring of `` elements more convenient. 2 | 3 | _Currently only the simplest cases work, but the goal is to support nested objects and arrays. Please refer to the test suite for more info._ 4 | 5 | Basic usage 6 | ----------- 7 | 8 | Objects can be serialized to a querystring fragment: 9 | 10 | $.querystring({name: "John", age:42}); // "name=John&age=42" 11 | 12 | Conversely, querystring fragments can be parsed back into objects: 13 | 14 | $.querystring("name=John&age=42"); // {name: "John", age:42} 15 | 16 | Element methods 17 | --------------- 18 | 19 | You can access an element's querystring directly: 20 | 21 | // Markup: Search for bikes! 22 | $('a').querystring(); // {terms: 'bicycles'} 23 | 24 | and modify it, too: 25 | 26 | $('a').querystring({perPage: 10}); 27 | // Search for bikes! 28 | 29 | You'll notice the new parameter was merged into the existing querystring. You can optionally pass a second argument (boolean) indicating if the existing querystring should be cleared: 30 | 31 | $('a').querystring({foo: 'bar'}, true); 32 | // Search for bikes! 33 | 34 | You can also use this to remove the querystring: 35 | 36 | $('a').querystring({}, true); 37 | 38 | _Right now only `` tags are supported, but support for `
` tags is planned._ 39 | 40 | Syntax 41 | ------ 42 | 43 | The syntax for objects and arrays is similar to that of [Rack](http://rack.rubyforge.org/). For example, an object like this: 44 | 45 | {person: {name: "Johnny"}} 46 | 47 | equates to a string like this: 48 | 49 | "person[name]=Johnny" 50 | 51 | Arrays are similar: 52 | 53 | {drinks: ["Whisky", "Beer", "Wine"]} 54 | 55 | equates to: 56 | 57 | "drinks[]=Whisky&drinks[]=Beer&drinks[]=Wine" 58 | 59 | Tests & Contributing 60 | -------------------- 61 | 62 | Testing is done with [QUnit](http://docs.jquery.com/Qunit). Have a look at [tests/tests.js](https://github.com/kylefox/jquery-querystring/blob/master/tests/tests.js) to learn more about the intended API. Running the tests (ie, opening `tests/index.html` in your browser) will give you an idea of which features remain to be implemented & clarified. 63 | 64 | I would love help with this plugin, so please fork & submit pull requests. Here's an immediate hit-list of things to implement: 65 | 66 | * Parsing & serialization of nested objects & arrays 67 | * Coercion to native types (ex: Number, Boolean) 68 | * Handling of blank/empty values 69 | 70 | -------------------------------------------------------------------------------- /jquery.querystring.js: -------------------------------------------------------------------------------- 1 | (function($) { 2 | 3 | // Naive method of yanking the querystring portion from a string (just splits on the first '?', if present). 4 | function extractQuery(string) { 5 | if(string.indexOf('?') >= 0) { 6 | return string.split('?')[1]; 7 | } else if(string.indexOf('=') >= 0) { 8 | return string; 9 | } else { 10 | return ''; 11 | } 12 | }; 13 | 14 | // Returns the JavaScript value of a querystring parameter. 15 | // Decodes the string & coerces it to the appropriate JavaScript type. 16 | // Examples: 17 | // 'Coffee%20and%20milk' => 'Coffee and milk' 18 | // 'true' => true 19 | // '21' => 21 20 | function parseValue(value) { 21 | value = decodeURIComponent(value); 22 | try { 23 | return JSON.parse(value); 24 | } catch(e) { 25 | return value; 26 | } 27 | } 28 | 29 | // Takes a URL (or fragment) and parses the querystring portion into an object. 30 | // Returns an empty object if there is no querystring. 31 | function parse(url) { 32 | var params = {}, 33 | query = extractQuery(url); 34 | 35 | if(!query) { 36 | return params; 37 | } 38 | 39 | $.each(query.split('&'), function(idx, pair) { 40 | var key, value, oldValue; 41 | pair = pair.split('='); 42 | key = pair[0].replace('[]', ''); // FIXME 43 | value = parseValue(pair[1] || ''); 44 | if (params.hasOwnProperty(key)) { 45 | if (!params[key].push) { 46 | oldValue = params[key]; 47 | params[key] = [oldValue]; 48 | } 49 | params[key].push(value); 50 | } else { 51 | params[key] = value; 52 | } 53 | }); 54 | 55 | return params; 56 | }; 57 | 58 | // Takes an object and converts it to a URL fragment suitable for use as a querystring. 59 | function serialize(params) { 60 | var pairs = [], currentKey, currentValue; 61 | 62 | for(key in params) { 63 | if(params.hasOwnProperty(key)) { 64 | currentKey = key; 65 | currentValue = params[key]; 66 | 67 | if(typeof currentValue === 'object') { 68 | for(subKey in currentValue) { 69 | if(currentValue.hasOwnProperty(subKey)) { 70 | // If subKey is an integer, we have an array. In that case, use `person[]` instead of `person[0]`. 71 | pairs.push(currentKey + '[' + (isNaN(subKey, 10) ? subKey : '') + ']=' + encodeURIComponent(currentValue[subKey])); 72 | } 73 | } 74 | } else { 75 | pairs.push(currentKey + '=' + encodeURIComponent(currentValue)); 76 | } 77 | } 78 | } 79 | 80 | return pairs.join("&"); 81 | }; 82 | 83 | // Public interface. 84 | $.querystring = function(param) { 85 | if(typeof param === 'string') { 86 | return parse(param); 87 | } else { 88 | return serialize(param); 89 | } 90 | }; 91 | 92 | // Adds a method to jQuery objects to get & querystring. 93 | // $('#my_link').querystring(); // => {name: "Joe", job: "Plumber"} 94 | // $('#my_link').querystring({name: 'Jack'}); // => Appends `?name=Jack` to href. 95 | $.fn.querystring = function() { 96 | var elm = $(this), 97 | existingData, 98 | newData = arguments[0] || {}, 99 | clearExisting = arguments[1] || false; 100 | 101 | if(!elm.attr('href')) { 102 | return; 103 | } 104 | 105 | existingData = parse(elm.attr('href')); 106 | 107 | // Get the querystring & bail. 108 | if(arguments.length === 0) { 109 | return existingData; 110 | } 111 | 112 | // Set the querystring. 113 | if(clearExisting) { 114 | existingData = newData; 115 | } else { 116 | $.extend(existingData, newData); 117 | } 118 | elm.attr('href', elm.attr('href').split("?")[0] + "?" + serialize(existingData)); 119 | return elm; 120 | 121 | }; 122 | 123 | })(jQuery); 124 | -------------------------------------------------------------------------------- /tests/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |

jQuery Querystring

12 |

13 |
14 |

15 |
    16 | 17 | -------------------------------------------------------------------------------- /tests/jquery.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * jQuery JavaScript Library v1.5.2 3 | * http://jquery.com/ 4 | * 5 | * Copyright 2011, John Resig 6 | * Dual licensed under the MIT or GPL Version 2 licenses. 7 | * http://jquery.org/license 8 | * 9 | * Includes Sizzle.js 10 | * http://sizzlejs.com/ 11 | * Copyright 2011, The Dojo Foundation 12 | * Released under the MIT, BSD, and GPL Licenses. 13 | * 14 | * Date: Thu Mar 31 15:28:23 2011 -0400 15 | */ 16 | (function(a,b){function ci(a){return d.isWindow(a)?a:a.nodeType===9?a.defaultView||a.parentWindow:!1}function cf(a){if(!b_[a]){var b=d("<"+a+">").appendTo("body"),c=b.css("display");b.remove();if(c==="none"||c==="")c="block";b_[a]=c}return b_[a]}function ce(a,b){var c={};d.each(cd.concat.apply([],cd.slice(0,b)),function(){c[this]=a});return c}function b$(){try{return new a.ActiveXObject("Microsoft.XMLHTTP")}catch(b){}}function bZ(){try{return new a.XMLHttpRequest}catch(b){}}function bY(){d(a).unload(function(){for(var a in bW)bW[a](0,1)})}function bS(a,c){a.dataFilter&&(c=a.dataFilter(c,a.dataType));var e=a.dataTypes,f={},g,h,i=e.length,j,k=e[0],l,m,n,o,p;for(g=1;g=0===c})}function P(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function H(a,b){return(a&&a!=="*"?a+".":"")+b.replace(t,"`").replace(u,"&")}function G(a){var b,c,e,f,g,h,i,j,k,l,m,n,o,p=[],q=[],s=d._data(this,"events");if(a.liveFired!==this&&s&&s.live&&!a.target.disabled&&(!a.button||a.type!=="click")){a.namespace&&(n=new RegExp("(^|\\.)"+a.namespace.split(".").join("\\.(?:.*\\.)?")+"(\\.|$)")),a.liveFired=this;var t=s.live.slice(0);for(i=0;ic)break;a.currentTarget=f.elem,a.data=f.handleObj.data,a.handleObj=f.handleObj,o=f.handleObj.origHandler.apply(f.elem,arguments);if(o===!1||a.isPropagationStopped()){c=f.level,o===!1&&(b=!1);if(a.isImmediatePropagationStopped())break}}return b}}function E(a,c,e){var f=d.extend({},e[0]);f.type=a,f.originalEvent={},f.liveFired=b,d.event.handle.call(c,f),f.isDefaultPrevented()&&e[0].preventDefault()}function y(){return!0}function x(){return!1}function i(a){for(var b in a)if(b!=="toJSON")return!1;return!0}function h(a,c,e){if(e===b&&a.nodeType===1){e=a.getAttribute("data-"+c);if(typeof e==="string"){try{e=e==="true"?!0:e==="false"?!1:e==="null"?null:d.isNaN(e)?g.test(e)?d.parseJSON(e):e:parseFloat(e)}catch(f){}d.data(a,c,e)}else e=b}return e}var c=a.document,d=function(){function G(){if(!d.isReady){try{c.documentElement.doScroll("left")}catch(a){setTimeout(G,1);return}d.ready()}}var d=function(a,b){return new d.fn.init(a,b,g)},e=a.jQuery,f=a.$,g,h=/^(?:[^<]*(<[\w\W]+>)[^>]*$|#([\w\-]+)$)/,i=/\S/,j=/^\s+/,k=/\s+$/,l=/\d/,m=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,n=/^[\],:{}\s]*$/,o=/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,p=/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,q=/(?:^|:|,)(?:\s*\[)+/g,r=/(webkit)[ \/]([\w.]+)/,s=/(opera)(?:.*version)?[ \/]([\w.]+)/,t=/(msie) ([\w.]+)/,u=/(mozilla)(?:.*? rv:([\w.]+))?/,v=navigator.userAgent,w,x,y,z=Object.prototype.toString,A=Object.prototype.hasOwnProperty,B=Array.prototype.push,C=Array.prototype.slice,D=String.prototype.trim,E=Array.prototype.indexOf,F={};d.fn=d.prototype={constructor:d,init:function(a,e,f){var g,i,j,k;if(!a)return this;if(a.nodeType){this.context=this[0]=a,this.length=1;return this}if(a==="body"&&!e&&c.body){this.context=c,this[0]=c.body,this.selector="body",this.length=1;return this}if(typeof a==="string"){g=h.exec(a);if(!g||!g[1]&&e)return!e||e.jquery?(e||f).find(a):this.constructor(e).find(a);if(g[1]){e=e instanceof d?e[0]:e,k=e?e.ownerDocument||e:c,j=m.exec(a),j?d.isPlainObject(e)?(a=[c.createElement(j[1])],d.fn.attr.call(a,e,!0)):a=[k.createElement(j[1])]:(j=d.buildFragment([g[1]],[k]),a=(j.cacheable?d.clone(j.fragment):j.fragment).childNodes);return d.merge(this,a)}i=c.getElementById(g[2]);if(i&&i.parentNode){if(i.id!==g[2])return f.find(a);this.length=1,this[0]=i}this.context=c,this.selector=a;return this}if(d.isFunction(a))return f.ready(a);a.selector!==b&&(this.selector=a.selector,this.context=a.context);return d.makeArray(a,this)},selector:"",jquery:"1.5.2",length:0,size:function(){return this.length},toArray:function(){return C.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var e=this.constructor();d.isArray(a)?B.apply(e,a):d.merge(e,a),e.prevObject=this,e.context=this.context,b==="find"?e.selector=this.selector+(this.selector?" ":"")+c:b&&(e.selector=this.selector+"."+b+"("+c+")");return e},each:function(a,b){return d.each(this,a,b)},ready:function(a){d.bindReady(),x.done(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(C.apply(this,arguments),"slice",C.call(arguments).join(","))},map:function(a){return this.pushStack(d.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:B,sort:[].sort,splice:[].splice},d.fn.init.prototype=d.fn,d.extend=d.fn.extend=function(){var a,c,e,f,g,h,i=arguments[0]||{},j=1,k=arguments.length,l=!1;typeof i==="boolean"&&(l=i,i=arguments[1]||{},j=2),typeof i!=="object"&&!d.isFunction(i)&&(i={}),k===j&&(i=this,--j);for(;j0)return;x.resolveWith(c,[d]),d.fn.trigger&&d(c).trigger("ready").unbind("ready")}},bindReady:function(){if(!x){x=d._Deferred();if(c.readyState==="complete")return setTimeout(d.ready,1);if(c.addEventListener)c.addEventListener("DOMContentLoaded",y,!1),a.addEventListener("load",d.ready,!1);else if(c.attachEvent){c.attachEvent("onreadystatechange",y),a.attachEvent("onload",d.ready);var b=!1;try{b=a.frameElement==null}catch(e){}c.documentElement.doScroll&&b&&G()}}},isFunction:function(a){return d.type(a)==="function"},isArray:Array.isArray||function(a){return d.type(a)==="array"},isWindow:function(a){return a&&typeof a==="object"&&"setInterval"in a},isNaN:function(a){return a==null||!l.test(a)||isNaN(a)},type:function(a){return a==null?String(a):F[z.call(a)]||"object"},isPlainObject:function(a){if(!a||d.type(a)!=="object"||a.nodeType||d.isWindow(a))return!1;if(a.constructor&&!A.call(a,"constructor")&&!A.call(a.constructor.prototype,"isPrototypeOf"))return!1;var c;for(c in a){}return c===b||A.call(a,c)},isEmptyObject:function(a){for(var b in a)return!1;return!0},error:function(a){throw a},parseJSON:function(b){if(typeof b!=="string"||!b)return null;b=d.trim(b);if(n.test(b.replace(o,"@").replace(p,"]").replace(q,"")))return a.JSON&&a.JSON.parse?a.JSON.parse(b):(new Function("return "+b))();d.error("Invalid JSON: "+b)},parseXML:function(b,c,e){a.DOMParser?(e=new DOMParser,c=e.parseFromString(b,"text/xml")):(c=new ActiveXObject("Microsoft.XMLDOM"),c.async="false",c.loadXML(b)),e=c.documentElement,(!e||!e.nodeName||e.nodeName==="parsererror")&&d.error("Invalid XML: "+b);return c},noop:function(){},globalEval:function(a){if(a&&i.test(a)){var b=c.head||c.getElementsByTagName("head")[0]||c.documentElement,e=c.createElement("script");d.support.scriptEval()?e.appendChild(c.createTextNode(a)):e.text=a,b.insertBefore(e,b.firstChild),b.removeChild(e)}},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,e){var f,g=0,h=a.length,i=h===b||d.isFunction(a);if(e){if(i){for(f in a)if(c.apply(a[f],e)===!1)break}else for(;g1?f.call(arguments,0):c,--g||h.resolveWith(h,f.call(b,0))}}var b=arguments,c=0,e=b.length,g=e,h=e<=1&&a&&d.isFunction(a.promise)?a:d.Deferred();if(e>1){for(;c
    a";var e=b.getElementsByTagName("*"),f=b.getElementsByTagName("a")[0],g=c.createElement("select"),h=g.appendChild(c.createElement("option")),i=b.getElementsByTagName("input")[0];if(e&&e.length&&f){d.support={leadingWhitespace:b.firstChild.nodeType===3,tbody:!b.getElementsByTagName("tbody").length,htmlSerialize:!!b.getElementsByTagName("link").length,style:/red/.test(f.getAttribute("style")),hrefNormalized:f.getAttribute("href")==="/a",opacity:/^0.55$/.test(f.style.opacity),cssFloat:!!f.style.cssFloat,checkOn:i.value==="on",optSelected:h.selected,deleteExpando:!0,optDisabled:!1,checkClone:!1,noCloneEvent:!0,noCloneChecked:!0,boxModel:null,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableHiddenOffsets:!0,reliableMarginRight:!0},i.checked=!0,d.support.noCloneChecked=i.cloneNode(!0).checked,g.disabled=!0,d.support.optDisabled=!h.disabled;var j=null;d.support.scriptEval=function(){if(j===null){var b=c.documentElement,e=c.createElement("script"),f="script"+d.now();try{e.appendChild(c.createTextNode("window."+f+"=1;"))}catch(g){}b.insertBefore(e,b.firstChild),a[f]?(j=!0,delete a[f]):j=!1,b.removeChild(e)}return j};try{delete b.test}catch(k){d.support.deleteExpando=!1}!b.addEventListener&&b.attachEvent&&b.fireEvent&&(b.attachEvent("onclick",function l(){d.support.noCloneEvent=!1,b.detachEvent("onclick",l)}),b.cloneNode(!0).fireEvent("onclick")),b=c.createElement("div"),b.innerHTML="";var m=c.createDocumentFragment();m.appendChild(b.firstChild),d.support.checkClone=m.cloneNode(!0).cloneNode(!0).lastChild.checked,d(function(){var a=c.createElement("div"),b=c.getElementsByTagName("body")[0];if(b){a.style.width=a.style.paddingLeft="1px",b.appendChild(a),d.boxModel=d.support.boxModel=a.offsetWidth===2,"zoom"in a.style&&(a.style.display="inline",a.style.zoom=1,d.support.inlineBlockNeedsLayout=a.offsetWidth===2,a.style.display="",a.innerHTML="
    ",d.support.shrinkWrapBlocks=a.offsetWidth!==2),a.innerHTML="
    t
    ";var e=a.getElementsByTagName("td");d.support.reliableHiddenOffsets=e[0].offsetHeight===0,e[0].style.display="",e[1].style.display="none",d.support.reliableHiddenOffsets=d.support.reliableHiddenOffsets&&e[0].offsetHeight===0,a.innerHTML="",c.defaultView&&c.defaultView.getComputedStyle&&(a.style.width="1px",a.style.marginRight="0",d.support.reliableMarginRight=(parseInt(c.defaultView.getComputedStyle(a,null).marginRight,10)||0)===0),b.removeChild(a).style.display="none",a=e=null}});var n=function(a){var b=c.createElement("div");a="on"+a;if(!b.attachEvent)return!0;var d=a in b;d||(b.setAttribute(a,"return;"),d=typeof b[a]==="function");return d};d.support.submitBubbles=n("submit"),d.support.changeBubbles=n("change"),b=e=f=null}}();var g=/^(?:\{.*\}|\[.*\])$/;d.extend({cache:{},uuid:0,expando:"jQuery"+(d.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){a=a.nodeType?d.cache[a[d.expando]]:a[d.expando];return!!a&&!i(a)},data:function(a,c,e,f){if(d.acceptData(a)){var g=d.expando,h=typeof c==="string",i,j=a.nodeType,k=j?d.cache:a,l=j?a[d.expando]:a[d.expando]&&d.expando;if((!l||f&&l&&!k[l][g])&&h&&e===b)return;l||(j?a[d.expando]=l=++d.uuid:l=d.expando),k[l]||(k[l]={},j||(k[l].toJSON=d.noop));if(typeof c==="object"||typeof c==="function")f?k[l][g]=d.extend(k[l][g],c):k[l]=d.extend(k[l],c);i=k[l],f&&(i[g]||(i[g]={}),i=i[g]),e!==b&&(i[c]=e);if(c==="events"&&!i[c])return i[g]&&i[g].events;return h?i[c]:i}},removeData:function(b,c,e){if(d.acceptData(b)){var f=d.expando,g=b.nodeType,h=g?d.cache:b,j=g?b[d.expando]:d.expando;if(!h[j])return;if(c){var k=e?h[j][f]:h[j];if(k){delete k[c];if(!i(k))return}}if(e){delete h[j][f];if(!i(h[j]))return}var l=h[j][f];d.support.deleteExpando||h!=a?delete h[j]:h[j]=null,l?(h[j]={},g||(h[j].toJSON=d.noop),h[j][f]=l):g&&(d.support.deleteExpando?delete b[d.expando]:b.removeAttribute?b.removeAttribute(d.expando):b[d.expando]=null)}},_data:function(a,b,c){return d.data(a,b,c,!0)},acceptData:function(a){if(a.nodeName){var b=d.noData[a.nodeName.toLowerCase()];if(b)return b!==!0&&a.getAttribute("classid")===b}return!0}}),d.fn.extend({data:function(a,c){var e=null;if(typeof a==="undefined"){if(this.length){e=d.data(this[0]);if(this[0].nodeType===1){var f=this[0].attributes,g;for(var i=0,j=f.length;i-1)return!0;return!1},val:function(a){if(!arguments.length){var c=this[0];if(c){if(d.nodeName(c,"option")){var e=c.attributes.value;return!e||e.specified?c.value:c.text}if(d.nodeName(c,"select")){var f=c.selectedIndex,g=[],h=c.options,i=c.type==="select-one";if(f<0)return null;for(var j=i?f:0,k=i?f+1:h.length;j=0;else if(d.nodeName(this,"select")){var f=d.makeArray(e);d("option",this).each(function(){this.selected=d.inArray(d(this).val(),f)>=0}),f.length||(this.selectedIndex=-1)}else this.value=e}})}}),d.extend({attrFn:{val:!0,css:!0,html:!0,text:!0,data:!0,width:!0,height:!0,offset:!0},attr:function(a,c,e,f){if(!a||a.nodeType===3||a.nodeType===8||a.nodeType===2)return b;if(f&&c in d.attrFn)return d(a)[c](e);var g=a.nodeType!==1||!d.isXMLDoc(a),h=e!==b;c=g&&d.props[c]||c;if(a.nodeType===1){var i=m.test(c);if(c==="selected"&&!d.support.optSelected){var j=a.parentNode;j&&(j.selectedIndex,j.parentNode&&j.parentNode.selectedIndex)}if((c in a||a[c]!==b)&&g&&!i){h&&(c==="type"&&n.test(a.nodeName)&&a.parentNode&&d.error("type property can't be changed"),e===null?a.nodeType===1&&a.removeAttribute(c):a[c]=e);if(d.nodeName(a,"form")&&a.getAttributeNode(c))return a.getAttributeNode(c).nodeValue;if(c==="tabIndex"){var k=a.getAttributeNode("tabIndex");return k&&k.specified?k.value:o.test(a.nodeName)||p.test(a.nodeName)&&a.href?0:b}return a[c]}if(!d.support.style&&g&&c==="style"){h&&(a.style.cssText=""+e);return a.style.cssText}h&&a.setAttribute(c,""+e);if(!a.attributes[c]&&(a.hasAttribute&&!a.hasAttribute(c)))return b;var l=!d.support.hrefNormalized&&g&&i?a.getAttribute(c,2):a.getAttribute(c);return l===null?b:l}h&&(a[c]=e);return a[c]}});var r=/\.(.*)$/,s=/^(?:textarea|input|select)$/i,t=/\./g,u=/ /g,v=/[^\w\s.|`]/g,w=function(a){return a.replace(v,"\\$&")};d.event={add:function(c,e,f,g){if(c.nodeType!==3&&c.nodeType!==8){try{d.isWindow(c)&&(c!==a&&!c.frameElement)&&(c=a)}catch(h){}if(f===!1)f=x;else if(!f)return;var i,j;f.handler&&(i=f,f=i.handler),f.guid||(f.guid=d.guid++);var k=d._data(c);if(!k)return;var l=k.events,m=k.handle;l||(k.events=l={}),m||(k.handle=m=function(a){return typeof d!=="undefined"&&d.event.triggered!==a.type?d.event.handle.apply(m.elem,arguments):b}),m.elem=c,e=e.split(" ");var n,o=0,p;while(n=e[o++]){j=i?d.extend({},i):{handler:f,data:g},n.indexOf(".")>-1?(p=n.split("."),n=p.shift(),j.namespace=p.slice(0).sort().join(".")):(p=[],j.namespace=""),j.type=n,j.guid||(j.guid=f.guid);var q=l[n],r=d.event.special[n]||{};if(!q){q=l[n]=[];if(!r.setup||r.setup.call(c,g,p,m)===!1)c.addEventListener?c.addEventListener(n,m,!1):c.attachEvent&&c.attachEvent("on"+n,m)}r.add&&(r.add.call(c,j),j.handler.guid||(j.handler.guid=f.guid)),q.push(j),d.event.global[n]=!0}c=null}},global:{},remove:function(a,c,e,f){if(a.nodeType!==3&&a.nodeType!==8){e===!1&&(e=x);var g,h,i,j,k=0,l,m,n,o,p,q,r,s=d.hasData(a)&&d._data(a),t=s&&s.events;if(!s||!t)return;c&&c.type&&(e=c.handler,c=c.type);if(!c||typeof c==="string"&&c.charAt(0)==="."){c=c||"";for(h in t)d.event.remove(a,h+c);return}c=c.split(" ");while(h=c[k++]){r=h,q=null,l=h.indexOf(".")<0,m=[],l||(m=h.split("."),h=m.shift(),n=new RegExp("(^|\\.)"+d.map(m.slice(0).sort(),w).join("\\.(?:.*\\.)?")+"(\\.|$)")),p=t[h];if(!p)continue;if(!e){for(j=0;j=0&&(a.type=f=f.slice(0,-1),a.exclusive=!0),e||(a.stopPropagation(),d.event.global[f]&&d.each(d.cache,function(){var b=d.expando,e=this[b];e&&e.events&&e.events[f]&&d.event.trigger(a,c,e.handle.elem)}));if(!e||e.nodeType===3||e.nodeType===8)return b;a.result=b,a.target=e,c=d.makeArray(c),c.unshift(a)}a.currentTarget=e;var h=d._data(e,"handle");h&&h.apply(e,c);var i=e.parentNode||e.ownerDocument;try{e&&e.nodeName&&d.noData[e.nodeName.toLowerCase()]||e["on"+f]&&e["on"+f].apply(e,c)===!1&&(a.result=!1,a.preventDefault())}catch(j){}if(!a.isPropagationStopped()&&i)d.event.trigger(a,c,i,!0);else if(!a.isDefaultPrevented()){var k,l=a.target,m=f.replace(r,""),n=d.nodeName(l,"a")&&m==="click",o=d.event.special[m]||{};if((!o._default||o._default.call(e,a)===!1)&&!n&&!(l&&l.nodeName&&d.noData[l.nodeName.toLowerCase()])){try{l[m]&&(k=l["on"+m],k&&(l["on"+m]=null),d.event.triggered=a.type,l[m]())}catch(p){}k&&(l["on"+m]=k),d.event.triggered=b}}},handle:function(c){var e,f,g,h,i,j=[],k=d.makeArray(arguments);c=k[0]=d.event.fix(c||a.event),c.currentTarget=this,e=c.type.indexOf(".")<0&&!c.exclusive,e||(g=c.type.split("."),c.type=g.shift(),j=g.slice(0).sort(),h=new RegExp("(^|\\.)"+j.join("\\.(?:.*\\.)?")+"(\\.|$)")),c.namespace=c.namespace||j.join("."),i=d._data(this,"events"),f=(i||{})[c.type];if(i&&f){f=f.slice(0);for(var l=0,m=f.length;l-1?d.map(a.options,function(a){return a.selected}).join("-"):"":a.nodeName.toLowerCase()==="select"&&(c=a.selectedIndex);return c},D=function D(a){var c=a.target,e,f;if(s.test(c.nodeName)&&!c.readOnly){e=d._data(c,"_change_data"),f=C(c),(a.type!=="focusout"||c.type!=="radio")&&d._data(c,"_change_data",f);if(e===b||f===e)return;if(e!=null||f)a.type="change",a.liveFired=b,d.event.trigger(a,arguments[1],c)}};d.event.special.change={filters:{focusout:D,beforedeactivate:D,click:function(a){var b=a.target,c=b.type;(c==="radio"||c==="checkbox"||b.nodeName.toLowerCase()==="select")&&D.call(this,a)},keydown:function(a){var b=a.target,c=b.type;(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(c==="checkbox"||c==="radio")||c==="select-multiple")&&D.call(this,a)},beforeactivate:function(a){var b=a.target;d._data(b,"_change_data",C(b))}},setup:function(a,b){if(this.type==="file")return!1;for(var c in B)d.event.add(this,c+".specialChange",B[c]);return s.test(this.nodeName)},teardown:function(a){d.event.remove(this,".specialChange");return s.test(this.nodeName)}},B=d.event.special.change.filters,B.focus=B.beforeactivate}c.addEventListener&&d.each({focus:"focusin",blur:"focusout"},function(a,b){function f(a){var c=d.event.fix(a);c.type=b,c.originalEvent={},d.event.trigger(c,null,c.target),c.isDefaultPrevented()&&a.preventDefault()}var e=0;d.event.special[b]={setup:function(){e++===0&&c.addEventListener(a,f,!0)},teardown:function(){--e===0&&c.removeEventListener(a,f,!0)}}}),d.each(["bind","one"],function(a,c){d.fn[c]=function(a,e,f){if(typeof a==="object"){for(var g in a)this[c](g,e,a[g],f);return this}if(d.isFunction(e)||e===!1)f=e,e=b;var h=c==="one"?d.proxy(f,function(a){d(this).unbind(a,h);return f.apply(this,arguments)}):f;if(a==="unload"&&c!=="one")this.one(a,e,f);else for(var i=0,j=this.length;i0?this.bind(b,a,c):this.trigger(b)},d.attrFn&&(d.attrFn[b]=!0)}),function(){function u(a,b,c,d,e,f){for(var g=0,h=d.length;g0){j=i;break}}i=i[a]}d[g]=j}}}function t(a,b,c,d,e,f){for(var g=0,h=d.length;g+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,e=0,f=Object.prototype.toString,g=!1,h=!0,i=/\\/g,j=/\W/;[0,0].sort(function(){h=!1;return 0});var k=function(b,d,e,g){e=e||[],d=d||c;var h=d;if(d.nodeType!==1&&d.nodeType!==9)return[];if(!b||typeof b!=="string")return e;var i,j,n,o,q,r,s,t,u=!0,w=k.isXML(d),x=[],y=b;do{a.exec(""),i=a.exec(y);if(i){y=i[3],x.push(i[1]);if(i[2]){o=i[3];break}}}while(i);if(x.length>1&&m.exec(b))if(x.length===2&&l.relative[x[0]])j=v(x[0]+x[1],d);else{j=l.relative[x[0]]?[d]:k(x.shift(),d);while(x.length)b=x.shift(),l.relative[b]&&(b+=x.shift()),j=v(b,j)}else{!g&&x.length>1&&d.nodeType===9&&!w&&l.match.ID.test(x[0])&&!l.match.ID.test(x[x.length-1])&&(q=k.find(x.shift(),d,w),d=q.expr?k.filter(q.expr,q.set)[0]:q.set[0]);if(d){q=g?{expr:x.pop(),set:p(g)}:k.find(x.pop(),x.length===1&&(x[0]==="~"||x[0]==="+")&&d.parentNode?d.parentNode:d,w),j=q.expr?k.filter(q.expr,q.set):q.set,x.length>0?n=p(j):u=!1;while(x.length)r=x.pop(),s=r,l.relative[r]?s=x.pop():r="",s==null&&(s=d),l.relative[r](n,s,w)}else n=x=[]}n||(n=j),n||k.error(r||b);if(f.call(n)==="[object Array]")if(u)if(d&&d.nodeType===1)for(t=0;n[t]!=null;t++)n[t]&&(n[t]===!0||n[t].nodeType===1&&k.contains(d,n[t]))&&e.push(j[t]);else for(t=0;n[t]!=null;t++)n[t]&&n[t].nodeType===1&&e.push(j[t]);else e.push.apply(e,n);else p(n,e);o&&(k(o,h,e,g),k.uniqueSort(e));return e};k.uniqueSort=function(a){if(r){g=h,a.sort(r);if(g)for(var b=1;b0},k.find=function(a,b,c){var d;if(!a)return[];for(var e=0,f=l.order.length;e":function(a,b){var c,d=typeof b==="string",e=0,f=a.length;if(d&&!j.test(b)){b=b.toLowerCase();for(;e=0)?c||d.push(h):c&&(b[g]=!1));return!1},ID:function(a){return a[1].replace(i,"")},TAG:function(a,b){return a[1].replace(i,"").toLowerCase()},CHILD:function(a){if(a[1]==="nth"){a[2]||k.error(a[0]),a[2]=a[2].replace(/^\+|\s*/g,"");var b=/(-?)(\d*)(?:n([+\-]?\d*))?/.exec(a[2]==="even"&&"2n"||a[2]==="odd"&&"2n+1"||!/\D/.test(a[2])&&"0n+"+a[2]||a[2]);a[2]=b[1]+(b[2]||1)-0,a[3]=b[3]-0}else a[2]&&k.error(a[0]);a[0]=e++;return a},ATTR:function(a,b,c,d,e,f){var g=a[1]=a[1].replace(i,"");!f&&l.attrMap[g]&&(a[1]=l.attrMap[g]),a[4]=(a[4]||a[5]||"").replace(i,""),a[2]==="~="&&(a[4]=" "+a[4]+" ");return a},PSEUDO:function(b,c,d,e,f){if(b[1]==="not")if((a.exec(b[3])||"").length>1||/^\w/.test(b[3]))b[3]=k(b[3],null,null,c);else{var g=k.filter(b[3],c,d,!0^f);d||e.push.apply(e,g);return!1}else if(l.match.POS.test(b[0])||l.match.CHILD.test(b[0]))return!0;return b},POS:function(a){a.unshift(!0);return a}},filters:{enabled:function(a){return a.disabled===!1&&a.type!=="hidden"},disabled:function(a){return a.disabled===!0},checked:function(a){return a.checked===!0},selected:function(a){a.parentNode&&a.parentNode.selectedIndex;return a.selected===!0},parent:function(a){return!!a.firstChild},empty:function(a){return!a.firstChild},has:function(a,b,c){return!!k(c[3],a).length},header:function(a){return/h\d/i.test(a.nodeName)},text:function(a){var b=a.getAttribute("type"),c=a.type;return"text"===c&&(b===c||b===null)},radio:function(a){return"radio"===a.type},checkbox:function(a){return"checkbox"===a.type},file:function(a){return"file"===a.type},password:function(a){return"password"===a.type},submit:function(a){return"submit"===a.type},image:function(a){return"image"===a.type},reset:function(a){return"reset"===a.type},button:function(a){return"button"===a.type||a.nodeName.toLowerCase()==="button"},input:function(a){return/input|select|textarea|button/i.test(a.nodeName)}},setFilters:{first:function(a,b){return b===0},last:function(a,b,c,d){return b===d.length-1},even:function(a,b){return b%2===0},odd:function(a,b){return b%2===1},lt:function(a,b,c){return bc[3]-0},nth:function(a,b,c){return c[3]-0===b},eq:function(a,b,c){return c[3]-0===b}},filter:{PSEUDO:function(a,b,c,d){var e=b[1],f=l.filters[e];if(f)return f(a,c,b,d);if(e==="contains")return(a.textContent||a.innerText||k.getText([a])||"").indexOf(b[3])>=0;if(e==="not"){var g=b[3];for(var h=0,i=g.length;h=0}},ID:function(a,b){return a.nodeType===1&&a.getAttribute("id")===b},TAG:function(a,b){return b==="*"&&a.nodeType===1||a.nodeName.toLowerCase()===b},CLASS:function(a,b){return(" "+(a.className||a.getAttribute("class"))+" ").indexOf(b)>-1},ATTR:function(a,b){var c=b[1],d=l.attrHandle[c]?l.attrHandle[c](a):a[c]!=null?a[c]:a.getAttribute(c),e=d+"",f=b[2],g=b[4];return d==null?f==="!=":f==="="?e===g:f==="*="?e.indexOf(g)>=0:f==="~="?(" "+e+" ").indexOf(g)>=0:g?f==="!="?e!==g:f==="^="?e.indexOf(g)===0:f==="$="?e.substr(e.length-g.length)===g:f==="|="?e===g||e.substr(0,g.length+1)===g+"-":!1:e&&d!==!1},POS:function(a,b,c,d){var e=b[2],f=l.setFilters[e];if(f)return f(a,c,b,d)}}},m=l.match.POS,n=function(a,b){return"\\"+(b-0+1)};for(var o in l.match)l.match[o]=new RegExp(l.match[o].source+/(?![^\[]*\])(?![^\(]*\))/.source),l.leftMatch[o]=new RegExp(/(^(?:.|\r|\n)*?)/.source+l.match[o].source.replace(/\\(\d+)/g,n));var p=function(a,b){a=Array.prototype.slice.call(a,0);if(b){b.push.apply(b,a);return b}return a};try{Array.prototype.slice.call(c.documentElement.childNodes,0)[0].nodeType}catch(q){p=function(a,b){var c=0,d=b||[];if(f.call(a)==="[object Array]")Array.prototype.push.apply(d,a);else if(typeof a.length==="number")for(var e=a.length;c",e.insertBefore(a,e.firstChild),c.getElementById(d)&&(l.find.ID=function(a,c,d){if(typeof c.getElementById!=="undefined"&&!d){var e=c.getElementById(a[1]);return e?e.id===a[1]||typeof e.getAttributeNode!=="undefined"&&e.getAttributeNode("id").nodeValue===a[1]?[e]:b:[]}},l.filter.ID=function(a,b){var c=typeof a.getAttributeNode!=="undefined"&&a.getAttributeNode("id");return a.nodeType===1&&c&&c.nodeValue===b}),e.removeChild(a),e=a=null}(),function(){var a=c.createElement("div");a.appendChild(c.createComment("")),a.getElementsByTagName("*").length>0&&(l.find.TAG=function(a,b){var c=b.getElementsByTagName(a[1]);if(a[1]==="*"){var d=[];for(var e=0;c[e];e++)c[e].nodeType===1&&d.push(c[e]);c=d}return c}),a.innerHTML="",a.firstChild&&typeof a.firstChild.getAttribute!=="undefined"&&a.firstChild.getAttribute("href")!=="#"&&(l.attrHandle.href=function(a){return a.getAttribute("href",2)}),a=null}(),c.querySelectorAll&&function(){var a=k,b=c.createElement("div"),d="__sizzle__";b.innerHTML="

    ";if(!b.querySelectorAll||b.querySelectorAll(".TEST").length!==0){k=function(b,e,f,g){e=e||c;if(!g&&!k.isXML(e)){var h=/^(\w+$)|^\.([\w\-]+$)|^#([\w\-]+$)/.exec(b);if(h&&(e.nodeType===1||e.nodeType===9)){if(h[1])return p(e.getElementsByTagName(b),f);if(h[2]&&l.find.CLASS&&e.getElementsByClassName)return p(e.getElementsByClassName(h[2]),f)}if(e.nodeType===9){if(b==="body"&&e.body)return p([e.body],f);if(h&&h[3]){var i=e.getElementById(h[3]);if(!i||!i.parentNode)return p([],f);if(i.id===h[3])return p([i],f)}try{return p(e.querySelectorAll(b),f)}catch(j){}}else if(e.nodeType===1&&e.nodeName.toLowerCase()!=="object"){var m=e,n=e.getAttribute("id"),o=n||d,q=e.parentNode,r=/^\s*[+~]/.test(b);n?o=o.replace(/'/g,"\\$&"):e.setAttribute("id",o),r&&q&&(e=e.parentNode);try{if(!r||q)return p(e.querySelectorAll("[id='"+o+"'] "+b),f)}catch(s){}finally{n||m.removeAttribute("id")}}}return a(b,e,f,g)};for(var e in a)k[e]=a[e];b=null}}(),function(){var a=c.documentElement,b=a.matchesSelector||a.mozMatchesSelector||a.webkitMatchesSelector||a.msMatchesSelector;if(b){var d=!b.call(c.createElement("div"),"div"),e=!1;try{b.call(c.documentElement,"[test!='']:sizzle")}catch(f){e=!0}k.matchesSelector=function(a,c){c=c.replace(/\=\s*([^'"\]]*)\s*\]/g,"='$1']");if(!k.isXML(a))try{if(e||!l.match.PSEUDO.test(c)&&!/!=/.test(c)){var f=b.call(a,c);if(f||!d||a.document&&a.document.nodeType!==11)return f}}catch(g){}return k(c,null,null,[a]).length>0}}}(),function(){var a=c.createElement("div");a.innerHTML="
    ";if(a.getElementsByClassName&&a.getElementsByClassName("e").length!==0){a.lastChild.className="e";if(a.getElementsByClassName("e").length===1)return;l.order.splice(1,0,"CLASS"),l.find.CLASS=function(a,b,c){if(typeof b.getElementsByClassName!=="undefined"&&!c)return b.getElementsByClassName(a[1])},a=null}}(),c.documentElement.contains?k.contains=function(a,b){return a!==b&&(a.contains?a.contains(b):!0)}:c.documentElement.compareDocumentPosition?k.contains=function(a,b){return!!(a.compareDocumentPosition(b)&16)}:k.contains=function(){return!1},k.isXML=function(a){var b=(a?a.ownerDocument||a:0).documentElement;return b?b.nodeName!=="HTML":!1};var v=function(a,b){var c,d=[],e="",f=b.nodeType?[b]:b;while(c=l.match.PSEUDO.exec(a))e+=c[0],a=a.replace(l.match.PSEUDO,"");a=l.relative[a]?a+"*":a;for(var g=0,h=f.length;g0)for(var g=c;g0},closest:function(a,b){var c=[],e,f,g=this[0];if(d.isArray(a)){var h,i,j={},k=1;if(g&&a.length){for(e=0,f=a.length;e-1:d(g).is(h))&&c.push({selector:i,elem:g,level:k});g=g.parentNode,k++}}return c}var l=N.test(a)?d(a,b||this.context):null;for(e=0,f=this.length;e-1:d.find.matchesSelector(g,a)){c.push(g);break}g=g.parentNode;if(!g||!g.ownerDocument||g===b)break}}c=c.length>1?d.unique(c):c;return this.pushStack(c,"closest",a)},index:function(a){if(!a||typeof a==="string")return d.inArray(this[0],a?d(a):this.parent().children());return d.inArray(a.jquery?a[0]:a,this)},add:function(a,b){var c=typeof a==="string"?d(a,b):d.makeArray(a),e=d.merge(this.get(),c);return this.pushStack(P(c[0])||P(e[0])?e:d.unique(e))},andSelf:function(){return this.add(this.prevObject)}}),d.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return d.dir(a,"parentNode")},parentsUntil:function(a,b,c){return d.dir(a,"parentNode",c)},next:function(a){return d.nth(a,2,"nextSibling")},prev:function(a){return d.nth(a,2,"previousSibling")},nextAll:function(a){return d.dir(a,"nextSibling")},prevAll:function(a){return d.dir(a,"previousSibling")},nextUntil:function(a,b,c){return d.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return d.dir(a,"previousSibling",c)},siblings:function(a){return d.sibling(a.parentNode.firstChild,a)},children:function(a){return d.sibling(a.firstChild)},contents:function(a){return d.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:d.makeArray(a.childNodes)}},function(a,b){d.fn[a]=function(c,e){var f=d.map(this,b,c),g=M.call(arguments);I.test(a)||(e=c),e&&typeof e==="string"&&(f=d.filter(e,f)),f=this.length>1&&!O[a]?d.unique(f):f,(this.length>1||K.test(e))&&J.test(a)&&(f=f.reverse());return this.pushStack(f,a,g.join(","))}}),d.extend({filter:function(a,b,c){c&&(a=":not("+a+")");return b.length===1?d.find.matchesSelector(b[0],a)?[b[0]]:[]:d.find.matches(a,b)},dir:function(a,c,e){var f=[],g=a[c];while(g&&g.nodeType!==9&&(e===b||g.nodeType!==1||!d(g).is(e)))g.nodeType===1&&f.push(g),g=g[c];return f},nth:function(a,b,c,d){b=b||1;var e=0;for(;a;a=a[c])if(a.nodeType===1&&++e===b)break;return a},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var R=/ jQuery\d+="(?:\d+|null)"/g,S=/^\s+/,T=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/ig,U=/<([\w:]+)/,V=/",""],legend:[1,"
    ","
    "],thead:[1,"","
    "],tr:[2,"","
    "],td:[3,"","
    "],col:[2,"","
    "],area:[1,"",""],_default:[0,"",""]};Z.optgroup=Z.option,Z.tbody=Z.tfoot=Z.colgroup=Z.caption=Z.thead,Z.th=Z.td,d.support.htmlSerialize||(Z._default=[1,"div
    ","
    "]),d.fn.extend({text:function(a){if(d.isFunction(a))return this.each(function(b){var c=d(this);c.text(a.call(this,b,c.text()))});if(typeof a!=="object"&&a!==b)return this.empty().append((this[0]&&this[0].ownerDocument||c).createTextNode(a));return d.text(this)},wrapAll:function(a){if(d.isFunction(a))return this.each(function(b){d(this).wrapAll(a.call(this,b))});if(this[0]){var b=d(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){if(d.isFunction(a))return this.each(function(b){d(this).wrapInner(a.call(this,b))});return this.each(function(){var b=d(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){d(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){d.nodeName(this,"body")||d(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=d(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,d(arguments[0]).toArray());return a}},remove:function(a,b){for(var c=0,e;(e=this[c])!=null;c++)if(!a||d.filter(a,[e]).length)!b&&e.nodeType===1&&(d.cleanData(e.getElementsByTagName("*")),d.cleanData([e])),e.parentNode&&e.parentNode.removeChild(e);return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++){b.nodeType===1&&d.cleanData(b.getElementsByTagName("*"));while(b.firstChild)b.removeChild(b.firstChild)}return this},clone:function(a,b){a=a==null?!1:a,b=b==null?a:b;return this.map(function(){return d.clone(this,a,b)})},html:function(a){if(a===b)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(R,""):null;if(typeof a!=="string"||X.test(a)||!d.support.leadingWhitespace&&S.test(a)||Z[(U.exec(a)||["",""])[1].toLowerCase()])d.isFunction(a)?this.each(function(b){var c=d(this);c.html(a.call(this,b,c.html()))}):this.empty().append(a);else{a=a.replace(T,"<$1>");try{for(var c=0,e=this.length;c1&&l0?this.clone(!0):this).get();d(f[h])[b](j),e=e.concat(j)}return this.pushStack(e,a,f.selector)}}),d.extend({clone:function(a,b,c){var e=a.cloneNode(!0),f,g,h;if((!d.support.noCloneEvent||!d.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!d.isXMLDoc(a)){ba(a,e),f=bb(a),g=bb(e);for(h=0;f[h];++h)ba(f[h],g[h])}if(b){_(a,e);if(c){f=bb(a),g=bb(e);for(h=0;f[h];++h)_(f[h],g[h])}}return e},clean:function(a,b,e,f){b=b||c,typeof b.createElement==="undefined"&&(b=b.ownerDocument||b[0]&&b[0].ownerDocument||c);var g=[];for(var h=0,i;(i=a[h])!=null;h++){typeof i==="number"&&(i+="");if(!i)continue;if(typeof i!=="string"||W.test(i)){if(typeof i==="string"){i=i.replace(T,"<$1>");var j=(U.exec(i)||["",""])[1].toLowerCase(),k=Z[j]||Z._default,l=k[0],m=b.createElement("div");m.innerHTML=k[1]+i+k[2];while(l--)m=m.lastChild;if(!d.support.tbody){var n=V.test(i),o=j==="table"&&!n?m.firstChild&&m.firstChild.childNodes:k[1]===""&&!n?m.childNodes:[];for(var p=o.length-1;p>=0;--p)d.nodeName(o[p],"tbody")&&!o[p].childNodes.length&&o[p].parentNode.removeChild(o[p])}!d.support.leadingWhitespace&&S.test(i)&&m.insertBefore(b.createTextNode(S.exec(i)[0]),m.firstChild),i=m.childNodes}}else i=b.createTextNode(i);i.nodeType?g.push(i):g=d.merge(g,i)}if(e)for(h=0;g[h];h++)!f||!d.nodeName(g[h],"script")||g[h].type&&g[h].type.toLowerCase()!=="text/javascript"?(g[h].nodeType===1&&g.splice.apply(g,[h+1,0].concat(d.makeArray(g[h].getElementsByTagName("script")))),e.appendChild(g[h])):f.push(g[h].parentNode?g[h].parentNode.removeChild(g[h]):g[h]);return g},cleanData:function(a){var b,c,e=d.cache,f=d.expando,g=d.event.special,h=d.support.deleteExpando;for(var i=0,j;(j=a[i])!=null;i++){if(j.nodeName&&d.noData[j.nodeName.toLowerCase()])continue;c=j[d.expando];if(c){b=e[c]&&e[c][f];if(b&&b.events){for(var k in b.events)g[k]?d.event.remove(j,k):d.removeEvent(j,k,b.handle);b.handle&&(b.handle.elem=null)}h?delete j[d.expando]:j.removeAttribute&&j.removeAttribute(d.expando),delete e[c]}}}});var bd=/alpha\([^)]*\)/i,be=/opacity=([^)]*)/,bf=/-([a-z])/ig,bg=/([A-Z]|^ms)/g,bh=/^-?\d+(?:px)?$/i,bi=/^-?\d/,bj={position:"absolute",visibility:"hidden",display:"block"},bk=["Left","Right"],bl=["Top","Bottom"],bm,bn,bo,bp=function(a,b){return b.toUpperCase()};d.fn.css=function(a,c){if(arguments.length===2&&c===b)return this;return d.access(this,a,c,!0,function(a,c,e){return e!==b?d.style(a,c,e):d.css(a,c)})},d.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=bm(a,"opacity","opacity");return c===""?"1":c}return a.style.opacity}}},cssNumber:{zIndex:!0,fontWeight:!0,opacity:!0,zoom:!0,lineHeight:!0},cssProps:{"float":d.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,e,f){if(a&&a.nodeType!==3&&a.nodeType!==8&&a.style){var g,h=d.camelCase(c),i=a.style,j=d.cssHooks[h];c=d.cssProps[h]||h;if(e===b){if(j&&"get"in j&&(g=j.get(a,!1,f))!==b)return g;return i[c]}if(typeof e==="number"&&isNaN(e)||e==null)return;typeof e==="number"&&!d.cssNumber[h]&&(e+="px");if(!j||!("set"in j)||(e=j.set(a,e))!==b)try{i[c]=e}catch(k){}}},css:function(a,c,e){var f,g=d.camelCase(c),h=d.cssHooks[g];c=d.cssProps[g]||g;if(h&&"get"in h&&(f=h.get(a,!0,e))!==b)return f;if(bm)return bm(a,c,g)},swap:function(a,b,c){var d={};for(var e in b)d[e]=a.style[e],a.style[e]=b[e];c.call(a);for(e in b)a.style[e]=d[e]},camelCase:function(a){return a.replace(bf,bp)}}),d.curCSS=d.css,d.each(["height","width"],function(a,b){d.cssHooks[b]={get:function(a,c,e){var f;if(c){a.offsetWidth!==0?f=bq(a,b,e):d.swap(a,bj,function(){f=bq(a,b,e)});if(f<=0){f=bm(a,b,b),f==="0px"&&bo&&(f=bo(a,b,b));if(f!=null)return f===""||f==="auto"?"0px":f}if(f<0||f==null){f=a.style[b];return f===""||f==="auto"?"0px":f}return typeof f==="string"?f:f+"px"}},set:function(a,b){if(!bh.test(b))return b;b=parseFloat(b);if(b>=0)return b+"px"}}}),d.support.opacity||(d.cssHooks.opacity={get:function(a,b){return be.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?parseFloat(RegExp.$1)/100+"":b?"1":""},set:function(a,b){var c=a.style;c.zoom=1;var e=d.isNaN(b)?"":"alpha(opacity="+b*100+")",f=c.filter||"";c.filter=bd.test(f)?f.replace(bd,e):c.filter+" "+e}}),d(function(){d.support.reliableMarginRight||(d.cssHooks.marginRight={get:function(a,b){var c;d.swap(a,{display:"inline-block"},function(){b?c=bm(a,"margin-right","marginRight"):c=a.style.marginRight});return c}})}),c.defaultView&&c.defaultView.getComputedStyle&&(bn=function(a,c,e){var f,g,h;e=e.replace(bg,"-$1").toLowerCase();if(!(g=a.ownerDocument.defaultView))return b;if(h=g.getComputedStyle(a,null))f=h.getPropertyValue(e),f===""&&!d.contains(a.ownerDocument.documentElement,a)&&(f=d.style(a,e));return f}),c.documentElement.currentStyle&&(bo=function(a,b){var c,d=a.currentStyle&&a.currentStyle[b],e=a.runtimeStyle&&a.runtimeStyle[b],f=a.style;!bh.test(d)&&bi.test(d)&&(c=f.left,e&&(a.runtimeStyle.left=a.currentStyle.left),f.left=b==="fontSize"?"1em":d||0,d=f.pixelLeft+"px",f.left=c,e&&(a.runtimeStyle.left=e));return d===""?"auto":d}),bm=bn||bo,d.expr&&d.expr.filters&&(d.expr.filters.hidden=function(a){var b=a.offsetWidth,c=a.offsetHeight;return b===0&&c===0||!d.support.reliableHiddenOffsets&&(a.style.display||d.css(a,"display"))==="none"},d.expr.filters.visible=function(a){return!d.expr.filters.hidden(a)});var br=/%20/g,bs=/\[\]$/,bt=/\r?\n/g,bu=/#.*$/,bv=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,bw=/^(?:color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,bx=/^(?:about|app|app\-storage|.+\-extension|file|widget):$/,by=/^(?:GET|HEAD)$/,bz=/^\/\//,bA=/\?/,bB=/)<[^<]*)*<\/script>/gi,bC=/^(?:select|textarea)/i,bD=/\s+/,bE=/([?&])_=[^&]*/,bF=/(^|\-)([a-z])/g,bG=function(a,b,c){return b+c.toUpperCase()},bH=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+))?)?/,bI=d.fn.load,bJ={},bK={},bL,bM;try{bL=c.location.href}catch(bN){bL=c.createElement("a"),bL.href="",bL=bL.href}bM=bH.exec(bL.toLowerCase())||[],d.fn.extend({load:function(a,c,e){if(typeof a!=="string"&&bI)return bI.apply(this,arguments);if(!this.length)return this;var f=a.indexOf(" ");if(f>=0){var g=a.slice(f,a.length);a=a.slice(0,f)}var h="GET";c&&(d.isFunction(c)?(e=c,c=b):typeof c==="object"&&(c=d.param(c,d.ajaxSettings.traditional),h="POST"));var i=this;d.ajax({url:a,type:h,dataType:"html",data:c,complete:function(a,b,c){c=a.responseText,a.isResolved()&&(a.done(function(a){c=a}),i.html(g?d("
    ").append(c.replace(bB,"")).find(g):c)),e&&i.each(e,[c,b,a])}});return this},serialize:function(){return d.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?d.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||bC.test(this.nodeName)||bw.test(this.type))}).map(function(a,b){var c=d(this).val();return c==null?null:d.isArray(c)?d.map(c,function(a,c){return{name:b.name,value:a.replace(bt,"\r\n")}}):{name:b.name,value:c.replace(bt,"\r\n")}}).get()}}),d.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){d.fn[b]=function(a){return this.bind(b,a)}}),d.each(["get","post"],function(a,c){d[c]=function(a,e,f,g){d.isFunction(e)&&(g=g||f,f=e,e=b);return d.ajax({type:c,url:a,data:e,success:f,dataType:g})}}),d.extend({getScript:function(a,c){return d.get(a,b,c,"script")},getJSON:function(a,b,c){return d.get(a,b,c,"json")},ajaxSetup:function(a,b){b?d.extend(!0,a,d.ajaxSettings,b):(b=a,a=d.extend(!0,d.ajaxSettings,b));for(var c in {context:1,url:1})c in b?a[c]=b[c]:c in d.ajaxSettings&&(a[c]=d.ajaxSettings[c]);return a},ajaxSettings:{url:bL,isLocal:bx.test(bM[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":"*/*"},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":d.parseJSON,"text xml":d.parseXML}},ajaxPrefilter:bO(bJ),ajaxTransport:bO(bK),ajax:function(a,c){function v(a,c,l,n){if(r!==2){r=2,p&&clearTimeout(p),o=b,m=n||"",u.readyState=a?4:0;var q,t,v,w=l?bR(e,u,l):b,x,y;if(a>=200&&a<300||a===304){if(e.ifModified){if(x=u.getResponseHeader("Last-Modified"))d.lastModified[k]=x;if(y=u.getResponseHeader("Etag"))d.etag[k]=y}if(a===304)c="notmodified",q=!0;else try{t=bS(e,w),c="success",q=!0}catch(z){c="parsererror",v=z}}else{v=c;if(!c||a)c="error",a<0&&(a=0)}u.status=a,u.statusText=c,q?h.resolveWith(f,[t,c,u]):h.rejectWith(f,[u,c,v]),u.statusCode(j),j=b,s&&g.trigger("ajax"+(q?"Success":"Error"),[u,e,q?t:v]),i.resolveWith(f,[u,c]),s&&(g.trigger("ajaxComplete",[u,e]),--d.active||d.event.trigger("ajaxStop"))}}typeof a==="object"&&(c=a,a=b),c=c||{};var e=d.ajaxSetup({},c),f=e.context||e,g=f!==e&&(f.nodeType||f instanceof d)?d(f):d.event,h=d.Deferred(),i=d._Deferred(),j=e.statusCode||{},k,l={},m,n,o,p,q,r=0,s,t,u={readyState:0,setRequestHeader:function(a,b){r||(l[a.toLowerCase().replace(bF,bG)]=b);return this},getAllResponseHeaders:function(){return r===2?m:null},getResponseHeader:function(a){var c;if(r===2){if(!n){n={};while(c=bv.exec(m))n[c[1].toLowerCase()]=c[2]}c=n[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){r||(e.mimeType=a);return this},abort:function(a){a=a||"abort",o&&o.abort(a),v(0,a);return this}};h.promise(u),u.success=u.done,u.error=u.fail,u.complete=i.done,u.statusCode=function(a){if(a){var b;if(r<2)for(b in a)j[b]=[j[b],a[b]];else b=a[u.status],u.then(b,b)}return this},e.url=((a||e.url)+"").replace(bu,"").replace(bz,bM[1]+"//"),e.dataTypes=d.trim(e.dataType||"*").toLowerCase().split(bD),e.crossDomain==null&&(q=bH.exec(e.url.toLowerCase()),e.crossDomain=q&&(q[1]!=bM[1]||q[2]!=bM[2]||(q[3]||(q[1]==="http:"?80:443))!=(bM[3]||(bM[1]==="http:"?80:443)))),e.data&&e.processData&&typeof e.data!=="string"&&(e.data=d.param(e.data,e.traditional)),bP(bJ,e,c,u);if(r===2)return!1;s=e.global,e.type=e.type.toUpperCase(),e.hasContent=!by.test(e.type),s&&d.active++===0&&d.event.trigger("ajaxStart");if(!e.hasContent){e.data&&(e.url+=(bA.test(e.url)?"&":"?")+e.data),k=e.url;if(e.cache===!1){var w=d.now(),x=e.url.replace(bE,"$1_="+w);e.url=x+(x===e.url?(bA.test(e.url)?"&":"?")+"_="+w:"")}}if(e.data&&e.hasContent&&e.contentType!==!1||c.contentType)l["Content-Type"]=e.contentType;e.ifModified&&(k=k||e.url,d.lastModified[k]&&(l["If-Modified-Since"]=d.lastModified[k]),d.etag[k]&&(l["If-None-Match"]=d.etag[k])),l.Accept=e.dataTypes[0]&&e.accepts[e.dataTypes[0]]?e.accepts[e.dataTypes[0]]+(e.dataTypes[0]!=="*"?", */*; q=0.01":""):e.accepts["*"];for(t in e.headers)u.setRequestHeader(t,e.headers[t]);if(e.beforeSend&&(e.beforeSend.call(f,u,e)===!1||r===2)){u.abort();return!1}for(t in {success:1,error:1,complete:1})u[t](e[t]);o=bP(bK,e,c,u);if(o){u.readyState=1,s&&g.trigger("ajaxSend",[u,e]),e.async&&e.timeout>0&&(p=setTimeout(function(){u.abort("timeout")},e.timeout));try{r=1,o.send(l,v)}catch(y){status<2?v(-1,y):d.error(y)}}else v(-1,"No Transport");return u},param:function(a,c){var e=[],f=function(a,b){b=d.isFunction(b)?b():b,e[e.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=d.ajaxSettings.traditional);if(d.isArray(a)||a.jquery&&!d.isPlainObject(a))d.each(a,function(){f(this.name,this.value)});else for(var g in a)bQ(g,a[g],c,f);return e.join("&").replace(br,"+")}}),d.extend({active:0,lastModified:{},etag:{}});var bT=d.now(),bU=/(\=)\?(&|$)|\?\?/i;d.ajaxSetup({jsonp:"callback",jsonpCallback:function(){return d.expando+"_"+bT++}}),d.ajaxPrefilter("json jsonp",function(b,c,e){var f=typeof b.data==="string";if(b.dataTypes[0]==="jsonp"||c.jsonpCallback||c.jsonp!=null||b.jsonp!==!1&&(bU.test(b.url)||f&&bU.test(b.data))){var g,h=b.jsonpCallback=d.isFunction(b.jsonpCallback)?b.jsonpCallback():b.jsonpCallback,i=a[h],j=b.url,k=b.data,l="$1"+h+"$2",m=function(){a[h]=i,g&&d.isFunction(i)&&a[h](g[0])};b.jsonp!==!1&&(j=j.replace(bU,l),b.url===j&&(f&&(k=k.replace(bU,l)),b.data===k&&(j+=(/\?/.test(j)?"&":"?")+b.jsonp+"="+h))),b.url=j,b.data=k,a[h]=function(a){g=[a]},e.then(m,m),b.converters["script json"]=function(){g||d.error(h+" was not called");return g[0]},b.dataTypes[0]="json";return"script"}}),d.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){d.globalEval(a);return a}}}),d.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),d.ajaxTransport("script",function(a){if(a.crossDomain){var d,e=c.head||c.getElementsByTagName("head")[0]||c.documentElement;return{send:function(f,g){d=c.createElement("script"),d.async="async",a.scriptCharset&&(d.charset=a.scriptCharset),d.src=a.url,d.onload=d.onreadystatechange=function(a,c){if(!d.readyState||/loaded|complete/.test(d.readyState))d.onload=d.onreadystatechange=null,e&&d.parentNode&&e.removeChild(d),d=b,c||g(200,"success")},e.insertBefore(d,e.firstChild)},abort:function(){d&&d.onload(0,1)}}}});var bV=d.now(),bW,bX;d.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&bZ()||b$()}:bZ,bX=d.ajaxSettings.xhr(),d.support.ajax=!!bX,d.support.cors=bX&&"withCredentials"in bX,bX=b,d.support.ajax&&d.ajaxTransport(function(a){if(!a.crossDomain||d.support.cors){var c;return{send:function(e,f){var g=a.xhr(),h,i;a.username?g.open(a.type,a.url,a.async,a.username,a.password):g.open(a.type,a.url,a.async);if(a.xhrFields)for(i in a.xhrFields)g[i]=a.xhrFields[i];a.mimeType&&g.overrideMimeType&&g.overrideMimeType(a.mimeType),!a.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(i in e)g.setRequestHeader(i,e[i])}catch(j){}g.send(a.hasContent&&a.data||null),c=function(e,i){var j,k,l,m,n;try{if(c&&(i||g.readyState===4)){c=b,h&&(g.onreadystatechange=d.noop,delete bW[h]);if(i)g.readyState!==4&&g.abort();else{j=g.status,l=g.getAllResponseHeaders(),m={},n=g.responseXML,n&&n.documentElement&&(m.xml=n),m.text=g.responseText;try{k=g.statusText}catch(o){k=""}j||!a.isLocal||a.crossDomain?j===1223&&(j=204):j=m.text?200:404}}}catch(p){i||f(-1,p)}m&&f(j,k,m,l)},a.async&&g.readyState!==4?(bW||(bW={},bY()),h=bV++,g.onreadystatechange=bW[h]=c):c()},abort:function(){c&&c(0,1)}}}});var b_={},ca=/^(?:toggle|show|hide)$/,cb=/^([+\-]=)?([\d+.\-]+)([a-z%]*)$/i,cc,cd=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];d.fn.extend({show:function(a,b,c){var e,f;if(a||a===0)return this.animate(ce("show",3),a,b,c);for(var g=0,h=this.length;g=0;a--)c[a].elem===this&&(b&&c[a](!0),c.splice(a,1))}),b||this.dequeue();return this}}),d.each({slideDown:ce("show",1),slideUp:ce("hide",1),slideToggle:ce("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"},fadeToggle:{opacity:"toggle"}},function(a,b){d.fn[a]=function(a,c,d){return this.animate(b,a,c,d)}}),d.extend({speed:function(a,b,c){var e=a&&typeof a==="object"?d.extend({},a):{complete:c||!c&&b||d.isFunction(a)&&a,duration:a,easing:c&&b||b&&!d.isFunction(b)&&b};e.duration=d.fx.off?0:typeof e.duration==="number"?e.duration:e.duration in d.fx.speeds?d.fx.speeds[e.duration]:d.fx.speeds._default,e.old=e.complete,e.complete=function(){e.queue!==!1&&d(this).dequeue(),d.isFunction(e.old)&&e.old.call(this)};return e},easing:{linear:function(a,b,c,d){return c+d*a},swing:function(a,b,c,d){return(-Math.cos(a*Math.PI)/2+.5)*d+c}},timers:[],fx:function(a,b,c){this.options=b,this.elem=a,this.prop=c,b.orig||(b.orig={})}}),d.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this),(d.fx.step[this.prop]||d.fx.step._default)(this)},cur:function(){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];var a,b=d.css(this.elem,this.prop);return isNaN(a=parseFloat(b))?!b||b==="auto"?0:b:a},custom:function(a,b,c){function g(a){return e.step(a)}var e=this,f=d.fx;this.startTime=d.now(),this.start=a,this.end=b,this.unit=c||this.unit||(d.cssNumber[this.prop]?"":"px"),this.now=this.start,this.pos=this.state=0,g.elem=this.elem,g()&&d.timers.push(g)&&!cc&&(cc=setInterval(f.tick,f.interval))},show:function(){this.options.orig[this.prop]=d.style(this.elem,this.prop),this.options.show=!0,this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur()),d(this.elem).show()},hide:function(){this.options.orig[this.prop]=d.style(this.elem,this.prop),this.options.hide=!0,this.custom(this.cur(),0)},step:function(a){var b=d.now(),c=!0;if(a||b>=this.options.duration+this.startTime){this.now=this.end,this.pos=this.state=1,this.update(),this.options.curAnim[this.prop]=!0;for(var e in this.options.curAnim)this.options.curAnim[e]!==!0&&(c=!1);if(c){if(this.options.overflow!=null&&!d.support.shrinkWrapBlocks){var f=this.elem,g=this.options;d.each(["","X","Y"],function(a,b){f.style["overflow"+b]=g.overflow[a]})}this.options.hide&&d(this.elem).hide();if(this.options.hide||this.options.show)for(var h in this.options.curAnim)d.style(this.elem,h,this.options.orig[h]);this.options.complete.call(this.elem)}return!1}var i=b-this.startTime;this.state=i/this.options.duration;var j=this.options.specialEasing&&this.options.specialEasing[this.prop],k=this.options.easing||(d.easing.swing?"swing":"linear");this.pos=d.easing[j||k](this.state,i,0,1,this.options.duration),this.now=this.start+(this.end-this.start)*this.pos,this.update();return!0}},d.extend(d.fx,{tick:function(){var a=d.timers;for(var b=0;b
    ";d.extend(b.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"}),b.innerHTML=j,a.insertBefore(b,a.firstChild),e=b.firstChild,f=e.firstChild,h=e.nextSibling.firstChild.firstChild,this.doesNotAddBorder=f.offsetTop!==5,this.doesAddBorderForTableAndCells=h.offsetTop===5,f.style.position="fixed",f.style.top="20px",this.supportsFixedPosition=f.offsetTop===20||f.offsetTop===15,f.style.position=f.style.top="",e.style.overflow="hidden",e.style.position="relative",this.subtractsBorderForOverflowNotVisible=f.offsetTop===-5,this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==i,a.removeChild(b),d.offset.initialize=d.noop},bodyOffset:function(a){var b=a.offsetTop,c=a.offsetLeft;d.offset.initialize(),d.offset.doesNotIncludeMarginInBodyOffset&&(b+=parseFloat(d.css(a,"marginTop"))||0,c+=parseFloat(d.css(a,"marginLeft"))||0);return{top:b,left:c}},setOffset:function(a,b,c){var e=d.css(a,"position");e==="static"&&(a.style.position="relative");var f=d(a),g=f.offset(),h=d.css(a,"top"),i=d.css(a,"left"),j=(e==="absolute"||e==="fixed")&&d.inArray("auto",[h,i])>-1,k={},l={},m,n;j&&(l=f.position()),m=j?l.top:parseInt(h,10)||0,n=j?l.left:parseInt(i,10)||0,d.isFunction(b)&&(b=b.call(a,c,g)),b.top!=null&&(k.top=b.top-g.top+m),b.left!=null&&(k.left=b.left-g.left+n),"using"in b?b.using.call(a,k):f.css(k)}},d.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),c=this.offset(),e=ch.test(b[0].nodeName)?{top:0,left:0}:b.offset();c.top-=parseFloat(d.css(a,"marginTop"))||0,c.left-=parseFloat(d.css(a,"marginLeft"))||0,e.top+=parseFloat(d.css(b[0],"borderTopWidth"))||0,e.left+=parseFloat(d.css(b[0],"borderLeftWidth"))||0;return{top:c.top-e.top,left:c.left-e.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||c.body;while(a&&(!ch.test(a.nodeName)&&d.css(a,"position")==="static"))a=a.offsetParent;return a})}}),d.each(["Left","Top"],function(a,c){var e="scroll"+c;d.fn[e]=function(c){var f=this[0],g;if(!f)return null;if(c!==b)return this.each(function(){g=ci(this),g?g.scrollTo(a?d(g).scrollLeft():c,a?c:d(g).scrollTop()):this[e]=c});g=ci(f);return g?"pageXOffset"in g?g[a?"pageYOffset":"pageXOffset"]:d.support.boxModel&&g.document.documentElement[e]||g.document.body[e]:f[e]}}),d.each(["Height","Width"],function(a,c){var e=c.toLowerCase();d.fn["inner"+c]=function(){return this[0]?parseFloat(d.css(this[0],e,"padding")):null},d.fn["outer"+c]=function(a){return this[0]?parseFloat(d.css(this[0],e,a?"margin":"border")):null},d.fn[e]=function(a){var f=this[0];if(!f)return a==null?null:this;if(d.isFunction(a))return this.each(function(b){var c=d(this);c[e](a.call(this,b,c[e]()))});if(d.isWindow(f)){var g=f.document.documentElement["client"+c];return f.document.compatMode==="CSS1Compat"&&g||f.document.body["client"+c]||g}if(f.nodeType===9)return Math.max(f.documentElement["client"+c],f.body["scroll"+c],f.documentElement["scroll"+c],f.body["offset"+c],f.documentElement["offset"+c]);if(a===b){var h=d.css(f,e),i=parseFloat(h);return d.isNaN(i)?h:i}return this.css(e,typeof a==="string"?a:a+"px")}}),a.jQuery=a.$=d})(window); -------------------------------------------------------------------------------- /tests/qunit.css: -------------------------------------------------------------------------------- 1 | /** Font Family and Sizes */ 2 | 3 | #qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult { 4 | font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial; 5 | } 6 | 7 | #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; } 8 | #qunit-tests { font-size: smaller; } 9 | 10 | 11 | /** Resets */ 12 | 13 | #qunit-tests, #qunit-tests ol, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult { 14 | margin: 0; 15 | padding: 0; 16 | } 17 | 18 | 19 | /** Header */ 20 | 21 | #qunit-header { 22 | padding: 0.5em 0 0.5em 1em; 23 | 24 | color: #8699a4; 25 | background-color: #0d3349; 26 | 27 | font-size: 1.5em; 28 | line-height: 1em; 29 | font-weight: normal; 30 | 31 | border-radius: 15px 15px 0 0; 32 | -moz-border-radius: 15px 15px 0 0; 33 | -webkit-border-top-right-radius: 15px; 34 | -webkit-border-top-left-radius: 15px; 35 | } 36 | 37 | #qunit-header a { 38 | text-decoration: none; 39 | color: #c2ccd1; 40 | } 41 | 42 | #qunit-header a:hover, 43 | #qunit-header a:focus { 44 | color: #fff; 45 | } 46 | 47 | #qunit-banner { 48 | height: 5px; 49 | } 50 | 51 | #qunit-testrunner-toolbar { 52 | padding: 0.5em 0 0.5em 2em; 53 | color: #5E740B; 54 | background-color: #eee; 55 | } 56 | 57 | #qunit-userAgent { 58 | padding: 0.5em 0 0.5em 2.5em; 59 | background-color: #2b81af; 60 | color: #fff; 61 | text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px; 62 | } 63 | 64 | 65 | /** Tests: Pass/Fail */ 66 | 67 | #qunit-tests { 68 | list-style-position: inside; 69 | } 70 | 71 | #qunit-tests li { 72 | padding: 0.4em 0.5em 0.4em 2.5em; 73 | border-bottom: 1px solid #fff; 74 | list-style-position: inside; 75 | } 76 | 77 | #qunit-tests.hidepass li.pass, #qunit-tests.hidepass li.running { 78 | display: none; 79 | } 80 | 81 | #qunit-tests li strong { 82 | cursor: pointer; 83 | } 84 | 85 | #qunit-tests li a { 86 | padding: 0.5em; 87 | color: #c2ccd1; 88 | text-decoration: none; 89 | } 90 | #qunit-tests li a:hover, 91 | #qunit-tests li a:focus { 92 | color: #000; 93 | } 94 | 95 | #qunit-tests ol { 96 | margin-top: 0.5em; 97 | padding: 0.5em; 98 | 99 | background-color: #fff; 100 | 101 | border-radius: 15px; 102 | -moz-border-radius: 15px; 103 | -webkit-border-radius: 15px; 104 | 105 | box-shadow: inset 0px 2px 13px #999; 106 | -moz-box-shadow: inset 0px 2px 13px #999; 107 | -webkit-box-shadow: inset 0px 2px 13px #999; 108 | } 109 | 110 | #qunit-tests table { 111 | border-collapse: collapse; 112 | margin-top: .2em; 113 | } 114 | 115 | #qunit-tests th { 116 | text-align: right; 117 | vertical-align: top; 118 | padding: 0 .5em 0 0; 119 | } 120 | 121 | #qunit-tests td { 122 | vertical-align: top; 123 | } 124 | 125 | #qunit-tests pre { 126 | margin: 0; 127 | white-space: pre-wrap; 128 | word-wrap: break-word; 129 | } 130 | 131 | #qunit-tests del { 132 | background-color: #e0f2be; 133 | color: #374e0c; 134 | text-decoration: none; 135 | } 136 | 137 | #qunit-tests ins { 138 | background-color: #ffcaca; 139 | color: #500; 140 | text-decoration: none; 141 | } 142 | 143 | /*** Test Counts */ 144 | 145 | #qunit-tests b.counts { color: black; } 146 | #qunit-tests b.passed { color: #5E740B; } 147 | #qunit-tests b.failed { color: #710909; } 148 | 149 | #qunit-tests li li { 150 | margin: 0.5em; 151 | padding: 0.4em 0.5em 0.4em 0.5em; 152 | background-color: #fff; 153 | border-bottom: none; 154 | list-style-position: inside; 155 | } 156 | 157 | /*** Passing Styles */ 158 | 159 | #qunit-tests li li.pass { 160 | color: #5E740B; 161 | background-color: #fff; 162 | border-left: 26px solid #C6E746; 163 | } 164 | 165 | #qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; } 166 | #qunit-tests .pass .test-name { color: #366097; } 167 | 168 | #qunit-tests .pass .test-actual, 169 | #qunit-tests .pass .test-expected { color: #999999; } 170 | 171 | #qunit-banner.qunit-pass { background-color: #C6E746; } 172 | 173 | /*** Failing Styles */ 174 | 175 | #qunit-tests li li.fail { 176 | color: #710909; 177 | background-color: #fff; 178 | border-left: 26px solid #EE5757; 179 | } 180 | 181 | #qunit-tests > li:last-child { 182 | border-radius: 0 0 15px 15px; 183 | -moz-border-radius: 0 0 15px 15px; 184 | -webkit-border-bottom-right-radius: 15px; 185 | -webkit-border-bottom-left-radius: 15px; 186 | } 187 | 188 | #qunit-tests .fail { color: #000000; background-color: #EE5757; } 189 | #qunit-tests .fail .test-name, 190 | #qunit-tests .fail .module-name { color: #000000; } 191 | 192 | #qunit-tests .fail .test-actual { color: #EE5757; } 193 | #qunit-tests .fail .test-expected { color: green; } 194 | 195 | #qunit-banner.qunit-fail { background-color: #EE5757; } 196 | 197 | 198 | /** Result */ 199 | 200 | #qunit-testresult { 201 | padding: 0.5em 0.5em 0.5em 2.5em; 202 | 203 | color: #2b81af; 204 | background-color: #D2E0E6; 205 | 206 | border-bottom: 1px solid white; 207 | } 208 | 209 | /** Fixture */ 210 | 211 | #qunit-fixture { 212 | position: absolute; 213 | top: -10000px; 214 | left: -10000px; 215 | } 216 | -------------------------------------------------------------------------------- /tests/qunit.js: -------------------------------------------------------------------------------- 1 | /* 2 | * QUnit - A JavaScript Unit Testing Framework 3 | * 4 | * http://docs.jquery.com/QUnit 5 | * 6 | * Copyright (c) 2011 John Resig, Jörn Zaefferer 7 | * Dual licensed under the MIT (MIT-LICENSE.txt) 8 | * or GPL (GPL-LICENSE.txt) licenses. 9 | */ 10 | 11 | (function(window) { 12 | 13 | var defined = { 14 | setTimeout: typeof window.setTimeout !== "undefined", 15 | sessionStorage: (function() { 16 | try { 17 | return !!sessionStorage.getItem; 18 | } catch(e){ 19 | return false; 20 | } 21 | })() 22 | }; 23 | 24 | var testId = 0; 25 | 26 | var Test = function(name, testName, expected, testEnvironmentArg, async, callback) { 27 | this.name = name; 28 | this.testName = testName; 29 | this.expected = expected; 30 | this.testEnvironmentArg = testEnvironmentArg; 31 | this.async = async; 32 | this.callback = callback; 33 | this.assertions = []; 34 | }; 35 | Test.prototype = { 36 | init: function() { 37 | var tests = id("qunit-tests"); 38 | if (tests) { 39 | var b = document.createElement("strong"); 40 | b.innerHTML = "Running " + this.name; 41 | var li = document.createElement("li"); 42 | li.appendChild( b ); 43 | li.className = "running"; 44 | li.id = this.id = "test-output" + testId++; 45 | tests.appendChild( li ); 46 | } 47 | }, 48 | setup: function() { 49 | if (this.module != config.previousModule) { 50 | if ( config.previousModule ) { 51 | QUnit.moduleDone( { 52 | name: config.previousModule, 53 | failed: config.moduleStats.bad, 54 | passed: config.moduleStats.all - config.moduleStats.bad, 55 | total: config.moduleStats.all 56 | } ); 57 | } 58 | config.previousModule = this.module; 59 | config.moduleStats = { all: 0, bad: 0 }; 60 | QUnit.moduleStart( { 61 | name: this.module 62 | } ); 63 | } 64 | 65 | config.current = this; 66 | this.testEnvironment = extend({ 67 | setup: function() {}, 68 | teardown: function() {} 69 | }, this.moduleTestEnvironment); 70 | if (this.testEnvironmentArg) { 71 | extend(this.testEnvironment, this.testEnvironmentArg); 72 | } 73 | 74 | QUnit.testStart( { 75 | name: this.testName 76 | } ); 77 | 78 | // allow utility functions to access the current test environment 79 | // TODO why?? 80 | QUnit.current_testEnvironment = this.testEnvironment; 81 | 82 | try { 83 | if ( !config.pollution ) { 84 | saveGlobal(); 85 | } 86 | 87 | this.testEnvironment.setup.call(this.testEnvironment); 88 | } catch(e) { 89 | QUnit.ok( false, "Setup failed on " + this.testName + ": " + e.message ); 90 | } 91 | }, 92 | run: function() { 93 | if ( this.async ) { 94 | QUnit.stop(); 95 | } 96 | 97 | if ( config.notrycatch ) { 98 | this.callback.call(this.testEnvironment); 99 | return; 100 | } 101 | try { 102 | this.callback.call(this.testEnvironment); 103 | } catch(e) { 104 | fail("Test " + this.testName + " died, exception and test follows", e, this.callback); 105 | QUnit.ok( false, "Died on test #" + (this.assertions.length + 1) + ": " + e.message + " - " + QUnit.jsDump.parse(e) ); 106 | // else next test will carry the responsibility 107 | saveGlobal(); 108 | 109 | // Restart the tests if they're blocking 110 | if ( config.blocking ) { 111 | start(); 112 | } 113 | } 114 | }, 115 | teardown: function() { 116 | try { 117 | checkPollution(); 118 | this.testEnvironment.teardown.call(this.testEnvironment); 119 | } catch(e) { 120 | QUnit.ok( false, "Teardown failed on " + this.testName + ": " + e.message ); 121 | } 122 | }, 123 | finish: function() { 124 | if ( this.expected && this.expected != this.assertions.length ) { 125 | QUnit.ok( false, "Expected " + this.expected + " assertions, but " + this.assertions.length + " were run" ); 126 | } 127 | 128 | var good = 0, bad = 0, 129 | tests = id("qunit-tests"); 130 | 131 | config.stats.all += this.assertions.length; 132 | config.moduleStats.all += this.assertions.length; 133 | 134 | if ( tests ) { 135 | var ol = document.createElement("ol"); 136 | 137 | for ( var i = 0; i < this.assertions.length; i++ ) { 138 | var assertion = this.assertions[i]; 139 | 140 | var li = document.createElement("li"); 141 | li.className = assertion.result ? "pass" : "fail"; 142 | li.innerHTML = assertion.message || (assertion.result ? "okay" : "failed"); 143 | ol.appendChild( li ); 144 | 145 | if ( assertion.result ) { 146 | good++; 147 | } else { 148 | bad++; 149 | config.stats.bad++; 150 | config.moduleStats.bad++; 151 | } 152 | } 153 | 154 | // store result when possible 155 | if ( QUnit.config.reorder && defined.sessionStorage ) { 156 | if (bad) { 157 | sessionStorage.setItem("qunit-" + this.module + "-" + this.testName, bad) 158 | } else { 159 | sessionStorage.removeItem("qunit-" + this.testName); 160 | } 161 | } 162 | 163 | if (bad == 0) { 164 | ol.style.display = "none"; 165 | } 166 | 167 | var b = document.createElement("strong"); 168 | b.innerHTML = this.name + " (" + bad + ", " + good + ", " + this.assertions.length + ")"; 169 | 170 | var a = document.createElement("a"); 171 | a.innerHTML = "Rerun"; 172 | a.href = QUnit.url({ filter: getText([b]).replace(/\([^)]+\)$/, "").replace(/(^\s*|\s*$)/g, "") }); 173 | 174 | addEvent(b, "click", function() { 175 | var next = b.nextSibling.nextSibling, 176 | display = next.style.display; 177 | next.style.display = display === "none" ? "block" : "none"; 178 | }); 179 | 180 | addEvent(b, "dblclick", function(e) { 181 | var target = e && e.target ? e.target : window.event.srcElement; 182 | if ( target.nodeName.toLowerCase() == "span" || target.nodeName.toLowerCase() == "b" ) { 183 | target = target.parentNode; 184 | } 185 | if ( window.location && target.nodeName.toLowerCase() === "strong" ) { 186 | window.location = QUnit.url({ filter: getText([target]).replace(/\([^)]+\)$/, "").replace(/(^\s*|\s*$)/g, "") }); 187 | } 188 | }); 189 | 190 | var li = id(this.id); 191 | li.className = bad ? "fail" : "pass"; 192 | li.removeChild( li.firstChild ); 193 | li.appendChild( b ); 194 | li.appendChild( a ); 195 | li.appendChild( ol ); 196 | 197 | } else { 198 | for ( var i = 0; i < this.assertions.length; i++ ) { 199 | if ( !this.assertions[i].result ) { 200 | bad++; 201 | config.stats.bad++; 202 | config.moduleStats.bad++; 203 | } 204 | } 205 | } 206 | 207 | try { 208 | QUnit.reset(); 209 | } catch(e) { 210 | fail("reset() failed, following Test " + this.testName + ", exception and reset fn follows", e, QUnit.reset); 211 | } 212 | 213 | QUnit.testDone( { 214 | name: this.testName, 215 | failed: bad, 216 | passed: this.assertions.length - bad, 217 | total: this.assertions.length 218 | } ); 219 | }, 220 | 221 | queue: function() { 222 | var test = this; 223 | synchronize(function() { 224 | test.init(); 225 | }); 226 | function run() { 227 | // each of these can by async 228 | synchronize(function() { 229 | test.setup(); 230 | }); 231 | synchronize(function() { 232 | test.run(); 233 | }); 234 | synchronize(function() { 235 | test.teardown(); 236 | }); 237 | synchronize(function() { 238 | test.finish(); 239 | }); 240 | } 241 | // defer when previous test run passed, if storage is available 242 | var bad = QUnit.config.reorder && defined.sessionStorage && +sessionStorage.getItem("qunit-" + this.module + "-" + this.testName); 243 | if (bad) { 244 | run(); 245 | } else { 246 | synchronize(run); 247 | }; 248 | } 249 | 250 | }; 251 | 252 | var QUnit = { 253 | 254 | // call on start of module test to prepend name to all tests 255 | module: function(name, testEnvironment) { 256 | config.currentModule = name; 257 | config.currentModuleTestEnviroment = testEnvironment; 258 | }, 259 | 260 | asyncTest: function(testName, expected, callback) { 261 | if ( arguments.length === 2 ) { 262 | callback = expected; 263 | expected = 0; 264 | } 265 | 266 | QUnit.test(testName, expected, callback, true); 267 | }, 268 | 269 | test: function(testName, expected, callback, async) { 270 | var name = '' + testName + '', testEnvironmentArg; 271 | 272 | if ( arguments.length === 2 ) { 273 | callback = expected; 274 | expected = null; 275 | } 276 | // is 2nd argument a testEnvironment? 277 | if ( expected && typeof expected === 'object') { 278 | testEnvironmentArg = expected; 279 | expected = null; 280 | } 281 | 282 | if ( config.currentModule ) { 283 | name = '' + config.currentModule + ": " + name; 284 | } 285 | 286 | if ( !validTest(config.currentModule + ": " + testName) ) { 287 | return; 288 | } 289 | 290 | var test = new Test(name, testName, expected, testEnvironmentArg, async, callback); 291 | test.module = config.currentModule; 292 | test.moduleTestEnvironment = config.currentModuleTestEnviroment; 293 | test.queue(); 294 | }, 295 | 296 | /** 297 | * Specify the number of expected assertions to gurantee that failed test (no assertions are run at all) don't slip through. 298 | */ 299 | expect: function(asserts) { 300 | config.current.expected = asserts; 301 | }, 302 | 303 | /** 304 | * Asserts true. 305 | * @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" ); 306 | */ 307 | ok: function(a, msg) { 308 | a = !!a; 309 | var details = { 310 | result: a, 311 | message: msg 312 | }; 313 | msg = escapeHtml(msg); 314 | QUnit.log(details); 315 | config.current.assertions.push({ 316 | result: a, 317 | message: msg 318 | }); 319 | }, 320 | 321 | /** 322 | * Checks that the first two arguments are equal, with an optional message. 323 | * Prints out both actual and expected values. 324 | * 325 | * Prefered to ok( actual == expected, message ) 326 | * 327 | * @example equal( format("Received {0} bytes.", 2), "Received 2 bytes." ); 328 | * 329 | * @param Object actual 330 | * @param Object expected 331 | * @param String message (optional) 332 | */ 333 | equal: function(actual, expected, message) { 334 | QUnit.push(expected == actual, actual, expected, message); 335 | }, 336 | 337 | notEqual: function(actual, expected, message) { 338 | QUnit.push(expected != actual, actual, expected, message); 339 | }, 340 | 341 | deepEqual: function(actual, expected, message) { 342 | QUnit.push(QUnit.equiv(actual, expected), actual, expected, message); 343 | }, 344 | 345 | notDeepEqual: function(actual, expected, message) { 346 | QUnit.push(!QUnit.equiv(actual, expected), actual, expected, message); 347 | }, 348 | 349 | strictEqual: function(actual, expected, message) { 350 | QUnit.push(expected === actual, actual, expected, message); 351 | }, 352 | 353 | notStrictEqual: function(actual, expected, message) { 354 | QUnit.push(expected !== actual, actual, expected, message); 355 | }, 356 | 357 | raises: function(block, expected, message) { 358 | var actual, ok = false; 359 | 360 | if (typeof expected === 'string') { 361 | message = expected; 362 | expected = null; 363 | } 364 | 365 | try { 366 | block(); 367 | } catch (e) { 368 | actual = e; 369 | } 370 | 371 | if (actual) { 372 | // we don't want to validate thrown error 373 | if (!expected) { 374 | ok = true; 375 | // expected is a regexp 376 | } else if (QUnit.objectType(expected) === "regexp") { 377 | ok = expected.test(actual); 378 | // expected is a constructor 379 | } else if (actual instanceof expected) { 380 | ok = true; 381 | // expected is a validation function which returns true is validation passed 382 | } else if (expected.call({}, actual) === true) { 383 | ok = true; 384 | } 385 | } 386 | 387 | QUnit.ok(ok, message); 388 | }, 389 | 390 | start: function() { 391 | config.semaphore--; 392 | if (config.semaphore > 0) { 393 | // don't start until equal number of stop-calls 394 | return; 395 | } 396 | if (config.semaphore < 0) { 397 | // ignore if start is called more often then stop 398 | config.semaphore = 0; 399 | } 400 | // A slight delay, to avoid any current callbacks 401 | if ( defined.setTimeout ) { 402 | window.setTimeout(function() { 403 | if ( config.timeout ) { 404 | clearTimeout(config.timeout); 405 | } 406 | 407 | config.blocking = false; 408 | process(); 409 | }, 13); 410 | } else { 411 | config.blocking = false; 412 | process(); 413 | } 414 | }, 415 | 416 | stop: function(timeout) { 417 | config.semaphore++; 418 | config.blocking = true; 419 | 420 | if ( timeout && defined.setTimeout ) { 421 | clearTimeout(config.timeout); 422 | config.timeout = window.setTimeout(function() { 423 | QUnit.ok( false, "Test timed out" ); 424 | QUnit.start(); 425 | }, timeout); 426 | } 427 | } 428 | }; 429 | 430 | // Backwards compatibility, deprecated 431 | QUnit.equals = QUnit.equal; 432 | QUnit.same = QUnit.deepEqual; 433 | 434 | // Maintain internal state 435 | var config = { 436 | // The queue of tests to run 437 | queue: [], 438 | 439 | // block until document ready 440 | blocking: true, 441 | 442 | // by default, run previously failed tests first 443 | // very useful in combination with "Hide passed tests" checked 444 | reorder: true, 445 | 446 | noglobals: false, 447 | notrycatch: false 448 | }; 449 | 450 | // Load paramaters 451 | (function() { 452 | var location = window.location || { search: "", protocol: "file:" }, 453 | params = location.search.slice( 1 ).split( "&" ), 454 | length = params.length, 455 | urlParams = {}, 456 | current; 457 | 458 | if ( params[ 0 ] ) { 459 | for ( var i = 0; i < length; i++ ) { 460 | current = params[ i ].split( "=" ); 461 | current[ 0 ] = decodeURIComponent( current[ 0 ] ); 462 | // allow just a key to turn on a flag, e.g., test.html?noglobals 463 | current[ 1 ] = current[ 1 ] ? decodeURIComponent( current[ 1 ] ) : true; 464 | urlParams[ current[ 0 ] ] = current[ 1 ]; 465 | if ( current[ 0 ] in config ) { 466 | config[ current[ 0 ] ] = current[ 1 ]; 467 | } 468 | } 469 | } 470 | 471 | QUnit.urlParams = urlParams; 472 | config.filter = urlParams.filter; 473 | 474 | // Figure out if we're running the tests from a server or not 475 | QUnit.isLocal = !!(location.protocol === 'file:'); 476 | })(); 477 | 478 | // Expose the API as global variables, unless an 'exports' 479 | // object exists, in that case we assume we're in CommonJS 480 | if ( typeof exports === "undefined" || typeof require === "undefined" ) { 481 | extend(window, QUnit); 482 | window.QUnit = QUnit; 483 | } else { 484 | extend(exports, QUnit); 485 | exports.QUnit = QUnit; 486 | } 487 | 488 | // define these after exposing globals to keep them in these QUnit namespace only 489 | extend(QUnit, { 490 | config: config, 491 | 492 | // Initialize the configuration options 493 | init: function() { 494 | extend(config, { 495 | stats: { all: 0, bad: 0 }, 496 | moduleStats: { all: 0, bad: 0 }, 497 | started: +new Date, 498 | updateRate: 1000, 499 | blocking: false, 500 | autostart: true, 501 | autorun: false, 502 | filter: "", 503 | queue: [], 504 | semaphore: 0 505 | }); 506 | 507 | var tests = id( "qunit-tests" ), 508 | banner = id( "qunit-banner" ), 509 | result = id( "qunit-testresult" ); 510 | 511 | if ( tests ) { 512 | tests.innerHTML = ""; 513 | } 514 | 515 | if ( banner ) { 516 | banner.className = ""; 517 | } 518 | 519 | if ( result ) { 520 | result.parentNode.removeChild( result ); 521 | } 522 | 523 | if ( tests ) { 524 | result = document.createElement( "p" ); 525 | result.id = "qunit-testresult"; 526 | result.className = "result"; 527 | tests.parentNode.insertBefore( result, tests ); 528 | result.innerHTML = 'Running...
     '; 529 | } 530 | }, 531 | 532 | /** 533 | * Resets the test setup. Useful for tests that modify the DOM. 534 | * 535 | * If jQuery is available, uses jQuery's html(), otherwise just innerHTML. 536 | */ 537 | reset: function() { 538 | if ( window.jQuery ) { 539 | jQuery( "#main, #qunit-fixture" ).html( config.fixture ); 540 | } else { 541 | var main = id( 'main' ) || id( 'qunit-fixture' ); 542 | if ( main ) { 543 | main.innerHTML = config.fixture; 544 | } 545 | } 546 | }, 547 | 548 | /** 549 | * Trigger an event on an element. 550 | * 551 | * @example triggerEvent( document.body, "click" ); 552 | * 553 | * @param DOMElement elem 554 | * @param String type 555 | */ 556 | triggerEvent: function( elem, type, event ) { 557 | if ( document.createEvent ) { 558 | event = document.createEvent("MouseEvents"); 559 | event.initMouseEvent(type, true, true, elem.ownerDocument.defaultView, 560 | 0, 0, 0, 0, 0, false, false, false, false, 0, null); 561 | elem.dispatchEvent( event ); 562 | 563 | } else if ( elem.fireEvent ) { 564 | elem.fireEvent("on"+type); 565 | } 566 | }, 567 | 568 | // Safe object type checking 569 | is: function( type, obj ) { 570 | return QUnit.objectType( obj ) == type; 571 | }, 572 | 573 | objectType: function( obj ) { 574 | if (typeof obj === "undefined") { 575 | return "undefined"; 576 | 577 | // consider: typeof null === object 578 | } 579 | if (obj === null) { 580 | return "null"; 581 | } 582 | 583 | var type = Object.prototype.toString.call( obj ) 584 | .match(/^\[object\s(.*)\]$/)[1] || ''; 585 | 586 | switch (type) { 587 | case 'Number': 588 | if (isNaN(obj)) { 589 | return "nan"; 590 | } else { 591 | return "number"; 592 | } 593 | case 'String': 594 | case 'Boolean': 595 | case 'Array': 596 | case 'Date': 597 | case 'RegExp': 598 | case 'Function': 599 | return type.toLowerCase(); 600 | } 601 | if (typeof obj === "object") { 602 | return "object"; 603 | } 604 | return undefined; 605 | }, 606 | 607 | push: function(result, actual, expected, message) { 608 | var details = { 609 | result: result, 610 | message: message, 611 | actual: actual, 612 | expected: expected 613 | }; 614 | 615 | message = escapeHtml(message) || (result ? "okay" : "failed"); 616 | message = '' + message + ""; 617 | expected = escapeHtml(QUnit.jsDump.parse(expected)); 618 | actual = escapeHtml(QUnit.jsDump.parse(actual)); 619 | var output = message + ''; 620 | if (actual != expected) { 621 | output += ''; 622 | output += ''; 623 | } 624 | if (!result) { 625 | var source = sourceFromStacktrace(); 626 | if (source) { 627 | details.source = source; 628 | output += ''; 629 | } 630 | } 631 | output += "
    Expected:
    ' + expected + '
    Result:
    ' + actual + '
    Diff:
    ' + QUnit.diff(expected, actual) +'
    Source:
    ' + source +'
    "; 632 | 633 | QUnit.log(details); 634 | 635 | config.current.assertions.push({ 636 | result: !!result, 637 | message: output 638 | }); 639 | }, 640 | 641 | url: function( params ) { 642 | params = extend( extend( {}, QUnit.urlParams ), params ); 643 | var querystring = "?", 644 | key; 645 | for ( key in params ) { 646 | querystring += encodeURIComponent( key ) + "=" + 647 | encodeURIComponent( params[ key ] ) + "&"; 648 | } 649 | return window.location.pathname + querystring.slice( 0, -1 ); 650 | }, 651 | 652 | // Logging callbacks; all receive a single argument with the listed properties 653 | // run test/logs.html for any related changes 654 | begin: function() {}, 655 | // done: { failed, passed, total, runtime } 656 | done: function() {}, 657 | // log: { result, actual, expected, message } 658 | log: function() {}, 659 | // testStart: { name } 660 | testStart: function() {}, 661 | // testDone: { name, failed, passed, total } 662 | testDone: function() {}, 663 | // moduleStart: { name } 664 | moduleStart: function() {}, 665 | // moduleDone: { name, failed, passed, total } 666 | moduleDone: function() {} 667 | }); 668 | 669 | if ( typeof document === "undefined" || document.readyState === "complete" ) { 670 | config.autorun = true; 671 | } 672 | 673 | addEvent(window, "load", function() { 674 | QUnit.begin({}); 675 | 676 | // Initialize the config, saving the execution queue 677 | var oldconfig = extend({}, config); 678 | QUnit.init(); 679 | extend(config, oldconfig); 680 | 681 | config.blocking = false; 682 | 683 | var userAgent = id("qunit-userAgent"); 684 | if ( userAgent ) { 685 | userAgent.innerHTML = navigator.userAgent; 686 | } 687 | var banner = id("qunit-header"); 688 | if ( banner ) { 689 | banner.innerHTML = ' ' + banner.innerHTML + ' ' + 690 | '' + 691 | ''; 692 | addEvent( banner, "change", function( event ) { 693 | var params = {}; 694 | params[ event.target.name ] = event.target.checked ? true : undefined; 695 | window.location = QUnit.url( params ); 696 | }); 697 | } 698 | 699 | var toolbar = id("qunit-testrunner-toolbar"); 700 | if ( toolbar ) { 701 | var filter = document.createElement("input"); 702 | filter.type = "checkbox"; 703 | filter.id = "qunit-filter-pass"; 704 | addEvent( filter, "click", function() { 705 | var ol = document.getElementById("qunit-tests"); 706 | if ( filter.checked ) { 707 | ol.className = ol.className + " hidepass"; 708 | } else { 709 | var tmp = " " + ol.className.replace( /[\n\t\r]/g, " " ) + " "; 710 | ol.className = tmp.replace(/ hidepass /, " "); 711 | } 712 | if ( defined.sessionStorage ) { 713 | if (filter.checked) { 714 | sessionStorage.setItem("qunit-filter-passed-tests", "true"); 715 | } else { 716 | sessionStorage.removeItem("qunit-filter-passed-tests"); 717 | } 718 | } 719 | }); 720 | if ( defined.sessionStorage && sessionStorage.getItem("qunit-filter-passed-tests") ) { 721 | filter.checked = true; 722 | var ol = document.getElementById("qunit-tests"); 723 | ol.className = ol.className + " hidepass"; 724 | } 725 | toolbar.appendChild( filter ); 726 | 727 | var label = document.createElement("label"); 728 | label.setAttribute("for", "qunit-filter-pass"); 729 | label.innerHTML = "Hide passed tests"; 730 | toolbar.appendChild( label ); 731 | } 732 | 733 | var main = id('main') || id('qunit-fixture'); 734 | if ( main ) { 735 | config.fixture = main.innerHTML; 736 | } 737 | 738 | if (config.autostart) { 739 | QUnit.start(); 740 | } 741 | }); 742 | 743 | function done() { 744 | config.autorun = true; 745 | 746 | // Log the last module results 747 | if ( config.currentModule ) { 748 | QUnit.moduleDone( { 749 | name: config.currentModule, 750 | failed: config.moduleStats.bad, 751 | passed: config.moduleStats.all - config.moduleStats.bad, 752 | total: config.moduleStats.all 753 | } ); 754 | } 755 | 756 | var banner = id("qunit-banner"), 757 | tests = id("qunit-tests"), 758 | runtime = +new Date - config.started, 759 | passed = config.stats.all - config.stats.bad, 760 | html = [ 761 | 'Tests completed in ', 762 | runtime, 763 | ' milliseconds.
    ', 764 | '', 765 | passed, 766 | ' tests of ', 767 | config.stats.all, 768 | ' passed, ', 769 | config.stats.bad, 770 | ' failed.' 771 | ].join(''); 772 | 773 | if ( banner ) { 774 | banner.className = (config.stats.bad ? "qunit-fail" : "qunit-pass"); 775 | } 776 | 777 | if ( tests ) { 778 | id( "qunit-testresult" ).innerHTML = html; 779 | } 780 | 781 | QUnit.done( { 782 | failed: config.stats.bad, 783 | passed: passed, 784 | total: config.stats.all, 785 | runtime: runtime 786 | } ); 787 | } 788 | 789 | function validTest( name ) { 790 | var filter = config.filter, 791 | run = false; 792 | 793 | if ( !filter ) { 794 | return true; 795 | } 796 | 797 | not = filter.charAt( 0 ) === "!"; 798 | if ( not ) { 799 | filter = filter.slice( 1 ); 800 | } 801 | 802 | if ( name.indexOf( filter ) !== -1 ) { 803 | return !not; 804 | } 805 | 806 | if ( not ) { 807 | run = true; 808 | } 809 | 810 | return run; 811 | } 812 | 813 | // so far supports only Firefox, Chrome and Opera (buggy) 814 | // could be extended in the future to use something like https://github.com/csnover/TraceKit 815 | function sourceFromStacktrace() { 816 | try { 817 | throw new Error(); 818 | } catch ( e ) { 819 | if (e.stacktrace) { 820 | // Opera 821 | return e.stacktrace.split("\n")[6]; 822 | } else if (e.stack) { 823 | // Firefox, Chrome 824 | return e.stack.split("\n")[4]; 825 | } 826 | } 827 | } 828 | 829 | function escapeHtml(s) { 830 | if (!s) { 831 | return ""; 832 | } 833 | s = s + ""; 834 | return s.replace(/[\&"<>\\]/g, function(s) { 835 | switch(s) { 836 | case "&": return "&"; 837 | case "\\": return "\\\\"; 838 | case '"': return '\"'; 839 | case "<": return "<"; 840 | case ">": return ">"; 841 | default: return s; 842 | } 843 | }); 844 | } 845 | 846 | function synchronize( callback ) { 847 | config.queue.push( callback ); 848 | 849 | if ( config.autorun && !config.blocking ) { 850 | process(); 851 | } 852 | } 853 | 854 | function process() { 855 | var start = (new Date()).getTime(); 856 | 857 | while ( config.queue.length && !config.blocking ) { 858 | if ( config.updateRate <= 0 || (((new Date()).getTime() - start) < config.updateRate) ) { 859 | config.queue.shift()(); 860 | } else { 861 | window.setTimeout( process, 13 ); 862 | break; 863 | } 864 | } 865 | if (!config.blocking && !config.queue.length) { 866 | done(); 867 | } 868 | } 869 | 870 | function saveGlobal() { 871 | config.pollution = []; 872 | 873 | if ( config.noglobals ) { 874 | for ( var key in window ) { 875 | config.pollution.push( key ); 876 | } 877 | } 878 | } 879 | 880 | function checkPollution( name ) { 881 | var old = config.pollution; 882 | saveGlobal(); 883 | 884 | var newGlobals = diff( config.pollution, old ); 885 | if ( newGlobals.length > 0 ) { 886 | ok( false, "Introduced global variable(s): " + newGlobals.join(", ") ); 887 | } 888 | 889 | var deletedGlobals = diff( old, config.pollution ); 890 | if ( deletedGlobals.length > 0 ) { 891 | ok( false, "Deleted global variable(s): " + deletedGlobals.join(", ") ); 892 | } 893 | } 894 | 895 | // returns a new Array with the elements that are in a but not in b 896 | function diff( a, b ) { 897 | var result = a.slice(); 898 | for ( var i = 0; i < result.length; i++ ) { 899 | for ( var j = 0; j < b.length; j++ ) { 900 | if ( result[i] === b[j] ) { 901 | result.splice(i, 1); 902 | i--; 903 | break; 904 | } 905 | } 906 | } 907 | return result; 908 | } 909 | 910 | function fail(message, exception, callback) { 911 | if ( typeof console !== "undefined" && console.error && console.warn ) { 912 | console.error(message); 913 | console.error(exception); 914 | console.warn(callback.toString()); 915 | 916 | } else if ( window.opera && opera.postError ) { 917 | opera.postError(message, exception, callback.toString); 918 | } 919 | } 920 | 921 | function extend(a, b) { 922 | for ( var prop in b ) { 923 | if ( b[prop] === undefined ) { 924 | delete a[prop]; 925 | } else { 926 | a[prop] = b[prop]; 927 | } 928 | } 929 | 930 | return a; 931 | } 932 | 933 | function addEvent(elem, type, fn) { 934 | if ( elem.addEventListener ) { 935 | elem.addEventListener( type, fn, false ); 936 | } else if ( elem.attachEvent ) { 937 | elem.attachEvent( "on" + type, fn ); 938 | } else { 939 | fn(); 940 | } 941 | } 942 | 943 | function id(name) { 944 | return !!(typeof document !== "undefined" && document && document.getElementById) && 945 | document.getElementById( name ); 946 | } 947 | 948 | // Test for equality any JavaScript type. 949 | // Discussions and reference: http://philrathe.com/articles/equiv 950 | // Test suites: http://philrathe.com/tests/equiv 951 | // Author: Philippe Rathé 952 | QUnit.equiv = function () { 953 | 954 | var innerEquiv; // the real equiv function 955 | var callers = []; // stack to decide between skip/abort functions 956 | var parents = []; // stack to avoiding loops from circular referencing 957 | 958 | // Call the o related callback with the given arguments. 959 | function bindCallbacks(o, callbacks, args) { 960 | var prop = QUnit.objectType(o); 961 | if (prop) { 962 | if (QUnit.objectType(callbacks[prop]) === "function") { 963 | return callbacks[prop].apply(callbacks, args); 964 | } else { 965 | return callbacks[prop]; // or undefined 966 | } 967 | } 968 | } 969 | 970 | var callbacks = function () { 971 | 972 | // for string, boolean, number and null 973 | function useStrictEquality(b, a) { 974 | if (b instanceof a.constructor || a instanceof b.constructor) { 975 | // to catch short annotaion VS 'new' annotation of a declaration 976 | // e.g. var i = 1; 977 | // var j = new Number(1); 978 | return a == b; 979 | } else { 980 | return a === b; 981 | } 982 | } 983 | 984 | return { 985 | "string": useStrictEquality, 986 | "boolean": useStrictEquality, 987 | "number": useStrictEquality, 988 | "null": useStrictEquality, 989 | "undefined": useStrictEquality, 990 | 991 | "nan": function (b) { 992 | return isNaN(b); 993 | }, 994 | 995 | "date": function (b, a) { 996 | return QUnit.objectType(b) === "date" && a.valueOf() === b.valueOf(); 997 | }, 998 | 999 | "regexp": function (b, a) { 1000 | return QUnit.objectType(b) === "regexp" && 1001 | a.source === b.source && // the regex itself 1002 | a.global === b.global && // and its modifers (gmi) ... 1003 | a.ignoreCase === b.ignoreCase && 1004 | a.multiline === b.multiline; 1005 | }, 1006 | 1007 | // - skip when the property is a method of an instance (OOP) 1008 | // - abort otherwise, 1009 | // initial === would have catch identical references anyway 1010 | "function": function () { 1011 | var caller = callers[callers.length - 1]; 1012 | return caller !== Object && 1013 | typeof caller !== "undefined"; 1014 | }, 1015 | 1016 | "array": function (b, a) { 1017 | var i, j, loop; 1018 | var len; 1019 | 1020 | // b could be an object literal here 1021 | if ( ! (QUnit.objectType(b) === "array")) { 1022 | return false; 1023 | } 1024 | 1025 | len = a.length; 1026 | if (len !== b.length) { // safe and faster 1027 | return false; 1028 | } 1029 | 1030 | //track reference to avoid circular references 1031 | parents.push(a); 1032 | for (i = 0; i < len; i++) { 1033 | loop = false; 1034 | for(j=0;j= 0) { 1179 | type = "array"; 1180 | } else { 1181 | type = typeof obj; 1182 | } 1183 | return type; 1184 | }, 1185 | separator:function() { 1186 | return this.multiline ? this.HTML ? '
    ' : '\n' : this.HTML ? ' ' : ' '; 1187 | }, 1188 | indent:function( extra ) {// extra can be a number, shortcut for increasing-calling-decreasing 1189 | if ( !this.multiline ) 1190 | return ''; 1191 | var chr = this.indentChar; 1192 | if ( this.HTML ) 1193 | chr = chr.replace(/\t/g,' ').replace(/ /g,' '); 1194 | return Array( this._depth_ + (extra||0) ).join(chr); 1195 | }, 1196 | up:function( a ) { 1197 | this._depth_ += a || 1; 1198 | }, 1199 | down:function( a ) { 1200 | this._depth_ -= a || 1; 1201 | }, 1202 | setParser:function( name, parser ) { 1203 | this.parsers[name] = parser; 1204 | }, 1205 | // The next 3 are exposed so you can use them 1206 | quote:quote, 1207 | literal:literal, 1208 | join:join, 1209 | // 1210 | _depth_: 1, 1211 | // This is the list of parsers, to modify them, use jsDump.setParser 1212 | parsers:{ 1213 | window: '[Window]', 1214 | document: '[Document]', 1215 | error:'[ERROR]', //when no parser is found, shouldn't happen 1216 | unknown: '[Unknown]', 1217 | 'null':'null', 1218 | 'undefined':'undefined', 1219 | 'function':function( fn ) { 1220 | var ret = 'function', 1221 | name = 'name' in fn ? fn.name : (reName.exec(fn)||[])[1];//functions never have name in IE 1222 | if ( name ) 1223 | ret += ' ' + name; 1224 | ret += '('; 1225 | 1226 | ret = [ ret, QUnit.jsDump.parse( fn, 'functionArgs' ), '){'].join(''); 1227 | return join( ret, QUnit.jsDump.parse(fn,'functionCode'), '}' ); 1228 | }, 1229 | array: array, 1230 | nodelist: array, 1231 | arguments: array, 1232 | object:function( map ) { 1233 | var ret = [ ]; 1234 | QUnit.jsDump.up(); 1235 | for ( var key in map ) 1236 | ret.push( QUnit.jsDump.parse(key,'key') + ': ' + QUnit.jsDump.parse(map[key]) ); 1237 | QUnit.jsDump.down(); 1238 | return join( '{', ret, '}' ); 1239 | }, 1240 | node:function( node ) { 1241 | var open = QUnit.jsDump.HTML ? '<' : '<', 1242 | close = QUnit.jsDump.HTML ? '>' : '>'; 1243 | 1244 | var tag = node.nodeName.toLowerCase(), 1245 | ret = open + tag; 1246 | 1247 | for ( var a in QUnit.jsDump.DOMAttrs ) { 1248 | var val = node[QUnit.jsDump.DOMAttrs[a]]; 1249 | if ( val ) 1250 | ret += ' ' + a + '=' + QUnit.jsDump.parse( val, 'attribute' ); 1251 | } 1252 | return ret + close + open + '/' + tag + close; 1253 | }, 1254 | functionArgs:function( fn ) {//function calls it internally, it's the arguments part of the function 1255 | var l = fn.length; 1256 | if ( !l ) return ''; 1257 | 1258 | var args = Array(l); 1259 | while ( l-- ) 1260 | args[l] = String.fromCharCode(97+l);//97 is 'a' 1261 | return ' ' + args.join(', ') + ' '; 1262 | }, 1263 | key:quote, //object calls it internally, the key part of an item in a map 1264 | functionCode:'[code]', //function calls it internally, it's the content of the function 1265 | attribute:quote, //node calls it internally, it's an html attribute value 1266 | string:quote, 1267 | date:quote, 1268 | regexp:literal, //regex 1269 | number:literal, 1270 | 'boolean':literal 1271 | }, 1272 | DOMAttrs:{//attributes to dump from nodes, name=>realName 1273 | id:'id', 1274 | name:'name', 1275 | 'class':'className' 1276 | }, 1277 | HTML:false,//if true, entities are escaped ( <, >, \t, space and \n ) 1278 | indentChar:' ',//indentation unit 1279 | multiline:true //if true, items in a collection, are separated by a \n, else just a space. 1280 | }; 1281 | 1282 | return jsDump; 1283 | })(); 1284 | 1285 | // from Sizzle.js 1286 | function getText( elems ) { 1287 | var ret = "", elem; 1288 | 1289 | for ( var i = 0; elems[i]; i++ ) { 1290 | elem = elems[i]; 1291 | 1292 | // Get the text from text nodes and CDATA nodes 1293 | if ( elem.nodeType === 3 || elem.nodeType === 4 ) { 1294 | ret += elem.nodeValue; 1295 | 1296 | // Traverse everything else, except comment nodes 1297 | } else if ( elem.nodeType !== 8 ) { 1298 | ret += getText( elem.childNodes ); 1299 | } 1300 | } 1301 | 1302 | return ret; 1303 | }; 1304 | 1305 | /* 1306 | * Javascript Diff Algorithm 1307 | * By John Resig (http://ejohn.org/) 1308 | * Modified by Chu Alan "sprite" 1309 | * 1310 | * Released under the MIT license. 1311 | * 1312 | * More Info: 1313 | * http://ejohn.org/projects/javascript-diff-algorithm/ 1314 | * 1315 | * Usage: QUnit.diff(expected, actual) 1316 | * 1317 | * QUnit.diff("the quick brown fox jumped over", "the quick fox jumps over") == "the quick brown fox jumped jumps over" 1318 | */ 1319 | QUnit.diff = (function() { 1320 | function diff(o, n){ 1321 | var ns = new Object(); 1322 | var os = new Object(); 1323 | 1324 | for (var i = 0; i < n.length; i++) { 1325 | if (ns[n[i]] == null) 1326 | ns[n[i]] = { 1327 | rows: new Array(), 1328 | o: null 1329 | }; 1330 | ns[n[i]].rows.push(i); 1331 | } 1332 | 1333 | for (var i = 0; i < o.length; i++) { 1334 | if (os[o[i]] == null) 1335 | os[o[i]] = { 1336 | rows: new Array(), 1337 | n: null 1338 | }; 1339 | os[o[i]].rows.push(i); 1340 | } 1341 | 1342 | for (var i in ns) { 1343 | if (ns[i].rows.length == 1 && typeof(os[i]) != "undefined" && os[i].rows.length == 1) { 1344 | n[ns[i].rows[0]] = { 1345 | text: n[ns[i].rows[0]], 1346 | row: os[i].rows[0] 1347 | }; 1348 | o[os[i].rows[0]] = { 1349 | text: o[os[i].rows[0]], 1350 | row: ns[i].rows[0] 1351 | }; 1352 | } 1353 | } 1354 | 1355 | for (var i = 0; i < n.length - 1; i++) { 1356 | if (n[i].text != null && n[i + 1].text == null && n[i].row + 1 < o.length && o[n[i].row + 1].text == null && 1357 | n[i + 1] == o[n[i].row + 1]) { 1358 | n[i + 1] = { 1359 | text: n[i + 1], 1360 | row: n[i].row + 1 1361 | }; 1362 | o[n[i].row + 1] = { 1363 | text: o[n[i].row + 1], 1364 | row: i + 1 1365 | }; 1366 | } 1367 | } 1368 | 1369 | for (var i = n.length - 1; i > 0; i--) { 1370 | if (n[i].text != null && n[i - 1].text == null && n[i].row > 0 && o[n[i].row - 1].text == null && 1371 | n[i - 1] == o[n[i].row - 1]) { 1372 | n[i - 1] = { 1373 | text: n[i - 1], 1374 | row: n[i].row - 1 1375 | }; 1376 | o[n[i].row - 1] = { 1377 | text: o[n[i].row - 1], 1378 | row: i - 1 1379 | }; 1380 | } 1381 | } 1382 | 1383 | return { 1384 | o: o, 1385 | n: n 1386 | }; 1387 | } 1388 | 1389 | return function(o, n){ 1390 | o = o.replace(/\s+$/, ''); 1391 | n = n.replace(/\s+$/, ''); 1392 | var out = diff(o == "" ? [] : o.split(/\s+/), n == "" ? [] : n.split(/\s+/)); 1393 | 1394 | var str = ""; 1395 | 1396 | var oSpace = o.match(/\s+/g); 1397 | if (oSpace == null) { 1398 | oSpace = [" "]; 1399 | } 1400 | else { 1401 | oSpace.push(" "); 1402 | } 1403 | var nSpace = n.match(/\s+/g); 1404 | if (nSpace == null) { 1405 | nSpace = [" "]; 1406 | } 1407 | else { 1408 | nSpace.push(" "); 1409 | } 1410 | 1411 | if (out.n.length == 0) { 1412 | for (var i = 0; i < out.o.length; i++) { 1413 | str += '' + out.o[i] + oSpace[i] + ""; 1414 | } 1415 | } 1416 | else { 1417 | if (out.n[0].text == null) { 1418 | for (n = 0; n < out.o.length && out.o[n].text == null; n++) { 1419 | str += '' + out.o[n] + oSpace[n] + ""; 1420 | } 1421 | } 1422 | 1423 | for (var i = 0; i < out.n.length; i++) { 1424 | if (out.n[i].text == null) { 1425 | str += '' + out.n[i] + nSpace[i] + ""; 1426 | } 1427 | else { 1428 | var pre = ""; 1429 | 1430 | for (n = out.n[i].row + 1; n < out.o.length && out.o[n].text == null; n++) { 1431 | pre += '' + out.o[n] + oSpace[n] + ""; 1432 | } 1433 | str += " " + out.n[i].text + nSpace[i] + pre; 1434 | } 1435 | } 1436 | } 1437 | 1438 | return str; 1439 | }; 1440 | })(); 1441 | 1442 | })(this); 1443 | -------------------------------------------------------------------------------- /tests/tests.js: -------------------------------------------------------------------------------- 1 | $(function() { 2 | 3 | 4 | module("Serialize"); 5 | 6 | test("simple properties", function() { 7 | equals($.querystring({name: "John"}), "name=John"); 8 | equals($.querystring({name: "John", age:59}), "name=John&age=59"); 9 | }); 10 | 11 | test("properties requiring URI encoding", function() { 12 | equals($.querystring({name: "John Doe"}), "name=John%20Doe"); 13 | equals($.querystring({name: "John Doe", garble: " /# #/ "}), "name=John%20Doe&garble=%20%2F%23%20%23%2F%20"); 14 | }); 15 | 16 | test("empty values", function() { 17 | equals($.querystring({}), "", "Empty objects serialize to empty strings."); 18 | equals($.querystring({name:null}), "", "Properties with null values are omitted from resulting string."); 19 | equals($.querystring({name:''}), "name=", 'Blank strings are included in result.'); 20 | equals($.querystring({boring:false}), "boring=false", '`false` literal is included in result.'); 21 | }); 22 | 23 | test("arrays", function() { 24 | equals($.querystring({people: ['Larry', 'Curly', 'Moe']}), "people[]=Larry&people[]=Curly&people[]=Moe"); 25 | }); 26 | 27 | test("simple objects", function() { 28 | equals($.querystring({person: {name:"John", age:110}}), "person[name]=John&person[age]=110"); 29 | }); 30 | 31 | test("nested objects & arrays", function() { 32 | equals( 33 | $.querystring({person: {name:"John", age:110, drinks: ['Beer', 'Whisky', 'Wine'], location: {city: "New York", state:"NY"}}}), 34 | "person[name]=John&person[age]=110&person[drinks][]=Beer&person[drinks][]=Whisky&person[drinks][]=Wine&person[location][city]=New%20York&person[location][state]=NY" 35 | ); 36 | }); 37 | 38 | 39 | module("Parse"); 40 | 41 | test("simple properties", function() { 42 | deepEqual($.querystring("?name=John"), {name: "John"}, "With leading '?'"); 43 | deepEqual($.querystring("name=John"), {name: "John"}, "Without leading '?'"); 44 | deepEqual($.querystring("http://google.com/?name=John"), {name: "John"}, "With domains prefix."); 45 | }); 46 | 47 | test("coercion to native types", function() { 48 | deepEqual($.querystring('?boring=false'), {boring:false}, 'Should parse true/false to native boolean types.'); 49 | deepEqual($.querystring('?awesome=true'), {awesome:true}, 'Should parse true/false to native boolean types.'); 50 | deepEqual($.querystring('?age=21'), {age:21}); 51 | deepEqual($.querystring('?things=null'), {things: null}); 52 | }); 53 | 54 | test("properties requiring URI decoding", function() { 55 | deepEqual($.querystring("?name=John%20Doe"), {name: "John Doe"}); 56 | deepEqual($.querystring("?name=John%20Doe&garble=%20%2F%23%20%23%2F%20"), {name: "John Doe", garble: " /# #/ "}); 57 | }); 58 | 59 | test('empty values', function() { 60 | deepEqual($.querystring('?awesome'), {awesome:""}); 61 | deepEqual($.querystring('?awesome='), {awesome:""}); 62 | }); 63 | 64 | test('arrays', function() { 65 | deepEqual( 66 | $.querystring("?drinks[]=Beer&drinks[]=Whisky&drinks[]=Wine"), 67 | {drinks: ['Beer', 'Whisky', 'Wine']} 68 | ); 69 | }); 70 | 71 | test('simple objects', function() { 72 | deepEqual($.querystring("?person[name]=John"), {person: {name: "John"}}); 73 | }); 74 | 75 | test('nested objects & arrays', function() { 76 | deepEqual( 77 | $.querystring("?person[location][city]=NYC&&person[drinks][]=Beer&person[drinks][]=Whisky&person[drinks][]=Wine"), 78 | {person: {location: {city:'NYC'}}, drinks: ['Beer', 'Whisky', 'Wine']} 79 | ); 80 | }); 81 | 82 | test('missing querystring', function() { 83 | deepEqual($.querystring('/foo?'), {}, 'Should give empty object with ? only') 84 | deepEqual($.querystring('/foo'), {}, 'Should give empty object with no ?') 85 | deepEqual($.querystring('http://google.com/foo?'), {}, 'Should give empty object with host/path and ?') 86 | deepEqual($.querystring('http://google.com/foo'), {}, 'Should give empty object with host/path and no ?') 87 | }); 88 | 89 | module("Element methods"); 90 | 91 | test("get querystring from link", function() { 92 | deepEqual($('link').querystring(), {}, "With no querystring"); 93 | deepEqual($('link').querystring(), {name: "John"}, "With querystring"); 94 | deepEqual($('link').querystring(), {name: "John"}, "With domains prefix."); 95 | deepEqual($('link').querystring(), {}, "With no ?"); 96 | }); 97 | test("set link querystring existing", function() { 98 | equals( 99 | $('link').querystring({awesome:'value'}).attr('href'), 100 | '/foo?name=John&awesome=value' 101 | ) 102 | }); 103 | test("set link querystring non-existent", function() { 104 | equals( 105 | $('link').querystring({awesome:'value'}).attr('href'), 106 | '/foo?awesome=value' 107 | ) 108 | }); 109 | // TODO: Tests for clearing, merging & using `form` elements. 110 | 111 | }); 112 | --------------------------------------------------------------------------------