Skip to content

Instantly share code, notes, and snippets.

@erikroyall
Created July 1, 2015 09:29
Show Gist options
  • Save erikroyall/fb937d6621f44a0ecd75 to your computer and use it in GitHub Desktop.
Save erikroyall/fb937d6621f44a0ecd75 to your computer and use it in GitHub Desktop.
// # Hilo
// `0.1.0-pre-dev-beta-10`<br/>
// Built on: 2015-05-06<br/>
// Project started before 2 years and 28 months and 6 days<br/>
// http://erikroyall.github.com/hilo<br/>
// Copyright (c) 2013 Erik Royall<br/>
// Licensed under MIT (see LICENSE-MIT)
(function (A, M, D) {
/* Register Hilo as an AMD module */
/*globals YUI: false, module: false, define: false*/
if (typeof module !== "undefined" && module.exports) {
module.exports = D;
} else if (typeof define === "function" && define.amd) {
define(D);
} else if (typeof YUI === "function") {
YUI.add(A, D);
} else {
M[A] = D();
}
}("Hilo", this, function () {
/*jshint -W083, -W064, -W061, -W030*/
/* JSHint escapes:
- W083 - Don't make function within a loop (Evts)
- W064 - Eval can be harmful (JSON)
- W064 - Missing new prefix when invoking constructor (Sizzle)
- W030 - Saw an expression (Sizzle, Me) */
"use strict";
var hilo /* Public API */
/* Used to measure performace (Hilo.perf) */
, start
/* References to browser objects */
, win = window // Reference to window
, doc = win.document // Reference to document
/* Used for storing detected features */
, detected
/* Key mappings (Hilo.keys) */
, key
/*Array of callbacks to be exec.ed on DOMReady */
, callbacks = [] // Array of functions to be executed on DOMReady
/* Private Selector Function */
, select
/* Feature Detection (Hilo.feature) */
, feature
/* Main AJAX function (Hilo.ajax) */
, hiloAjax
/* hasOwnProperty helper */
, own = function (obj, prop) {
return obj.hasOwnProperty(prop);
}
/* Loop Variable */
, _i;
/* Start performace testing */
start = new Date().getTime();
// ## Feature Detection
feature = (function () {
var c = function (tagName) {
return doc.createElement(tagName);
}
, i = c("input")
, d = c("div")
, cn = c("canvas")
, fr = c("iframe")
, is = function (i, attr, val) {
return !!(i.setAttribute (attr, val));
}
, a = c("audio")
, s = c("span")
, v = c("video")
, xr = new XMLHttpRequest();
return {
// addEventListener()
addEventListener: (function () {
return typeof win.addEventListener === "function";
}()),
// Application Cache (or Offline Web Apps)
applicationCache: (function () {
return !!win.applicationCache;
}()),
// Audio (tag)
audio: (function () {
return !!a.canPlayType;
}()),
// Preload audio (hmm.. background music?)
audioPreload: (function () {
return "preload" in a;
}()),
// Audio Types
audioType: {
// MP3 audio format
mp3: (function () {
return !!(a.canPlayType && a.canPlayType("audio/mpeg;").replace(/no/, ""));
}()),
// Vorbis audio format
vorbis: (function () {
return !!(a.canPlayType && a.canPlayType("audio/ogg; codecs='vorbis'").replace(/no/, ""));
}()),
// MS WAV audio format
wav: (function () {
return !!(a.canPlayType && a.canPlayType("audio/wav; codecs='1'").replace(/no/, ""));
}()),
// AAC audio format
aac: (function () {
return !!(a.canPlayType && a.canPlayType("audio/mp4; codecs='mp4a.40.2'").replace(/no/, ""));
}())
},
// Canvas API
canvas: (function () {
return !!cn.getContext;
}()),
// Canvas Text
canvasText: (function () {
return !!cn.getContext && typeof cn.getContext("2d").fillText === "function";
}()),
// classList prop. in HTMLElement
classList: (function () {
return "classList" in s;
}()),
// Command
command: (function () {
return "type" in c("command");
}()),
// Form Constraint Validation
consval: (function () {
return "noValidate" in c("form");
}()),
// contentEditable attribute
contentEditable: (function () {
return "isContentEditable" in s;
}()),
// Datalist (tag)
datalist: (function () {
return "options" in c("datalist");
}()),
// Details (tag)
details: (function () {
return "open" in c("details");
}()),
// Drag & Drop
dragdrop: (function () {
return "draggable" in s;
}()),
// ECMAScript 6
es6: (function () {
return typeof String.prototype.contains === "function";
}()),
// File system API
fileapi: (function () {
return typeof FileReader !== "undefined";
}()),
// 5th Generation Rendering Engine
gen5: (function () {
return parseInt(win.navigator.appVersion, 10) === 5;
}()),
// Geolocation
geolocation: (function () {
return "geolocation" in win.navigator;
}()),
// window.getSelection() method
getSelection: (function () {
return typeof win.getSelection === "function";
}()),
// History API
history: (function () {
return !!(win.history && history.pushState);
}()),
// IFrame
iframe: {
sandbox: (function () {
return "sandbox" in fr;
}()),
srdoc: (function () {
return "srcdoc" in fr;
}())
},
// IndexedDB (use this instead of WebSQL)
indexeddb: (function () {
return !!(win.indexedDB && win.IDBKeyRange && win.IDBTransaction);
}()),
// Input
input: {
// Input Auto Focus
autofocus: (function () {
return "autofocus" in i;
}()),
// Placeholder
placeholder: (function () {
return "placeholder" in i;
}()),
// Input Types (they are pretty self-explanatory)
type: {
color: (function () {
is(i, "type", "color");
return i.type !== "text";
}()),
date: (function () {
is(i, "type", "date");
return i.type !== "text";
}()),
datetime: (function () {
is(i, "type", "datetime");
return i.type !== "text";
}()),
datetimeLocal: (function () {
is(i, "type", "datetime-local");
return i.type !== "text";
}()),
email: (function () {
is(i, "type", "email");
return i.type !== "text";
}()),
month: (function () {
is(i, "type", "month");
return i.type !== "text";
}()),
number: (function () {
is(i, "type", "number");
return i.type !== "text";
}()),
range: (function () {
is(i, "type", "range");
return i.type !== "text";
}()),
search: (function () {
is(i, "type", "search");
return i.type !== "text";
}()),
tel: (function () {
is(i, "type", "tel");
return i.type !== "text";
}()),
time: (function () {
is(i, "type", "time");
return i.type !== "text";
}()),
week: (function () {
is(i, "type", "week");
return i.type !== "text";
}())
}
},
// Local Storage
localStorage: (function () {
try {
return "localStorage" in win && win["localStorage"] !== null && !!win.localStorage.setItem;
} catch(e){
return false;
}
}()),
// Meter (tag)
meter: (function () {
return "value" in c("meter");
}()),
// Microdata
microdata: (function () {
return "getItems" in doc;
}()),
// Offline (App Cache)
offline: (function () {
return !!win.applicationCache;
}()),
// Output (tag)
output: (function () {
return "value" in c("output");
}()),
// Progress (tag)
progress: (function () {
return "value" in c("progress");
}()),
// querySelector & querySelectorAll
qsa: (function () {
return "querySelector" in doc && "querySelectorAll" in doc;
}()),
// CSS3 Selectors in querySelectorAll
qsa3: (function () {
try {
return doc.querySelectorAll(":root").length > 0;
} catch (e) {
return false;
}
}()),
// requestAnimationFrame
requestAnimationFrame: (function () {
if (typeof requestAnimationFrame === "function") {
return true;
} else if (typeof msRequestAnimationFrame === "function") {
return "ms";
} else if (typeof webkitRequestAnimationFrame === "function") {
return "webkit";
} else if (typeof mozRequestAnimationFrame === "function") {
return "moz";
} else {
return false;
}
}()),
// Server-sent Events
serverEvt: (function () {
return typeof EventSource !== "undefined";
}()),
// Session Storage
sessionStorage: (function () {
try {
return "sessionStorage" in win && win["sessionStorage"] !== null;
} catch(e) {
return false;
}
}()),
// Modal Dialog (showModalDialog)
showModalDialog: (function () {
return typeof win.showModalDialog === "function";
}()),
// SVG (Scalable Vector Graphics)
svg: (function () {
return !!(doc.createElementNS && doc.createElementNS("http://www.w3.org/2000/svg", "svg").createSVGRect);
}()),
// SVG in text/html
svginhtml:(function () {
d.innerHTML = "<svg></svg>";
return !!(win.SVGSVGElement && d.firstChild instanceof win.SVGSVGElement);
}()),
// Template (tag)
template: (function () {
return "content" in c("template");
}()),
// Time (tag)
time: (function () {
return "datetime" in c("time");
}()),
// Undo (not just Ctrl + Z)
undo: (function () {
return typeof UndoManager !== "undefined";
}()),
// Video
video: (function () {
try {
return !!v.canPlayType;
} catch (e) {
return false;
}
}()),
// Video Captions
videoCaptions: (function () {
return "src" in c("track");
}()),
// Video Formats
videoFormats: {
// H264 Video Format (MP4)
h264: (function () {
try {
return v.canPlayType("video/mp4; codecs='avc1.42E01E, mp4a.40.2'");
} catch (e) {
return false;
}
}()),
// WebM Video Format
webm: (function () {
try {
return v.canPlayType("video/webm; codecs='vp8, vorbis'");
} catch (e) {
return false;
}
}()),
// OGG Theora Video Format
ogg: (function () {
try {
return v.canPlayType("video/ogg; codecs='theora, vorbis'");
} catch (e) {
return false;
}
}())
},
// Video Poster
videoPoster: (function () {
return "poster" in c("video");
}()),
// Web Audio API (NOT the <audio> tag)
webAudio: (function () {
// return !!(win.webkitAudioContext || win.AudioContext);
if (win.AudioContext) {
return true;
} else if (win.webkitAudioContext) {
return "webkit";
}
return false;
}()),
// WebSockets
webSockets: (function () {
return !!win.webSocket;
}()),
// WebSQL (a deprecated specification; use IndexedDB instead)
websql: (function () {
return !!win.openDatabase;
}()),
// Web Workers
webWorkers: (function () {
return !!win.Worker;
}()),
// Widgets
widgets: (function () {
return typeof widget !== "undefined";
}()),
// Cross-document messaging
xdocmsg: (function () {
return !!win.postMessage;
}()),
// XML HTTP Request
xhr: {
// Cross-domain requests
xdr: (function () {
return "withCredentials" in xr;
}()),
// Send as form data
formdata: (function () {
return !!win.FormData;
}()),
// Upload progress events
upe: (function () {
return "upload" in xr;
}())
}
};
}());
// ## Browser, Engine, Platform Detection
detected = (function () {
var engine
, browser
, system
, ua = win.navigator.userAgent
, safariVersion
, p;
// Browser
browser = {
ie: 0,
firefox: 0,
safari: 0,
konq: 0,
opera: 0,
chrome: 0,
// Specific Version
ver: null
};
// System
system = {
win: false,
mac: false,
x11: false,
/* Mobile Devices */
iphone: false,
ipod: false,
ipad: false,
ios: false,
android: false,
nokiaN: false,
winMobile: false,
/* Game Consoles */
wii: false,
ps: false
};
// Redering engine
engine = {
ie: 0,
gecko: 0,
webkit: 0,
khtml: 0,
opera: 0,
/* Complete version*/
ver: null
};
if(window.opera) {
engine.ver = browser.ver = window.opera.version();
engine.opera = browser.opera = parseFloat(engine.ver);
} else if (/AppleWebKit\/(\S+)/.test(ua)) {
engine.ver = RegExp.$1;
engine.webkit = parseFloat(engine.ver);
/* Figures out if chrome or Safari */
if (/Chrome\/(\S+)/.test(ua)) {
browser.ver = RegExp.$1;
browser.chrome = parseFloat(browser.ver);
} else if (/Version\/(\S+)/.test(ua)) {
browser.ver = RegExp.$1;
browser.safari = parseFloat(browser.ver);
} else {
/* Approximate version */
safariVersion = 1;
if (engine.webkit < 100) {
safariVersion = 1;
} else if (engine.webkit < 312) {
safariVersion = 1.2;
} else if (engine.webkit < 412) {
safariVersion = 1.3;
} else {
safariVersion = 2;
}
browser.safari = browser.ver = safariVersion;
}
} else if (/KHTML\/(\S+)/.test(ua) || /Konqueror\/([^;]+)/.test(ua)) {
engine.ver = browser.ver = RegExp.$1;
engine.khtml = browser.konq = parseFloat(engine.ver);
} else if (/rv:([^\)]+)\) Gecko\/\d{8}/.test(ua)) {
engine.ver = RegExp.$1;
engine.gecko = parseFloat(engine.ver);
/* Determine if it's firefox */
if (/Firefox\/(\S+)/.test(ua)) {
browser.ver = RegExp.$1;
browser.firefox = parseFloat(browser.ver);
}
} else if (/MSIE ([^;]+)/.test(ua)) {
engine.ver = browser.ver = RegExp.$1;
engine.ie = browser.ie = parseFloat(engine.ver);
}
/* Detect browsers */
browser.ie = engine.ie;
browser.opera = engine.opera;
/* Detect platform */
p = navigator.platform;
system.win = p.indexOf("Win") === 0;
system.mac = p.indexOf("Mac") === 0;
system.x11 = (p === "X11") || (p.indexOf("Linux") === 0);
/* Detecting Windows OSs */
if (system.win) {
if (/Win(?:dows )?([^do]{2})\s?(\d+\.\d+)?/.test(ua)) {
if (RegExp.$1 === "NT") {
switch(RegExp["$2"]) {
case "5.0":
system.win = "2000";
break;
case "5.1":
system.win = "XP";
break;
case "6.0":
system.win = "Vista";
break;
case "6.1":
system.win = "7";
break;
default:
system.win = "NT";
break;
}
} else if (RegExp.$1 === "9x") {
system.win = "ME";
} else {
system.win = RegExp.$1;
}
}
}
/* Mobile Devices */
system.iphone = ua.indexOf("iPhone") > -1;
system.ipod = ua.indexOf("iPod") > -1;
system.ipad = ua.indexOf("iPad") > -1;
system.nokiaN = ua.indexOf("NokiaN") > -1;
/* Windows Mobile */
if (system.win === "CE") {
system.winMobile = system.win;
} else if (system.win === "Ph") {
if (/Windows Phone OS(\d+.\d+)/.test(ua)) {
system.win = "Phone";
system.winMobile = parseFloat(RegExp.$1);
}
}
/* Determine iOS Version */
if (system.mac && ua.indexOf("Mobile") > -1) {
if (/CPU (?:iPhone )?OS (\d+_\d+)/.test(ua)) {
system.ios = parseFloat(RegExp.$1.replace("_", "."));
} else {
system.ios = 2; // Can't really detect - so guess
}
}
/* Determine Android Version */
if (/Android (\d+\.\d+)/.test(ua)) {
system.android = parseFloat(RegExp.$1);
}
/* Gaming Consoles */
system.wii = ua.indexOf("Wii") > -1;
system.ps = /playstation/i.test(ua);
/* Name and Version */
if (system.win) {
system.name = "Windows";
system.version = system.win;
} else if (system.mac) {
system.name = "Mac";
} else if (system.x11) {
system.name = "Linux";
} else {
system.name = "Some other";
}
/* Engines */
if (browser.ie) {
browser.name = "IE";
browser.version = browser.ie;
} else if (browser.chrome) {
browser.name = "Chrome";
browser.version = browser.chrome;
} else if (browser.safari) {
browser.name = "Safari";
browser.version = browser.safari;
} else if (browser.konq) {
browser.name = "Konqueror";
browser.version = browser.konq;
} else if (browser.opera) {
browser.name = "Opera";
browser.version = browser.opera;
} else if (browser.firefox) {
browser.name = "Firefox";
browser.version = browser.firefox;
}
/* return them */
return {
engine: engine,
browser: browser,
system: system
};
}());
// --------------------------------------------------
// JSON
// --------------------------------------------------
/*!
* JSON Parser (Public Domain)
* by Douglas Crockford
* http://javascript.crockford.com/
*/
// 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 = {};
(function () {
if (typeof window.JSON === "object" && typeof window.JSON.parse === "function") {
json = window.JSON;
return;
}
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 () {
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 () {
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");
};
}
}());
// querySelector pollyfill using Sizzle
var sizzle = (function() {
if (feature.qsa3 === true) {
return;
}
/*!
* Sizzle CSS Selector Engine v1.10.6-pre
* http://sizzlejs.com/
*
* Copyright 2013 jQuery Foundation, Inc. and other contributors
* Released under the MIT license
* http://jquery.org/license
*
*/
var i,
support,
cachedruns = function () {
var keys = [];
function cache (key, value) {
// Use (key + " ") to avoid collision
// with native prototype properties (see Issue #157)
if (keys.push(key += " ") > Expr.cacheLength) {
// Only keep the most recent entries
delete cache[keys.shift()];
}
return (cache[key] = value);
}
return cache;
},
Expr,
getText,
isXML,
compile,
outermostContext,
sortInput,
// Local document vars
setDocument,
document,
docElem,
documentIsHTML,
rbuggyQSA,
rbuggyMatches,
matches,
contains,
// Instance-specific data
expando = "sizzle" + -(new Date()),
preferredDoc = win.document,
dirruns = 0,
done = 0,
classCache,
tokenCache,
compilerCache,
hasDuplicate = false,
sortOrder = function(a, b) {
if (a === b) {
hasDuplicate = true;
return 0;
}
return 0;
},
// General-purpose constants
strundefined = typeof undefined,
MAX_NEGATIVE = 1 << 31,
// Instance methods
hasOwn = ({}).hasOwnProperty,
arr = [],
pop = arr.pop,
push_native = arr.push,
push = arr.push,
slice = arr.slice,
// Use a stripped-down indexOf if we can't use a native one
indexOf = arr.indexOf || function(elem) {
var i = 0,
len = this.length;
for (; i < len; i++) {
if (this[i] === elem) {
return i;
}
}
return -1;
},
booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",
// Regular expressions
// Whitespace characters http://www.w3.org/TR/css3-selectors/#whitespace
whitespace = "[\\x20\\t\\r\\n\\f]",
// http://www.w3.org/TR/css3-syntax/#characters
characterEncoding = "(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",
// Loosely modeled on CSS identifier characters
// An unquoted value should be a CSS identifier
// http://www.w3.org/TR/css3-selectors/#attribute-selectors
// Proper syntax:
// http://www.w3.org/TR/CSS21/syndata.html#value-def-identifier
identifier = characterEncoding.replace("w", "w#"),
// Acceptable operators
// http://www.w3.org/TR/selectors/#attribute-selectors
attributes = "\\[" + whitespace + "*(" + characterEncoding + ")" +
whitespace +
"*(?:([*^$|!~]?=)" + whitespace +
"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|(" + identifier + ")|)|)" +
whitespace + "*\\]",
// Prefer arguments quoted,
// then not containing pseudos/brackets,
// then attribute selectors/non-parenthetical expressions,
// then anything else
// These preferences are here to reduce the number of selectors
// needing tokenize in the PSEUDO preFilter
pseudos = ":(" + characterEncoding +
")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|" +
attributes.replace(3, 8) + ")*)|.*)\\)|)",
// Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter
rtrim = new RegExp("^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" +
whitespace + "+$", "g"),
rcomma = new RegExp("^" + whitespace + "*," + whitespace + "*"),
rcombinators = new RegExp("^" + whitespace + "*([>+~]|" + whitespace +
")" + whitespace + "*"),
rsibling = new RegExp(whitespace + "*[+~]"),
rattributeQuotes = new RegExp("=" + whitespace + "*([^\\]'\"]*)" +
whitespace + "*\\]", "g"),
rpseudo = new RegExp(pseudos ),
ridentifier = new RegExp("^" + identifier + "$" ),
matchExpr = {
"ID": new RegExp("^#(" + characterEncoding + ")" ),
"CLASS": new RegExp("^\\.(" + characterEncoding + ")" ),
"TAG": new RegExp("^(" + characterEncoding.replace("w", "w*" ) + ")" ),
"ATTR": new RegExp("^" + attributes ),
"PSEUDO": new RegExp("^" + pseudos ),
"CHILD": new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + whitespace +
"*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + whitespace +
"*(\\d+)|))" + whitespace + "*\\)|)", "i" ),
"bool": new RegExp("^(?:" + booleans + ")$", "i" ),
// For use in libraries implementing .is()
// We use this for POS matching in `select`
"needsContext": new RegExp("^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" +
whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" )
},
rnative = /^[^{]+\{\s*\[native \w/,
// Easily-parseable/retrievable ID or TAG or CLASS selectors
rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,
rinputs = /^(?:input|select|textarea|button)$/i,
rheader = /^h\d$/i,
rescape = /'|\\/g,
// CSS escapes http://www.w3.org/TR/CSS21/syndata.html#escaped-characters
runescape = new RegExp("\\\\([\\da-f]{1,6}" + whitespace + "?|(" +
whitespace + ")|.)", "ig" ),
funescape = function(_, escaped, escapedWhitespace) {
var high = "0x" + escaped - 0x10000;
// NaN means non-codepoint
// Support: Firefox
// Workaround erroneous numeric interpretation of +"0x"
return high !== high || escapedWhitespace ?
escaped :
// BMP codepoint
high < 0 ?
String.fromCharCode(high + 0x10000) :
// Supplemental Plane codepoint (surrogate pair)
String.fromCharCode(high >> 10 | 0xD800, high & 0x3FF | 0xDC00);
};
// Optimize for push.apply(_, NodeList)
try {
push.apply(
(arr = slice.call(preferredDoc.childNodes)),
preferredDoc.childNodes
);
// Support: Android<4.0
// Detect silently failing push.apply
arr[preferredDoc.childNodes.length].nodeType;
} catch (e) {
push = { apply: arr.length ?
// Leverage slice if possible
function(target, els) {
push_native.apply(target, slice.call(els));
} :
// Support: IE<9
// Otherwise append directly
function(target, els) {
var j = target.length,
i = 0;
// Can't trust NodeList.length
while ((target[j++] = els[i++])) {}
target.length = j - 1;
}
};
}
/**
* Create key-value caches of limited size
* @returns {Function(string, Object)} Returns the Object data after storing it on itself with
* property name the (space-suffixed) string and (if the cache is larger than Expr.cacheLength)
* deleting the oldest entry
*/
function select(selector, context, results, seed) {
var i, tokens, token, type, find,
match = tokenize(selector);
if (!seed) {
// Try to minimize operations if there is only one group
if (match.length === 1) {
// Take a shortcut and set the context if the root selector is an ID
tokens = match[0] = match[0].slice(0);
if (tokens.length > 2 && (token = tokens[0]).type === "ID" &&
support.getById && context.nodeType === 9 && documentIsHTML &&
Expr.relative[tokens[1].type]) {
context = (Expr.find["ID"](token.matches[0].replace(runescape,
funescape), context) || [])[0];
if (!context) {
return results;
}
selector = selector.slice(tokens.shift().value.length);
}
// Fetch a seed set for right-to-left matching
i = matchExpr["needsContext"].test(selector) ? 0 : tokens.length;
while (i--) {
token = tokens[i];
// Abort if we hit a combinator
if (Expr.relative[(type = token.type)]) {
break;
}
if ((find = Expr.find[type])) {
// Search, expanding context for leading sibling combinators
if ((seed = find(
token.matches[0].replace(runescape, funescape),
rsibling.test(tokens[0].type) && context.parentNode || context
))) {
// If seed is empty or no tokens remain, we can return early
tokens.splice(i, 1);
selector = seed.length && toSelector(tokens);
if (!selector) {
push.apply(results, seed);
return results;
}
break;
}
}
}
}
}
// Compile and execute a filtering function
// Provide `match` to avoid retokenization if we modified the selector above
compile(selector, match)(
seed,
context,
!documentIsHTML,
results,
rsibling.test(selector)
);
return results;
}
function Sizzle(selector, context, results, seed) {
var match, elem, m, nodeType,
// QSA vars
i, groups, old, nid, newContext, newSelector;
if ((context ? context.ownerDocument || context : preferredDoc) !== document) {
setDocument(context);
}
context = context || document;
results = results || [];
if (!selector || typeof selector !== "string") {
return results;
}
if ((nodeType = context.nodeType) !== 1 && nodeType !== 9) {
return [];
}
if (documentIsHTML && !seed) {
// Shortcuts
if ((match = rquickExpr.exec(selector))) {
// Speed-up: Sizzle("#ID")
if ((m = match[1])) {
if (nodeType === 9) {
elem = context.getElementById(m);
// Check parentNode to catch when Blackberry 4.6 returns
// nodes that are no longer in the document #6963
if (elem && elem.parentNode) {
// Handle the case where IE, Opera, and Webkit return items
// by name instead of ID
if (elem.id === m) {
results.push(elem);
return results;
}
} else {
return results;
}
} else {
// Context is not a document
if (context.ownerDocument && (elem = context.ownerDocument.getElementById(m)) &&
contains(context, elem) && elem.id === m) {
results.push(elem);
return results;
}
}
// Speed-up: Sizzle("TAG")
} else if (match[2]) {
push.apply(results, context.getElementsByTagName(selector));
return results;
// Speed-up: Sizzle(".CLASS")
} else if ((m = match[3]) && support.getElementsByClassName && context.getElementsByClassName) {
push.apply(results, context.getElementsByClassName(m));
return results;
}
}
// QSA path
if (support.qsa && (!rbuggyQSA || !rbuggyQSA.test(selector))) {
nid = old = expando;
newContext = context;
newSelector = nodeType === 9 && selector;
// qSA works strangely on Element-rooted queries
// We can work around this by specifying an extra ID on the root
// and working up from there (Thanks to Andrew Dupont for the technique)
// IE 8 doesn't work on object elements
if (nodeType === 1 && context.nodeName.toLowerCase() !== "object") {
groups = tokenize(selector);
if ((old = context.getAttribute("id"))) {
nid = old.replace(rescape, "\\$&");
} else {
context.setAttribute("id", nid);
}
nid = "[id='" + nid + "'] ";
i = groups.length;
while (i--) {
groups[i] = nid + toSelector(groups[i]);
}
newContext = rsibling.test(selector) && context.parentNode || context;
newSelector = groups.join(",");
}
if (newSelector) {
try {
push.apply(results,
newContext.querySelectorAll(newSelector)
);
return results;
} catch(qsaError) {
} finally {
if (!old) {
context.removeAttribute("id");
}
}
}
}
}
// All others
return select(selector.replace(rtrim, "$1"), context, results, seed);
}
function createCache () {
var keys = [];
function cache(key, value) {
// Use (key + " ") to avoid collision with native prototype properties (see Issue #157)
if (keys.push(key += " ") > Expr.cacheLength) {
// Only keep the most recent entries
delete cache[keys.shift()];
}
return (cache[key] = value);
}
return cache;
}
classCache = createCache();
tokenCache = createCache();
compilerCache = createCache();
/**
* Mark a function for special use by Sizzle
* @param {Function} fn The function to mark
*/
function markFunction(fn) {
fn[expando] = true;
return fn;
}
/**
* Support testing using an element
* @param {Function} fn Passed the created div and expects a boolean result
*/
function assert(fn) {
var div = document.createElement("div");
try {
return !!fn(div);
} catch (e) {
return false;
} finally {
// Remove from its parent by default
if (div.parentNode) {
div.parentNode.removeChild(div);
}
// release memory in IE
div = null;
}
}
/**
* Adds the same handler for all of the specified attrs
* @param {String} attrs Pipe-separated list of attributes
* @param {Function} handler The method that will be applied
*/
function addHandle(attrs, handler) {
var arr = attrs.split("|"),
i = attrs.length;
while (i--) {
Expr.attrHandle[arr[i]] = handler;
}
}
/**
* Checks document order of two siblings
* @param {Element} a
* @param {Element} b
* @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b
*/
function siblingCheck(a, b) {
var cur = b && a,
diff = cur && a.nodeType === 1 && b.nodeType === 1 &&
(~b.sourceIndex || MAX_NEGATIVE) -
(~a.sourceIndex || MAX_NEGATIVE);
// Use IE sourceIndex if available on both nodes
if (diff) {
return diff;
}
// Check if b follows a
if (cur) {
while ((cur = cur.nextSibling)) {
if (cur === b) {
return -1;
}
}
}
return a ? 1 : -1;
}
/**
* Returns a function to use in pseudos for input types
* @param {String} type
*/
function createInputPseudo(type) {
return function(elem) {
var name = elem.nodeName.toLowerCase();
return name === "input" && elem.type === type;
};
}
/**
* Returns a function to use in pseudos for buttons
* @param {String} type
*/
function createButtonPseudo(type) {
return function(elem) {
var name = elem.nodeName.toLowerCase();
return (name === "input" || name === "button") && elem.type === type;
};
}
/**
* Returns a function to use in pseudos for positionals
* @param {Function} fn
*/
function createPositionalPseudo(fn) {
return markFunction(function(argument) {
argument = +argument;
return markFunction(function(seed, matches) {
var j,
matchIndexes = fn([], seed.length, argument),
i = matchIndexes.length;
// Match elements found at the specified indexes
while (i--) {
if (seed[(j = matchIndexes[i])]) {
seed[j] = !(matches[j] = seed[j]);
}
}
});
});
}
/**
* Detect xml
* @param {Element|Object} elem An element or a document
*/
isXML = Sizzle.isXML = function(elem) {
// documentElement is verified for cases where it doesn't yet exist
// (such as loading iframes in IE - #4833)
var documentElement = elem && (elem.ownerDocument || elem).documentElement;
return documentElement ? documentElement.nodeName !== "HTML" : false;
};
// Expose support vars for convenience
support = Sizzle.support = {};
/**
* Sets document-related variables once based on the current document
* @param {Element|Object} [doc] An element or document object to use to set the document
* @returns {Object} Returns the current document
*/
setDocument = Sizzle.setDocument = function(node) {
var doc = node ? node.ownerDocument || node : preferredDoc,
parent = doc.defaultView;
// If no document and documentElement is available, return
if (doc === document || doc.nodeType !== 9 || !doc.documentElement) {
return document;
}
// Set our document
document = doc;
docElem = doc.documentElement;
// Support tests
documentIsHTML = !isXML(doc);
// Support: IE>8
// If iframe document is assigned to "document" variable and if iframe has been reloaded,
// IE will throw "permission denied" error when accessing "document" variable, see jQuery #13936
// IE6-8 do not support the defaultView property so parent will be undefined
if (parent && parent.attachEvent && parent !== parent.top) {
parent.attachEvent("onbeforeunload", function() {
setDocument();
});
}
/* Attributes
---------------------------------------------------------------------- */
// Support: IE<8
// Verify that getAttribute really returns attributes and not properties (excepting IE8 booleans)
support.attributes = assert(function(div) {
div.className = "i";
return !div.getAttribute("className");
});
/* getElement(s)By*
---------------------------------------------------------------------- */
// Check if getElementsByTagName("*") returns only elements
support.getElementsByTagName = assert(function(div) {
div.appendChild(doc.createComment(""));
return !div.getElementsByTagName("*").length;
});
// Check if getElementsByClassName can be trusted
support.getElementsByClassName = assert(function(div) {
div.innerHTML = "<div class='a'></div><div class='a i'></div>";
// Support: Safari<4
// Catch class over-caching
div.firstChild.className = "i";
// Support: Opera<10
// Catch gEBCN failure to find non-leading classes
return div.getElementsByClassName("i").length === 2;
});
// Support: IE<10
// Check if getElementById returns elements by name
// The broken getElementById methods don't pick up programatically-set names,
// so use a roundabout getElementsByName test
support.getById = assert(function(div) {
docElem.appendChild(div).id = expando;
return !doc.getElementsByName || !doc.getElementsByName(expando).length;
});
// ID find and filter
if (support.getById) {
Expr.find["ID"] = function(id, context) {
if (typeof context.getElementById !== strundefined && documentIsHTML) {
var m = context.getElementById(id);
// Check parentNode to catch when Blackberry 4.6 returns
// nodes that are no longer in the document #6963
return m && m.parentNode ? [m] : [];
}
};
Expr.filter["ID"] = function(id) {
var attrId = id.replace(runescape, funescape);
return function(elem) {
return elem.getAttribute("id") === attrId;
};
};
} else {
// Support: IE6/7
// getElementById is not reliable as a find shortcut
delete Expr.find["ID"];
Expr.filter["ID"] = function(id) {
var attrId = id.replace(runescape, funescape);
return function(elem) {
var node = typeof elem.getAttributeNode !== strundefined && elem.getAttributeNode("id");
return node && node.value === attrId;
};
};
}
// Tag
Expr.find["TAG"] = support.getElementsByTagName ?
function(tag, context) {
if (typeof context.getElementsByTagName !== strundefined) {
return context.getElementsByTagName(tag);
}
} :
function(tag, context) {
var elem,
tmp = [],
i = 0,
results = context.getElementsByTagName(tag);
// Filter out possible comments
if (tag === "*") {
while ((elem = results[i++])) {
if (elem.nodeType === 1) {
tmp.push(elem);
}
}
return tmp;
}
return results;
};
// Class
Expr.find["CLASS"] = support.getElementsByClassName && function(className, context) {
if (typeof context.getElementsByClassName !== strundefined && documentIsHTML) {
return context.getElementsByClassName(className);
}
};
/* QSA/matchesSelector
---------------------------------------------------------------------- */
// QSA and matchesSelector support
// matchesSelector(:active) reports false when true (IE9/Opera 11.5)
rbuggyMatches = [];
// qSa(:focus) reports false when true (Chrome 21)
// We allow this because of a bug in IE8/9 that throws an error
// whenever `document.activeElement` is accessed on an iframe
// So, we allow :focus to pass through QSA all the time to avoid the IE error
// See http://bugs.jquery.com/ticket/13378
rbuggyQSA = [];
if ((support.qsa = rnative.test(doc.querySelectorAll))) {
// Build QSA regex
// Regex strategy adopted from Diego Perini
assert(function(div) {
// Select is set to empty string on purpose
// This is to test IE's treatment of not explicitly
// setting a boolean content attribute,
// since its presence should be enough
// http://bugs.jquery.com/ticket/12359
div.innerHTML = "<select><option selected=''></option></select>";
// Support: IE8
// Boolean attributes and "value" are not treated correctly
if (!div.querySelectorAll("[selected]").length) {
rbuggyQSA.push("\\[" + whitespace + "*(?:value|" + booleans + ")");
}
// Webkit/Opera - :checked should return selected option elements
// http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
// IE8 throws error here and will not see later tests
if (!div.querySelectorAll(":checked").length) {
rbuggyQSA.push(":checked");
}
});
assert(function(div) {
// Support: Opera 10-12/IE8
// ^= $= *= and empty values
// Should not select anything
// Support: Windows 8 Native Apps
// The type attribute is restricted during .innerHTML assignment
var input = doc.createElement("input");
input.setAttribute("type", "hidden");
div.appendChild(input).setAttribute("t", "");
if (div.querySelectorAll("[t^='']").length) {
rbuggyQSA.push("[*^$]=" + whitespace + "*(?:''|\"\")");
}
// FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled)
// IE8 throws error here and will not see later tests
if (!div.querySelectorAll(":enabled").length) {
rbuggyQSA.push(":enabled", ":disabled");
}
// Opera 10-11 does not throw on post-comma invalid pseudos
div.querySelectorAll("*,:x");
rbuggyQSA.push(",.*:");
});
}
if ((support.matchesSelector = rnative.test((matches = docElem.webkitMatchesSelector ||
docElem.mozMatchesSelector ||
docElem.oMatchesSelector ||
docElem.msMatchesSelector)))) {
assert(function(div) {
// Check to see if it's possible to do matchesSelector
// on a disconnected node (IE 9)
support.disconnectedMatch = matches.call(div, "div");
// This should fail with an exception
// Gecko does not error, returns false instead
matches.call(div, "[s!='']:x");
rbuggyMatches.push("!=", pseudos);
});
}
rbuggyQSA = rbuggyQSA.length && new RegExp(rbuggyQSA.join("|"));
rbuggyMatches = rbuggyMatches.length && new RegExp(rbuggyMatches.join("|"));
/* Contains
---------------------------------------------------------------------- */
// Element contains another
// Purposefully does not implement inclusive descendent
// As in, an element does not contain itself
contains = rnative.test(docElem.contains) || docElem.compareDocumentPosition ?
function(a, b) {
var adown = a.nodeType === 9 ? a.documentElement : a,
bup = b && b.parentNode;
return a === bup || !!(bup && bup.nodeType === 1 && (
adown.contains ?
adown.contains(bup) :
a.compareDocumentPosition && a.compareDocumentPosition(bup) & 16
));
} :
function(a, b) {
if (b) {
while ((b = b.parentNode)) {
if (b === a) {
return true;
}
}
}
return false;
};
/* Sorting
---------------------------------------------------------------------- */
// Document order sorting
sortOrder = docElem.compareDocumentPosition ?
function(a, b) {
// Flag for duplicate removal
if (a === b) {
hasDuplicate = true;
return 0;
}
var compare = b.compareDocumentPosition && a.compareDocumentPosition && a.compareDocumentPosition(b);
if (compare) {
// Disconnected nodes
if (compare & 1 ||
(!support.sortDetached && b.compareDocumentPosition(a) === compare)) {
// Choose the first element that is related to our preferred document
if (a === doc || contains(preferredDoc, a)) {
return -1;
}
if (b === doc || contains(preferredDoc, b)) {
return 1;
}
// Maintain original order
return sortInput ?
(indexOf.call(sortInput, a) - indexOf.call(sortInput, b)) :
0;
}
return compare & 4 ? -1 : 1;
}
// Not directly comparable, sort on existence of method
return a.compareDocumentPosition ? -1 : 1;
} :
function(a, b) {
var cur,
i = 0,
aup = a.parentNode,
bup = b.parentNode,
ap = [a],
bp = [b];
// Exit early if the nodes are identical
if (a === b) {
hasDuplicate = true;
return 0;
// Parentless nodes are either documents or disconnected
} else if (!aup || !bup) {
return a === doc ? -1 :
b === doc ? 1 :
aup ? -1 :
bup ? 1 :
sortInput ?
(indexOf.call(sortInput, a) - indexOf.call(sortInput, b)) :
0;
// If the nodes are siblings, we can do a quick check
} else if (aup === bup) {
return siblingCheck(a, b);
}
// Otherwise we need full lists of their ancestors for comparison
cur = a;
while ((cur = cur.parentNode)) {
ap.unshift(cur);
}
cur = b;
while ((cur = cur.parentNode)) {
bp.unshift(cur);
}
// Walk down the tree looking for a discrepancy
while (ap[i] === bp[i]) {
i++;
}
return i ?
// Do a sibling check if the nodes have a common ancestor
siblingCheck(ap[i], bp[i]) :
// Otherwise nodes in our document sort first
ap[i] === preferredDoc ? -1 :
bp[i] === preferredDoc ? 1 :
0;
};
return doc;
};
// Sizzle.matches = function(expr, elements) {
// return Sizzle(expr, null, null, elements);
// };
Sizzle.matchesSelector = function(elem, expr) {
// Set document vars if needed
if ((elem.ownerDocument || elem) !== document) {
setDocument(elem);
}
// Make sure that attribute selectors are quoted
expr = expr.replace(rattributeQuotes, "='$1']");
if (support.matchesSelector && documentIsHTML &&
(!rbuggyMatches || !rbuggyMatches.test(expr)) &&
(!rbuggyQSA || !rbuggyQSA.test(expr))) {
try {
var ret = matches.call(elem, expr);
// IE 9's matchesSelector returns false on disconnected nodes
if (ret || support.disconnectedMatch ||
// As well, disconnected nodes are said to be in a document
// fragment in IE 9
elem.document && elem.document.nodeType !== 11) {
return ret;
}
} catch(e) {}
}
return Sizzle(expr, document, null, [elem]).length > 0;
};
Sizzle.contains = function(context, elem) {
// Set document vars if needed
if ((context.ownerDocument || context) !== document) {
setDocument(context);
}
return contains(context, elem);
};
Sizzle.attr = function(elem, name) {
// Set document vars if needed
if ((elem.ownerDocument || elem) !== document) {
setDocument(elem);
}
var fn = Expr.attrHandle[name.toLowerCase()],
// Don't get fooled by Object.prototype properties (jQuery #13807)
val = fn && hasOwn.call(Expr.attrHandle, name.toLowerCase()) ?
fn(elem, name, !documentIsHTML) :
undefined;
return val === undefined ?
support.attributes || !documentIsHTML ?
elem.getAttribute(name) :
(val = elem.getAttributeNode(name)) && val.specified ?
val.value :
null :
val;
};
Sizzle.error = function(msg) {
throw new Error("Syntax error, unrecognized expression: " + msg);
};
/**
* Document sorting and removing duplicates
* @param {ArrayLike} results
*/
Sizzle.uniqueSort = function(results) {
var elem,
duplicates = [],
j = 0,
i = 0;
// Unless we *know* we can detect duplicates, assume their presence
hasDuplicate = !support.detectDuplicates;
sortInput = !support.sortStable && results.slice(0);
results.sort(sortOrder);
if (hasDuplicate) {
while ((elem = results[i++])) {
if (elem === results[i]) {
j = duplicates.push(i);
}
}
while (j--) {
results.splice(duplicates[j], 1);
}
}
return results;
};
/**
* Utility function for retrieving the text value of an array of DOM nodes
* @param {Array|Element} elem
*/
getText = Sizzle.getText = function(elem) {
var node,
ret = "",
i = 0,
nodeType = elem.nodeType;
if (!nodeType) {
// If no nodeType, this is expected to be an array
for (; (node = elem[i]); i++) {
// Do not traverse comment nodes
ret += getText(node);
}
} else if (nodeType === 1 || nodeType === 9 || nodeType === 11) {
// Use textContent for elements
// innerText usage removed for consistency of new lines (see #11153)
if (typeof elem.textContent === "string") {
return elem.textContent;
} else {
// Traverse its children
for (elem = elem.firstChild; elem; elem = elem.nextSibling) {
ret += getText(elem);
}
}
} else if (nodeType === 3 || nodeType === 4) {
return elem.nodeValue;
}
// Do not include comment or processing instruction nodes
return ret;
};
Expr = Sizzle.selectors = {
// Can be adjusted by the user
cacheLength: 50,
createPseudo: markFunction,
match: matchExpr,
attrHandle: {},
find: {},
relative: {
">": { dir: "parentNode", first: true },
" ": { dir: "parentNode" },
"+": { dir: "previousSibling", first: true },
"~": { dir: "previousSibling" }
},
preFilter: {
"ATTR": function(match) {
match[1] = match[1].replace(runescape, funescape);
// Move the given value to match[3] whether quoted or unquoted
match[3] = (match[4] || match[5] || "").replace(runescape, funescape);
if (match[2] === "~=") {
match[3] = " " + match[3] + " ";
}
return match.slice(0, 4);
},
"CHILD": function(match) {
/* matches from matchExpr["CHILD"]
1 type (only|nth|...)
2 what (child|of-type)
3 argument (even|odd|\d*|\d*n([+-]\d+)?|...)
4 xn-component of xn+y argument ([+-]?\d*n|)
5 sign of xn-component
6 x of xn-component
7 sign of y-component
8 y of y-component
*/
match[1] = match[1].toLowerCase();
if (match[1].slice(0, 3) === "nth") {
// nth-* requires argument
if (!match[3]) {
Sizzle.error(match[0]);
}
// numeric x and y parameters for Expr.filter.CHILD
// remember that false/true cast respectively to 0/1
match[4] = +(match[4] ? match[5] + (match[6] || 1) : 2 * (match[3] === "even" || match[3] === "odd"));
match[5] = +((match[7] + match[8]) || match[3] === "odd");
// other types prohibit arguments
} else if (match[3]) {
Sizzle.error(match[0]);
}
return match;
},
"PSEUDO": function(match) {
var excess,
unquoted = !match[5] && match[2];
if (matchExpr["CHILD"].test(match[0])) {
return null;
}
// Accept quoted arguments as-is
if (match[3] && match[4] !== undefined) {
match[2] = match[4];
// Strip excess characters from unquoted arguments
} else if (unquoted && rpseudo.test(unquoted) &&
// Get excess from tokenize (recursively)
(excess = tokenize(unquoted, true)) &&
// advance to the next closing parenthesis
(excess = unquoted.indexOf(")", unquoted.length - excess) - unquoted.length)) {
// excess is a negative index
match[0] = match[0].slice(0, excess);
match[2] = unquoted.slice(0, excess);
}
// Return only captures needed by the pseudo filter method (type and argument)
return match.slice(0, 3);
}
},
filter: {
"TAG": function(nodeNameSelector) {
var nodeName = nodeNameSelector.replace(runescape, funescape).toLowerCase();
return nodeNameSelector === "*" ?
function() { return true; } :
function(elem) {
return elem.nodeName && elem.nodeName.toLowerCase() === nodeName;
};
},
"CLASS": function(className) {
var pattern = classCache[className + " "];
return pattern ||
(pattern = new RegExp("(^|" + whitespace + ")" + className + "(" + whitespace + "|$)")) &&
classCache(className, function(elem) {
return pattern.test(typeof elem.className === "string" && elem.className || typeof elem.getAttribute !== strundefined && elem.getAttribute("class") || "");
});
},
"ATTR": function(name, operator, check) {
return function(elem) {
var result = Sizzle.attr(elem, name);
if (result == null) {
return operator === "!=";
}
if (!operator) {
return true;
}
result += "";
return operator === "=" ? result === check :
operator === "!=" ? result !== check :
operator === "^=" ? check && result.indexOf(check) === 0 :
operator === "*=" ? check && result.indexOf(check) > -1 :
operator === "$=" ? check && result.slice(-check.length) === check :
operator === "~=" ? (" " + result + " ").indexOf(check) > -1 :
operator === "|=" ? result === check || result.slice(0, check.length + 1) === check + "-" :
false;
};
},
"CHILD": function(type, what, argument, first, last) {
var simple = type.slice(0, 3) !== "nth",
forward = type.slice(-4) !== "last",
ofType = what === "of-type";
return first === 1 && last === 0 ?
// Shortcut for :nth-*(n)
function(elem) {
return !!elem.parentNode;
} :
function(elem, context, xml) {
var cache, outerCache, node, diff, nodeIndex, start,
dir = simple !== forward ? "nextSibling" : "previousSibling",
parent = elem.parentNode,
name = ofType && elem.nodeName.toLowerCase(),
useCache = !xml && !ofType;
if (parent) {
// :(first|last|only)-(child|of-type)
if (simple) {
while (dir) {
node = elem;
while ((node = node[dir])) {
if (ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1) {
return false;
}
}
// Reverse direction for :only-* (if we haven't yet done so)
start = dir = type === "only" && !start && "nextSibling";
}
return true;
}
start = [forward ? parent.firstChild : parent.lastChild];
// non-xml :nth-child(...) stores cache data on `parent`
if (forward && useCache) {
// Seek `elem` from a previously-cached index
outerCache = parent[expando] || (parent[expando] = {});
cache = outerCache[type] || [];
nodeIndex = cache[0] === dirruns && cache[1];
diff = cache[0] === dirruns && cache[2];
node = nodeIndex && parent.childNodes[nodeIndex];
while ((node = ++nodeIndex && node && node[dir] ||
// Fallback to seeking `elem` from the start
(diff = nodeIndex = 0) || start.pop())) {
// When found, cache indexes on `parent` and break
if (node.nodeType === 1 && ++diff && node === elem) {
outerCache[type] = [dirruns, nodeIndex, diff];
break;
}
}
// Use previously-cached element index if available
} else if (useCache && (cache = (elem[expando] || (elem[expando] = {}))[type]) && cache[0] === dirruns) {
diff = cache[1];
// xml :nth-child(...) or :nth-last-child(...) or :nth(-last)?-of-type(...)
} else {
// Use the same loop as above to seek `elem` from the start
while ((node = ++nodeIndex && node && node[dir] ||
(diff = nodeIndex = 0) || start.pop())) {
if ((ofType ? node.nodeName.toLowerCase() === name : node.nodeType === 1) && ++diff) {
// Cache the index of each encountered element
if (useCache) {
(node[expando] || (node[expando] = {}))[type] = [dirruns, diff];
}
if (node === elem) {
break;
}
}
}
}
// Incorporate the offset, then check against cycle size
diff -= last;
return diff === first || (diff % first === 0 && diff / first >= 0);
}
};
},
"PSEUDO": function(pseudo, argument) {
// pseudo-class names are case-insensitive
// http://www.w3.org/TR/selectors/#pseudo-classes
// Prioritize by case sensitivity in case custom pseudos are added with uppercase letters
// Remember that SetFilters inherits from pseudos
var args,
fn = Expr.pseudos[pseudo] || Expr.SetFilters[pseudo.toLowerCase()] ||
Sizzle.error("unsupported pseudo: " + pseudo);
// The user may use createPseudo to indicate that
// arguments are needed to create the filter function
// just as Sizzle does
if (fn[expando]) {
return fn(argument);
}
// But maintain support for old signatures
if (fn.length > 1) {
args = [pseudo, pseudo, "", argument];
return Expr.SetFilters.hasOwnProperty(pseudo.toLowerCase()) ?
markFunction(function(seed, matches) {
var idx,
matched = fn(seed, argument),
i = matched.length;
while (i--) {
idx = indexOf.call(seed, matched[i]);
seed[idx] = !(matches[idx] = matched[i]);
}
}) :
function(elem) {
return fn(elem, 0, args);
};
}
return fn;
}
},
pseudos: {
// Potentially complex pseudos
"not": markFunction(function(selector) {
// Trim the selector passed to compile
// to avoid treating leading and trailing
// spaces as combinators
var input = [],
results = [],
matcher = compile(selector.replace(rtrim, "$1"));
return matcher[expando] ?
markFunction(function(seed, matches, context, xml) {
var elem,
unmatched = matcher(seed, null, xml, []),
i = seed.length;
// Match elements unmatched by `matcher`
while (i--) {
if ((elem = unmatched[i])) {
seed[i] = !(matches[i] = elem);
}
}
}) :
function(elem, context, xml) {
input[0] = elem;
matcher(input, null, xml, results);
return !results.pop();
};
}),
"has": markFunction(function(selector) {
return function(elem) {
return Sizzle(selector, elem).length > 0;
};
}),
"contains": markFunction(function(text) {
return function(elem) {
return (elem.textContent || elem.innerText || getText(elem)).indexOf(text) > -1;
};
}),
// "Whether an element is represented by a :lang() selector
// is based solely on the element's language value
// being equal to the identifier C,
// or beginning with the identifier C immediately followed by "-".
// The matching of C against the element's language value is performed case-insensitively.
// The identifier C does not have to be a valid language name."
// http://www.w3.org/TR/selectors/#lang-pseudo
"lang": markFunction(function(lang) {
// lang value must be a valid identifier
if (!ridentifier.test(lang || "")) {
Sizzle.error("unsupported lang: " + lang);
}
lang = lang.replace(runescape, funescape).toLowerCase();
return function(elem) {
var elemLang;
do {
if ((elemLang = documentIsHTML ?
elem.lang :
elem.getAttribute("xml:lang") || elem.getAttribute("lang"))) {
elemLang = elemLang.toLowerCase();
return elemLang === lang || elemLang.indexOf(lang + "-") === 0;
}
} while ((elem = elem.parentNode) && elem.nodeType === 1);
return false;
};
}),
// Miscellaneous
"target": function(elem) {
var hash = win.location && win.location.hash;
return hash && hash.slice(1) === elem.id;
},
"root": function(elem) {
return elem === docElem;
},
"focus": function(elem) {
return elem === document.activeElement && (!document.hasFocus || document.hasFocus()) && !!(elem.type || elem.href || ~elem.tabIndex);
},
// Boolean properties
"enabled": function(elem) {
return elem.disabled === false;
},
"disabled": function(elem) {
return elem.disabled === true;
},
"checked": function(elem) {
// In CSS3, :checked should return both checked and selected elements
// http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked
var nodeName = elem.nodeName.toLowerCase();
return (nodeName === "input" && !!elem.checked) || (nodeName === "option" && !!elem.selected);
},
"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;
},
// Contents
"empty": function(elem) {
// http://www.w3.org/TR/selectors/#empty-pseudo
// :empty is only affected by element nodes and content nodes(including text(3), cdata(4)),
// not comment, processing instructions, or others
// Thanks to Diego Perini for the nodeName shortcut
// Greater than "@" means alpha characters (specifically not starting with "#" or "?")
for (elem = elem.firstChild; elem; elem = elem.nextSibling) {
if (elem.nodeName > "@" || elem.nodeType === 3 || elem.nodeType === 4) {
return false;
}
}
return true;
},
"parent": function(elem) {
return !Expr.pseudos["empty"](elem);
},
// Element/input types
"header": function(elem) {
return rheader.test(elem.nodeName);
},
"input": function(elem) {
return rinputs.test(elem.nodeName);
},
"button": function(elem) {
var name = elem.nodeName.toLowerCase();
return name === "input" && elem.type === "button" || name === "button";
},
"text": function(elem) {
var attr;
// IE6 and 7 will map elem.type to 'text' for new HTML5 types (search, etc)
// use getAttribute instead to test this case
return elem.nodeName.toLowerCase() === "input" &&
elem.type === "text" &&
((attr = elem.getAttribute("type")) == null || attr.toLowerCase() === elem.type);
},
// Position-in-collection
"first": createPositionalPseudo(function() {
return [0];
}),
"last": createPositionalPseudo(function(matchIndexes, length) {
return [length - 1];
}),
"eq": createPositionalPseudo(function(matchIndexes, length, argument) {
return [argument < 0 ? argument + length : argument];
}),
"even": createPositionalPseudo(function(matchIndexes, length) {
var i = 0;
for (; i < length; i += 2) {
matchIndexes.push(i);
}
return matchIndexes;
}),
"odd": createPositionalPseudo(function(matchIndexes, length) {
var i = 1;
for (; i < length; i += 2) {
matchIndexes.push(i);
}
return matchIndexes;
}),
"lt": createPositionalPseudo(function(matchIndexes, length, argument) {
var i = argument < 0 ? argument + length : argument;
for (; --i >= 0;) {
matchIndexes.push(i);
}
return matchIndexes;
}),
"gt": createPositionalPseudo(function(matchIndexes, length, argument) {
var i = argument < 0 ? argument + length : argument;
for (; ++i < length;) {
matchIndexes.push(i);
}
return matchIndexes;
})
}
};
Expr.pseudos["nth"] = Expr.pseudos["eq"];
// Add button/input type pseudos
for (i in { radio: true, checkbox: true, file: true, password: true, image: true }) {
Expr.pseudos[i] = createInputPseudo(i);
}
for (i in { submit: true, reset: true }) {
Expr.pseudos[i] = createButtonPseudo(i);
}
// Easy API for creating new SetFilters
function SetFilters() {}
SetFilters.prototype = Expr.filters = Expr.pseudos;
Expr.SetFilters = new SetFilters();
function tokenize(selector, parseOnly) {
var matched, match, tokens, type,
soFar, groups, preFilters,
cached = tokenCache[selector + " "];
if (cached) {
return parseOnly ? 0 : cached.slice(0);
}
soFar = selector;
groups = [];
preFilters = Expr.preFilter;
while (soFar) {
// Comma and first run
if (!matched || (match = rcomma.exec(soFar))) {
if (match) {
// Don't consume trailing commas as valid
soFar = soFar.slice(match[0].length) || soFar;
}
groups.push(tokens = []);
}
matched = false;
// Combinators
if ((match = rcombinators.exec(soFar))) {
matched = match.shift();
tokens.push({
value: matched,
// Cast descendant combinators to space
type: match[0].replace(rtrim, " ")
});
soFar = soFar.slice(matched.length);
}
// Filters
for (type in Expr.filter) {
if ((match = matchExpr[type].exec(soFar)) && (!preFilters[type] ||
(match = preFilters[type](match)))) {
matched = match.shift();
tokens.push({
value: matched,
type: type,
matches: match
});
soFar = soFar.slice(matched.length);
}
}
if (!matched) {
break;
}
}
// Return the length of the invalid excess
// if we're just parsing
// Otherwise, throw an error or return tokens
return parseOnly ?
soFar.length :
soFar ?
Sizzle.error(selector) :
// Cache the tokens
tokenCache(selector, groups).slice(0);
}
function toSelector(tokens) {
var i = 0,
len = tokens.length,
selector = "";
for (; i < len; i++) {
selector += tokens[i].value;
}
return selector;
}
function addCombinator(matcher, combinator, base) {
var dir = combinator.dir,
checkNonElements = base && dir === "parentNode",
doneName = done++;
return combinator.first ?
// Check against closest ancestor/preceding element
function(elem, context, xml) {
while ((elem = elem[dir])) {
if (elem.nodeType === 1 || checkNonElements) {
return matcher(elem, context, xml);
}
}
} :
// Check against all ancestor/preceding elements
function(elem, context, xml) {
var data, cache, outerCache,
dirkey = dirruns + " " + doneName;
// We can't set arbitrary data on XML nodes, so they don't benefit from dir caching
if (xml) {
while ((elem = elem[dir])) {
if (elem.nodeType === 1 || checkNonElements) {
if (matcher(elem, context, xml)) {
return true;
}
}
}
} else {
while ((elem = elem[dir])) {
if (elem.nodeType === 1 || checkNonElements) {
outerCache = elem[expando] || (elem[expando] = {});
if ((cache = outerCache[dir]) && cache[0] === dirkey) {
if ((data = cache[1]) === true || data === cachedruns) {
return data === true;
}
} else {
cache = outerCache[dir] = [dirkey];
cache[1] = matcher(elem, context, xml) || cachedruns;
if (cache[1] === true) {
return true;
}
}
}
}
}
};
}
function elementMatcher(matchers) {
return matchers.length > 1 ?
function(elem, context, xml) {
var i = matchers.length;
while (i--) {
if (!matchers[i](elem, context, xml)) {
return false;
}
}
return true;
} :
matchers[0];
}
function condense(unmatched, map, filter, context, xml) {
var elem,
newUnmatched = [],
i = 0,
len = unmatched.length,
mapped = map != null;
for (; i < len; i++) {
if ((elem = unmatched[i])) {
if (!filter || filter(elem, context, xml)) {
newUnmatched.push(elem);
if (mapped) {
map.push(i);
}
}
}
}
return newUnmatched;
}
function setMatcher(preFilter, selector, matcher, postFilter, postFinder, postSelector) {
if (postFilter && !postFilter[expando]) {
postFilter = setMatcher(postFilter);
}
if (postFinder && !postFinder[expando]) {
postFinder = setMatcher(postFinder, postSelector);
}
return markFunction(function(seed, results, context, xml) {
var temp, i, elem,
preMap = [],
postMap = [],
preexisting = results.length,
// Get initial elements from seed or context
elems = seed || multipleContexts(selector || "*", context.nodeType ? [context] : context, []),
// Prefilter to get matcher input, preserving a map for seed-results synchronization
matcherIn = preFilter && (seed || !selector) ?
condense(elems, preMap, preFilter, context, xml) :
elems,
matcherOut = matcher ?
// If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results,
postFinder || (seed ? preFilter : preexisting || postFilter) ?
// ...intermediate processing is necessary
[] :
// ...otherwise use results directly
results :
matcherIn;
// Find primary matches
if (matcher) {
matcher(matcherIn, matcherOut, context, xml);
}
// Apply postFilter
if (postFilter) {
temp = condense(matcherOut, postMap);
postFilter(temp, [], context, xml);
// Un-match failing elements by moving them back to matcherIn
i = temp.length;
while (i--) {
if ((elem = temp[i])) {
matcherOut[postMap[i]] = !(matcherIn[postMap[i]] = elem);
}
}
}
if (seed) {
if (postFinder || preFilter) {
if (postFinder) {
// Get the final matcherOut by condensing this intermediate into postFinder contexts
temp = [];
i = matcherOut.length;
while (i--) {
if ((elem = matcherOut[i])) {
// Restore matcherIn since elem is not yet a final match
temp.push((matcherIn[i] = elem));
}
}
postFinder(null, (matcherOut = []), temp, xml);
}
// Move matched elements from seed to results to keep them synchronized
i = matcherOut.length;
while (i--) {
if ((elem = matcherOut[i]) &&
(temp = postFinder ? indexOf.call(seed, elem) : preMap[i]) > -1) {
seed[temp] = !(results[temp] = elem);
}
}
}
// Add elements to results, through postFinder if defined
} else {
matcherOut = condense(
matcherOut === results ?
matcherOut.splice(preexisting, matcherOut.length) :
matcherOut
);
if (postFinder) {
postFinder(null, results, matcherOut, xml);
} else {
push.apply(results, matcherOut);
}
}
});
}
function matcherFromTokens(tokens) {
var checkContext, matcher, j,
len = tokens.length,
leadingRelative = Expr.relative[tokens[0].type],
implicitRelative = leadingRelative || Expr.relative[" "],
i = leadingRelative ? 1 : 0,
// The foundational matcher ensures that elements are reachable from top-level context(s)
matchContext = addCombinator(function(elem) {
return elem === checkContext;
}, implicitRelative, true),
matchAnyContext = addCombinator(function(elem) {
return indexOf.call(checkContext, elem) > -1;
}, implicitRelative, true),
matchers = [function(elem, context, xml) {
return (!leadingRelative && (xml || context !== outermostContext)) || (
(checkContext = context).nodeType ?
matchContext(elem, context, xml) :
matchAnyContext(elem, context, xml));
}];
for (; i < len; i++) {
if ((matcher = Expr.relative[tokens[i].type])) {
matchers = [addCombinator(elementMatcher(matchers), matcher)];
} else {
matcher = Expr.filter[tokens[i].type].apply(null, tokens[i].matches);
// Return special upon seeing a positional matcher
if (matcher[expando]) {
// Find the next relative operator (if any) for proper handling
j = ++i;
for (; j < len; j++) {
if (Expr.relative[tokens[j].type]) {
break;
}
}
return setMatcher(
i > 1 && elementMatcher(matchers),
i > 1 && toSelector(
// If the preceding token was a descendant combinator, insert an implicit any-element `*`
tokens.slice(0, i - 1).concat({ value: tokens[i - 2].type === " " ? "*" : "" })
).replace(rtrim, "$1"),
matcher,
i < j && matcherFromTokens(tokens.slice(i, j)),
j < len && matcherFromTokens((tokens = tokens.slice(j))),
j < len && toSelector(tokens)
);
}
matchers.push(matcher);
}
}
return elementMatcher(matchers);
}
function matcherFromGroupMatchers(elementMatchers, setMatchers) {
// A counter to specify which element is currently being matched
var matcherCachedRuns = 0,
bySet = setMatchers.length > 0,
byElement = elementMatchers.length > 0,
superMatcher = function(seed, context, xml, results, expandContext) {
var elem, j, matcher,
setMatched = [],
matchedCount = 0,
i = "0",
unmatched = seed && [],
outermost = expandContext != null,
contextBackup = outermostContext,
// We must always have either seed elements or context
elems = seed || byElement && Expr.find["TAG"]("*", expandContext && context.parentNode || context),
// Use integer dirruns iff this is the outermost matcher
dirrunsUnique = (dirruns += contextBackup == null ? 1 : Math.random() || 0.1),
len = elems.length;
if (outermost) {
outermostContext = context !== document && context;
cachedruns = matcherCachedRuns;
}
// Add elements passing elementMatchers directly to results
// Keep `i` a string if there are no elements so `matchedCount` will be "00" below
// Support: IE<9, Safari
// Tolerate NodeList properties (IE: "length"; Safari: <number>) matching elements by id
for (; i !== len && (elem = elems[i]) != null; i++) {
if (byElement && elem) {
j = 0;
while ((matcher = elementMatchers[j++])) {
if (matcher(elem, context, xml)) {
results.push(elem);
break;
}
}
if (outermost) {
dirruns = dirrunsUnique;
cachedruns = ++matcherCachedRuns;
}
}
// Track unmatched elements for set filters
if (bySet) {
// They will have gone through all possible matchers
if ((elem = !matcher && elem)) {
matchedCount--;
}
// Lengthen the array for every element, matched or not
if (seed) {
unmatched.push(elem);
}
}
}
// Apply set filters to unmatched elements
matchedCount += i;
if (bySet && i !== matchedCount) {
j = 0;
while ((matcher = setMatchers[j++])) {
matcher(unmatched, setMatched, context, xml);
}
if (seed) {
// Reintegrate element matches to eliminate the need for sorting
if (matchedCount > 0) {
while (i--) {
if (!(unmatched[i] || setMatched[i])) {
setMatched[i] = pop.call(results);
}
}
}
// Discard index placeholder values to get only actual matches
setMatched = condense(setMatched);
}
// Add matches to results
push.apply(results, setMatched);
// Seedless set matches succeeding multiple successful matchers stipulate sorting
if (outermost && !seed && setMatched.length > 0 &&
(matchedCount + setMatchers.length) > 1) {
Sizzle.uniqueSort(results);
}
}
// Override manipulation of globals by nested matchers
if (outermost) {
dirruns = dirrunsUnique;
outermostContext = contextBackup;
}
return unmatched;
};
return bySet ?
markFunction(superMatcher) :
superMatcher;
}
compile = Sizzle.compile = function(selector, group /* Internal Use Only */) {
var i,
setMatchers = [],
elementMatchers = [],
cached = compilerCache[selector + " "];
if (!cached) {
// Generate a function of recursive functions that can be used to check each element
if (!group) {
group = tokenize(selector);
}
i = group.length;
while (i--) {
cached = matcherFromTokens(group[i]);
if (cached[expando]) {
setMatchers.push(cached);
} else {
elementMatchers.push(cached);
}
}
// Cache the compiled function
cached = compilerCache(selector, matcherFromGroupMatchers(elementMatchers, setMatchers));
}
return cached;
};
function multipleContexts(selector, contexts, results) {
var i = 0,
len = contexts.length;
for (; i < len; i++) {
Sizzle(selector, contexts[i], results);
}
return results;
}
// One-time assignments
// Sort stability
support.sortStable = expando.split("").sort(sortOrder).join("") === expando;
// Support: Chrome<14
// Always assume duplicates if they aren't passed to the comparison function
support.detectDuplicates = hasDuplicate;
// Initialize against the default document
setDocument();
// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27)
// Detached nodes confoundingly follow *each other*
support.sortDetached = assert(function(div1) {
// Should return 1, but returns 4 (following)
return div1.compareDocumentPosition(document.createElement("div")) & 1;
});
// Support: IE<8
// Prevent attribute/property "interpolation"
// http://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx
if (!assert(function(div) {
div.innerHTML = "<a href='#'></a>";
return div.firstChild.getAttribute("href") === "#" ;
})) {
addHandle("type|href|height|width", function(elem, name, isXML) {
if (!isXML) {
return elem.getAttribute(name, name.toLowerCase() === "type" ? 1 : 2);
}
});
}
// Support: IE<9
// Use defaultValue in place of getAttribute("value")
if (!support.attributes || !assert(function(div) {
div.innerHTML = "<input/>";
div.firstChild.setAttribute("value", "");
return div.firstChild.getAttribute("value") === "";
})) {
addHandle("value", function(elem, name, isXML) {
if (!isXML && elem.nodeName.toLowerCase() === "input") {
return elem.defaultValue;
}
});
}
// Support: IE<9
// Use getAttributeNode to fetch booleans when getAttribute lies
if (!assert(function(div) {
return div.getAttribute("disabled") == null;
})) {
addHandle(booleans, function(elem, name, isXML) {
var val;
if (!isXML) {
return (val = elem.getAttributeNode(name)) && val.specified ?
val.value :
elem[name] === true ? name.toLowerCase() : null;
}
});
}
return Sizzle;
}());
// --------------------------------------------------
// Utilities
// --------------------------------------------------
var boxedString = Object("a")
, splitString = boxedString[0] !== "a" || !(0 in boxedString);
var toObject = function toObject (o) {
if (typeof o === "undefined") { // this matches both null and undefined
throw new TypeError("can't convert "+o+" to object");
}
return Object(o);
};
var toInteger = function toInteger (value) {
var number = +value;
if (Number.isNaN(number)) {
return 0;
}
if (number === 0 || !isFinite(number)) {
return number;
}
return sign(number) * Math.floor(Math.abs(number));
};
var isPrimitive = function isPrimitive(input) {
var type = typeof input;
return (
input === null ||
type === "undefined" ||
type === "boolean" ||
type === "number" ||
type === "string"
);
};
var toPrimitive = function toPrimitive (input) {
var val, valueOf, toString;
if (isPrimitive(input)) {
return input;
}
valueOf = input.valueOf;
if (typeof valueOf === "function") {
val = valueOf.call(input);
if (isPrimitive(val)) {
return val;
}
}
toString = input.toString;
if (typeof toString === "function") {
val = toString.call(input);
if (isPrimitive(val)) {
return val;
}
}
throw new TypeError();
};
var sign = function sign(value) {
var number = +value;
if (number === 0) {
return number;
}
if (Number.isNaN(number)) {
return number;
}
return number < 0 ? -1 : 1;
};
// --------------------------------------------------
// Array Utilities
// --------------------------------------------------
// Executes a function on each of the element
// in the array
var each = function each (arr, fn, thisRef) {
var _i, _l;
// Use Array.prototype.forEach if available
if (Array.prototype.forEach) {
return Array.prototype.forEach.call(arr, fn);
}
// Throw an error if array and function are not provided
if (!(arr && fn)) {
throw new Error (
"Not enough arguments provided for each()"
);
}
// Make the this variable the array itself if not provided
thisRef = thisRef || arr;
for (_i = 0, _l = arr.length; _i < _l; _i += 1) {
fn.call(thisRef, arr[_i]);
}
};
// Iterate over an object and execute a function on each 'value'
// of it
var forIn = function forIn (obj, fn, thisRef) {
var _i;
// Throw an error if object and function are not provided
if (!(obj && fn)) {
throw new Error (
"Not enough arguments provided for forIn()"
);
}
// Make the given object as the `this` value if one is not provided
thisRef = thisRef || obj;
for (_i in obj) {
if (own(obj, _i)) {
fn.call(thisRef, _i);
}
}
};
// Append all the properties of the second object to the first
var extend = function extend (obj, ext) {
var _i;
// Throw an error if object and extension are not provided
if (!(obj && ext)) {
throw new Error (
"Not enough arguments provided for extend()"
);
}
for (_i in ext) {
if (own(ext, _i)) {
obj[_i] = ext[_i];
}
}
return obj;
};
// Check if every element in the object passes the test
var every = function every (o, fun) {
var t, len, thisp, _i;
if (o === null) {
throw new TypeError();
}
t = Object(o);
len = t.length >>> 0;
if (typeof fun !== "function") {
throw new TypeError();
}
thisp = arguments[1];
for (_i = 0; _i < len; _i++) {
if (_i in t && !fun.call(thisp, t[_i], _i, t)) {
return false;
}
}
return true;
};
// --------------------------------------------------
// String Utilities
// --------------------------------------------------
// Remove whitespace at the start and end of a string
var trim = function trim (str) {
var ws = "\x09\x0A\x0B\x0C\x0D\x20\xA0\u1680\u180E\u2000\u2001\u2002\u2003" +
"\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\u2028" +
"\u2029\uFEFF";
ws = "[" + ws + "]";
var trimBeginRegexp = new RegExp("^" + ws + ws + "*"),
trimEndRegexp = new RegExp(ws + ws + "*$");
if (str === void 0 || str === null) {
throw new TypeError("can't convert "+ str +" to object");
}
return String(str).replace(trimBeginRegexp, "").replace(trimEndRegexp, "");
};
// Check if a string contains another string in it
var contains = function contains (haystack, needle) {
var position = arguments[1];
return haystack.indexOf(needle, position) !== -1;
};
var indexOf = function indexOf(arr, sought /*, fromIndex */ ) {
var self = splitString && arr.toString() === "[object String]" ?
this.split("") :
toObject(this)
, length = self.length >>> 0;
if (!length) {
return -1;
}
var i = 0;
if (arguments.length > 1) {
i = toInteger(arguments[1]);
}
// handle negative indices
i = i >= 0 ? i : Math.max(0, length + i);
for (; i < length; i++) {
if (i in self && self[i] === sought) {
return i;
}
}
return -1;
};
select = feature.qsa3 ? function (selector, root) {
// Set root to given root or document
root = root || doc;
return root.querySelectorAll(selector);
} : function (selector, root) {
return sizzle(selector, root);
};
// --------------------------------------------------
// Core Library
// --------------------------------------------------
// If there is a select function (sizzle), use it
// or use the native querySelectorAll()
select = select || function (selector, root) {
// Set root to given root or document
root = root || doc;
// Use the native querySelectorAll
return root.querySelectorAll(selector);
};
/**
* The main Hilo Object / function
*
* @module Hilo
* @static
* @class hilo
* @author Erik Royall
*/
hilo = function (input, root, en) {
if (typeof input === "undefined") {
// It's better than not returning anything
return win.Hilo;
} else if (typeof input === "number") {
return new NumberObject(input);
} else if (typeof input === "string") {
if (trim(input) === "") {
// Can't pass empty string to querySelectorAll()
return new Dom({length:0});
}
// Most common, return based on selector
return new Dom(select(input, root, en), input);
} else if (typeof input === "function") {
if (document.readyState === "complete") {
input();
} else {
callbacks.push(input);
}
// Allows to immediately start executing more code
// It's better than not returning anything!
return win.Hilo;
} else if (input.length) { // DOM Node List | Hilo DOM Object
return new Dom(input);
} else { // DOM Node
input = [input];
return new Dom(input);
}
};
// Enable Selector Caching
hilo.temp = {};
// Version info
hilo.version = "0.1.0-pre-dev-beta-9";
// Detections
hilo.feature = feature;
hilo.browser = detected.browser;
hilo.engine = detected.engine;
hilo.platform = detected.system;
// ES Utils
extend(hilo, {
each: each,
extend: extend,
every: every,
trim: trim,
contains: contains,
indexOf: indexOf,
isPrimitive: isPrimitive,
toObject: toObject,
toInteger: toInteger,
toPrimitive: toPrimitive
});
// JSON
hilo.json = {
parse: json.parse,
stringify: json.stringify
};
// Legacy
hilo.legacy = typeof sizzle === "function";
// --------------------------------------------------
// Testing
// --------------------------------------------------
extend(hilo, {
/**
* Public test function
*
* @for hilo
* @method test
* @param con
* @return {Test}
* @example
* <div class="code"><pre class="prettyprint">
* $.test("hello");
* </pre></div>
*
* <div class="code"><pre class="prettyprint">
* $.test({
* name: "Erik Royall",
* age: 14,
* projects: ["hilo", "helio"]
* });
* </pre></div>
* @since 0.1.0
*/
test: function (con) {
return new Test(con);
}
});
/**
* Main Test Class
*
* @constructor
* @class Test
* @param {Any} con To compare
* @param {boolean} neg Whether to inverse the result
* @return void
* @example
* <div class="code"><pre class="prettyprint">
* new Test({});
* new Test("Hilo", true);
* </pre></div>
* @since 0.1.0
*/
function Test (con, neg) {
this.con = con;
if (neg) {
this.neg = true;
}
}
// --------------------------------------------------
// Test Comparisions
// --------------------------------------------------
extend(Test.prototype, {
/**
* Test if equal
*
* @for Test
* @method ifEquals
* @param {Any} tw Comparision object
* @return {boolean}
* @example
* <div class="code"><pre class="prettyprint">
* var isIt = $.test(hilo.version).ifEquals("v0.1.0-pre-dev-beta-9");
* </pre></div>
* @since 0.1.0
*/
ifEquals: function (tw) {
var val = this.con === tw;
return this.neg ? !val : val;
},
/**
* Test if contains
*
* @for Test
* @method ifContains
* @param {Any} tw Comparision object
* @return {boolean}
* @example
* <div class="code"><pre class="prettyprint">
* var isHiloBeta = $.test(hilo.version).ifContains("beta");
* </pre></div>
* @since 0.1.0
*/
ifContains: function (tw) {
var ifString = this.con.split(tw).length === 1 ? false : true;
if (typeof tw === "string" && typeof this.con === "object" && this.con.length) {
} else if (typeof tw === "string" && typeof this.con === "string") {
return this.neg ? !ifString : ifString;
}
},
/**
* Inverse a test
*
* @for Test
* @method not
* @return {Test}
* @example
* <div class="code"><pre class="prettyprint">
* $("Hilo.js").not().ifEquals("Hilo");
* </pre></div>
* @since 0.1.0
*/
not: function () {
return new Test(this, true);
}
});
//
// ** `hiloAjax` **
//
// Makes an AJAX request
//
// Param:
//
// `config // {Object} Configuration Options`
//
// For the list of all config opts, see below.
//
// Example:
//
// ```
// Hilo.ajax({
// url: "requestHandler.php",
// success: function (data, xhr) {
// console.log(data, xhr);
// },
// method: "GET"
// });
// ```
//
hiloAjax = function (config) {
// ```
// config.
// method // HTTP Method (default: "POST")
// url // The file to send request
// async // Whether to perform an asynchronous request (default: true)
// data // Data to be sent to the server
// response // HTTP Response type
// callback // function to be executed on readystatechange
// complete // {Function} (xhr.readyState = 4) To be triggered when request is complete
// error // {Function} To be triggered when request fails with an error
// timeout // {Function} To be triggered when request time's out
// success // {Function} (200) To be triggered when request is successfully made (Commonly registered event)
// notfound // {Function} (404) To be triggered when there has been a 4oh4 NotFound exception
// forbidden // {Function} (403) To be triggered when making the request is forbidden
// username // {String} Username to be provided, if authentication is required
// password // {String} Password to be provided, if...
// contentType // HTTP Content-Type
// ```
var xhr;
/* Use the `XMLHttpRequest` object if available
or use `ActiveXObject` */
if (win.XMLHttpRequest) {
xhr = new win.XMLHttpRequest();
} else if (win.ActiveXObject) {
xhr = new win.ActiveXObject("Microsoft.XMLHTTP");
}
/* Throw an error if a URL hasn't been provided
Seriously, wth can this do without a target url? */
if (!config.url) {
throw new TypeError("url parameter not provided to hilo.ajax");
}
/* Perform an asynchronous request by default */
config.async = config.async || true;
/* Authentication params */
config.username = config.username || null;
config.password = config.password || null;
/* contentType.. "application/x-www-form-urlencoded; charset=UTF-8" is preferred */
config.contentType = config.contentType || "application/x-www-form-urlencoded; charset=UTF-8";
xhr.onreadystatechange = function () {
if (config.callback) {
config.callback(xhr);
}
if (xhr.readyState === 4) { // Request is completed
typeof config.complete ? config.complete.call(this, xhr) : null;
switch (xhr.status) {
case 200: // Success
typeof config.success ? config.success.call(this, xhr) : null;
typeof config.error ? config.error.call(this, xhr) : null;
break;
case 404: // Not Found
typeof config.notfound ? config.notfound.call(this, xhr) : null;
typeof config.error ? config.error.call(this, xhr) : null;
break;
case 403: // Forbidden
typeof config.forbidden ? config.forbidden.call(this, xhr) : null;
typeof config.error ? config.error.call(this, xhr) : null;
break;
default: // Some Error
typeof config.error ? config.error.call(this, xhr) : null;
break;
}
} else if (xhr.readyState === 3) {
typeof config.sent ? config.sent.call(this, xhr) : null;
}
};
/* Run this function when the request has timed out :'( */
xhr.timeout = config.timeout;
/* Open the request (Could've been more verbose) */
xhr.open(
config.method.trim().toUpperCase() || "POST",
config.url,
config.async,
config.username,
config.password
);
/* If config.data is an object, JSON.encode it */
if (typeof config.data === "object") {
config.data = JSON.encode(config.data);
}
/* Lauch the request */
xhr.send(typeof config.data === "string" ? config.data : null);
};
hilo.ajax = hiloAjax;
//
// `ajaxRequest` _Internal_
//
// Param:
//
// * `method`: {String} HTTP Method
// * `strOpt`: {String} URL, or options object (see above)
// * `callback`: {Function} To be executed on `success`
// * `oOpt`: {Object} For providing more options
//
function ajaxRequest (method, strOpt, callback, oOpt) {
oOpt = (typeof oOpt === "object" ? oOpt : undefined);
if (typeof strOpt === "string" && typeof callback === "function") {
hiloAjax(extend({
method: method,
url: strOpt,
success: callback
}, oOpt));
} else {
hiloAjax(extend({
method: method
}, strOpt));
}
}
//
// ### Make an asynchronous GET Request
//
// Params are similar to those of the internal `ajaxRequest` method (see above)
//
// ```
// $.get({
// url: "path/to/file.js",
// success: function (data) {
// console.log(data);
// }
// }); // Long form
// ```
//
// ```
// $.get("path/to/file.js", function (data) {
// console.log(data);
// }); // This does the exact same function as above
// ```
//
// ```
// $.get("path/to/file.js", function (data) {
// console.log(data);
// }, {
// error: function (err) {
// console.error(err);
// }
// }); // Short form, with more options
// ```
//
hilo.get = function (strOpt, callback, oOpt) {
ajaxRequest("GET", strOpt, callback, oOpt);
};
//
// ### Make an asynchronous POST Request
//
// Params are similar to those of the internal `ajaxRequest` method (see above)
//
// ```
// $.post({
// url: "path/to/file.js",
// success: function (data) {
// console.log(data);
// },
// data: JSON.encode(obj)
// }); // Long form
// ```
//
// ```
// $.post("path/to/file.js", function (data) {
// console.log(data);
// }, {
// data: JSON.encode(obj),
// error: function (err) {
// console.error(err);
// }
// }); // Short form, with more options
// ```
//
hilo.post = function (strOpt, callback, oOpt) {
ajaxRequest("POST", strOpt, callback, oOpt);
};
//
// ### Main DOM Class
//
// ** Params: **
// - `els` {Array} The elements to manipulate
// - `sel` {String} The selector used
//
// ** Examples **
//
// ```
// new Dom (document.querySelectorAll(p:first-child);
// ```
// ```
// new Dom ([document.createElement("div")]);
// ```
// ```
// new Dom ([document.getElementByid("box")]);
// ```
// ```
// new Dom (document.getElementsByClassName("hidden"));
// ```
// ```
// new Dom (document.getElementsByTagName("mark"));
// ```
//
function Dom (els, sel) {
var _i, _l;
/* Note that `this` is an object and NOT an Array */
/* Loop thorugh the NodeList and set `this[index]` for `els[index]` */
for (_i = 0, _l = els.length; _i < _l; _i += 1) {
this[_i] = els[_i];
}
/* Useful for looping through as ours is an object and not an array */
this.length = els.length;
/* Know what selector is used to select the elements */
this.sel = sel;
}
/* Make it _look_ like an array */
Dom.prototype = Array.prototype;
extend(Dom.prototype, {
/* Set the constructor to Dom. It defaults to Array. We don't that */
constructor: Dom
});
// ### Hilo CSS Helper Methods
//
// **_unhyph_** *Internal*
//
// Return a string repacing all `-`s with `""` and making the letter
// next to every `-` uppercase
//
// **Param**:
// - `prop`: {String} CSS Property Name
//
// **Examples**:
// ```
// unhyph("background-color"); // backgroundColor
// unhyph("color"); // color
// ```
//
function unhyph (prop) {
return prop.toLowerCase().replace(/-(.)/g, function (m, m1) {
return m1.toUpperCase();
});
}
//
// **_unitize_** *Internal*
//
// Add necessary suffix to the number for certain CSS properties
// _This will later be used by .css() and a number of other methods_
//
// **Param**:
// - `unit`: {String|Number} Valid CSS Unit (`unitize()` Returns the same thing if {String})
// - `prop`: {String} CSS Property Name
//
// **Examples**:
// ```
// unitize("background-color"); // backgroundColor
// unhyph("color"); // color
// ```
//
function unitize (unit, prop) {
/* All the CSS props. that are to be defaulted to px values */
var pixel = {
"width": true,
"maxWidth": true,
"minWidth": true,
"height": true,
"maxHeight": true,
"minHeight": true,
"borderWidth": true,
"borderTopWidth": true,
"borderLeftWidth": true,
"borderBottomWidth": true,
"borderRightWidth": true,
"borderRadius": true,
"outlineWidth": true,
"outlineOffset": true,
"strokeWidth": true,
"fontSize": true,
"lineHeight": true,
"letterSpacing": true,
"textIndent": true,
"textUnderlineWidth": true,
"margin": true,
"marginTop": true,
"marginLeft": true,
"marginBottom": true,
"marginRight": true,
"padding": true,
"paddingTop": true,
"paddingLeft": true,
"paddingBottom": true,
"paddingRight": true,
"top": true,
"left": true,
"bottom": true,
"right": true
};
/* String values are not be unitized no matter what */
if (typeof unit === "string") {
return unit;
}
/* If the property is present in the previously mentioned
object, the unit is suffixed with "px" */
if (pixel[prop] === true) {
return unit + "px";
}
return unit;
}
//
// **_hilo.create_**
//
// Create an element
//
// **Params**:
// - `tagName`: {String} Tag Name or Node name of element
// - `attrs`: {Object} An object containing the attributes and values
//
// **Example**:
// ```
// $.create("div", {
// class: "post",
// "data-id": 2
// });
// ```
//
hilo.create = function (tagName, attrs) {
var el = new Dom([document.createElement(tagName)]), key;
if (attrs) {
/* Add Class if the `className` is set */
if (attrs.className) {
el.addClass(attrs.className);
delete attrs.className;
}
/* Set html to if `text` content is given */
if (attrs.text) {
el.html(attrs.text);
delete attrs.text;
}
/* Set other attributes */
for (key in attrs) {
if(attrs.hasOwnProperty(key)) {
el.attr(key, attrs[key]);
}
}
}
return el;
};
extend(Dom.prototype, {
// ## Helper Functions
//
// **_Hilo.Dom.prototype.each_**
//
// Execute a function on selected elements
//
// **Param**:
// - `fn`: {Function} The function to be called
//
// **Example**:
// ```
// $("p").each(function (el) {
// doSomethingWith(e);
// });
// ```
//
each: function (fn) {
this.map(fn);
return this; /* return the current Dom instance */
},
//
// **_Hilo.Dom.prototype.map_**
//
// Return the results of executing a function
// on all the selected elements
//
// **Param**:
// - `fn`: {Function} The function to be called
//
// **Example**:
// ```
// $("div.need-cf").map(function (e) {
// doSomethingWith(e);
// });
// ```
//
map: function (fn) {
var results = []
, _i
, _l;
for (_i = 0, _l = this.length; _i < _l; _i += 1) {
results.push(fn.call(this, this[_i], _i));
}
return results;
},
//
// **_Hilo.Dom.prototype.one_**
//
// Map on selected elements and return them based
// on the number of selected elements
//
// **Param**:
// - `fn`: {Function} The function to be called
//
one: function (fn) {
var m = this.map(fn);
return m.length > 1 ? m : m[0];
},
//
// **_Hilo.Dom.prototype.first_**
//
// Execute a function on the first selected element
//
// **Param**:
// - `fn`: {Function} The function to be called
//
// **Example**:
// ```
// $("div").first(function (e) {
// console.log(e + " is the first div");
// });
// ```
//
first: function (fn) {
return fn(this[0]);
},
//
// **_Hilo.Dom.prototype.filter_**
//
// Filter the selected element and return the
// elements that pass the test (or return true)
//
// **Param**:
// - `fn`: {Function} The function to be called
//
// **Example**:
// ```
// $("div").filter(function (el) {
// return el.className.split("hidden").length > 1;
// });
// ```
//
filter: function (fn) {
var len = this.length >>> 0
, _i
, t = Object(this)
, res = []
, val;
for (_i = 0; _i < len; _i++) {
if (_i in t) {
val = t[_i];
if (fn.call(this, val, _i, t)) {
res.push(val);
}
}
}
return new Dom(res);
},
// ## Element Selections, etc.
//
// **_Hilo.Dom.prototype.get_**
//
// Get a JavaScript Array containing selected elements
//
// **Example**:
// ```
// $("script").get();
// ```
//
get: function () {
var els = [];
this.each(function (el) {
els.push(el);
});
return els;
},
//
// **_Hilo.Dom.prototype.firstEl_**
//
// Return first element of the selected elements
//
// **Example**:
// ```
// $("p.hidden").firstEl().show();
// ```
//
firstEl: function () {
return new Dom([this[0]]);
},
//
// **_Hilo.Dom.prototype.lastEl_**
//
// Return last element of the selected elements
//
// **Example**:
// ```
// $("p.hidden").lastEl().show();
// ```
//
lastEl: function () {
return new Dom([this[this.length - 1]]);
},
//
// **_Hilo.Dom.prototype.el_**
//
// Return nth element of the selected elements
//
// **Param**:
// - `place`: {Number} The index of the element (Starts from 1)
//
// **Example**:
// ```
// $("p.hidden").el(3).show();
// ```
//
el: function (place) {
return new Dom([this[place - 1]]);
},
//
// **_Hilo.Dom.prototype.children_**
//
// Return the children of selected elements
//
// **Param**:
// - `sel`: {String} Optional filtering selector
//
// **Example**:
// ```
// var childrenOfContainer = $("div.container").children();
// $("div.container").children(":not(.hidden)").addClass("me");
// ```
//
children: function (sel) {
var children = [], _i, _l;
this.each(function (el) {
var childNodes = select(sel ? sel : "*", el);
for (_i = 0, _l = childNodes.length; _i < _l; _i += 1) {
children = children.concat(childNodes[_i]);
}
});
return children;
},
//
// **_Hilo.Dom.prototype.parents_**
//
// Return the parents of selected elements
//
// **Example**:
// ```
// $("div#editor").parent().hide()
// ```
//
parents: function () {
var pars = [];
this.each(function (el) {
pars = pars.concat(el.parentElement);
});
return new Dom(pars);
},
//
// **_Hilo.Dom.prototype.parent_**
//
// Return the parent of the first selected element
//
// **Example**:
// ```
// $("div#editor").parent().hide()
// ```
//
parent: function () {
return this.first(function (el) {
return new Dom([el.parentElement]);
});
},
//
// **_Hilo.Dom.prototype.rel_**
//
// Return relatives of selected elements based
// on the given relation
//
// **Param**:
// - `sul`: {String} Relation
//
// **Example**:
// ```
// $("div#editor").rel("nextSibling").addClass("next-to-editor");
// ```
//
rel: function (sul) {
var els = [];
this.each(function (el) {
els.push(el[sul]);
});
return els;
},
//
// **_Hilo.Dom.prototype.next_**
//
// Return next sibling elements of selected elements
//
// **Example**:
// ```
// $("div#editor").next().addClass("next-to-editor");
// ```
//
next: function () {
return this.rel("nextElementSibling");
},
//
// **_Hilo.Dom.prototype.prev_**
//
// Return next sibling elements of selected elements
//
// **Example**:
// ```
// $("div#editor").prev().addClass("prev-to-editor");
// ```
//
prev: function () {
return this.rel("previousElementSibling");
},
//
// **_Hilo.Dom.prototype.html_**
//
// Set or return innerHTML of selected elements
//
// **Param**:
// - `html`: {String} HTML Code to be inserted
//
// **Example**:
// ```
// $("p:first-child").html("first-p");
// var html = $("span").html();
// ```
//
html: function (htmlCode) {
if (typeof htmlCode !== "undefined") {
return this.each(function(el) {
el.innerHTML = htmlCode;
});
} else {
return this.first(function(el) {
return el.innerHTML;
});
}
},
//
// **_Hilo.Dom.prototype.empty_**
//
// Empty the selected elements
//
// **Example**:
// ```
// $("#todo-list").empty();
// ```
//
empty: function () {
return this.html("");
},
//
// **_Hilo.Dom.prototype.append_**
//
// Append html to selected elements
//
// **Param**:
// - `html`: {String} HTML Code to be appeneded
//
// **Example**:
// ```
// $("p:first-child").append(" - From the first p child")
// ```
//
append: function (html) {
return this.each(function (el) {
el.innerHTML += html;
});
},
//
// **_Hilo.Dom.prototype.prepend_**
//
// Prepend html to selected elements
//
// **Param**:
// - `html`: {String} HTML Code to be appeneded
//
// **Example**:
// ```
// $("p:first-child").append(" - From the first p child")
// ```
//
prepend: function (html) {
return this.each(function (el) {
el.innerHTML = html + el.innerHTML;
});
},
//
// **_Hilo.Dom.prototype.value_**
//
// Get or set the value attribute of the selected element
//
// **Param**:
// - `val`: {String} Value to set to
//
// **Example**:
// ```
// $("#my-form").children("input#name").value();
// ```
//
value: function (val) {
if (val) {
return this.each(function (el) {
el.value = val;
});
} else {
this.first(function (el) {
return el.value;
});
}
},
//
// **_Hilo.Dom.prototype.id_**
//
// Get or set the ID of first element
//
// **Param**:
// - `id`: {String} ID to set
//
// **Example**:
// ```
// $("p.rect").first().id("square");
// ```
//
id: function (id) {
if (id) {
// Setting id of only one element because
// id is intended to be an unique identifier
return this.first(function(el) {
el.id = id;
});
} else {
return this.first(function (el) {
return el.id;
});
}
},
// ### Classes and IDs
//
// **_Hilo.Dom.prototype.class_**
//
// Add, remove, or check class(es)
//
// **Param**:
// - `action`: {String} Action to take ("add", "remove", "has")
// - `className`: {String|Array} Class(es) to add or remove
//
// **Examples**:
// ```
// $("div#editor").class("add", "no-js");
// ```
// ```
// $("div#editor").class("remove", "no-js");
// ```
// ```
// var isHidden = $("p").class("has", "hidden");
// ```
//
"class": feature.classList === true ? function (action, className) {
return this.each(function (el) {
var _i, parts, contains, res = [];
if (typeof className === "string") { // A String
parts = className.split(" ");
if (parts.length === 1) { // String, one class
contains = el.classList.contains(className);
switch (action) {
case "add": {
if (!contains) {
el.classList.add(className);
}
} break;
case "remove": {
if (contains) {
el.classList.remove(className);
}
} break;
case "has": {
res = true;
} break;
case "toggle": {
for (_i = 0; _i < parts.length; _i += 1) {
if (contains) {
el.classList.remove(parts[_i]);
} else {
el.classList.add(parts[_i]);
}
}
} break;
}
} else { // String, many classes
contains = function (className) {
return el.classList.contains(className);
};
switch (action) {
case "add": {
for (_i = 0; _i < parts.length; _i += 1) {
if (!contains(parts[_i])) {
el.classList.add(parts[_i]);
}
}
} break;
case "remove": {
for (_i = 0; _i < parts.length; _i += 1) {
if (contains(parts[_i])) {
el.classList.remove(parts[_i]);
}
}
} break;
case "has": {
for (_i = 0; _i < parts.length; _i += 1) {
res.push(contains(parts[_i]));
}
} break;
case "toggle": {
for (_i = 0; _i < parts.length; _i += 1) {
if (contains(parts[_i])) {
el.classList.remove(parts[_i]);
} else {
el.classList.add(parts[_i]);
}
}
} break;
}
}
} else if (className.length) { // Array
parts = className;
contains = function (className) {
return el.classList.contains(className);
};
switch (action) {
case "add": {
for (_i = 0; _i < parts.length; _i += 1) {
if (!contains(parts[_i])) {
el.classList.add(parts[_i]);
}
}
} break;
case "remove": {
for (_i = 0; _i < parts.length; _i += 1) {
if (contains(parts[_i])) {
el.classList.remove(parts[_i]);
}
}
} break;
case "has": {
for (_i = 0; _i < parts.length; _i += 1) {
res.push(contains(parts[_i]));
}
} break;
case "toggle": {
for (_i = 0; _i < parts.length; _i += 1) {
if (contains(parts[_i])) {
el.classList.remove(parts[_i]);
} else {
el.classList.add(parts[_i]);
}
}
} break;
}
}
return typeof res === "boolean" ? res : res.every(function (el) {
return el === true;
});
});
} : function (action, className) {
return this.each(function (el) {
var _i, parts, contains, res = [];
if (typeof className === "string") {
parts = className.split(" ");
if (parts.length === 1) {
contains = el.className.split(className).length > 1;
switch (action) {
case "add": {
if (!contains) {
el.className += " " + (className);
}
} break;
case "remove": {
if (contains) {
el.className.replace(className, "");
}
} break;
case "has": {
res = contains;
} break;
case "toggle": {
for (_i = 0; _i < parts.length; _i += 1) {
if (contains) {
el.className.replace(className, "");
} else {
el.className += " " + className;
}
}
} break;
}
} else {
contains = function (className) {
return el.className.split(className).length > 1;
};
switch (action) {
case "add": {
for (_i = 0; _i < parts.length; _i += 1) {
if (!contains(parts[_i])) {
el.className += " " + parts[_i];
}
}
} break;
case "remove": {
for (_i = 0; _i < parts.length; _i += 1) {
if (contains(parts[_i])) {
el.className.replace(parts[_i], "");
}
}
} break;
case "has": {
for (_i = 0; _i < parts.length; _i += 1) {
res.push(contains(parts[_i]));
}
} break;
case "toggle": {
for (_i = 0; _i < parts.length; _i += 1) {
if (contains(parts[_i])) {
el.className.replace(parts[_i], "");
} else {
el.className += " " + parts[_i];
}
}
} break;
}
}
} else if (className.length) {
parts = className;
contains = function (className) {
return el.className.split(className).length > 1;
};
switch (action) {
case "add": {
for (_i = 0; _i < parts.length; _i += 1) {
if (!contains(parts[_i])) {
el.className += " " + parts[_i];
}
}
} break;
case "remove": {
for (_i = 0; _i < parts.length; _i += 1) {
if (contains(parts[_i])) {
el.className.replace(parts[_i], "");
}
}
} break;
case "has": {
for (_i = 0; _i < parts.length; _i += 1) {
res.push(contains(parts[_i]));
}
} break;
case "toggle": {
for (_i = 0; _i < parts.length; _i += 1) {
if (contains(parts[_i])) {
el.className.replace(parts[_i], "");
} else {
el.className += " " + parts[_i];
}
}
} break;
}
}
return typeof res === "boolean" ? res : res.every(function (el) {
return el === true;
});
});
},
//
// **_Hilo.Dom.prototype.addClass_**
//
// Adds class(es) to selected elements
//
// **Param**:
// - `className`: {String|Array} The class(es) to add
//
// **Example**:
// ```
// $("p").addClass("paragraph");
// ```
//
addClass: function (className) {
return this["class"]("add", className);
},
//
// **_Hilo.Dom.prototype.removeClass_**
//
// Remove class(es) from selected elements
//
// **Param**:
// - `className`: {String|Array} The class(es) to remove
//
// **Example**:
// ```
// $("p.hidden").removeClass("hidden");
// ```
//
removeClass: function (className) {
return this["class"]("remove", className);
},
//
// **_Hilo.Dom.prototype.hasClass_**
//
// Check if selected elements have the specified class(es)
//
// **Param**:
// - `className`: {String|Array} The class(es) to check if exists
//
// **Example**:
// ```
// $("pre").hasClass("prettyprint");
// ```
//
hasClass: function (className) {
return this["class"]("has", className);
},
//
// **_Hilo.Dom.prototype.toggleClass_**
//
// Add class(es) if it/they do(es) not exist(s),
// remove if exist(s)
//
// **Param**:
// - `className`: {String|Array} The class(es) to toggle
//
// **Example**:
// ```
// $(".someClass").on("click", function () {
// $(this).toggleClass("opaque");
// });
// ```
//
toggleClass: function (className) {
return this["class"]("toggle", className);
},
//
// **_Hilo.Dom.prototype.attr_**
//
// Set or return attribute values
//
// **Param**:
// - `name`: {String} Name of attribute
// - `val`: {String} Value of attribute
//
// **Example**:
// ```
// $("p.hidden").attr("hidden");
// ```
// ```
// $("div.edit").attr("contentEditable", "true");
// ```
// ```
// $("body").attr("hilo", "0.1.0");
// ```
//
attr: function (name, val) {
if(val) {
return this.each(function(el) {
el.setAttribute(name, val);
});
} else {
return this.first(function (el) {
return el.getAttribute(name);
});
}
},
// ### Hilo CSS
//
// **_Hilo.Dom.prototype.css_**
//
// Set or return css property values
//
// **Param**:
// - `prop`: {String|Object} Name of the propety | Properties
// - `value`: {String} Value of property
//
// **Example**:
// ```
// $("p").css("margin-left", "10em");
// ```
// ```
// $("p.round").css({
// "border-radius": 10,
// width: 100
// });
// ```
//
css: function (prop, value) {
var unhyphed;
if (typeof prop === "string") {
unhyphed = unhyph(prop);
if (value) {
return this.each(function (el) {
el.style[unhyphed] = unitize(value, unhyphed);
});
} else {
return this.first(function (el) {
return el.style[unhyphed];
});
}
} else if (typeof prop === "object") {
forIn(prop, function (pr) {
unhyphed = unhyph(pr);
this.each(function (el) {
el.style[unhyphed] = unitize(prop[pr], unhyphed);
});
}, this);
}
},
//
// **_Hilo.Dom.prototype.computed_**
//
// Get computed property
//
// **Param**:
// - `prop`: {String|Object} Name of property
//
// **Example**:
// ```
// $("#box").computed("width");
// ```
//
computed: function (prop) {
return this.first(function (el) {
return win.getComputedStyle(el)[prop];
});
},
// Get outer width
outerWidth: function () {
return parseFloat(this.computed("width")) +
parseFloat(this.computed("paddingLeft")) +
parseFloat(this.computed("paddingRight")) +
parseFloat(this.computed("borderLeft")) +
parseFloat(this.computed("borderRight")) + "px";
},
// Get inner width
innerWidth: function () {
return parseFloat(this.computed("width")) +
parseFloat(this.computed("paddingLeft")) +
parseFloat(this.computed("paddingRight")) + "px";
},
// Get outer height
outerHeight: function () {
return parseFloat(this.computed("height")) +
parseFloat(this.computed("paddingTop")) +
parseFloat(this.computed("paddingBottom")) +
parseFloat(this.computed("borderTop")) +
parseFloat(this.computed("borderBottom")) + "px";
},
// Get inner height
innerHeight: function () {
return parseFloat(this.computed("height")) +
parseFloat(this.computed("paddingTop")) +
parseFloat(this.computed("paddingBottom")) + "px";
}
});
// --------------------------------------------------
// Events
// --------------------------------------------------
extend(Dom.prototype, {
// Listen to an event and execute a function when that event happend
/**
* Listen to an event and execute a function when that event happend
*
* @for Dom
* @method on
* @param {String} evt Name of event
* @param {Function} fn Function to be executed when the event is fired
* @return {Dom}
* @example
* <div class="code"><pre class="prettyprint">
* $("#box").on("click", function (e) {
* console.log("#box was clicked");
* });
* </pre></div>
* @since 0.1.0
*/
on: (function () {
// Check if `document.addEventListener` method
// is available and use it if it is
if (document.addEventListener) {
return function (evt, fn) {
return this.each(function (el) {
el.addEventListener(evt, fn, false);
});
};
// Otherwise check if `document.attachEvent`
// legacy method is available and use it if it is
} else if (document.attachEvent) {
return function (evt, fn) {
return this.each(function (el) {
el.attachEvent("on" + evt, fn);
});
};
// Add event the DOM Level 0 Style
} else {
return function (evt, fn) {
return this.each(function (el) {
el["on" + evt] = fn;
});
};
}
}()),
// Stop listening to an event
/**
* Stop listening to an event
*
* @for Dom
* @method on
* @param {String} evt Name of event
* @param {Function} fn Function to stop listening to
* @return {Dom}
* @example
* <div class="code"><pre class="prettyprint">
* $("#box").off("click", fn);
* </pre></div>
* @since 0.1.0
*/
off: (function () {
if (document.removeEventListener) {
return function (evt, fn) {
return this.each(function (el) {
el.removeEventListener(evt, fn, false);
});
};
} else if (document.detachEvent) {
return function (evt, fn) {
return this.each(function (el) {
el.detachEvent("on" + evt, fn);
});
};
} else {
return function (evt) {
return this.each(function (el) {
el["on" + evt] = null;
});
};
}
}()),
/**
* Trigger or fire an event
*
* @for Dom
* @method fire
* @param {String} evt Name of event to fire
* @return {Dom}
* @example
* <div class="code"><pre class="prettyprint">
* $("#uploadForm").fire("overload");
* </pre></div>
* @since 0.1.0
*/
fire: (function () {
if (document.dispatchEvent) {
return function (event) {
var evt;
try {
evt = document.createEvent("Events");
} catch (e) {
evt = document.createEvent("UIEvents");
}
evt.initUIEvent(event, true, true, window, 1);
return this.each(function (el) {
el.dispatchEvent(evt);
});
};
} else if (document.fireEvent) {
return function (event) {
var evt = document.createEventObject();
evt.button = 1;
return this.each(function(el) {
el.fireEvent("on" + event, evt);
});
};
} else {
return function (event) {
return this.each(function (el) {
el["on" + event].call();
});
};
}
}())
});
// --------------------------------------------------
// Events (imp.)
// --------------------------------------------------
extend(Dom.prototype, {
ready: function (fn) {
this.each(function (el) {
el.onreadystatechange = function () {
if (el.readyState = "complete") {
fn();
}
};
});
}
});
(function () {
var evtObj = {}
, impEvts;
impEvts = [
"blur",
"click",
"change",
"dblclick",
"drag",
"dragstart",
"dragend",
"dragenter",
"dragleave",
"dragover",
"drop",
"error",
"focus",
"keyup",
"keydown",
"keypress",
"load",
"mousedown",
"mouseleave",
"mouseenter",
"mouseover",
"mousemove",
"mouseout",
"submit"
];
for (_i = 0; _i < impEvts.length; _i += 1) {
evtObj[impEvts[_i]] = function (fn) {
if (typeof fn === "function") {
return this.on(impEvts[_i], fn);
}
return this.fire(impEvts[_i]);
};
}
extend(Dom.prototype, evtObj);
}());
// These keyborad key mappings will be later used
// to enable use of shortcut keys or the like
key = {
// Numbers
"0": 48,
"1": 49,
"2": 50,
"3": 51,
"4": 52,
"5": 53,
"6": 54,
"7": 55,
"8": 56,
"9": 57,
// Uppercase letters
A: 65,
B: 66,
C: 67,
D: 68,
E: 69,
F: 70,
G: 71,
H: 72,
I: 73,
J: 74,
K: 75,
L: 76,
M: 77,
N: 78,
O: 79,
P: 80,
Q: 81,
R: 82,
S: 83,
T: 84,
U: 85,
V: 86,
W: 87,
X: 88,
Y: 89,
Z: 90,
// Lowecase letters
a: 97,
b: 98,
c: 99,
d: 100,
e: 101,
f: 102,
g: 103,
h: 104,
i: 105,
j: 106,
k: 107,
l: 108,
m: 109,
n: 110,
o: 111,
p: 112,
q: 113,
r: 114,
s: 115,
t: 116,
u: 117,
v: 118,
w: 119,
x: 120,
y: 121,
z: 122,
// Other Important Keys
alt: 18,
caps: 20,
ctrl: 17,
cmd: 17,
enter: 13,
esc: 27,
del: 46,
end: 35,
back: 8,
// Arrows
left: 37,
up: 38,
right: 39,
down: 40,
// F-keys
f1: 112,
f2: 113,
f3: 114,
f4: 115,
f5: 116,
f6: 117,
f7: 118,
f8: 119,
f9: 120,
f10: 121,
f11: 122,
f12: 123,
// Less-used keys
home: 36,
insert: 45,
numlock: 144,
// Symbols
"`": 222,
"-": 189,
",": 188,
".": 190,
"/": 191,
";": 186,
"[": 219,
"\\": 220,
"]": 221,
"=": 187
};
hilo.keys = key;
// --------------------------------------------------
// Effects (fx)
// --------------------------------------------------
extend(Dom.prototype, {
/**
* Sets the display property of sel.els. to "" or given value
*
* @for Dom
* @method show
* @param {string} display Value of display prop.
* @return {Dom}
* @example
* <div class="code"><pre class="prettyprint">
* $("p").show();
* </pre></div>
* @since 0.1.0
*/
show: function (display) {
display = display || "";
return this.each(function (el) {
el.style.display = display;
el.setAttribute("aria-hidden", false);
});
},
/**
* Sets the display property of sel.els. to "none"
*
* @for Dom
* @method hide
* @return {Dom}
* @example
* <div class="code"><pre class="prettyprint">
* $("p").hide();
* </pre></div>
* @since 0.1.0
*/
hide: function () {
return this.each(function (el) {
el.style.display = "none";
// ARIA
el.setAttribute("aria-hidden", true);
});
},
/**
* Shows hidden elements, hides shown elements
*
* @for Dom
* @method toggle
* @return {Dom}
* @example
* <div class="code"><pre class="prettyprint">
* $("p").toggle();
* </pre></div>
* @since 0.1.0
*/
toggle: function (display) {
return this.each(function (el) {
if (el.style.display === "none") {
el.style.display = display ? display : "";
// ARIA
el.setAttribute("aria-hidden", false);
} else {
el.style.display = "none";
// ARIA
el.setAttribute("aria-hidden", true);
}
});
},
/**
* Sets visibility to "visible"
*
* @for Dom
* @method appear
* @return {Dom}
* @example
* <div class="code"><pre class="prettyprint">
* $("p").appear();
* </pre></div>
* @since 0.1.0
*/
appear: function () {
return this.each(function (el) {
el.style.visibility = "visible";
// ARIA
el.setAttribute("aria-hidden", false);
});
},
/**
* Sets visiblity to "hidden"
*
* @for Dom
* @method disappear
* @return {Dom}
* @example
* <div class="code"><pre class="prettyprint">
* $("p").disappear();
* </pre></div>
* @since 0.1.0
*/
disappear: function () {
return this.each(function (el) {
el.style.visibility = "hidden";
// ARIA
el.setAttribute("aria-hidden", true);
});
},
/**
* Appears a disappeared element, disappears an appeared element
*
* @for Dom
* @method toggleVisibility
* @return {Dom}
* @example
* <div class="code"><pre class="prettyprint">
* $("p").toggleVisibility();
* </pre></div>
* @since 0.1.0
*/
toggleVisibility: function () {
return this.each(function (el) {
if (el.style.opacity === "0") {
el.style.visibility = "visible";
// ARIA
el.setAttribute("aria-hidden", true);
} else {
el.style.visibility = "hidden";
// ARIA
el.setAttribute("aria-hidden", true);
}
});
},
/**
* Animates opacity prop. from 0 to 1 or 1 to 0
*
* @for Dom
* @method fade
* @param {string} inOut Whether "in" or "out"
* @param {number|string} "fast", "slow", "normal" or a number
* @return {Dom}
* @example
* <div class="code"><pre class="prettyprint">
* $("p").fade("in");
* $("p").fade("out");
* $("p").fade("in", 140);
* $("p").fade("out", 100);
* </pre></div>
* @since 0.1.0
*/
fade: function (inOut, timing) {
if (inOut === "in") {
this.show();
}
return this.each(function (el) {
var time;
switch(timing) {
case "slow":
time = 200;
break;
case "normal":
time = 120;
break;
case "fast":
time = 80;
break;
default:
time = time || 120;
break;
}
function animate () {
var val = 0.3
, end = 1;
if (parseFloat(el.style.opacity) === (inOut === "in" ? 1 : 0)) {
// Stop the animation if the opacity is set to the final value
clearInterval(win.Hilo.temp.anim);
} else {
if (inOut === "out") {
val = -val;
end = 0;
}
el.style.opacity = parseFloat(el.style.opacity || end) + val;
}
}
win.Hilo.temp.anim = setInterval(animate, timing);
});
},
/**
* Animates opacity prop. from 0 to 1
*
* @for Dom
* @method fadeIn
* @param {number|string} "fast", "slow", "normal" or a number
* @return {Dom}
* @example
* <div class="code"><pre class="prettyprint">
* $("p").fadeIn();
* $("p").fadeIn(140);
* </pre></div>
* @since 0.1.0
*/
fadeIn: function (timing) {
this.fade("in", timing);
},
/**
* Animates opacity prop. from 1 to 0
*
* @for Dom
* @method fadeOut
* @param {number|string} "fast", "slow", "normal" or a number
* @return {Dom}
* @example
* <div class="code"><pre class="prettyprint">
* $("p").fadeOut();
* $("p").fadeOut(140);
* </pre></div>
* @since 0.1.0
*/
fadeOut: function (timing) {
this.fade("out", timing);
}
});
hilo.classify = function () {
var body = win.Hilo("body")
, classes = ["js"]
, _i;
// Remove the default no-js class
body.removeClass("no-js");
if (hilo.browser.chrome) {
classes.push("chrome");
} else if (hilo.browser.firefox) {
classes.push("firefox");
} else if (hilo.browser.safari) {
classes.push("safari");
} else if (hilo.browser.ie) {
for (_i = 6; _i <= 11; _i++) {
if (hilo.browser.ie <= _i) {
classes.push("lte-ie" + _i);
if (hilo.browser.ie < _i) {
classes.push("lt-ie" + _i);
}
}
if (hilo.browser.ie >= _i) {
classes.push("gte-ie" + _i);
if (hilo.browser.ie > _i) {
classes.push("gt-ie" + _i);
}
}
if (hilo.browser.ie === _i) {
classes.push("ie" + _i);
}
}
classes.push("ie");
} else if (hilo.browser.opera) {
classes.push("opera");
} else if (hilo.browser.konq) {
classes.push("konqueror");
}
classes.push((function () {
switch (hilo.platform.name) {
case "Windows":
return "windows";
case "Mac":
return "mac";
case "Linux":
return "linux";
}
})());
if (hilo.engine.webkit) {
classes.push("webkit");
} else if (hilo.engine.ie) {
classes.push("trident");
} else if (hilo.engine.opera) {
classes.push("presto");
} else if (hilo.engine.gecko) {
classes.push("gecko");
}
classes.push(hilo.browser.name.toLowerCase() + parseInt(hilo.browser.version, 10));
function getBrowserVersion () {
return String(hilo.browser.version).replace(".", "-");
}
if (getBrowserVersion() !== parseInt(hilo.browser.version, 10)) {
classes.push(hilo.browser.name.toLowerCase() + getBrowserVersion());
}
for (_i in hilo.feature) {
if (hilo.feature.hasOwnProperty(_i)) {
if (hilo.feature[_i] === true) {
classes.push(_i.toLowerCase());
} else if (hilo.feature[_i] === false) {
classes.push("no-" + _i.toLowerCase());
}
}
}
body.addClass(classes);
return classes;
};
// --------------------------------------------------
// More Functionality
// --------------------------------------------------
/**
* NumberObject Class
*
* @constructor
* @class NumberObject
* @param {Number} num Number
* @example
* <div class="code"><pre class="prettyprint">
* new NumberObject(2);
* </pre></div>
* <div class="code"><pre class="prettyprint">
* new NumberObject(Math.PI);
* </pre></div>
* @since 0.1.0
*/
function NumberObject (num) {
this.num = num;
}
extend(NumberObject.prototype, {
/**
* NumberObject.MAX_INTEGER = 9007199254740991
* The maximum value of a JavaScript integer
*
* @for NumberObject
* @property MAX_INTEGER
* @type Number
* @since 0.1.0
*/
MAX_INTEGER: 9007199254740991,
/**
* Epsilon
*
* @for NumberObject
* @property EPSILON
* @type Number
* @since 0.1.0
*/
EPSILON: 2.220446049250313e-16,
/**
* Parses integer value from a string or number
*
* @for NumberObject
* @method parseInt
* @return {Number}
* @example
* <div class="code"><pre class="prettyprint">
* new NumberObject(3.5).parseInt() // 3
* </pre></div>
* @since 0.1.0
*/
parseInt: function () {
parseInt.call(this, this.num);
},
/**
* Parses float point number value from a string or number
*
* @for NumberObject
* @method parseFloat
* @return {Number}
* @example
* <div class="code"><pre class="prettyprint">
* new NumberObject("5.3").parseFloat() // 5.3
* </pre></div>
* @since 0.1.0
*/
parseFloat: function () {
parseFloat.call(this, this.num);
},
/**
* Returns true if a number is a finite value
*
* @for NumberObject
* @method isFinite
* @return {Bolean} Whether the number is finite
* @example
* <div class="code"><pre class="prettyprint">
* new NumberObject(NaN).isFinite() // false
* new NumberObject(3).isFinite() // true
* </pre></div>
* @since 0.1.0
*/
isFinite: function() {
return typeof this.num === 'number' && isFinite(this.num);
},
/**
* If the number is an integer
*
* @for NumberObject
* @method isInteger
* @return {Number} Whether the number is an integer
* @example
* <div class="code"><pre class="prettyprint">
* new NumberObject(5.3).isInteger() // false
* new NumberObject(4).isInteger() // true
* </pre></div>
* @since 0.1.0
*/
isInteger: function() {
return typeof this.num === 'number' &&
!isNaN(this.num) &&
isFinite(this.num) &&
parseInt(this.num, 10) === this.num;
},
/**
* Returns if the number is NaN (Not a number)
*
* @for NumberObject
* @method isNan
* @return {Number} Whether the number is not a number (NaN)
* @example
* <div class="code"><pre class="prettyprint">
* new NumberObject(5).isNan() // false
* new NumberObject(NaN).isNan() // true
* </pre></div>
* @since 0.1.0
*/
isNaN: function() {
// NaN !== NaN, but they are identical.
// NaNs are the only non-reflexive value, i.e., if x !== x,
// then x is NaN.
// isNaN is broken: it converts its argument to number, so
// isNaN('foo') => true
return this.num !== this.num;
},
/**
* Converts ant value to an integer
*
* @for NumberObject
* @method toInteger
* @return {Number} The converted integer
* @example
* <div class="code"><pre class="prettyprint">
* new NumberObject(5).toInteger() // 5
* new NumberObject(NaN).toInteger() // 0
* </pre></div>
* @since 0.1.0
*/
toInteger: function() {
var number = +this.num;
if (isNaN(number)) {
return 0;
}
if (number === 0 || !isFinite(number)) {
return number;
}
return sign.call(this, number) * Math.floor(Math.abs(number));
},
sign: function (value) {
sign.call(this, this.num, value);
},
/**
* Call a function n times
*
* @for NumberObject
* @method times
* @param {Function} fn The function to be called
* @param {Array} args The arguments to be passed
* @return {Number} The converted integer
* @example
* <div class="code"><pre class="prettyprint">
* var i = 0;
* new NumberObject(100).times(function () {
* console.log(i++);
* });
* </pre></div>
* <div class="code"><pre class="prettyprint">
* new NumberObject(100).times(function () {
* consolee.log
* });
* </pre></div>
* @since 0.1.0
*/
times: function (fn, args) {
var _i = 0;
while (_i < this.num) {
fn.apply(this, args);
_i += 1;
}
}
});
// ## Hilo Extension API
/* Provide Extension API */
extend(hilo, {
Dom: Dom.prototype,
Test: Test.prototype
});
/* Set event handler for triggering DOM Events */
doc.onreadystatechange = function () {
if (doc.readyState === "complete") {
for (_i = 0; _i < callbacks.length; _i += 1) {
callbacks[_i]();
}
}
};
/* Get the total time took to execute the script */
hilo.perf = new Date().getTime() - start;
// Finally return Hilo
return hilo;
}));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment