Skip to content

Embed URL

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
This is an example implementation of Mappa (github.com/Wolfy87/Mappa)

This is an example implementation of Mappa (github.com/Wolfy87/Mappa)

So all you need to do is include the Mappa script which you can download from the repo. The minified version is absolutely tiny.

Then include toolbox.js. Then what do ya know! It just works. Now if you scroll to the bottom of toolbox.js you will see the mappings. It is self explaining documentation. To learn about the functions you will need to go to their respective docs which are linked to from the read me. The read me can be found in the repository.

For example, here is how you dump all of the cookies from the page.

Toolbox.cookies.dump();

Enjoy and create your own toolboxes.

/*
Oliver Caldwell's JavaScript toolbox
An example use of Mappa (github.com/Wolfy87/Mappa)
Copyright 2011, Oliver Caldwell (flowdev.co.uk)
*/
// All the mapping is done at the bottom, after the functions have initialised
// Cookie Toolbox Javascript
// copyright 4th September 2002, by Stephen Chapman, Felgall Pty Ltd
// You have permission to copy and use this javascript provided that
// the content of the script is not changed in any way.
// For instructions on how to use these functions see "A Cookie Toolbox"
// in the Javascript section of our site at http://www.felgall.com/
var dbug = 0; function d_a(ary) {var beg = next_entry(ary) - 1; for (var i = beg ; i > -1; i--) {ary[i] = null;}} function init_array() {if (dbug) alert('init_cookie'); var ary = new Array(null); return ary;} function set_cookie(name,value,expires) {if (dbug) alert('set_cookie'); if (!expires) expires = new Date();
document.cookie = name + '=' + escape(value) + '; expires=' + expires.toGMTString() + '; path=/';} function get_cookie(name) {if (dbug) alert('get_cookie'); var dcookie = document.cookie; var cname = name + "="; var clen = dcookie.length; var cbegin = 0; while (cbegin < clen) {var vbegin = cbegin + cname.length;
if (dcookie.substring(cbegin, vbegin) == cname) {var vend = dcookie.indexOf (";", vbegin); if (vend == -1) vend = clen; return unescape(dcookie.substring(vbegin, vend));} cbegin = dcookie.indexOf(" ", cbegin) + 1; if (cbegin == 0) break;} return null;} function del_cookie(name) {if (dbug) alert('del_cookie');
document.cookie = name + '=' + '; expires=Thu, 01-Jan-70 00:00:01 GMT; path=/';} function get_array(name, ary) {if (dbug) alert('get_array'); d_a(ary); var ent = get_cookie(name); if (ent) {i = 1; while (ent.indexOf('^') != '-1') {ary[i] = ent.substring(0,ent.indexOf('^')); i++;
ent = ent.substring(ent.indexOf('^')+1, ent.length);}}} function set_array(name, ary, expires) {if (dbug) alert('set_array'); var value = ''; for (var i = 1; ary[i]; i++) {value += ary[i] + '^';} set_cookie(name, value, expires);} function del_entry(name, ary, pos, expires) {if (dbug) alert('del_entry');
var value = ''; get_array(name, ary); for (var i = 1; i < pos; i++) {value += ary[i] + '^';} for (var j = pos + 1; ary[j]; j++) {value += ary[j] + '^';} set_cookie(name, value, expires);} function next_entry(ary) {if (dbug) alert('next_entry'); var j = 0; for (var i = 1; ary[i]; i++) {j = i} return j + 1;}
function debug_on() {dbug = 1;} function debug_off() {dbug = 0;} function dump_cookies() {if (document.cookie == '') document.write('No Cookies Found'); else {thisCookie = document.cookie.split('; '); for (i=0; i<thisCookie.length; i++) {document.write(thisCookie[i] + '<br \/>');}}}
// ===================================================================
// Author: Matt Kruse <matt@ajaxtoolbox.com>
// WWW: http://www.AjaxToolbox.com/
//
// NOTICE: You may use this code for any purpose, commercial or
// private, without any further permission from the author. You may
// remove this notice from your final code if you wish, however it is
// appreciated by the author if at least my web site address is kept.
//
// You may *NOT* re-distribute this code in any way except through its
// use. That means, you can include it in your product, or your web
// site, or any other form where the code is actually being used. You
// may not put the plain javascript up on your site for download or
// include it in your javascript libraries for download.
// If you wish to share this code with others, please just point them
// to the URL instead.
// Please DO NOT link directly to my .js files from your site. Copy
// the files to your server and use them there. Thank you.
// ===================================================================
/**
* The AjaxRequest class is a wrapper for the XMLHttpRequest objects which
* are available in most modern browsers. It simplifies the interfaces for
* making Ajax requests, adds commonly-used convenience methods, and makes
* the process of handling state changes more intuitive.
* An object may be instantiated and used, or the Class methods may be used
* which internally create an AjaxRequest object.
*/
function AjaxRequest() {
var req = new Object();
// -------------------
// Instance properties
// -------------------
/**
* Timeout period (in ms) until an async request will be aborted, and
* the onTimeout function will be called
*/
req.timeout = null;
/**
* Since some browsers cache GET requests via XMLHttpRequest, an
* additional parameter called AjaxRequestUniqueId will be added to
* the request URI with a unique numeric value appended so that the requested
* URL will not be cached.
*/
req.generateUniqueUrl = true;
/**
* The url that the request will be made to, which defaults to the current
* url of the window
*/
req.url = window.location.href;
/**
* The method of the request, either GET (default), POST, or HEAD
*/
req.method = "GET";
/**
* Whether or not the request will be asynchronous. In general, synchronous
* requests should not be used so this should rarely be changed from true
*/
req.async = true;
/**
* The username used to access the URL
*/
req.username = null;
/**
* The password used to access the URL
*/
req.password = null;
/**
* The parameters is an object holding name/value pairs which will be
* added to the url for a GET request or the request content for a POST request
*/
req.parameters = new Object();
/**
* The sequential index number of this request, updated internally
*/
req.requestIndex = AjaxRequest.numAjaxRequests++;
/**
* Indicates whether a response has been received yet from the server
*/
req.responseReceived = false;
/**
* The name of the group that this request belongs to, for activity
* monitoring purposes
*/
req.groupName = null;
/**
* The query string to be added to the end of a GET request, in proper
* URIEncoded format
*/
req.queryString = "";
/**
* After a response has been received, this will hold the text contents of
* the response - even in case of error
*/
req.responseText = null;
/**
* After a response has been received, this will hold the XML content
*/
req.responseXML = null;
/**
* After a response has been received, this will hold the status code of
* the response as returned by the server.
*/
req.status = null;
/**
* After a response has been received, this will hold the text description
* of the response code
*/
req.statusText = null;
/**
* An internal flag to indicate whether the request has been aborted
*/
req.aborted = false;
/**
* The XMLHttpRequest object used internally
*/
req.xmlHttpRequest = null;
// --------------
// Event handlers
// --------------
/**
* If a timeout period is set, and it is reached before a response is
* received, a function reference assigned to onTimeout will be called
*/
req.onTimeout = null;
/**
* A function reference assigned will be called when readyState=1
*/
req.onLoading = null;
/**
* A function reference assigned will be called when readyState=2
*/
req.onLoaded = null;
/**
* A function reference assigned will be called when readyState=3
*/
req.onInteractive = null;
/**
* A function reference assigned will be called when readyState=4
*/
req.onComplete = null;
/**
* A function reference assigned will be called after onComplete, if
* the statusCode=200
*/
req.onSuccess = null;
/**
* A function reference assigned will be called after onComplete, if
* the statusCode != 200
*/
req.onError = null;
/**
* If this request has a group name, this function reference will be called
* and passed the group name if this is the first request in the group to
* become active
*/
req.onGroupBegin = null;
/**
* If this request has a group name, and this request is the last request
* in the group to complete, this function reference will be called
*/
req.onGroupEnd = null;
// Get the XMLHttpRequest object itself
req.xmlHttpRequest = AjaxRequest.getXmlHttpRequest();
if (req.xmlHttpRequest==null) { return null; }
// -------------------------------------------------------
// Attach the event handlers for the XMLHttpRequest object
// -------------------------------------------------------
req.xmlHttpRequest.onreadystatechange =
function() {
if (req==null || req.xmlHttpRequest==null) { return; }
if (req.xmlHttpRequest.readyState==1) { req.onLoadingInternal(req); }
if (req.xmlHttpRequest.readyState==2) { req.onLoadedInternal(req); }
if (req.xmlHttpRequest.readyState==3) { req.onInteractiveInternal(req); }
if (req.xmlHttpRequest.readyState==4) { req.onCompleteInternal(req); }
};
// ---------------------------------------------------------------------------
// Internal event handlers that fire, and in turn fire the user event handlers
// ---------------------------------------------------------------------------
// Flags to keep track if each event has been handled, in case of
// multiple calls (some browsers may call the onreadystatechange
// multiple times for the same state)
req.onLoadingInternalHandled = false;
req.onLoadedInternalHandled = false;
req.onInteractiveInternalHandled = false;
req.onCompleteInternalHandled = false;
req.onLoadingInternal =
function() {
if (req.onLoadingInternalHandled) { return; }
AjaxRequest.numActiveAjaxRequests++;
if (AjaxRequest.numActiveAjaxRequests==1 && typeof(window['AjaxRequestBegin'])=="function") {
AjaxRequestBegin();
}
if (req.groupName!=null) {
if (typeof(AjaxRequest.numActiveAjaxGroupRequests[req.groupName])=="undefined") {
AjaxRequest.numActiveAjaxGroupRequests[req.groupName] = 0;
}
AjaxRequest.numActiveAjaxGroupRequests[req.groupName]++;
if (AjaxRequest.numActiveAjaxGroupRequests[req.groupName]==1 && typeof(req.onGroupBegin)=="function") {
req.onGroupBegin(req.groupName);
}
}
if (typeof(req.onLoading)=="function") {
req.onLoading(req);
}
req.onLoadingInternalHandled = true;
};
req.onLoadedInternal =
function() {
if (req.onLoadedInternalHandled) { return; }
if (typeof(req.onLoaded)=="function") {
req.onLoaded(req);
}
req.onLoadedInternalHandled = true;
};
req.onInteractiveInternal =
function() {
if (req.onInteractiveInternalHandled) { return; }
if (typeof(req.onInteractive)=="function") {
req.onInteractive(req);
}
req.onInteractiveInternalHandled = true;
};
req.onCompleteInternal =
function() {
if (req.onCompleteInternalHandled || req.aborted) { return; }
req.onCompleteInternalHandled = true;
AjaxRequest.numActiveAjaxRequests--;
if (AjaxRequest.numActiveAjaxRequests==0 && typeof(window['AjaxRequestEnd'])=="function") {
AjaxRequestEnd(req.groupName);
}
if (req.groupName!=null) {
AjaxRequest.numActiveAjaxGroupRequests[req.groupName]--;
if (AjaxRequest.numActiveAjaxGroupRequests[req.groupName]==0 && typeof(req.onGroupEnd)=="function") {
req.onGroupEnd(req.groupName);
}
}
req.responseReceived = true;
req.status = req.xmlHttpRequest.status;
req.statusText = req.xmlHttpRequest.statusText;
req.responseText = req.xmlHttpRequest.responseText;
req.responseXML = req.xmlHttpRequest.responseXML;
if (typeof(req.onComplete)=="function") {
req.onComplete(req);
}
if (req.xmlHttpRequest.status==200 && typeof(req.onSuccess)=="function") {
req.onSuccess(req);
}
else if (typeof(req.onError)=="function") {
req.onError(req);
}
// Clean up so IE doesn't leak memory
delete req.xmlHttpRequest['onreadystatechange'];
req.xmlHttpRequest = null;
};
req.onTimeoutInternal =
function() {
if (req!=null && req.xmlHttpRequest!=null && !req.onCompleteInternalHandled) {
req.aborted = true;
req.xmlHttpRequest.abort();
AjaxRequest.numActiveAjaxRequests--;
if (AjaxRequest.numActiveAjaxRequests==0 && typeof(window['AjaxRequestEnd'])=="function") {
AjaxRequestEnd(req.groupName);
}
if (req.groupName!=null) {
AjaxRequest.numActiveAjaxGroupRequests[req.groupName]--;
if (AjaxRequest.numActiveAjaxGroupRequests[req.groupName]==0 && typeof(req.onGroupEnd)=="function") {
req.onGroupEnd(req.groupName);
}
}
if (typeof(req.onTimeout)=="function") {
req.onTimeout(req);
}
// Opera won't fire onreadystatechange after abort, but other browsers do.
// So we can't rely on the onreadystate function getting called. Clean up here!
delete req.xmlHttpRequest['onreadystatechange'];
req.xmlHttpRequest = null;
}
};
// ----------------
// Instance methods
// ----------------
/**
* The process method is called to actually make the request. It builds the
* querystring for GET requests (the content for POST requests), sets the
* appropriate headers if necessary, and calls the
* XMLHttpRequest.send() method
*/
req.process =
function() {
if (req.xmlHttpRequest!=null) {
// Some logic to get the real request URL
if (req.generateUniqueUrl && req.method=="GET") {
req.parameters["AjaxRequestUniqueId"] = new Date().getTime() + "" + req.requestIndex;
}
var content = null; // For POST requests, to hold query string
for (var i in req.parameters) {
if (req.queryString.length>0) { req.queryString += "&"; }
req.queryString += encodeURIComponent(i) + "=" + encodeURIComponent(req.parameters[i]);
}
if (req.method=="GET") {
if (req.queryString.length>0) {
req.url += ((req.url.indexOf("?")>-1)?"&":"?") + req.queryString;
}
}
req.xmlHttpRequest.open(req.method,req.url,req.async,req.username,req.password);
if (req.method=="POST") {
if (typeof(req.xmlHttpRequest.setRequestHeader)!="undefined") {
req.xmlHttpRequest.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
}
content = req.queryString;
}
if (req.timeout>0) {
setTimeout(req.onTimeoutInternal,req.timeout);
}
req.xmlHttpRequest.send(content);
}
};
/**
* An internal function to handle an Object argument, which may contain
* either AjaxRequest field values or parameter name/values
*/
req.handleArguments =
function(args) {
for (var i in args) {
// If the AjaxRequest object doesn't have a property which was passed, treat it as a url parameter
if (typeof(req[i])=="undefined") {
req.parameters[i] = args[i];
}
else {
req[i] = args[i];
}
}
};
/**
* Returns the results of XMLHttpRequest.getAllResponseHeaders().
* Only available after a response has been returned
*/
req.getAllResponseHeaders =
function() {
if (req.xmlHttpRequest!=null) {
if (req.responseReceived) {
return req.xmlHttpRequest.getAllResponseHeaders();
}
alert("Cannot getAllResponseHeaders because a response has not yet been received");
}
};
/**
* Returns the the value of a response header as returned by
* XMLHttpRequest,getResponseHeader().
* Only available after a response has been returned
*/
req.getResponseHeader =
function(headerName) {
if (req.xmlHttpRequest!=null) {
if (req.responseReceived) {
return req.xmlHttpRequest.getResponseHeader(headerName);
}
alert("Cannot getResponseHeader because a response has not yet been received");
}
};
return req;
}
// ---------------------------------------
// Static methods of the AjaxRequest class
// ---------------------------------------
/**
* Returns an XMLHttpRequest object, either as a core object or an ActiveX
* implementation. If an object cannot be instantiated, it will return null;
*/
AjaxRequest.getXmlHttpRequest = function() {
if (window.XMLHttpRequest) {
return new XMLHttpRequest();
}
else if (window.ActiveXObject) {
// Based on http://jibbering.com/2002/4/httprequest.html
/*@cc_on @*/
/*@if (@_jscript_version >= 5)
try {
return new ActiveXObject("Msxml2.XMLHTTP");
} catch (e) {
try {
return new ActiveXObject("Microsoft.XMLHTTP");
} catch (E) {
return null;
}
}
@end @*/
}
else {
return null;
}
};
/**
* See if any request is active in the background
*/
AjaxRequest.isActive = function() {
return (AjaxRequest.numActiveAjaxRequests>0);
};
/**
* Make a GET request. Pass an object containing parameters and arguments as
* the second argument.
* These areguments may be either AjaxRequest properties to set on the request
* object or name/values to set in the request querystring.
*/
AjaxRequest.get = function(args) {
AjaxRequest.doRequest("GET",args);
};
/**
* Make a POST request. Pass an object containing parameters and arguments as
* the second argument.
* These areguments may be either AjaxRequest properties to set on the request
* object or name/values to set in the request querystring.
*/
AjaxRequest.post = function(args) {
AjaxRequest.doRequest("POST",args);
};
/**
* The internal method used by the .get() and .post() methods
*/
AjaxRequest.doRequest = function(method,args) {
if (typeof(args)!="undefined" && args!=null) {
var myRequest = new AjaxRequest();
myRequest.method = method;
myRequest.handleArguments(args);
myRequest.process();
}
} ;
/**
* Submit a form. The requested URL will be the form's ACTION, and the request
* method will be the form's METHOD.
* Returns true if the submittal was handled successfully, else false so it
* can easily be used with an onSubmit event for a form, and fallback to
* submitting the form normally.
*/
AjaxRequest.submit = function(theform, args) {
var myRequest = new AjaxRequest();
if (myRequest==null) { return false; }
var serializedForm = AjaxRequest.serializeForm(theform);
myRequest.method = theform.method.toUpperCase();
myRequest.url = theform.action;
myRequest.handleArguments(args);
myRequest.queryString = serializedForm;
myRequest.process();
return true;
};
/**
* Serialize a form into a format which can be sent as a GET string or a POST
* content.It correctly ignores disabled fields, maintains order of the fields
* as in the elements[] array. The 'file' input type is not supported, as
* its content is not available to javascript. This method is used internally
* by the submit class method.
*/
AjaxRequest.serializeForm = function(theform) {
var els = theform.elements;
var len = els.length;
var queryString = "";
this.addField =
function(name,value) {
if (queryString.length>0) {
queryString += "&";
}
queryString += encodeURIComponent(name) + "=" + encodeURIComponent(value);
};
for (var i=0; i<len; i++) {
var el = els[i];
if (!el.disabled) {
switch(el.type) {
case 'text': case 'password': case 'hidden': case 'textarea':
this.addField(el.name,el.value);
break;
case 'select-one':
if (el.selectedIndex>=0) {
this.addField(el.name,el.options[el.selectedIndex].value);
}
break;
case 'select-multiple':
for (var j=0; j<el.options.length; j++) {
if (el.options[j].selected) {
this.addField(el.name,el.options[j].value);
}
}
break;
case 'checkbox': case 'radio':
if (el.checked) {
this.addField(el.name,el.value);
}
break;
}
}
}
return queryString;
};
// -----------------------
// Static Class variables
// -----------------------
/**
* The number of total AjaxRequest objects currently active and running
*/
AjaxRequest.numActiveAjaxRequests = 0;
/**
* An object holding the number of active requests for each group
*/
AjaxRequest.numActiveAjaxGroupRequests = new Object();
/**
* The total number of AjaxRequest objects instantiated
*/
AjaxRequest.numAjaxRequests = 0;
var BrowserDetect = {
init: function () {
this.browser = this.searchString(this.dataBrowser) || "An unknown browser";
this.version = this.searchVersion(navigator.userAgent)
|| this.searchVersion(navigator.appVersion)
|| "an unknown version";
this.OS = this.searchString(this.dataOS) || "an unknown OS";
},
searchString: function (data) {
for (var i=0;i<data.length;i++) {
var dataString = data[i].string;
var dataProp = data[i].prop;
this.versionSearchString = data[i].versionSearch || data[i].identity;
if (dataString) {
if (dataString.indexOf(data[i].subString) != -1)
return data[i].identity;
}
else if (dataProp)
return data[i].identity;
}
},
searchVersion: function (dataString) {
var index = dataString.indexOf(this.versionSearchString);
if (index == -1) return;
return parseFloat(dataString.substring(index+this.versionSearchString.length+1));
},
dataBrowser: [
{
string: navigator.userAgent,
subString: "Chrome",
identity: "Chrome"
},
{ string: navigator.userAgent,
subString: "OmniWeb",
versionSearch: "OmniWeb/",
identity: "OmniWeb"
},
{
string: navigator.vendor,
subString: "Apple",
identity: "Safari",
versionSearch: "Version"
},
{
prop: window.opera,
identity: "Opera"
},
{
string: navigator.vendor,
subString: "iCab",
identity: "iCab"
},
{
string: navigator.vendor,
subString: "KDE",
identity: "Konqueror"
},
{
string: navigator.userAgent,
subString: "Firefox",
identity: "Firefox"
},
{
string: navigator.vendor,
subString: "Camino",
identity: "Camino"
},
{ // for newer Netscapes (6+)
string: navigator.userAgent,
subString: "Netscape",
identity: "Netscape"
},
{
string: navigator.userAgent,
subString: "MSIE",
identity: "Explorer",
versionSearch: "MSIE"
},
{
string: navigator.userAgent,
subString: "Gecko",
identity: "Mozilla",
versionSearch: "rv"
},
{ // for older Netscapes (4-)
string: navigator.userAgent,
subString: "Mozilla",
identity: "Netscape",
versionSearch: "Mozilla"
}
],
dataOS : [
{
string: navigator.platform,
subString: "Win",
identity: "Windows"
},
{
string: navigator.platform,
subString: "Mac",
identity: "Mac"
},
{
string: navigator.userAgent,
subString: "iPhone",
identity: "iPhone/iPod"
},
{
string: navigator.platform,
subString: "Linux",
identity: "Linux"
}
]
};
BrowserDetect.init();
// Cookie Toolbox Javascript
// // copyright 4th September 2002, by Stephen Chapman, Felgall Pty Ltd
//
// // You have permission to copy and use this javascript provided that
// // the content of the script is not changed in any way.
// // For instructions on how to use these functions see "A Cookie Toolbox"
// // in the Javascript section of our site at http://www.felgall.com/
//
// var dbug = 0; function d_a(ary) {var beg = next_entry(ary) - 1; for (var i = beg ; i > -1; i--) {ary[i] = null;}} function init_array() {if (dbug) alert('init_cookie'); var ary = new Array(null); return ary;} function set_cookie(name,value,expires) {if (dbug) alert('set_cookie'); if (!expires) expires = new Date();
// document.cookie = name + '=' + escape(value) + '; expires=' + expires.toGMTString() + '; path=/';} function get_cookie(name) {if (dbug) alert('get_cookie'); var dcookie = document.cookie; var cname = name + "="; var clen = dcookie.length; var cbegin = 0; while (cbegin < clen) {var vbegin = cbegin + cname.length;
// if (dcookie.substring(cbegin, vbegin) == cname) {var vend = dcookie.indexOf (";", vbegin); if (vend == -1) vend = clen; return unescape(dcookie.substring(vbegin, vend));} cbegin = dcookie.indexOf(" ", cbegin) + 1; if (cbegin == 0) break;} return null;} function del_cookie(name) {if (dbug) alert('del_cookie');
// document.cookie = name + '=' + '; expires=Thu, 01-Jan-70 00:00:01 GMT; path=/';} function get_array(name, ary) {if (dbug) alert('get_array'); d_a(ary); var ent = get_cookie(name); if (ent) {i = 1; while (ent.indexOf('^') != '-1') {ary[i] = ent.substring(0,ent.indexOf('^')); i++;
// ent = ent.substring(ent.indexOf('^')+1, ent.length);}}} function set_array(name, ary, expires) {if (dbug) alert('set_array'); var value = ''; for (var i = 1; ary[i]; i++) {value += ary[i] + '^';} set_cookie(name, value, expires);} function del_entry(name, ary, pos, expires) {if (dbug) alert('del_entry');
// var value = ''; get_array(name, ary); for (var i = 1; i < pos; i++) {value += ary[i] + '^';} for (var j = pos + 1; ary[j]; j++) {value += ary[j] + '^';} set_cookie(name, value, expires);} function next_entry(ary) {if (dbug) alert('next_entry'); var j = 0; for (var i = 1; ary[i]; i++) {j = i} return j + 1;}
// function debug_on() {dbug = 1;} function debug_off() {dbug = 0;} function dump_cookies() {if (document.cookie == '') document.write('No Cookies Found'); else {thisCookie = document.cookie.split('; '); for (i=0; i<thisCookie.length; i++) {document.write(thisCookie[i] + '<br \/>');}}}
/**
Written by Peter Wilkinson of http://dynamic-tools.net
Feel free to use or modify this script for any purpose. I'd appreciate you leaving
this header in though.
*/
function addEvent(elem, eventType, handler)
{
if (!elem.eventHandlers) elem.eventHandlers = [];
if (!elem.eventHandlers[eventType])
{
elem.eventHandlers[eventType] = [];
if (elem['on' + eventType]) elem.eventHandlers[eventType].push(elem['on' + eventType]);
elem['on' + eventType] = handleEvent;
}
elem.eventHandlers[eventType].push(handler);
}
function removeEvent(elem, eventType, handler)
{
var handlers = elem.eventHandlers[eventType];
for (var i in handlers) if (handlers[i] == handler) delete handlers[i];
}
function handleEvent(e)
{
var returnValue = true;
if (!e) e = fixEvent(event);
var handlers = this.eventHandlers[e.type]
for (var i in handlers)
{
this.$$handleEvent = handlers[i];
returnValue = !((returnValue && this.$$handleEvent(e)) === false);
}
return returnValue;
}
function fixEvent(e)
{
// add W3C standard event methods
e.preventDefault = fixEvent.preventDefault;
e.stopPropagation = fixEvent.stopPropagation;
return event;
};
fixEvent.preventDefault = function() {
this.returnValue = false;
};
fixEvent.stopPropagation = function() {
this.cancelBubble = true;
}; /*!
jsAnim: Powerful javascript animation
--------------------------------------------
Copyright 2009 Kevin Dolan
-http://www.thekevindolan.com
Code licensed under the MIT license
-See license.txt
v0.2
*/
//These vars are used to hold all jsAnimManagers
var jsAnimManagers = new Array();
var jsAnimManagerId = 0;
/*! public, accessible
jsAnimManager object constructor
Used by end-user to manage jsAnim objects
Params:
-[timestep] : time between frames, defaults to 40
*/
function jsAnimManager(timestep) {
jsAnimManagers[jsAnimManagerId] = this;
this.myId = jsAnimManagerId;
jsAnimManagerId++;
if(timestep)
this.timestep = timestep;
else
this.timestep = 40;
this.paused = false;
this.animObjects = new Array();
this.index = 0;
//Used internally to process a single frame of all active animations
this.step = function() {
if(!this.paused) {
for(x in this.animObjects) {
this.animObjects[x].step();
}
setTimeout("jsAnimManagers["+this.myId+"].step()",this.timestep);
}
};
//Used internally to kill a jsAnimObject
this.kill = function(id) {
delete this.animObjects[id];
};
/*! public
Called to create a new animation object
Params:
-objId : id of object being controlled
*/
this.createAnimObject = function(objId) {
var el = document.getElementById(objId);
var id = this.index;
this.animObjects[id] = new jsAnimObject(el, id, this);
this.index++;
return this.animObjects[id];
};
/*! public
Called to pause the animation manager
*/
this.pause = function() {
this.paused = true;
};
/*! public
Called to unpause the animation manager
*/
this.resume = function() {
this.paused = false;
this.step();
};
/*! public
Called to set the appropriate style values to allow position to be controlled by jsAnim
Params:
-objId : id of object to be registered
-[fixed] : fixed positioning, false to absolute fixed, defaults to false
*/
this.registerPosition = function(objId, fixed) {
var el = document.getElementById(objId);
var width = el.offsetWidth;
var height = el.offsetHeight;
if(fixed)
el.style.position = "fixed";
else
el.style.position = "absolute";
el.style.top = "0px";
el.style.left = "50%";
el.halfWidth = Math.floor(width/2);
el.halfHeight = Math.floor(height/2);
el.style.marginLeft = (-el.halfWidth)+"px";
el.style.marginTop = (-el.halfHeight)+"px";
el.positionRegistered = true;
/*! public
Called to manually set the position of this object
*/
el.setPosition = function(x, y) {
this.style.marginLeft = (x - this.halfWidth)+"px";
this.style.marginTop = (y - this.halfHeight)+"px";
};
};
this.step();
return true;
}
/*! accesible
jsAnimObject object constructor
Used internally to hold the state of a single animation manager
Params:
-obj : object being animated
*/
function jsAnimObject(obj, id, manager) {
this.id = id;
this.obj = obj;
this.paused = false;
this.animEntries = new Array();
this.animLoops = new Array();
this.current = 0;
this.manager = manager;
this.step = function() {
if(!this.paused) {
//Ugly code to get only the first element in the array
for(x in this.animEntries) {
var finished = this.animEntries[x].step();
if(finished) {
this.animLoops[x]--;
this.animEntries[x].current = 0;
this.animEntries[x].onLoop();
}
if(this.animLoops[x] == 0) {
this.animEntries[x].onComplete();
delete this.animEntries[x];
}
break;
}
}
};
/*! public
Called to add an animation to the chain
Params:
-params : a collection in the containing the following elements
- property : the Prop object to animate
- [from] : optional from value, if unspecified current value is used
- to : the value to animate to
- duration : the length of time this animation should take
- [ease] : the jsAnimEase object to use, if unspecified linear will be used
- [loop] : the number of times to loop the animation, negative values are infinite, if unspecified 1 will be used
- [onLoop] : the callback function for loop completion
- [onComplete] : the callback function for animation completion
*/
this.add = function(params) {
var property = params.property;
var from = params.from
var to = params.to;
var duration = params.duration;
if(params.ease)
var ease = params.ease;
else
var ease = jsAnimEase.linear;
if(params.loop)
var loop = params.loop;
else
var loop = 1;
if(params.onLoop)
var onLoop = params.onLoop;
else
var onLoop = function() {};
if(params.onComplete)
var onComplete = params.onComplete;
else
var onComplete = function() {};
this.animEntries[this.current] = new jsAnimEntry(this.obj, property, from, to, duration, this.manager.timestep, ease, onLoop, onComplete);
this.animLoops[this.current] = loop;
this.current++;
};
/*! public
Called to skip the current animation, can be used to exit an infinite loop
*/
this.skip = function() {
//Ugly code to get only the first element in the array
for(x in this.animEntries) {
delete this.animEntries[x];
break;
}
};
/*! public
Called to pause this animator
*/
this.pause = function() {
this.paused = true;
};
/*! public
Called to resum this animator
*/
this.resume = function() {
this.paused = false;
};
/*! public
Called to kill this animator
*/
this.kill = function() {
this.manager.kill(this.id);
};
return true;
}
/*! public, accesible
Pos object constructor
Called to store an x and y coordinate representing an object's center
according to the jsAnim coordinate system
Params:
-x : x coordinate, 0 is center, negative is left, positive is right
-y : y coordinate, 0 is top, positive id below
*/
function Pos(x, y) {
//public
this.x = x;
//public
this.y = y;
return true;
}
/*! public, accesible
Dim object constructor
Called to store a width/height dimension
Params:
-w : width
-h : height
*/
function Dim(w, h) {
//public
this.w = w;
//public
this.h = h;
return true;
}
/*! public, accesible
Col object constructor
Called to store an RGB color
Params:
-r : red value (0,255)
-g : green value (0,255)
-b : blue value (0,255)
*/
function Col(r, g, b) {
//public
this.r = r;
//public
this.g = g;
//public
this.b = b;
return true;
}
/*!
jsAnimEntry object constructor
Used internally to hold the state of single animation entry
Params:
-property : jsAnimProp object
-from : initial value
-to : end value
-duration : total time of animation (ms)
-ease : jsAnimEase object
-timestep : the timestep value of the animation manager
-onLoop : called after each loop
-onComplete : called after completion
*/
function jsAnimEntry(obj, property, from, to, duration, timestep, ease, onLoop, onComplete) {
this.obj = obj;
this.property = property;
this.from = from;
this.to = to;
this.duration = duration;
this.timestep = timestep;
this.ease = ease;
this.current = 0;
this.onLoop = onLoop;
this.onComplete = onComplete;
/*!
Used internally to move the object one step
Returns : true if this anim entry has completed, false otherwise
*/
this.step = function() {
if(!this.from)
this.from = this.property.current(this.obj);
if(this.current >= this.duration) {
var p = this.ease.transform(1);
this.property.update(this.obj, this.from, this.to, p);
return true;
}
else {
var t = this.current / this.duration;
var p = this.ease.transform(t);
this.property.update(this.obj, this.from, this.to, p);
this.current += this.timestep;
return false;
}
};
return true;
}
/*! public
jsAnimEase objects
Used to control easing
Methods:
transform : Transform a number 0-1 representing a time proportion
to a new number 0-1 representing a progress proportion
*/
var jsAnimEase = {
/*!public
Constant Rate
*/
linear : {
transform : function(t) {
return t;
}
},
/*!public
Starts slow, then speeds up
*/
parabolicPos : {
transform : function(t) {
return t * t;
}
},
/*!public
Starts fast, then slows down
*/
parabolicNeg : {
transform : function(t) {
return 1 - (t-1) * (t-1);
}
},
/*!public
Overshoots target then returns to target
Params:
-g : overshoot amount [0-1]
*/
backout : function(g) {
return {
transform : function(t) {
return (-1 * t * (t + g - 2)) / (1 - g);
}
};
},
/*!public
Backs up a bit then moves to target
Params:
-g : backup amount [0-1]
*/
backin : function(g) {
return {
transform : function(t) {
return 1 + ((t+1-g) * ((t+1-g) + g - 2)) / (1 - g);
}
};
},
/*!public
Goes to target and then back at constant rate
*/
bounceLinear : {
transform : function(t) {
if(t < 0.5)
return 2 * t;
else
return 1 - 2 * (t - 0.5)
}
},
/*!public
Goes to target and then back at variable rate
*/
bounceParabolic : {
transform : function(t) {
return -4 * t * (t-1);
}
},
/*!public
Goes to target and then back smoothly
*/
bounceSmooth : {
transform : function(t) {
return 0.5 - 0.5 * Math.cos(2 * Math.PI * t);
}
}
}
/*!
Utility objects for internal use
*/
var jsAnimUtil = {
interp : function (v1, v2, percent) {
if(isNaN(v1))
v1 = 0;
if(isNaN(v2))
v2 = 0;
var v = v1 + percent * (v2-v1);
return Math.floor(v);
},
getCSS : function (elem, field) {
var css = document.defaultView && document.defaultView.getComputedStyle ?
document.defaultView.getComputedStyle(elem, null)
: elem.currentStyle || elem.style;
return css[field];
},
explode : function ( delimiter, string, limit ) {
// http://kevin.vanzonneveld.net
// + original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// + improved by: kenneth
// + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// + improved by: d3x
// + bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// * example 1: explode(' ', 'Kevin van Zonneveld');
// * returns 1: {0: 'Kevin', 1: 'van', 2: 'Zonneveld'}
// * example 2: explode('=', 'a=bc=d', 2);
// * returns 2: ['a', 'bc=d']
var emptyArray = { 0: '' };
// third argument is not required
if ( arguments.length < 2 ||
typeof arguments[0] == 'undefined' ||
typeof arguments[1] == 'undefined' )
{
return null;
}
if ( delimiter === '' ||
delimiter === false ||
delimiter === null )
{
return false;
}
if ( typeof delimiter == 'function' ||
typeof delimiter == 'object' ||
typeof string == 'function' ||
typeof string == 'object' )
{
return emptyArray;
}
if ( delimiter === true ) {
delimiter = '1';
}
if (!limit) {
return string.toString().split(delimiter.toString());
} else {
// support for limit argument
var splitted = string.toString().split(delimiter.toString());
var partA = splitted.splice(0, limit - 1);
var partB = splitted.join(delimiter.toString());
partA.push(partB);
return partA;
}
}
}
/*! public
Prop objects
Used to keep track of which property is being controlled
Methods:
update : update the property to where it should be at the given time
current : return a natural representation of the current property
*/
var Prop = {
/*! public
Wait, while doing no animating
*/
wait : {
update : function(obj, from, to, percent) {},
current : function(obj) {return 0;}
},
/*! public
Follows a linear path
*/
position : {
update : function(obj, from, to, percent) {
var x = jsAnimUtil.interp(from.x, to.x, percent);
var y = jsAnimUtil.interp(from.y, to.y, percent);
obj.setPosition(x, y);
},
current : function(obj) {
var left = parseInt(obj.style.marginLeft);
var top = parseInt(obj.style.marginTop);
var x = left + obj.halfWidth;
var y = top + obj.halfHeight;
return new Pos(x,y);
}
},
/*! public
Follows a semicircular path
Params:
-clockwise : True for clockwise, false otherwise
*/
positionSemicircle : function(clockwise) {
return {
update : function(obj, from, to, percent) {
var centerX = (from.x + to.x) / 2;
var centerY = (from.y + to.y) / 2;
var h = centerY - from.y;
var w = from.x - centerX;
var dist = Math.sqrt(h * h + w * w);
if(w == 0) {
if(h > 0)
var initAngle = - Math.PI / 2;
else
var initAngle = Math.PI / 2;
}
else {
var atan = Math.atan(h / Math.abs(w));
if(w > 0)
var initAngle = atan;
else {
var initAngle = Math.PI - atan;
}
}
if(clockwise)
var addAngle = - percent * Math.PI;
else
var addAngle = percent * Math.PI;
var angle = initAngle + addAngle;
var x = Math.floor(centerX + dist * Math.cos(angle));
var y = Math.floor(centerY - dist * Math.sin(angle));
obj.setPosition(x, y);
},
current : function(obj) {
var left = parseInt(obj.style.marginLeft);
var top = parseInt(obj.style.marginTop);
var x = left + obj.halfWidth;
var y = top + obj.halfHeight;
return new Pos(x,y);
}
}
},
/*! public
Follows a circular path through target then back to start
Params:
-clockwise : True for clockwise, false otherwise
*/
positionCircle : function(clockwise) {
return {
update : function(obj, from, to, percent) {
var centerX = (from.x + to.x) / 2;
var centerY = (from.y + to.y) / 2;
var h = centerY - from.y;
var w = from.x - centerX;
var dist = Math.sqrt(h * h + w * w);
if(w == 0) {
if(h > 0)
var initAngle = - Math.PI / 2;
else
var initAngle = Math.PI / 2;
}
else {
var atan = Math.atan(h / Math.abs(w));
if(w > 0)
var initAngle = atan;
else {
var initAngle = Math.PI - atan;
}
}
if(clockwise)
var addAngle = 2 * percent * Math.PI;
else
var addAngle = -2 * percent * Math.PI;
var angle = initAngle + addAngle;
var x = Math.floor(centerX + dist * Math.cos(angle));
var y = Math.floor(centerY + dist * Math.sin(angle));
obj.setPosition(x, y);
},
current : function(obj) {
var left = parseInt(obj.style.marginLeft);
var top = parseInt(obj.style.marginTop);
var x = left + obj.halfWidth;
var y = top + obj.halfHeight;
return new Pos(x,y);
}
}
},
//public
top : {
update : function(obj, from, to, percent) {
obj.style.top = jsAnimUtil.interp(from, to, percent) + "px";
},
current : function(obj) {
return parseInt(jsAnimUtil.getCSS(obj, 'top'));
}
},
//public
right : {
update : function(obj, from, to, percent) {
obj.style.right = jsAnimUtil.interp(from, to, percent) + "px";
},
current : function(obj) {
return parseInt(jsAnimUtil.getCSS(obj, 'right'));
}
},
//public
bottom : {
update : function(obj, from, to, percent) {
obj.style.bottom = jsAnimUtil.interp(from, to, percent) + "px";
},
current : function(obj) {
return parseInt(jsAnimUtil.getCSS(obj, 'bottom'));
}
},
//public
left : {
update : function(obj, from, to, percent) {
obj.style.left = jsAnimUtil.interp(from, to, percent) + "px";
},
current : function(obj) {
return parseInt(jsAnimUtil.getCSS(obj, 'left'));
}
},
//public
margin : {
update : function(obj, from, to, percent) {
obj.style.margin = jsAnimUtil.interp(from, to, percent) + "px";
},
current : function(obj) {
return parseInt(jsAnimUtil.getCSS(obj, 'margin'));
}
},
//public
marginTop : {
update : function(obj, from, to, percent) {
obj.style.marginTop = jsAnimUtil.interp(from, to, percent) + "px";
},
current : function(obj) {
return parseInt(jsAnimUtil.getCSS(obj, 'marginTop'));
}
},
//public
marginRight : {
update : function(obj, from, to, percent) {
obj.style.marginRight = jsAnimUtil.interp(from, to, percent) + "px";
},
current : function(obj) {
return parseInt(jsAnimUtil.getCSS(obj, 'marginRight'));
}
},
//public
marginBottom : {
update : function(obj, from, to, percent) {
obj.style.marginBottom = jsAnimUtil.interp(from, to, percent) + "px";
},
current : function(obj) {
return parseInt(jsAnimUtil.getCSS(obj, 'marginBottom'));
}
},
//public
marginLeft : {
update : function(obj, from, to, percent) {
obj.style.marginLeft = jsAnimUtil.interp(from, to, percent) + "px";
},
current : function(obj) {
return parseInt(jsAnimUtil.getCSS(obj, 'marginLeft'));
}
},
//public
padding : {
update : function(obj, from, to, percent) {
obj.style.padding = jsAnimUtil.interp(from, to, percent) + "px";
},
current : function(obj) {
return parseInt(jsAnimUtil.getCSS(obj, 'padding'));
}
},
//public
paddingTop : {
update : function(obj, from, to, percent) {
obj.style.paddingTop = jsAnimUtil.interp(from, to, percent) + "px";
},
current : function(obj) {
return parseInt(jsAnimUtil.getCSS(obj, 'paddingTop'));
}
},
//public
paddingRight : {
update : function(obj, from, to, percent) {
obj.style.paddingRight = jsAnimUtil.interp(from, to, percent) + "px";
},
current : function(obj) {
return parseInt(jsAnimUtil.getCSS(obj, 'paddingRight'));
}
},
//public
paddingBottom : {
update : function(obj, from, to, percent) {
obj.style.paddingBottom = jsAnimUtil.interp(from, to, percent) + "px";
},
current : function(obj) {
return parseInt(jsAnimUtil.getCSS(obj, 'paddingBottom'));
}
},
//public
paddingLeft : {
update : function(obj, from, to, percent) {
obj.style.paddingLeft = jsAnimUtil.interp(from, to, percent) + "px";
},
current : function(obj) {
return parseInt(jsAnimUtil.getCSS(obj, 'paddingLeft'));
}
},
//public
borderWidth : {
update : function(obj, from, to, percent) {
obj.style.borderWidth = jsAnimUtil.interp(from, to, percent) + "px";
},
current : function(obj) {
return parseInt(jsAnimUtil.getCSS(obj, 'borderWidth'));
}
},
//public
borderTopWidth : {
update : function(obj, from, to, percent) {
obj.style.borderTopWidth = jsAnimUtil.interp(from, to, percent) + "px";
},
current : function(obj) {
return parseInt(jsAnimUtil.getCSS(obj, 'borderTopWidth'));
}
},
//public
borderRightWidth : {
update : function(obj, from, to, percent) {
obj.style.borderRightWidth = jsAnimUtil.interp(from, to, percent) + "px";
},
current : function(obj) {
return parseInt(jsAnimUtil.getCSS(obj, 'borderRightWidth'));
}
},
//public
borderBottomWidth : {
update : function(obj, from, to, percent) {
obj.style.borderBottomWidth = jsAnimUtil.interp(from, to, percent) + "px";
},
current : function(obj) {
return parseInt(jsAnimUtil.getCSS(obj, 'borderBottomWidth'));
}
},
//public
borderLeftWidth : {
update : function(obj, from, to, percent) {
obj.style.borderLeftWidth = jsAnimUtil.interp(from, to, percent) + "px";
},
current : function(obj) {
return parseInt(jsAnimUtil.getCSS(obj, 'borderLeftWidth'));
}
},
//public
fontSize : {
update : function(obj, from, to, percent) {
obj.style.fontSize = jsAnimUtil.interp(from, to, percent) + "pt";
},
current : function(obj) {
return parseInt(jsAnimUtil.getCSS(obj, 'fontSize'));
}
},
//public
height : {
update : function(obj, from, to, percent) {
var v = jsAnimUtil.interp(from, to, percent);
obj.style.height = v + "px";
//Update the position if registered
if(obj.positionRegistered) {
var y = parseInt(obj.style.marginTop) + obj.halfHeight;
obj.halfHeight = Math.floor(obj.offsetHeight/2);
obj.style.marginTop = y - obj.halfHeight + "px";
}
},
current : function(obj) {
var ht = jsAnimUtil.getCSS(obj, 'height');
if(ht == "auto")
return obj.offsetHeight;
else
return parseInt(ht);
}
},
//public
width : {
update : function(obj, from, to, percent) {
var v = jsAnimUtil.interp(from, to, percent);
obj.style.width = v + "px";
//Update the position if registered
if(obj.positionRegistered) {
var x = parseInt(obj.style.marginLeft) + obj.halfWidth;
obj.halfWidth = Math.floor(obj.offsetWidth/2);
obj.style.marginLeft = x - obj.halfWidth + "px";
}
},
current : function(obj) {
return parseInt(jsAnimUtil.getCSS(obj, 'width'));
}
},
//public
dimension : {
update : function(obj, from, to, percent) {
var h = jsAnimUtil.interp(from.h, to.h, percent);
var w = jsAnimUtil.interp(from.w, to.w, percent);
obj.style.height = h + "px";
obj.style.width = w + "px";
//Update the position if registered
if(obj.positionRegistered) {
var y = parseInt(obj.style.marginTop) + obj.halfHeight;
obj.halfHeight = Math.floor(obj.offsetHeight/2);
obj.style.marginTop = (y - obj.halfHeight) + "px";
var x = parseInt(obj.style.marginLeft) + obj.halfWidth;
obj.halfWidth = Math.floor(obj.offsetWidth/2);
obj.style.marginLeft = (x - obj.halfWidth) + "px";
}
},
current : function(obj) {
var ht = jsAnimUtil.getCSS(obj, 'height');
if(ht == "auto")
var h = obj.offsetHeight;
else
var h = parseInt(ht);
var w = parseInt(jsAnimUtil.getCSS(obj, 'width'));
return new Dim(w, h);
}
},
//public
color : {
update : function(obj, from, to, percent) {
r = jsAnimUtil.interp(from.r, to.r, percent);
g = jsAnimUtil.interp(from.g, to.g, percent);
b = jsAnimUtil.interp(from.b, to.b, percent);
obj.style.color = "rgb("+r+","+g+","+b+")";
},
current : function(obj) {
var color = jsAnimUtil.getCSS(obj, 'color');
color = color.substring(4,color.length-1);
var rgb = jsAnimUtil.explode(",",color);
return new Col(parseInt(rgb[0]), parseInt(rgb[1]), parseInt(rgb[2]));
}
},
//public
backgroundColor : {
update : function(obj, from, to, percent) {
r = jsAnimUtil.interp(from.r, to.r, percent);
g = jsAnimUtil.interp(from.g, to.g, percent);
b = jsAnimUtil.interp(from.b, to.b, percent);
obj.style.backgroundColor = "rgb("+r+","+g+","+b+")";
},
current : function(obj) {
var color = jsAnimUtil.getCSS(obj, 'backgroundColor');
color = color.substring(4,color.length-1);
var rgb = jsAnimUtil.explode(",",color);
return new Col(parseInt(rgb[0]), parseInt(rgb[1]), parseInt(rgb[2]));
}
},
//public
borderColor : {
update : function(obj, from, to, percent) {
r = jsAnimUtil.interp(from.r, to.r, percent);
g = jsAnimUtil.interp(from.g, to.g, percent);
b = jsAnimUtil.interp(from.b, to.b, percent);
obj.style.borderColor = "rgb("+r+","+g+","+b+")";
},
current : function(obj) {
var color = jsAnimUtil.getCSS(obj, 'borderColor');
color = color.substring(4,color.length-1);
var rgb = jsAnimUtil.explode(",",color);
return new Col(parseInt(rgb[0]), parseInt(rgb[1]), parseInt(rgb[2]));
}
},
//public
opacity : {
update : function(obj, from, to, percent) {
v = jsAnimUtil.interp(100*from, 100*to, percent);
obj.style.opacity = v / 100;
},
current : function(obj) {
return jsAnimUtil.getCSS(obj, 'opacity');
}
}
}/*
http://www.JSON.org/json2.js
2011-02-23
Public Domain.
NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
See http://www.JSON.org/js.html
This code should be minified before deployment.
See http://javascript.crockford.com/jsmin.html
USE YOUR OWN COPY. IT IS EXTREMELY UNWISE TO LOAD CODE FROM SERVERS YOU DO
NOT CONTROL.
This file creates a global JSON object containing two methods: stringify
and parse.
JSON.stringify(value, replacer, space)
value any JavaScript value, usually an object or array.
replacer an optional parameter that determines how object
values are stringified for objects. It can be a
function or an array of strings.
space an optional parameter that specifies the indentation
of nested structures. If it is omitted, the text will
be packed without extra whitespace. If it is a number,
it will specify the number of spaces to indent at each
level. If it is a string (such as '\t' or '&nbsp;'),
it contains the characters used to indent at each level.
This method produces a JSON text from a JavaScript value.
When an object value is found, if the object contains a toJSON
method, its toJSON method will be called and the result will be
stringified. A toJSON method does not serialize: it returns the
value represented by the name/value pair that should be serialized,
or undefined if nothing should be serialized. The toJSON method
will be passed the key associated with the value, and this will be
bound to the value
For example, this would serialize Dates as ISO strings.
Date.prototype.toJSON = function (key) {
function f(n) {
// Format integers to have at least two digits.
return n < 10 ? '0' + n : n;
}
return this.getUTCFullYear() + '-' +
f(this.getUTCMonth() + 1) + '-' +
f(this.getUTCDate()) + 'T' +
f(this.getUTCHours()) + ':' +
f(this.getUTCMinutes()) + ':' +
f(this.getUTCSeconds()) + 'Z';
};
You can provide an optional replacer method. It will be passed the
key and value of each member, with this bound to the containing
object. The value that is returned from your method will be
serialized. If your method returns undefined, then the member will
be excluded from the serialization.
If the replacer parameter is an array of strings, then it will be
used to select the members to be serialized. It filters the results
such that only members with keys listed in the replacer array are
stringified.
Values that do not have JSON representations, such as undefined or
functions, will not be serialized. Such values in objects will be
dropped; in arrays they will be replaced with null. You can use
a replacer function to replace those with JSON values.
JSON.stringify(undefined) returns undefined.
The optional space parameter produces a stringification of the
value that is filled with line breaks and indentation to make it
easier to read.
If the space parameter is a non-empty string, then that string will
be used for indentation. If the space parameter is a number, then
the indentation will be that many spaces.
Example:
text = JSON.stringify(['e', {pluribus: 'unum'}]);
// text is '["e",{"pluribus":"unum"}]'
text = JSON.stringify(['e', {pluribus: 'unum'}], null, '\t');
// text is '[\n\t"e",\n\t{\n\t\t"pluribus": "unum"\n\t}\n]'
text = JSON.stringify([new Date()], function (key, value) {
return this[key] instanceof Date ?
'Date(' + this[key] + ')' : value;
});
// text is '["Date(---current time---)"]'
JSON.parse(text, reviver)
This method parses a JSON text to produce an object or array.
It can throw a SyntaxError exception.
The optional reviver parameter is a function that can filter and
transform the results. It receives each of the keys and values,
and its return value is used instead of the original value.
If it returns what it received, then the structure is not modified.
If it returns undefined then the member is deleted.
Example:
// Parse the text. Values that look like ISO date strings will
// be converted to Date objects.
myData = JSON.parse(text, function (key, value) {
var a;
if (typeof value === 'string') {
a =
/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/.exec(value);
if (a) {
return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4],
+a[5], +a[6]));
}
}
return value;
});
myData = JSON.parse('["Date(09/09/2001)"]', function (key, value) {
var d;
if (typeof value === 'string' &&
value.slice(0, 5) === 'Date(' &&
value.slice(-1) === ')') {
d = new Date(value.slice(5, -1));
if (d) {
return d;
}
}
return value;
});
This is a reference implementation. You are free to copy, modify, or
redistribute.
*/
/*jslint evil: true, strict: false, regexp: false */
/*members "", "\b", "\t", "\n", "\f", "\r", "\"", JSON, "\\", apply,
call, charCodeAt, getUTCDate, getUTCFullYear, getUTCHours,
getUTCMinutes, getUTCMonth, getUTCSeconds, hasOwnProperty, join,
lastIndex, length, parse, prototype, push, replace, slice, stringify,
test, toJSON, toString, valueOf
*/
// Create a JSON object only if one does not already exist. We create the
// methods in a closure to avoid creating global variables.
var JSON;
if (!JSON) {
JSON = {};
}
(function () {
"use strict";
function f(n) {
// Format integers to have at least two digits.
return n < 10 ? '0' + n : n;
}
if (typeof Date.prototype.toJSON !== 'function') {
Date.prototype.toJSON = function (key) {
return isFinite(this.valueOf()) ?
this.getUTCFullYear() + '-' +
f(this.getUTCMonth() + 1) + '-' +
f(this.getUTCDate()) + 'T' +
f(this.getUTCHours()) + ':' +
f(this.getUTCMinutes()) + ':' +
f(this.getUTCSeconds()) + 'Z' : null;
};
String.prototype.toJSON =
Number.prototype.toJSON =
Boolean.prototype.toJSON = function (key) {
return this.valueOf();
};
}
var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
gap,
indent,
meta = { // table of character substitutions
'\b': '\\b',
'\t': '\\t',
'\n': '\\n',
'\f': '\\f',
'\r': '\\r',
'"' : '\\"',
'\\': '\\\\'
},
rep;
function quote(string) {
// If the string contains no control characters, no quote characters, and no
// backslash characters, then we can safely slap some quotes around it.
// Otherwise we must also replace the offending characters with safe escape
// sequences.
escapable.lastIndex = 0;
return escapable.test(string) ? '"' + string.replace(escapable, function (a) {
var c = meta[a];
return typeof c === 'string' ? c :
'\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
}) + '"' : '"' + string + '"';
}
function str(key, holder) {
// Produce a string from holder[key].
var i, // The loop counter.
k, // The member key.
v, // The member value.
length,
mind = gap,
partial,
value = holder[key];
// If the value has a toJSON method, call it to obtain a replacement value.
if (value && typeof value === 'object' &&
typeof value.toJSON === 'function') {
value = value.toJSON(key);
}
// If we were called with a replacer function, then call the replacer to
// obtain a replacement value.
if (typeof rep === 'function') {
value = rep.call(holder, key, value);
}
// What happens next depends on the value's type.
switch (typeof value) {
case 'string':
return quote(value);
case 'number':
// JSON numbers must be finite. Encode non-finite numbers as null.
return isFinite(value) ? String(value) : 'null';
case 'boolean':
case 'null':
// If the value is a boolean or null, convert it to a string. Note:
// typeof null does not produce 'null'. The case is included here in
// the remote chance that this gets fixed someday.
return String(value);
// If the type is 'object', we might be dealing with an object or an array or
// null.
case 'object':
// Due to a specification blunder in ECMAScript, typeof null is 'object',
// so watch out for that case.
if (!value) {
return 'null';
}
// Make an array to hold the partial results of stringifying this object value.
gap += indent;
partial = [];
// Is the value an array?
if (Object.prototype.toString.apply(value) === '[object Array]') {
// The value is an array. Stringify every element. Use null as a placeholder
// for non-JSON values.
length = value.length;
for (i = 0; i < length; i += 1) {
partial[i] = str(i, value) || 'null';
}
// Join all of the elements together, separated with commas, and wrap them in
// brackets.
v = partial.length === 0 ? '[]' : gap ?
'[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']' :
'[' + partial.join(',') + ']';
gap = mind;
return v;
}
// If the replacer is an array, use it to select the members to be stringified.
if (rep && typeof rep === 'object') {
length = rep.length;
for (i = 0; i < length; i += 1) {
if (typeof rep[i] === 'string') {
k = rep[i];
v = str(k, value);
if (v) {
partial.push(quote(k) + (gap ? ': ' : ':') + v);
}
}
}
} else {
// Otherwise, iterate through all of the keys in the object.
for (k in value) {
if (Object.prototype.hasOwnProperty.call(value, k)) {
v = str(k, value);
if (v) {
partial.push(quote(k) + (gap ? ': ' : ':') + v);
}
}
}
}
// Join all of the member texts together, separated with commas,
// and wrap them in braces.
v = partial.length === 0 ? '{}' : gap ?
'{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}' :
'{' + partial.join(',') + '}';
gap = mind;
return v;
}
}
// If the JSON object does not yet have a stringify method, give it one.
if (typeof JSON.stringify !== 'function') {
JSON.stringify = function (value, replacer, space) {
// The stringify method takes a value and an optional replacer, and an optional
// space parameter, and returns a JSON text. The replacer can be a function
// that can replace values, or an array of strings that will select the keys.
// A default replacer method can be provided. Use of the space parameter can
// produce text that is more easily readable.
var i;
gap = '';
indent = '';
// If the space parameter is a number, make an indent string containing that
// many spaces.
if (typeof space === 'number') {
for (i = 0; i < space; i += 1) {
indent += ' ';
}
// If the space parameter is a string, it will be used as the indent string.
} else if (typeof space === 'string') {
indent = space;
}
// If there is a replacer, it must be a function or an array.
// Otherwise, throw an error.
rep = replacer;
if (replacer && typeof replacer !== 'function' &&
(typeof replacer !== 'object' ||
typeof replacer.length !== 'number')) {
throw new Error('JSON.stringify');
}
// Make a fake root object containing our value under the key of ''.
// Return the result of stringifying the value.
return str('', {'': value});
};
}
// If the JSON object does not yet have a parse method, give it one.
if (typeof JSON.parse !== 'function') {
JSON.parse = function (text, reviver) {
// The parse method takes a text and an optional reviver function, and returns
// a JavaScript value if the text is a valid JSON text.
var j;
function walk(holder, key) {
// The walk method is used to recursively walk the resulting structure so
// that modifications can be made.
var k, v, value = holder[key];
if (value && typeof value === 'object') {
for (k in value) {
if (Object.prototype.hasOwnProperty.call(value, k)) {
v = walk(value, k);
if (v !== undefined) {
value[k] = v;
} else {
delete value[k];
}
}
}
}
return reviver.call(holder, key, value);
}
// Parsing happens in four stages. In the first stage, we replace certain
// Unicode characters with escape sequences. JavaScript handles many characters
// incorrectly, either silently deleting them, or treating them as line endings.
text = String(text);
cx.lastIndex = 0;
if (cx.test(text)) {
text = text.replace(cx, function (a) {
return '\\u' +
('0000' + a.charCodeAt(0).toString(16)).slice(-4);
});
}
// In the second stage, we run the text against regular expressions that look
// for non-JSON patterns. We are especially concerned with '()' and 'new'
// because they can cause invocation, and '=' because it can cause mutation.
// But just to be safe, we want to reject all unexpected forms.
// We split the second stage into 4 regexp operations in order to work around
// crippling inefficiencies in IE's and Safari's regexp engines. First we
// replace the JSON backslash pairs with '@' (a non-JSON character). Second, we
// replace all simple value tokens with ']' characters. Third, we delete all
// open brackets that follow a colon or comma or that begin the text. Finally,
// we look to see that the remaining characters are only whitespace or ']' or
// ',' or ':' or '{' or '}'. If that is so, then the text is safe for eval.
if (/^[\],:{}\s]*$/
.test(text.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, '@')
.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']')
.replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
// In the third stage we use the eval function to compile the text into a
// JavaScript structure. The '{' operator is subject to a syntactic ambiguity
// in JavaScript: it can begin a block or an object literal. We wrap the text
// in parens to eliminate the ambiguity.
j = eval('(' + text + ')');
// In the optional fourth stage, we recursively walk the new structure, passing
// each name/value pair to a reviver function for possible transformation.
return typeof reviver === 'function' ?
walk({'': j}, '') : j;
}
// If the text is not JSON parseable, then a SyntaxError is thrown.
throw new SyntaxError('JSON.parse');
};
}
}());
/*!
* Sizzle CSS Selector Engine
* Copyright 2011, The Dojo Foundation
* Released under the MIT, BSD, and GPL Licenses.
* More information: http://sizzlejs.com/
*/
(function(){
var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^\[\]]*\]|['"][^'"]*['"]|[^\[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
done = 0,
toString = Object.prototype.toString,
hasDuplicate = false,
baseHasDuplicate = true,
rBackslash = /\\/g,
rNonWord = /\W/;
// Here we check if the JavaScript engine is using some sort of
// optimization where it does not always call our comparision
// function. If that is the case, discard the hasDuplicate value.
// Thus far that includes Google Chrome.
[0, 0].sort(function() {
baseHasDuplicate = false;
return 0;
});
var Sizzle = function( selector, context, results, seed ) {
results = results || [];
context = context || document;
var origContext = context;
if ( context.nodeType !== 1 && context.nodeType !== 9 ) {
return [];
}
if ( !selector || typeof selector !== "string" ) {
return results;
}
var m, set, checkSet, extra, ret, cur, pop, i,
prune = true,
contextXML = Sizzle.isXML( context ),
parts = [],
soFar = selector;
// Reset the position of the chunker regexp (start from head)
do {
chunker.exec( "" );
m = chunker.exec( soFar );
if ( m ) {
soFar = m[3];
parts.push( m[1] );
if ( m[2] ) {
extra = m[3];
break;
}
}
} while ( m );
if ( parts.length > 1 && origPOS.exec( selector ) ) {
if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
set = posProcess( parts[0] + parts[1], context );
} else {
set = Expr.relative[ parts[0] ] ?
[ context ] :
Sizzle( parts.shift(), context );
while ( parts.length ) {
selector = parts.shift();
if ( Expr.relative[ selector ] ) {
selector += parts.shift();
}
set = posProcess( selector, set );
}
}
} else {
// Take a shortcut and set the context if the root selector is an ID
// (but not if it'll be faster if the inner selector is an ID)
if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML &&
Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) {
ret = Sizzle.find( parts.shift(), context, contextXML );
context = ret.expr ?
Sizzle.filter( ret.expr, ret.set )[0] :
ret.set[0];
}
if ( context ) {
ret = seed ?
{ expr: parts.pop(), set: makeArray(seed) } :
Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML );
set = ret.expr ?
Sizzle.filter( ret.expr, ret.set ) :
ret.set;
if ( parts.length > 0 ) {
checkSet = makeArray( set );
} else {
prune = false;
}
while ( parts.length ) {
cur = parts.pop();
pop = cur;
if ( !Expr.relative[ cur ] ) {
cur = "";
} else {
pop = parts.pop();
}
if ( pop == null ) {
pop = context;
}
Expr.relative[ cur ]( checkSet, pop, contextXML );
}
} else {
checkSet = parts = [];
}
}
if ( !checkSet ) {
checkSet = set;
}
if ( !checkSet ) {
Sizzle.error( cur || selector );
}
if ( toString.call(checkSet) === "[object Array]" ) {
if ( !prune ) {
results.push.apply( results, checkSet );
} else if ( context && context.nodeType === 1 ) {
for ( i = 0; checkSet[i] != null; i++ ) {
if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && Sizzle.contains(context, checkSet[i])) ) {
results.push( set[i] );
}
}
} else {
for ( i = 0; checkSet[i] != null; i++ ) {
if ( checkSet[i] && checkSet[i].nodeType === 1 ) {
results.push( set[i] );
}
}
}
} else {
makeArray( checkSet, results );
}
if ( extra ) {
Sizzle( extra, origContext, results, seed );
Sizzle.uniqueSort( results );
}
return results;
};
Sizzle.uniqueSort = function( results ) {
if ( sortOrder ) {
hasDuplicate = baseHasDuplicate;
results.sort( sortOrder );
if ( hasDuplicate ) {
for ( var i = 1; i < results.length; i++ ) {
if ( results[i] === results[ i - 1 ] ) {
results.splice( i--, 1 );
}
}
}
}
return results;
};
Sizzle.matches = function( expr, set ) {
return Sizzle( expr, null, null, set );
};
Sizzle.matchesSelector = function( node, expr ) {
return Sizzle( expr, null, null, [node] ).length > 0;
};
Sizzle.find = function( expr, context, isXML ) {
var set;
if ( !expr ) {
return [];
}
for ( var i = 0, l = Expr.order.length; i < l; i++ ) {
var match,
type = Expr.order[i];
if ( (match = Expr.leftMatch[ type ].exec( expr )) ) {
var left = match[1];
match.splice( 1, 1 );
if ( left.substr( left.length - 1 ) !== "\\" ) {
match[1] = (match[1] || "").replace( rBackslash, "" );
set = Expr.find[ type ]( match, context, isXML );
if ( set != null ) {
expr = expr.replace( Expr.match[ type ], "" );
break;
}
}
}
}
if ( !set ) {
set = typeof context.getElementsByTagName !== "undefined" ?
context.getElementsByTagName( "*" ) :
[];
}
return { set: set, expr: expr };
};
Sizzle.filter = function( expr, set, inplace, not ) {
var match, anyFound,
old = expr,
result = [],
curLoop = set,
isXMLFilter = set && set[0] && Sizzle.isXML( set[0] );
while ( expr && set.length ) {
for ( var type in Expr.filter ) {
if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) {
var found, item,
filter = Expr.filter[ type ],
left = match[1];
anyFound = false;
match.splice(1,1);
if ( left.substr( left.length - 1 ) === "\\" ) {
continue;
}
if ( curLoop === result ) {
result = [];
}
if ( Expr.preFilter[ type ] ) {
match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter );
if ( !match ) {
anyFound = found = true;
} else if ( match === true ) {
continue;
}
}
if ( match ) {
for ( var i = 0; (item = curLoop[i]) != null; i++ ) {
if ( item ) {
found = filter( item, match, i, curLoop );
var pass = not ^ !!found;
if ( inplace && found != null ) {
if ( pass ) {
anyFound = true;
} else {
curLoop[i] = false;
}
} else if ( pass ) {
result.push( item );
anyFound = true;
}
}
}
}
if ( found !== undefined ) {
if ( !inplace ) {
curLoop = result;
}
expr = expr.replace( Expr.match[ type ], "" );
if ( !anyFound ) {
return [];
}
break;
}
}
}
// Improper expression
if ( expr === old ) {
if ( anyFound == null ) {
Sizzle.error( expr );
} else {
break;
}
}
old = expr;
}
return curLoop;
};
Sizzle.error = function( msg ) {
throw "Syntax error, unrecognized expression: " + msg;
};
var Expr = Sizzle.selectors = {
order: [ "ID", "NAME", "TAG" ],
match: {
ID: /#((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,
CLASS: /\.((?:[\w\u00c0-\uFFFF\-]|\\.)+)/,
NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF\-]|\\.)+)['"]*\]/,
ATTR: /\[\s*((?:[\w\u00c0-\uFFFF\-]|\\.)+)\s*(?:(\S?=)\s*(?:(['"])(.*?)\3|(#?(?:[\w\u00c0-\uFFFF\-]|\\.)*)|)|)\s*\]/,
TAG: /^((?:[\w\u00c0-\uFFFF\*\-]|\\.)+)/,
CHILD: /:(only|nth|last|first)-child(?:\(\s*(even|odd|(?:[+\-]?\d+|(?:[+\-]?\d*)?n\s*(?:[+\-]\s*\d+)?))\s*\))?/,
POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^\-]|$)/,
PSEUDO: /:((?:[\w\u00c0-\uFFFF\-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/
},
leftMatch: {},
attrMap: {
"class": "className",
"for": "htmlFor"
},
attrHandle: {
href: function( elem ) {
return elem.getAttribute( "href" );
},
type: function( elem ) {
return elem.getAttribute( "type" );
}
},
relative: {
"+": function(checkSet, part){
var isPartStr = typeof part === "string",
isTag = isPartStr && !rNonWord.test( part ),
isPartStrNotTag = isPartStr && !isTag;
if ( isTag ) {
part = part.toLowerCase();
}
for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {
if ( (elem = checkSet[i]) ) {
while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}
checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ?
elem || false :
elem === part;
}
}
if ( isPartStrNotTag ) {
Sizzle.filter( part, checkSet, true );
}
},
">": function( checkSet, part ) {
var elem,
isPartStr = typeof part === "string",
i = 0,
l = checkSet.length;
if ( isPartStr && !rNonWord.test( part ) ) {
part = part.toLowerCase();
for ( ; i < l; i++ ) {
elem = checkSet[i];
if ( elem ) {
var parent = elem.parentNode;
checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false;
}
}
} else {
for ( ; i < l; i++ ) {
elem = checkSet[i];
if ( elem ) {
checkSet[i] = isPartStr ?
elem.parentNode :
elem.parentNode === part;
}
}
if ( isPartStr ) {
Sizzle.filter( part, checkSet, true );
}
}
},
"": function(checkSet, part, isXML){
var nodeCheck,
doneName = done++,
checkFn = dirCheck;
if ( typeof part === "string" && !rNonWord.test( part ) ) {
part = part.toLowerCase();
nodeCheck = part;
checkFn = dirNodeCheck;
}
checkFn( "parentNode", part, doneName, checkSet, nodeCheck, isXML );
},
"~": function( checkSet, part, isXML ) {
var nodeCheck,
doneName = done++,
checkFn = dirCheck;
if ( typeof part === "string" && !rNonWord.test( part ) ) {
part = part.toLowerCase();
nodeCheck = part;
checkFn = dirNodeCheck;
}
checkFn( "previousSibling", part, doneName, checkSet, nodeCheck, isXML );
}
},
find: {
ID: function( match, context, isXML ) {
if ( typeof context.getElementById !== "undefined" && !isXML ) {
var m = context.getElementById(match[1]);
// Check parentNode to catch when Blackberry 4.6 returns
// nodes that are no longer in the document #6963
return m && m.parentNode ? [m] : [];
}
},
NAME: function( match, context ) {
if ( typeof context.getElementsByName !== "undefined" ) {
var ret = [],
results = context.getElementsByName( match[1] );
for ( var i = 0, l = results.length; i < l; i++ ) {
if ( results[i].getAttribute("name") === match[1] ) {
ret.push( results[i] );
}
}
return ret.length === 0 ? null : ret;
}
},
TAG: function( match, context ) {
if ( typeof context.getElementsByTagName !== "undefined" ) {
return context.getElementsByTagName( match[1] );
}
}
},
preFilter: {
CLASS: function( match, curLoop, inplace, result, not, isXML ) {
match = " " + match[1].replace( rBackslash, "" ) + " ";
if ( isXML ) {
return match;
}
for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {
if ( elem ) {
if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n\r]/g, " ").indexOf(match) >= 0) ) {
if ( !inplace ) {
result.push( elem );
}
} else if ( inplace ) {
curLoop[i] = false;
}
}
}
return false;
},
ID: function( match ) {
return match[1].replace( rBackslash, "" );
},
TAG: function( match, curLoop ) {
return match[1].replace( rBackslash, "" ).toLowerCase();
},
CHILD: function( match ) {
if ( match[1] === "nth" ) {
if ( !match[2] ) {
Sizzle.error( match[0] );
}
match[2] = match[2].replace(/^\+|\s*/g, '');
// parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'
var test = /(-?)(\d*)(?:n([+\-]?\d*))?/.exec(
match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" ||
!/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);
// calculate the numbers (first)n+(last) including if they are negative
match[2] = (test[1] + (test[2] || 1)) - 0;
match[3] = test[3] - 0;
}
else if ( match[2] ) {
Sizzle.error( match[0] );
}
// TODO: Move to normal caching system
match[0] = done++;
return match;
},
ATTR: function( match, curLoop, inplace, result, not, isXML ) {
var name = match[1] = match[1].replace( rBackslash, "" );
if ( !isXML && Expr.attrMap[name] ) {
match[1] = Expr.attrMap[name];
}
// Handle if an un-quoted value was used
match[4] = ( match[4] || match[5] || "" ).replace( rBackslash, "" );
if ( match[2] === "~=" ) {
match[4] = " " + match[4] + " ";
}
return match;
},
PSEUDO: function( match, curLoop, inplace, result, not ) {
if ( match[1] === "not" ) {
// If we're dealing with a complex expression, or a simple one
if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) {
match[3] = Sizzle(match[3], null, null, curLoop);
} else {
var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);
if ( !inplace ) {
result.push.apply( result, ret );
}
return false;
}
} else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {
return true;
}
return match;
},
POS: function( match ) {
match.unshift( true );
return match;
}
},
filters: {
enabled: function( elem ) {
return elem.disabled === false && elem.type !== "hidden";
},
disabled: function( elem ) {
return elem.disabled === true;
},
checked: function( elem ) {
return elem.checked === true;
},
selected: function( elem ) {
// Accessing this property makes selected-by-default
// options in Safari work properly
if ( elem.parentNode ) {
elem.parentNode.selectedIndex;
}
return elem.selected === true;
},
parent: function( elem ) {
return !!elem.firstChild;
},
empty: function( elem ) {
return !elem.firstChild;
},
has: function( elem, i, match ) {
return !!Sizzle( match[3], elem ).length;
},
header: function( elem ) {
return (/h\d/i).test( elem.nodeName );
},
text: function( elem ) {
var attr = elem.getAttribute( "type" ), type = elem.type;
// IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc)
// use getAttribute instead to test this case
return "text" === type && ( attr === type || attr === null );
},
radio: function( elem ) {
return "radio" === elem.type;
},
checkbox: function( elem ) {
return "checkbox" === elem.type;
},
file: function( elem ) {
return "file" === elem.type;
},
password: function( elem ) {
return "password" === elem.type;
},
submit: function( elem ) {
return "submit" === elem.type;
},
image: function( elem ) {
return "image" === elem.type;
},
reset: function( elem ) {
return "reset" === elem.type;
},
button: function( elem ) {
return "button" === elem.type || elem.nodeName.toLowerCase() === "button";
},
input: function( elem ) {
return (/input|select|textarea|button/i).test( elem.nodeName );
}
},
setFilters: {
first: function( elem, i ) {
return i === 0;
},
last: function( elem, i, match, array ) {
return i === array.length - 1;
},
even: function( elem, i ) {
return i % 2 === 0;
},
odd: function( elem, i ) {
return i % 2 === 1;
},
lt: function( elem, i, match ) {
return i < match[3] - 0;
},
gt: function( elem, i, match ) {
return i > match[3] - 0;
},
nth: function( elem, i, match ) {
return match[3] - 0 === i;
},
eq: function( elem, i, match ) {
return match[3] - 0 === i;
}
},
filter: {
PSEUDO: function( elem, match, i, array ) {
var name = match[1],
filter = Expr.filters[ name ];
if ( filter ) {
return filter( elem, i, match, array );
} else if ( name === "contains" ) {
return (elem.textContent || elem.innerText || Sizzle.getText([ elem ]) || "").indexOf(match[3]) >= 0;
} else if ( name === "not" ) {
var not = match[3];
for ( var j = 0, l = not.length; j < l; j++ ) {
if ( not[j] === elem ) {
return false;
}
}
return true;
} else {
Sizzle.error( name );
}
},
CHILD: function( elem, match ) {
var type = match[1],
node = elem;
switch ( type ) {
case "only":
case "first":
while ( (node = node.previousSibling) ) {
if ( node.nodeType === 1 ) {
return false;
}
}
if ( type === "first" ) {
return true;
}
node = elem;
case "last":
while ( (node = node.nextSibling) ) {
if ( node.nodeType === 1 ) {
return false;
}
}
return true;
case "nth":
var first = match[2],
last = match[3];
if ( first === 1 && last === 0 ) {
return true;
}
var doneName = match[0],
parent = elem.parentNode;
if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) {
var count = 0;
for ( node = parent.firstChild; node; node = node.nextSibling ) {
if ( node.nodeType === 1 ) {
node.nodeIndex = ++count;
}
}
parent.sizcache = doneName;
}
var diff = elem.nodeIndex - last;
if ( first === 0 ) {
return diff === 0;
} else {
return ( diff % first === 0 && diff / first >= 0 );
}
}
},
ID: function( elem, match ) {
return elem.nodeType === 1 && elem.getAttribute("id") === match;
},
TAG: function( elem, match ) {
return (match === "*" && elem.nodeType === 1) || elem.nodeName.toLowerCase() === match;
},
CLASS: function( elem, match ) {
return (" " + (elem.className || elem.getAttribute("class")) + " ")
.indexOf( match ) > -1;
},
ATTR: function( elem, match ) {
var name = match[1],
result = Expr.attrHandle[ name ] ?
Expr.attrHandle[ name ]( elem ) :
elem[ name ] != null ?
elem[ name ] :
elem.getAttribute( name ),
value = result + "",
type = match[2],
check = match[4];
return result == null ?
type === "!=" :
type === "=" ?
value === check :
type === "*=" ?
value.indexOf(check) >= 0 :
type === "~=" ?
(" " + value + " ").indexOf(check) >= 0 :
!check ?
value && result !== false :
type === "!=" ?
value !== check :
type === "^=" ?
value.indexOf(check) === 0 :
type === "$=" ?
value.substr(value.length - check.length) === check :
type === "|=" ?
value === check || value.substr(0, check.length + 1) === check + "-" :
false;
},
POS: function( elem, match, i, array ) {
var name = match[2],
filter = Expr.setFilters[ name ];
if ( filter ) {
return filter( elem, i, match, array );
}
}
}
};
var origPOS = Expr.match.POS,
fescape = function(all, num){
return "\\" + (num - 0 + 1);
};
for ( var type in Expr.match ) {
Expr.match[ type ] = new RegExp( Expr.match[ type ].source + (/(?![^\[]*\])(?![^\(]*\))/.source) );
Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source.replace(/\\(\d+)/g, fescape) );
}
var makeArray = function( array, results ) {
array = Array.prototype.slice.call( array, 0 );
if ( results ) {
results.push.apply( results, array );
return results;
}
return array;
};
// Perform a simple check to determine if the browser is capable of
// converting a NodeList to an array using builtin methods.
// Also verifies that the returned array holds DOM nodes
// (which is not the case in the Blackberry browser)
try {
Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType;
// Provide a fallback method if it does not work
} catch( e ) {
makeArray = function( array, results ) {
var i = 0,
ret = results || [];
if ( toString.call(array) === "[object Array]" ) {
Array.prototype.push.apply( ret, array );
} else {
if ( typeof array.length === "number" ) {
for ( var l = array.length; i < l; i++ ) {
ret.push( array[i] );
}
} else {
for ( ; array[i]; i++ ) {
ret.push( array[i] );
}
}
}
return ret;
};
}
var sortOrder, siblingCheck;
if ( document.documentElement.compareDocumentPosition ) {
sortOrder = function( a, b ) {
if ( a === b ) {
hasDuplicate = true;
return 0;
}
if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) {
return a.compareDocumentPosition ? -1 : 1;
}
return a.compareDocumentPosition(b) & 4 ? -1 : 1;
};
} else {
sortOrder = function( a, b ) {
var al, bl,
ap = [],
bp = [],
aup = a.parentNode,
bup = b.parentNode,
cur = aup;
// The nodes are identical, we can exit early
if ( a === b ) {
hasDuplicate = true;
return 0;
// If the nodes are siblings (or identical) we can do a quick check
} else if ( aup === bup ) {
return siblingCheck( a, b );
// If no parents were found then the nodes are disconnected
} else if ( !aup ) {
return -1;
} else if ( !bup ) {
return 1;
}
// Otherwise they're somewhere else in the tree so we need
// to build up a full list of the parentNodes for comparison
while ( cur ) {
ap.unshift( cur );
cur = cur.parentNode;
}
cur = bup;
while ( cur ) {
bp.unshift( cur );
cur = cur.parentNode;
}
al = ap.length;
bl = bp.length;
// Start walking down the tree looking for a discrepancy
for ( var i = 0; i < al && i < bl; i++ ) {
if ( ap[i] !== bp[i] ) {
return siblingCheck( ap[i], bp[i] );
}
}
// We ended someplace up the tree so do a sibling check
return i === al ?
siblingCheck( a, bp[i], -1 ) :
siblingCheck( ap[i], b, 1 );
};
siblingCheck = function( a, b, ret ) {
if ( a === b ) {
return ret;
}
var cur = a.nextSibling;
while ( cur ) {
if ( cur === b ) {
return -1;
}
cur = cur.nextSibling;
}
return 1;
};
}
// Utility function for retreiving the text value of an array of DOM nodes
Sizzle.getText = function( elems ) {
var ret = "", elem;
for ( var i = 0; elems[i]; i++ ) {
elem = elems[i];
// Get the text from text nodes and CDATA nodes
if ( elem.nodeType === 3 || elem.nodeType === 4 ) {
ret += elem.nodeValue;
// Traverse everything else, except comment nodes
} else if ( elem.nodeType !== 8 ) {
ret += Sizzle.getText( elem.childNodes );
}
}
return ret;
};
// Check to see if the browser returns elements by name when
// querying by getElementById (and provide a workaround)
(function(){
// We're going to inject a fake input element with a specified name
var form = document.createElement("div"),
id = "script" + (new Date()).getTime(),
root = document.documentElement;
form.innerHTML = "<a name='" + id + "'/>";
// Inject it into the root element, check its status, and remove it quickly
root.insertBefore( form, root.firstChild );
// The workaround has to do additional checks after a getElementById
// Which slows things down for other browsers (hence the branching)
if ( document.getElementById( id ) ) {
Expr.find.ID = function( match, context, isXML ) {
if ( typeof context.getElementById !== "undefined" && !isXML ) {
var m = context.getElementById(match[1]);
return m ?
m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ?
[m] :
undefined :
[];
}
};
Expr.filter.ID = function( elem, match ) {
var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");
return elem.nodeType === 1 && node && node.nodeValue === match;
};
}
root.removeChild( form );
// release memory in IE
root = form = null;
})();
(function(){
// Check to see if the browser returns only elements
// when doing getElementsByTagName("*")
// Create a fake element
var div = document.createElement("div");
div.appendChild( document.createComment("") );
// Make sure no comments are found
if ( div.getElementsByTagName("*").length > 0 ) {
Expr.find.TAG = function( match, context ) {
var results = context.getElementsByTagName( match[1] );
// Filter out possible comments
if ( match[1] === "*" ) {
var tmp = [];
for ( var i = 0; results[i]; i++ ) {
if ( results[i].nodeType === 1 ) {
tmp.push( results[i] );
}
}
results = tmp;
}
return results;
};
}
// Check to see if an attribute returns normalized href attributes
div.innerHTML = "<a href='#'></a>";
if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" &&
div.firstChild.getAttribute("href") !== "#" ) {
Expr.attrHandle.href = function( elem ) {
return elem.getAttribute( "href", 2 );
};
}
// release memory in IE
div = null;
})();
if ( document.querySelectorAll ) {
(function(){
var oldSizzle