shiro
Version:
Online quiz game engine, inspired by russian tv show 'What? Where? When?' (Million Dollar Mind Game).
1,571 lines (1,381 loc) • 334 kB
JavaScript
/*!
* =============================================================
* Ender: open module JavaScript framework (https://ender.no.de)
* Build: ender build jeesh lodash oatmeal --output static/a/ender
* Packages: ender-js@0.5.0 domready@0.2.12 qwery@3.4.1 bonzo@1.3.5 bean@1.0.4 jeesh@0.0.6 lodash@2.2.1 oatmeal@0.1.3
* =============================================================
*/
/*!
* Ender: open module JavaScript framework (client-lib)
* copyright Dustin Diaz & Jacob Thornton 2011-2012 (@ded @fat)
* http://ender.jit.su
* License MIT
*/
(function (context, window, document) {
// a global object for node.js module compatiblity
// ============================================
context['global'] = context
// Implements simple module system
// loosely based on CommonJS Modules spec v1.1.1
// ============================================
var modules = {}
, old = context['$']
, oldEnder = context['ender']
, oldRequire = context['require']
, oldProvide = context['provide']
/**
* @param {string} name
*/
function require(name) {
// modules can be required from ender's build system, or found on the window
var module = modules['$' + name] || window[name]
if (!module) throw new Error("Ender Error: Requested module '" + name + "' has not been defined.")
return module
}
/**
* @param {string} name
* @param {*} what
*/
function provide(name, what) {
return (modules['$' + name] = what)
}
context['provide'] = provide
context['require'] = require
function aug(o, o2) {
for (var k in o2) k != 'noConflict' && k != '_VERSION' && (o[k] = o2[k])
return o
}
/**
* @param {*} o is an item to count
* @return {number|boolean}
*/
function count(o) {
if (typeof o != 'object' || !o || o.nodeType || o === window)
return false
return typeof (o = o.length) == 'number' && o === o ? o : false
}
/**
* @constructor
* @param {*=} item selector|node|collection|callback|anything
* @param {Object=} root node(s) from which to base selector queries
*/
function Ender(item, root) {
var i
this.length = 0 // Ensure that instance owns length
if (typeof item == 'string')
// Start @ strings so the result parlays into the other checks
// The .selector prop only applies to strings
item = ender['_select'](this['selector'] = item, root)
if (null == item)
return this // Do not wrap null|undefined
if (typeof item == 'function')
ender['_closure'](item, root)
// DOM node | scalar | not array-like
else if (false === (i = count(item)))
this[this.length++] = item
// Array-like - Bitwise ensures integer length:
else for (this.length = i = i > 0 ? i >> 0 : 0; i--;)
this[i] = item[i]
}
/**
* @param {*=} item selector|node|collection|callback|anything
* @param {Object=} root node(s) from which to base selector queries
* @return {Ender}
*/
function ender(item, root) {
return new Ender(item, root)
}
ender['_VERSION'] = '0.4.x'
// Sync the prototypes for jQuery compatibility
ender['fn'] = ender.prototype = Ender.prototype
Ender.prototype['$'] = ender // handy reference to self
// dev tools secret sauce
Ender.prototype['splice'] = function () { throw new Error('Not implemented') }
/**
* @param {function(*, number, Ender)} fn
* @param {Object=} opt_scope
* @return {Ender}
*/
Ender.prototype['forEach'] = function (fn, opt_scope) {
var i, l
// opt out of native forEach so we can intentionally call our own scope
// defaulting to the current item and be able to return self
for (i = 0, l = this.length; i < l; ++i) i in this && fn.call(opt_scope || this[i], this[i], i, this)
// return self for chaining
return this
}
/**
* @param {Object|Function} o
* @param {boolean=} chain
*/
ender['ender'] = function (o, chain) {
aug(chain ? Ender.prototype : ender, o)
}
/**
* @param {string} s
* @param {Node=} r
*/
ender['_select'] = function (s, r) {
return s ? (r || document).querySelectorAll(s) : []
}
/**
* @param {Function} fn
*/
ender['_closure'] = function (fn) {
fn.call(document, ender)
}
/**
* @param {(boolean|Function)=} fn optional flag or callback
* To unclaim the global $, use no args. To unclaim *all* ender globals,
* use `true` or a callback that receives (require, provide, ender)
*/
ender['noConflict'] = function (fn) {
context['$'] = old
if (fn) {
context['provide'] = oldProvide
context['require'] = oldRequire
context['ender'] = oldEnder
typeof fn == 'function' && fn(require, provide, this)
}
return this
}
if (typeof module !== 'undefined' && module['exports']) module['exports'] = ender
// use subscript notation as extern for Closure compilation
// developers.google.com/closure/compiler/docs/api-tutorial3
context['ender'] = context['$'] = ender
}(this, window, document));
(function () {
var module = { exports: {} }, exports = module.exports;
/*!
* domready (c) Dustin Diaz 2012 - License MIT
*/
!function (name, definition) {
if (typeof module != 'undefined') module.exports = definition()
else if (typeof define == 'function' && typeof define.amd == 'object') define(definition)
else this[name] = definition()
}('domready', function (ready) {
var fns = [], fn, f = false
, doc = document
, testEl = doc.documentElement
, hack = testEl.doScroll
, domContentLoaded = 'DOMContentLoaded'
, addEventListener = 'addEventListener'
, onreadystatechange = 'onreadystatechange'
, readyState = 'readyState'
, loadedRgx = hack ? /^loaded|^c/ : /^loaded|c/
, loaded = loadedRgx.test(doc[readyState])
function flush(f) {
loaded = 1
while (f = fns.shift()) f()
}
doc[addEventListener] && doc[addEventListener](domContentLoaded, fn = function () {
doc.removeEventListener(domContentLoaded, fn, f)
flush()
}, f)
hack && doc.attachEvent(onreadystatechange, fn = function () {
if (/^c/.test(doc[readyState])) {
doc.detachEvent(onreadystatechange, fn)
flush()
}
})
return (ready = hack ?
function (fn) {
self != top ?
loaded ? fn() : fns.push(fn) :
function () {
try {
testEl.doScroll('left')
} catch (e) {
return setTimeout(function() { ready(fn) }, 50)
}
fn()
}()
} :
function (fn) {
loaded ? fn() : fns.push(fn)
})
})
if (typeof provide == "function") provide("domready", module.exports);
!function ($) {
var ready = require('domready')
$.ender({domReady: ready})
$.ender({
ready: function (f) {
ready(f)
return this
}
}, true)
}(ender);
}());
(function () {
var module = { exports: {} }, exports = module.exports;
/*!
* @preserve Qwery - A Blazing Fast query selector engine
* https://github.com/ded/qwery
* copyright Dustin Diaz 2012
* MIT License
*/
(function (name, context, definition) {
if (typeof module != 'undefined' && module.exports) module.exports = definition()
else if (typeof define == 'function' && define.amd) define(definition)
else context[name] = definition()
})('qwery', this, function () {
var doc = document
, html = doc.documentElement
, byClass = 'getElementsByClassName'
, byTag = 'getElementsByTagName'
, qSA = 'querySelectorAll'
, useNativeQSA = 'useNativeQSA'
, tagName = 'tagName'
, nodeType = 'nodeType'
, select // main select() method, assign later
, id = /#([\w\-]+)/
, clas = /\.[\w\-]+/g
, idOnly = /^#([\w\-]+)$/
, classOnly = /^\.([\w\-]+)$/
, tagOnly = /^([\w\-]+)$/
, tagAndOrClass = /^([\w]+)?\.([\w\-]+)$/
, splittable = /(^|,)\s*[>~+]/
, normalizr = /^\s+|\s*([,\s\+\~>]|$)\s*/g
, splitters = /[\s\>\+\~]/
, splittersMore = /(?![\s\w\-\/\?\&\=\:\.\(\)\!,@#%<>\{\}\$\*\^'"]*\]|[\s\w\+\-]*\))/
, specialChars = /([.*+?\^=!:${}()|\[\]\/\\])/g
, simple = /^(\*|[a-z0-9]+)?(?:([\.\#]+[\w\-\.#]+)?)/
, attr = /\[([\w\-]+)(?:([\|\^\$\*\~]?\=)['"]?([ \w\-\/\?\&\=\:\.\(\)\!,@#%<>\{\}\$\*\^]+)["']?)?\]/
, pseudo = /:([\w\-]+)(\(['"]?([^()]+)['"]?\))?/
, easy = new RegExp(idOnly.source + '|' + tagOnly.source + '|' + classOnly.source)
, dividers = new RegExp('(' + splitters.source + ')' + splittersMore.source, 'g')
, tokenizr = new RegExp(splitters.source + splittersMore.source)
, chunker = new RegExp(simple.source + '(' + attr.source + ')?' + '(' + pseudo.source + ')?')
var walker = {
' ': function (node) {
return node && node !== html && node.parentNode
}
, '>': function (node, contestant) {
return node && node.parentNode == contestant.parentNode && node.parentNode
}
, '~': function (node) {
return node && node.previousSibling
}
, '+': function (node, contestant, p1, p2) {
if (!node) return false
return (p1 = previous(node)) && (p2 = previous(contestant)) && p1 == p2 && p1
}
}
function cache() {
this.c = {}
}
cache.prototype = {
g: function (k) {
return this.c[k] || undefined
}
, s: function (k, v, r) {
v = r ? new RegExp(v) : v
return (this.c[k] = v)
}
}
var classCache = new cache()
, cleanCache = new cache()
, attrCache = new cache()
, tokenCache = new cache()
function classRegex(c) {
return classCache.g(c) || classCache.s(c, '(^|\\s+)' + c + '(\\s+|$)', 1)
}
// not quite as fast as inline loops in older browsers so don't use liberally
function each(a, fn) {
var i = 0, l = a.length
for (; i < l; i++) fn(a[i])
}
function flatten(ar) {
for (var r = [], i = 0, l = ar.length; i < l; ++i) arrayLike(ar[i]) ? (r = r.concat(ar[i])) : (r[r.length] = ar[i])
return r
}
function arrayify(ar) {
var i = 0, l = ar.length, r = []
for (; i < l; i++) r[i] = ar[i]
return r
}
function previous(n) {
while (n = n.previousSibling) if (n[nodeType] == 1) break;
return n
}
function q(query) {
return query.match(chunker)
}
// called using `this` as element and arguments from regex group results.
// given => div.hello[title="world"]:foo('bar')
// div.hello[title="world"]:foo('bar'), div, .hello, [title="world"], title, =, world, :foo('bar'), foo, ('bar'), bar]
function interpret(whole, tag, idsAndClasses, wholeAttribute, attribute, qualifier, value, wholePseudo, pseudo, wholePseudoVal, pseudoVal) {
var i, m, k, o, classes
if (this[nodeType] !== 1) return false
if (tag && tag !== '*' && this[tagName] && this[tagName].toLowerCase() !== tag) return false
if (idsAndClasses && (m = idsAndClasses.match(id)) && m[1] !== this.id) return false
if (idsAndClasses && (classes = idsAndClasses.match(clas))) {
for (i = classes.length; i--;) if (!classRegex(classes[i].slice(1)).test(this.className)) return false
}
if (pseudo && qwery.pseudos[pseudo] && !qwery.pseudos[pseudo](this, pseudoVal)) return false
if (wholeAttribute && !value) { // select is just for existance of attrib
o = this.attributes
for (k in o) {
if (Object.prototype.hasOwnProperty.call(o, k) && (o[k].name || k) == attribute) {
return this
}
}
}
if (wholeAttribute && !checkAttr(qualifier, getAttr(this, attribute) || '', value)) {
// select is for attrib equality
return false
}
return this
}
function clean(s) {
return cleanCache.g(s) || cleanCache.s(s, s.replace(specialChars, '\\$1'))
}
function checkAttr(qualify, actual, val) {
switch (qualify) {
case '=':
return actual == val
case '^=':
return actual.match(attrCache.g('^=' + val) || attrCache.s('^=' + val, '^' + clean(val), 1))
case '$=':
return actual.match(attrCache.g('$=' + val) || attrCache.s('$=' + val, clean(val) + '$', 1))
case '*=':
return actual.match(attrCache.g(val) || attrCache.s(val, clean(val), 1))
case '~=':
return actual.match(attrCache.g('~=' + val) || attrCache.s('~=' + val, '(?:^|\\s+)' + clean(val) + '(?:\\s+|$)', 1))
case '|=':
return actual.match(attrCache.g('|=' + val) || attrCache.s('|=' + val, '^' + clean(val) + '(-|$)', 1))
}
return 0
}
// given a selector, first check for simple cases then collect all base candidate matches and filter
function _qwery(selector, _root) {
var r = [], ret = [], i, l, m, token, tag, els, intr, item, root = _root
, tokens = tokenCache.g(selector) || tokenCache.s(selector, selector.split(tokenizr))
, dividedTokens = selector.match(dividers)
if (!tokens.length) return r
token = (tokens = tokens.slice(0)).pop() // copy cached tokens, take the last one
if (tokens.length && (m = tokens[tokens.length - 1].match(idOnly))) root = byId(_root, m[1])
if (!root) return r
intr = q(token)
// collect base candidates to filter
els = root !== _root && root[nodeType] !== 9 && dividedTokens && /^[+~]$/.test(dividedTokens[dividedTokens.length - 1]) ?
function (r) {
while (root = root.nextSibling) {
root[nodeType] == 1 && (intr[1] ? intr[1] == root[tagName].toLowerCase() : 1) && (r[r.length] = root)
}
return r
}([]) :
root[byTag](intr[1] || '*')
// filter elements according to the right-most part of the selector
for (i = 0, l = els.length; i < l; i++) {
if (item = interpret.apply(els[i], intr)) r[r.length] = item
}
if (!tokens.length) return r
// filter further according to the rest of the selector (the left side)
each(r, function (e) { if (ancestorMatch(e, tokens, dividedTokens)) ret[ret.length] = e })
return ret
}
// compare element to a selector
function is(el, selector, root) {
if (isNode(selector)) return el == selector
if (arrayLike(selector)) return !!~flatten(selector).indexOf(el) // if selector is an array, is el a member?
var selectors = selector.split(','), tokens, dividedTokens
while (selector = selectors.pop()) {
tokens = tokenCache.g(selector) || tokenCache.s(selector, selector.split(tokenizr))
dividedTokens = selector.match(dividers)
tokens = tokens.slice(0) // copy array
if (interpret.apply(el, q(tokens.pop())) && (!tokens.length || ancestorMatch(el, tokens, dividedTokens, root))) {
return true
}
}
return false
}
// given elements matching the right-most part of a selector, filter out any that don't match the rest
function ancestorMatch(el, tokens, dividedTokens, root) {
var cand
// recursively work backwards through the tokens and up the dom, covering all options
function crawl(e, i, p) {
while (p = walker[dividedTokens[i]](p, e)) {
if (isNode(p) && (interpret.apply(p, q(tokens[i])))) {
if (i) {
if (cand = crawl(p, i - 1, p)) return cand
} else return p
}
}
}
return (cand = crawl(el, tokens.length - 1, el)) && (!root || isAncestor(cand, root))
}
function isNode(el, t) {
return el && typeof el === 'object' && (t = el[nodeType]) && (t == 1 || t == 9)
}
function uniq(ar) {
var a = [], i, j;
o:
for (i = 0; i < ar.length; ++i) {
for (j = 0; j < a.length; ++j) if (a[j] == ar[i]) continue o
a[a.length] = ar[i]
}
return a
}
function arrayLike(o) {
return (typeof o === 'object' && isFinite(o.length))
}
function normalizeRoot(root) {
if (!root) return doc
if (typeof root == 'string') return qwery(root)[0]
if (!root[nodeType] && arrayLike(root)) return root[0]
return root
}
function byId(root, id, el) {
// if doc, query on it, else query the parent doc or if a detached fragment rewrite the query and run on the fragment
return root[nodeType] === 9 ? root.getElementById(id) :
root.ownerDocument &&
(((el = root.ownerDocument.getElementById(id)) && isAncestor(el, root) && el) ||
(!isAncestor(root, root.ownerDocument) && select('[id="' + id + '"]', root)[0]))
}
function qwery(selector, _root) {
var m, el, root = normalizeRoot(_root)
// easy, fast cases that we can dispatch with simple DOM calls
if (!root || !selector) return []
if (selector === window || isNode(selector)) {
return !_root || (selector !== window && isNode(root) && isAncestor(selector, root)) ? [selector] : []
}
if (selector && arrayLike(selector)) return flatten(selector)
if (m = selector.match(easy)) {
if (m[1]) return (el = byId(root, m[1])) ? [el] : []
if (m[2]) return arrayify(root[byTag](m[2]))
if (hasByClass && m[3]) return arrayify(root[byClass](m[3]))
}
return select(selector, root)
}
// where the root is not document and a relationship selector is first we have to
// do some awkward adjustments to get it to work, even with qSA
function collectSelector(root, collector) {
return function (s) {
var oid, nid
if (splittable.test(s)) {
if (root[nodeType] !== 9) {
// make sure the el has an id, rewrite the query, set root to doc and run it
if (!(nid = oid = root.getAttribute('id'))) root.setAttribute('id', nid = '__qwerymeupscotty')
s = '[id="' + nid + '"]' + s // avoid byId and allow us to match context element
collector(root.parentNode || root, s, true)
oid || root.removeAttribute('id')
}
return;
}
s.length && collector(root, s, false)
}
}
var isAncestor = 'compareDocumentPosition' in html ?
function (element, container) {
return (container.compareDocumentPosition(element) & 16) == 16
} : 'contains' in html ?
function (element, container) {
container = container[nodeType] === 9 || container == window ? html : container
return container !== element && container.contains(element)
} :
function (element, container) {
while (element = element.parentNode) if (element === container) return 1
return 0
}
, getAttr = function () {
// detect buggy IE src/href getAttribute() call
var e = doc.createElement('p')
return ((e.innerHTML = '<a href="#x">x</a>') && e.firstChild.getAttribute('href') != '#x') ?
function (e, a) {
return a === 'class' ? e.className : (a === 'href' || a === 'src') ?
e.getAttribute(a, 2) : e.getAttribute(a)
} :
function (e, a) { return e.getAttribute(a) }
}()
, hasByClass = !!doc[byClass]
// has native qSA support
, hasQSA = doc.querySelector && doc[qSA]
// use native qSA
, selectQSA = function (selector, root) {
var result = [], ss, e
try {
if (root[nodeType] === 9 || !splittable.test(selector)) {
// most work is done right here, defer to qSA
return arrayify(root[qSA](selector))
}
// special case where we need the services of `collectSelector()`
each(ss = selector.split(','), collectSelector(root, function (ctx, s) {
e = ctx[qSA](s)
if (e.length == 1) result[result.length] = e.item(0)
else if (e.length) result = result.concat(arrayify(e))
}))
return ss.length > 1 && result.length > 1 ? uniq(result) : result
} catch (ex) { }
return selectNonNative(selector, root)
}
// no native selector support
, selectNonNative = function (selector, root) {
var result = [], items, m, i, l, r, ss
selector = selector.replace(normalizr, '$1')
if (m = selector.match(tagAndOrClass)) {
r = classRegex(m[2])
items = root[byTag](m[1] || '*')
for (i = 0, l = items.length; i < l; i++) {
if (r.test(items[i].className)) result[result.length] = items[i]
}
return result
}
// more complex selector, get `_qwery()` to do the work for us
each(ss = selector.split(','), collectSelector(root, function (ctx, s, rewrite) {
r = _qwery(s, ctx)
for (i = 0, l = r.length; i < l; i++) {
if (ctx[nodeType] === 9 || rewrite || isAncestor(r[i], root)) result[result.length] = r[i]
}
}))
return ss.length > 1 && result.length > 1 ? uniq(result) : result
}
, configure = function (options) {
// configNativeQSA: use fully-internal selector or native qSA where present
if (typeof options[useNativeQSA] !== 'undefined')
select = !options[useNativeQSA] ? selectNonNative : hasQSA ? selectQSA : selectNonNative
}
configure({ useNativeQSA: true })
qwery.configure = configure
qwery.uniq = uniq
qwery.is = is
qwery.pseudos = {}
return qwery
});
if (typeof provide == "function") provide("qwery", module.exports);
(function ($) {
var q = function () {
var r
try {
r = require('qwery')
} catch (ex) {
r = require('qwery-mobile')
} finally {
return r
}
}()
$.pseudos = q.pseudos
$._select = function (s, r) {
// detect if sibling module 'bonzo' is available at run-time
// rather than load-time since technically it's not a dependency and
// can be loaded in any order
// hence the lazy function re-definition
return ($._select = (function () {
var b
if (typeof $.create == 'function') return function (s, r) {
return /^\s*</.test(s) ? $.create(s, r) : q(s, r)
}
try {
b = require('bonzo')
return function (s, r) {
return /^\s*</.test(s) ? b.create(s, r) : q(s, r)
}
} catch (e) { }
return q
})())(s, r)
}
$.ender({
find: function (s) {
var r = [], i, l, j, k, els
for (i = 0, l = this.length; i < l; i++) {
els = q(s, this[i])
for (j = 0, k = els.length; j < k; j++) r.push(els[j])
}
return $(q.uniq(r))
}
, and: function (s) {
var plus = $(s)
for (var i = this.length, j = 0, l = this.length + plus.length; i < l; i++, j++) {
this[i] = plus[j]
}
this.length += plus.length
return this
}
, is: function(s, r) {
var i, l
for (i = 0, l = this.length; i < l; i++) {
if (q.is(this[i], s, r)) {
return true
}
}
return false
}
}, true)
}(ender));
}());
(function () {
var module = { exports: {} }, exports = module.exports;
/*!
* Bonzo: DOM Utility (c) Dustin Diaz 2012
* https://github.com/ded/bonzo
* License MIT
*/
(function (name, context, definition) {
if (typeof module != 'undefined' && module.exports) module.exports = definition()
else if (typeof define == 'function' && define.amd) define(definition)
else context[name] = definition()
})('bonzo', this, function() {
var win = window
, doc = win.document
, html = doc.documentElement
, parentNode = 'parentNode'
, specialAttributes = /^(checked|value|selected|disabled)$/i
// tags that we have trouble inserting *into*
, specialTags = /^(select|fieldset|table|tbody|tfoot|td|tr|colgroup)$/i
, simpleScriptTagRe = /\s*<script +src=['"]([^'"]+)['"]>/
, table = ['<table>', '</table>', 1]
, td = ['<table><tbody><tr>', '</tr></tbody></table>', 3]
, option = ['<select>', '</select>', 1]
, noscope = ['_', '', 0, 1]
, tagMap = { // tags that we have trouble *inserting*
thead: table, tbody: table, tfoot: table, colgroup: table, caption: table
, tr: ['<table><tbody>', '</tbody></table>', 2]
, th: td , td: td
, col: ['<table><colgroup>', '</colgroup></table>', 2]
, fieldset: ['<form>', '</form>', 1]
, legend: ['<form><fieldset>', '</fieldset></form>', 2]
, option: option, optgroup: option
, script: noscope, style: noscope, link: noscope, param: noscope, base: noscope
}
, stateAttributes = /^(checked|selected|disabled)$/
, ie = /msie/i.test(navigator.userAgent)
, hasClass, addClass, removeClass
, uidMap = {}
, uuids = 0
, digit = /^-?[\d\.]+$/
, dattr = /^data-(.+)$/
, px = 'px'
, setAttribute = 'setAttribute'
, getAttribute = 'getAttribute'
, byTag = 'getElementsByTagName'
, features = function() {
var e = doc.createElement('p')
e.innerHTML = '<a href="#x">x</a><table style="float:left;"></table>'
return {
hrefExtended: e[byTag]('a')[0][getAttribute]('href') != '#x' // IE < 8
, autoTbody: e[byTag]('tbody').length !== 0 // IE < 8
, computedStyle: doc.defaultView && doc.defaultView.getComputedStyle
, cssFloat: e[byTag]('table')[0].style.styleFloat ? 'styleFloat' : 'cssFloat'
, transform: function () {
var props = ['transform', 'webkitTransform', 'MozTransform', 'OTransform', 'msTransform'], i
for (i = 0; i < props.length; i++) {
if (props[i] in e.style) return props[i]
}
}()
, classList: 'classList' in e
, opasity: function () {
return typeof doc.createElement('a').style.opacity !== 'undefined'
}()
}
}()
, trimReplace = /(^\s*|\s*$)/g
, whitespaceRegex = /\s+/
, toString = String.prototype.toString
, unitless = { lineHeight: 1, zoom: 1, zIndex: 1, opacity: 1, boxFlex: 1, WebkitBoxFlex: 1, MozBoxFlex: 1 }
, query = doc.querySelectorAll && function (selector) { return doc.querySelectorAll(selector) }
, trim = String.prototype.trim ?
function (s) {
return s.trim()
} :
function (s) {
return s.replace(trimReplace, '')
}
, getStyle = features.computedStyle
? function (el, property) {
var value = null
, computed = doc.defaultView.getComputedStyle(el, '')
computed && (value = computed[property])
return el.style[property] || value
}
: !(ie && html.currentStyle)
? function (el, property) {
return el.style[property]
}
:
/**
* @param {Element} el
* @param {string} property
* @return {string|number}
*/
function (el, property) {
var val, value
if (property == 'opacity' && !features.opasity) {
val = 100
try {
val = el['filters']['DXImageTransform.Microsoft.Alpha'].opacity
} catch (e1) {
try {
val = el['filters']('alpha').opacity
} catch (e2) {}
}
return val / 100
}
value = el.currentStyle ? el.currentStyle[property] : null
return el.style[property] || value
}
function isNode(node) {
return node && node.nodeName && (node.nodeType == 1 || node.nodeType == 11)
}
function normalize(node, host, clone) {
var i, l, ret
if (typeof node == 'string') return bonzo.create(node)
if (isNode(node)) node = [ node ]
if (clone) {
ret = [] // don't change original array
for (i = 0, l = node.length; i < l; i++) ret[i] = cloneNode(host, node[i])
return ret
}
return node
}
/**
* @param {string} c a class name to test
* @return {boolean}
*/
function classReg(c) {
return new RegExp('(^|\\s+)' + c + '(\\s+|$)')
}
/**
* @param {Bonzo|Array} ar
* @param {function(Object, number, (Bonzo|Array))} fn
* @param {Object=} opt_scope
* @param {boolean=} opt_rev
* @return {Bonzo|Array}
*/
function each(ar, fn, opt_scope, opt_rev) {
var ind, i = 0, l = ar.length
for (; i < l; i++) {
ind = opt_rev ? ar.length - i - 1 : i
fn.call(opt_scope || ar[ind], ar[ind], ind, ar)
}
return ar
}
/**
* @param {Bonzo|Array} ar
* @param {function(Object, number, (Bonzo|Array))} fn
* @param {Object=} opt_scope
* @return {Bonzo|Array}
*/
function deepEach(ar, fn, opt_scope) {
for (var i = 0, l = ar.length; i < l; i++) {
if (isNode(ar[i])) {
deepEach(ar[i].childNodes, fn, opt_scope)
fn.call(opt_scope || ar[i], ar[i], i, ar)
}
}
return ar
}
/**
* @param {string} s
* @return {string}
*/
function camelize(s) {
return s.replace(/-(.)/g, function (m, m1) {
return m1.toUpperCase()
})
}
/**
* @param {string} s
* @return {string}
*/
function decamelize(s) {
return s ? s.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase() : s
}
/**
* @param {Element} el
* @return {*}
*/
function data(el) {
el[getAttribute]('data-node-uid') || el[setAttribute]('data-node-uid', ++uuids)
var uid = el[getAttribute]('data-node-uid')
return uidMap[uid] || (uidMap[uid] = {})
}
/**
* removes the data associated with an element
* @param {Element} el
*/
function clearData(el) {
var uid = el[getAttribute]('data-node-uid')
if (uid) delete uidMap[uid]
}
function dataValue(d) {
var f
try {
return (d === null || d === undefined) ? undefined :
d === 'true' ? true :
d === 'false' ? false :
d === 'null' ? null :
(f = parseFloat(d)) == d ? f : d;
} catch(e) {}
return undefined
}
/**
* @param {Bonzo|Array} ar
* @param {function(Object, number, (Bonzo|Array))} fn
* @param {Object=} opt_scope
* @return {boolean} whether `some`thing was found
*/
function some(ar, fn, opt_scope) {
for (var i = 0, j = ar.length; i < j; ++i) if (fn.call(opt_scope || null, ar[i], i, ar)) return true
return false
}
/**
* this could be a giant enum of CSS properties
* but in favor of file size sans-closure deadcode optimizations
* we're just asking for any ol string
* then it gets transformed into the appropriate style property for JS access
* @param {string} p
* @return {string}
*/
function styleProperty(p) {
(p == 'transform' && (p = features.transform)) ||
(/^transform-?[Oo]rigin$/.test(p) && (p = features.transform + 'Origin')) ||
(p == 'float' && (p = features.cssFloat))
return p ? camelize(p) : null
}
// this insert method is intense
function insert(target, host, fn, rev) {
var i = 0, self = host || this, r = []
// target nodes could be a css selector if it's a string and a selector engine is present
// otherwise, just use target
, nodes = query && typeof target == 'string' && target.charAt(0) != '<' ? query(target) : target
// normalize each node in case it's still a string and we need to create nodes on the fly
each(normalize(nodes), function (t, j) {
each(self, function (el) {
fn(t, r[i++] = j > 0 ? cloneNode(self, el) : el)
}, null, rev)
}, this, rev)
self.length = i
each(r, function (e) {
self[--i] = e
}, null, !rev)
return self
}
/**
* sets an element to an explicit x/y position on the page
* @param {Element} el
* @param {?number} x
* @param {?number} y
*/
function xy(el, x, y) {
var $el = bonzo(el)
, style = $el.css('position')
, offset = $el.offset()
, rel = 'relative'
, isRel = style == rel
, delta = [parseInt($el.css('left'), 10), parseInt($el.css('top'), 10)]
if (style == 'static') {
$el.css('position', rel)
style = rel
}
isNaN(delta[0]) && (delta[0] = isRel ? 0 : el.offsetLeft)
isNaN(delta[1]) && (delta[1] = isRel ? 0 : el.offsetTop)
x != null && (el.style.left = x - offset.left + delta[0] + px)
y != null && (el.style.top = y - offset.top + delta[1] + px)
}
// classList support for class management
// altho to be fair, the api sucks because it won't accept multiple classes at once
if (features.classList) {
hasClass = function (el, c) {
return el.classList.contains(c)
}
addClass = function (el, c) {
el.classList.add(c)
}
removeClass = function (el, c) {
el.classList.remove(c)
}
}
else {
hasClass = function (el, c) {
return classReg(c).test(el.className)
}
addClass = function (el, c) {
el.className = trim(el.className + ' ' + c)
}
removeClass = function (el, c) {
el.className = trim(el.className.replace(classReg(c), ' '))
}
}
/**
* this allows method calling for setting values
*
* @example
* bonzo(elements).css('color', function (el) {
* return el.getAttribute('data-original-color')
* })
*
* @param {Element} el
* @param {function (Element)|string}
* @return {string}
*/
function setter(el, v) {
return typeof v == 'function' ? v(el) : v
}
function scroll(x, y, type) {
var el = this[0]
if (!el) return this
if (x == null && y == null) {
return (isBody(el) ? getWindowScroll() : { x: el.scrollLeft, y: el.scrollTop })[type]
}
if (isBody(el)) {
win.scrollTo(x, y)
} else {
x != null && (el.scrollLeft = x)
y != null && (el.scrollTop = y)
}
return this
}
/**
* @constructor
* @param {Array.<Element>|Element|Node|string} elements
*/
function Bonzo(elements) {
this.length = 0
if (elements) {
elements = typeof elements !== 'string' &&
!elements.nodeType &&
typeof elements.length !== 'undefined' ?
elements :
[elements]
this.length = elements.length
for (var i = 0; i < elements.length; i++) this[i] = elements[i]
}
}
Bonzo.prototype = {
/**
* @param {number} index
* @return {Element|Node}
*/
get: function (index) {
return this[index] || null
}
// itetators
/**
* @param {function(Element|Node)} fn
* @param {Object=} opt_scope
* @return {Bonzo}
*/
, each: function (fn, opt_scope) {
return each(this, fn, opt_scope)
}
/**
* @param {Function} fn
* @param {Object=} opt_scope
* @return {Bonzo}
*/
, deepEach: function (fn, opt_scope) {
return deepEach(this, fn, opt_scope)
}
/**
* @param {Function} fn
* @param {Function=} opt_reject
* @return {Array}
*/
, map: function (fn, opt_reject) {
var m = [], n, i
for (i = 0; i < this.length; i++) {
n = fn.call(this, this[i], i)
opt_reject ? (opt_reject(n) && m.push(n)) : m.push(n)
}
return m
}
// text and html inserters!
/**
* @param {string} h the HTML to insert
* @param {boolean=} opt_text whether to set or get text content
* @return {Bonzo|string}
*/
, html: function (h, opt_text) {
var method = opt_text
? html.textContent === undefined ? 'innerText' : 'textContent'
: 'innerHTML'
, that = this
, append = function (el, i) {
each(normalize(h, that, i), function (node) {
el.appendChild(node)
})
}
, updateElement = function (el, i) {
try {
if (opt_text || (typeof h == 'string' && !specialTags.test(el.tagName))) {
return el[method] = h
}
} catch (e) {}
append(el, i)
}
return typeof h != 'undefined'
? this.empty().each(updateElement)
: this[0] ? this[0][method] : ''
}
/**
* @param {string=} opt_text the text to set, otherwise this is a getter
* @return {Bonzo|string}
*/
, text: function (opt_text) {
return this.html(opt_text, true)
}
// more related insertion methods
/**
* @param {Bonzo|string|Element|Array} node
* @return {Bonzo}
*/
, append: function (node) {
var that = this
return this.each(function (el, i) {
each(normalize(node, that, i), function (i) {
el.appendChild(i)
})
})
}
/**
* @param {Bonzo|string|Element|Array} node
* @return {Bonzo}
*/
, prepend: function (node) {
var that = this
return this.each(function (el, i) {
var first = el.firstChild
each(normalize(node, that, i), function (i) {
el.insertBefore(i, first)
})
})
}
/**
* @param {Bonzo|string|Element|Array} target the location for which you'll insert your new content
* @param {Object=} opt_host an optional host scope (primarily used when integrated with Ender)
* @return {Bonzo}
*/
, appendTo: function (target, opt_host) {
return insert.call(this, target, opt_host, function (t, el) {
t.appendChild(el)
})
}
/**
* @param {Bonzo|string|Element|Array} target the location for which you'll insert your new content
* @param {Object=} opt_host an optional host scope (primarily used when integrated with Ender)
* @return {Bonzo}
*/
, prependTo: function (target, opt_host) {
return insert.call(this, target, opt_host, function (t, el) {
t.insertBefore(el, t.firstChild)
}, 1)
}
/**
* @param {Bonzo|string|Element|Array} node
* @return {Bonzo}
*/
, before: function (node) {
var that = this
return this.each(function (el, i) {
each(normalize(node, that, i), function (i) {
el[parentNode].insertBefore(i, el)
})
})
}
/**
* @param {Bonzo|string|Element|Array} node
* @return {Bonzo}
*/
, after: function (node) {
var that = this
return this.each(function (el, i) {
each(normalize(node, that, i), function (i) {
el[parentNode].insertBefore(i, el.nextSibling)
}, null, 1)
})
}
/**
* @param {Bonzo|string|Element|Array} target the location for which you'll insert your new content
* @param {Object=} opt_host an optional host scope (primarily used when integrated with Ender)
* @return {Bonzo}
*/
, insertBefore: function (target, opt_host) {
return insert.call(this, target, opt_host, function (t, el) {
t[parentNode].insertBefore(el, t)
})
}
/**
* @param {Bonzo|string|Element|Array} target the location for which you'll insert your new content
* @param {Object=} opt_host an optional host scope (primarily used when integrated with Ender)
* @return {Bonzo}
*/
, insertAfter: function (target, opt_host) {
return insert.call(this, target, opt_host, function (t, el) {
var sibling = t.nextSibling
sibling ?
t[parentNode].insertBefore(el, sibling) :
t[parentNode].appendChild(el)
}, 1)
}
/**
* @param {Bonzo|string|Element|Array} node
* @return {Bonzo}
*/
, replaceWith: function (node) {
bonzo(normalize(node)).insertAfter(this)
return this.remove()
}
/**
* @param {Object=} opt_host an optional host scope (primarily used when integrated with Ender)
* @return {Bonzo}
*/
, clone: function (opt_host) {
var ret = [] // don't change original array
, l, i
for (i = 0, l = this.length; i < l; i++) ret[i] = cloneNode(opt_host || this, this[i])
return bonzo(ret)
}
// class management
/**
* @param {string} c
* @return {Bonzo}
*/
, addClass: function (c) {
c = toString.call(c).split(whitespaceRegex)
return this.each(function (el) {
// we `each` here so you can do $el.addClass('foo bar')
each(c, function (c) {
if (c && !hasClass(el, setter(el, c)))
addClass(el, setter(el, c))
})
})
}
/**
* @param {string} c
* @return {Bonzo}
*/
, removeClass: function (c) {
c = toString.call(c).split(whitespaceRegex)
return this.each(function (el) {
each(c, function (c) {
if (c && hasClass(el, setter(el, c)))
removeClass(el, setter(el, c))
})
})
}
/**
* @param {string} c
* @return {boolean}
*/
, hasClass: function (c) {
c = toString.call(c).split(whitespaceRegex)
return some(this, function (el) {
return some(c, function (c) {
return c && hasClass(el, c)
})
})
}
/**
* @param {string} c classname to toggle
* @param {boolean=} opt_condition whether to add or remove the class straight away
* @return {Bonzo}
*/
, toggleClass: function (c, opt_condition) {
c = toString.call(c).split(whitespaceRegex)
return this.each(function (el) {
each(c, function (c) {
if (c) {
typeof opt_condition !== 'undefined' ?
opt_condition ? !hasClass(el, c) && addClass(el, c) : removeClass(el, c) :
hasClass(el, c) ? removeClass(el, c) : addClass(el, c)
}
})
})
}
// display togglers
/**
* @param {string=} opt_type useful to set back to anything other than an empty string
* @return {Bonzo}
*/
, show: function (opt_type) {
opt_type = typeof opt_type == 'string' ? opt_type : ''
return this.each(function (el) {
el.style.display = opt_type
})
}
/**
* @return {Bonzo}
*/
, hide: function () {
return this.each(function (el) {
el.style.display = 'none'
})
}
/**
* @param {Function=} opt_callback
* @param {string=} opt_type
* @return {Bonzo}
*/
, toggle: function (opt_callback, opt_type) {
opt_type = typeof opt_type == 'string' ? opt_type : '';
typeof opt_callback != 'function' && (opt_callback = null)
return this.each(function (el) {
el.style.display = (el.offsetWidth || el.offsetHeight) ? 'none' : opt_type;
opt_callback && opt_callback.call(el)
})
}
// DOM Walkers & getters
/**
* @return {Element|Node}
*/
, first: function () {
return bonzo(this.length ? this[0] : [])
}
/**
* @return {Element|Node}
*/
, last: function () {
return bonzo(this.length ? this[this.length - 1] : [])
}
/**
* @return {Element|Node}
*/
, next: function () {
return this.related('nextSibling')
}
/**
* @return {Element|Node}
*/
, previous: function () {
return this.related('previousSibling')
}
/**
* @return {Element|Node}
*/
, parent: function() {
return this.related(parentNode)
}
/**
* @private
* @param {string} method the directional DOM method
* @return {Element|Node}
*/
, related: function (method) {
return bonzo(this.map(
function (el) {
el = el[method]
while (el && el.nodeType !== 1) {
el = el[method]
}
return el || 0
},
function (el) {
return el
}
))
}
/**
* @return {Bonzo}
*/
, focus: function () {
this.length && this[0].focus()
return this
}
/**
* @return {Bonzo}
*/
, blur: function () {
this.length && this[0].blur()
return this
}
// style getter setter & related methods
/**
* @param {Object|string} o
* @param {string=} opt_v
* @return {Bonzo|string}
*/
, css: function (o, opt_v) {
var p, iter = o
// is this a request for just getting a style?
if (opt_v === undefined && typeof o == 'string') {
// repurpose 'v'
opt_v = this[0]
if (!opt_v) return null
if (opt_v === doc || opt_v === win) {
p = (opt_v === doc) ? bonzo.doc() : bonzo.viewport()
return o == 'width' ? p.width : o == 'height' ? p.height : ''
}
return (o = styleProperty(o)) ? getStyle(opt_v, o) : null
}
if (typeof o == 'string') {
iter = {}
iter[o] = opt_v
}
if (ie && iter.opacity) {
// oh this 'ol gamut
iter.filter = 'alpha(opacity=' + (iter.opacity * 100) + ')'
// give it layout
iter.zoom = o.zoom || 1;
delete iter.opacity;
}
function fn(el, p, v) {
for (var k in iter) {
if (iter.hasOwnProperty(k)) {
v = iter[k];
// change "5" to "5px" - unless you're line-height, which is allowed
(p = styleProperty(k)) && digit.test(v) && !(p in unitless) && (v += px)
try { el.style[p] = setter(el, v) } catch(e) {}
}
}
}
return this.each(fn)
}
/**
* @param {number=} opt_x
* @param {number=} opt_y
* @return {Bonzo|number}
*/
, offset: function (opt_x, opt_y) {
if (opt_x && typeof opt_x == 'object' && (typeof opt_x.top == 'number' || typeof opt_x.left == 'number')) {
return this.each(function (el) {
xy(el, opt_x.left, opt_x.top)
})
} else if (typeof opt_x == 'number' || typeof opt_y == 'number') {
return this.each(function (el) {
xy(el, opt_x, opt_y)
})
}
if (!this[0]) return {
top: 0
, left: 0
, height: 0
, width: 0
}
var el = this[0]
, de = el.ownerDocument.documentElement
, bcr = el.getBoundingClientRect()
, scroll = getWindowScroll()
, width = el.offsetWidth
, height = el.offsetHeight
, top = bcr.top + scroll.y - Math.max(0, de && de.clientTop, doc.body.clientTop)
, left = bcr.left + scroll.x - Math.max(0, de && de.clientLeft, doc.body.clientLeft)
return {
top: top
, left: left
, height: height
, width: width
}
}
/**
* @return {number}
*/
, dim: function () {
if (!this.length) return { height: 0, width: 0 }
var el = this[0]
, de = el.nodeType == 9 && el.documentElement // document
, orig = !de && !!el.style && !el.offsetWidth && !el.offsetHeight ?
// el isn't visible, can't be measured properly, so fix that
function (t) {
var s = {
position: el.style.position || ''
, visibility: el.style.visibility || ''
, display: el.style.display || ''
}
t.first().css({
position: 'absolute'
, visibility: 'hidden'
, display: 'block'
})
return s
}(this) : null