/*
* Sprint JavaScript Library v0.9.2
* http://sprintjs.com
*
* Copyright (c) 2014, 2015 Benjamin De Cock
* Released under the MIT license
* http://sprintjs.com/license
*/
var Sprint;
(function() {
"use strict";
var addEventListeners = function(listeners, el) {
var sprintClone = Sprint(el)
var events = Object.keys(listeners)
var eventsLen = events.length
for (var i = 0; i < eventsLen; i++) {
var event = events[i]
var handlers = listeners[event]
var handlersLen = handlers.length
for (var j = 0; j < handlersLen; j++) {
sprintClone.on(event, handlers[j])
}
}
}
var addPx = (function() {
var noPx = [
"animation-iteration-count",
"column-count",
"flex-grow",
"flex-shrink",
"font-weight",
"line-height",
"opacity",
"order",
"orphans",
"widows",
"z-index"
]
return function addPx(cssProperty, value) {
if (inArray(cssProperty, noPx)) return value
var stringValue = typeof value == "string" ? value : value.toString()
if (value && !/\D/.test(stringValue)) {
stringValue += "px"
}
return stringValue
}
}())
var createDOM = function(HTMLString) {
var tmp = document.createElement("div")
var tag = /[\w:-]+/.exec(HTMLString)[0]
var inMap = wrapMap[tag]
var validHTML = HTMLString.trim()
if (inMap) {
validHTML = inMap.intro + validHTML + inMap.outro
}
tmp.insertAdjacentHTML("afterbegin", validHTML)
var node = tmp.lastChild
if (inMap) {
var i = inMap.outro.match(/ 1) return
// Duplicate event listeners for the parent element...
var listeners = getEvents(el)
listeners && addEventListeners(listeners, clone)
// ... and its descendants.
var descendants = selectElements("*", el)
var descendantsLen = descendants.length
// cloneDescendants is defined later to avoid calling selectElements() if not needed
var cloneDescendants
for (var i = 0; i < descendantsLen; i++) {
var listeners = getEvents(descendants[i])
if (!listeners) continue
if (!cloneDescendants) {
cloneDescendants = selectElements("*", clone)
}
addEventListeners(listeners, cloneDescendants[i])
}
}
var findAncestors = function(startAtParent, limitToParent, limitToFirstMatch, selector, context) {
var dom = []
var self = this
this.each(function() {
var prt = startAtParent ? this.parentElement : this
while (prt) {
if (context && context == prt) break
if (!selector || self.is(selector, prt)) {
dom.push(prt)
if (limitToFirstMatch) break
}
if (limitToParent) break
prt = prt.parentElement
}
})
return Sprint(removeDuplicates(dom))
}
var getEventFromNamespace = function(event) {
return splitNamespaces(event)[0]
}
var getEvents = function(domElement) {
return domElement.sprintEventListeners
}
var getEventsToRemove = function(domElement, event) {
/*
* Returns an array with the sprintEventListeners events matching potentially
* incomplete event names passed to .off().
* Example: .off("click.myPlugin") and .off("click.simple") would both remove a
* "click.myPlugin.simple" event.
*/
return Object.keys(getEvents(domElement)).filter(function(prop) {
return splitNamespaces(event).every(function(name) {
return inArray(name, splitNamespaces(prop))
})
})
}
var getSetDimension = function(obj, prop, value) {
// get
if (value == null) {
var el = obj.get(0)
if (!el || el.nodeType > 1) return
var capitalizedProp = prop[0].toUpperCase() + prop.substring(1)
// dimension of HTML document
if (el == document) {
var offset = root["offset" + capitalizedProp]
var inner = window["inner" + capitalizedProp]
return offset > inner ? offset : inner
}
// dimension of viewport
if (el == window) {
return window["inner" + capitalizedProp]
}
// dimension of element
return el.getBoundingClientRect()[prop]
}
// set
var isFunction = typeof value == "function"
var stringValue = isFunction ? "" : addPx(prop, value)
return obj.each(function(index) {
if (this == document || this == window || this.nodeType > 1) return
if (isFunction) {
stringValue = addPx(prop, value.call(this, index, Sprint(this)[prop]()))
}
this.style[prop] = stringValue
})
}
var insertHTML = function(position, args) {
var argsLen = args.length
var contents = args
// reverse argument list for afterbegin and afterend
if (argsLen > 1 && position.indexOf("after") > -1) {
contents = []
var i = argsLen
while (i--) {
contents.push(args[i])
}
}
for (var i = 0; i < argsLen; i++) {
var content = contents[i]
if (typeof content == "string" || typeof content == "number") {
this.each(function() {
this.insertAdjacentHTML(position, content)
})
}
else if (typeof content == "function") {
this.each(function(index) {
var callbackValue = content.call(this, index, this.innerHTML)
insertHTML.call(Sprint(this), position, [callbackValue])
})
}
else {
var isSprintObj = content instanceof Init
var clonedElements = []
var elementsToInsert = (function() {
if (isSprintObj) {
return content.get()
}
if (Array.isArray(content)) {
return sanitize(content, true, true)
}
// DOM node
if (content.nodeType) {
return [content]
}
// getElementsByTagName, getElementsByClassName, querySelectorAll
return toArray(content)
}())
var elementsToInsertLen = elementsToInsert.length
this.each(function(index) {
/*
* The fragment serves multiple purposes:
* 1) It significantly boosts perf when multiple elements are added.
* 2) It avoids the need for elementsToInsert.reverse() for afterbegin and afterend
* 3) It removes an element from its original position before adding it back, which is
* especially useful for elements not part of the DOM tree. That means it's important even
* when elementsToInsertLen == 1.
*/
var fragment = document.createDocumentFragment()
for (var i = 0; i < elementsToInsertLen; i++) {
var element = elementsToInsert[i]
var elementToInsert
if (index) {
elementToInsert = element.cloneNode(true)
duplicateEventListeners(element, elementToInsert)
}
else {
elementToInsert = element
}
fragment.appendChild(elementToInsert)
clonedElements.push(elementToInsert)
}
domMethods[position].call(this, fragment)
})
if (isSprintObj) {
content.dom = clonedElements
content.length = clonedElements.length
}
if (i < argsLen-1) continue
return clonedElements
}
}
}
var inArray = function(el, arr) {
var i = arr.length
while (i--) {
if (arr[i] === el) return true
}
return false
}
var isNamespaced = function(event) {
return /\./.test(event)
}
var manipulateClass = function(method, className, bool) {
if (className == null) {
if (method == "add") {
return this
}
return this.removeAttr("class")
}
var isString
var classNames
var classNamesLen
if (typeof className == "string") {
isString = true
classNames = className.trim().split(" ")
classNamesLen = classNames.length
}
return this.each(function(i, el) {
if (this.nodeType > 1) return
if (!isString) {
// className is a function
var callbackValue = className.call(el, i, el.className)
if (!callbackValue) return
classNames = callbackValue.trim().split(" ")
classNamesLen = classNames.length
}
for (var j = 0; j < classNamesLen; j++) {
var name = classNames[j]
if (!name) continue
bool == null
? el.classList[method](name)
: el.classList.toggle(name, bool)
}
})
}
var matches = (function() {
var names = [
"mozMatchesSelector",
"webkitMatchesSelector",
"msMatchesSelector",
"matches"
]
var i = names.length
while (i--) {
var name = names[i]
if (!Element.prototype[name]) continue
return name
}
}())
var removeDuplicates = function(arr) {
var clean = []
var cleanLen = 0
var arrLen = arr.length
for (var i = 0; i < arrLen; i++) {
var el = arr[i]
var duplicate = false
for (var j = 0; j < cleanLen; j++) {
if (el !== clean[j]) continue
duplicate = true
break
}
if (duplicate) continue
clean[cleanLen++] = el
}
return clean
}
var removeEvent = (function() {
var isHandlerShared = function(el, event, registeredHandler) {
var similarEventsHandlers = Object.keys(getEvents(el)).filter(function(prop) {
return getEventFromNamespace(event) === getEventFromNamespace(prop)
}).map(function(ev) {
return getEvents(el)[ev]
}).reduce(function(a, b) {
return a.concat(b)
}).filter(function(handler) {
return handler === registeredHandler
})
if (similarEventsHandlers.length < 2) return false
return true
}
var removeListener = function(el, event, namedHandler) {
return function(registeredHandler) {
if (namedHandler && namedHandler !== registeredHandler) return
el.removeEventListener(event, registeredHandler)
if (!isNamespaced(event) || isHandlerShared(el, event, registeredHandler)) return
el.removeEventListener(getEventFromNamespace(event), registeredHandler)
}
}
var clearRegisteredHandlers = function(registeredHandlers, namedHandler) {
return registeredHandlers.filter(function(handler) {
return namedHandler && namedHandler !== handler
})
}
return function(el, namedHandler) {
return function(event) {
getEvents(el)[event].forEach(removeListener(el, event, namedHandler))
getEvents(el)[event] = clearRegisteredHandlers(getEvents(el)[event], namedHandler)
}
}
}())
var removeMatchedEvents = function(el, namedHandler) {
return function(event) {
getEventsToRemove(el, event).forEach(removeEvent(el, namedHandler))
}
}
var root = document.documentElement
var sanitize = function(arr, flattenObjects, requireDomNodes) {
/*
* Remove null's from array. Optionally, flatten Sprint objects and convert strings and numbers
* to DOM text nodes.
*/
var arrLen = arr.length
var i = arrLen
// Check if arr needs to be sanitized first (significant perf boost for the most common case)
while (i--) {
// arr needs to be sanitized
if ( (!arr[i] && arr[i] !== 0)
|| (flattenObjects && arr[i] instanceof Init)
|| (requireDomNodes && (typeof arr[i] == "string" || typeof arr[i] == "number"))
) {
var sanitized = []
for (var j = 0; j < arrLen; j++) {
var el = arr[j]
if (!el && el !== 0) continue
if (flattenObjects && el instanceof Init) {
for (var k = 0; k < el.length; k++) {
sanitized.push(el.get(k))
}
continue
}
if (requireDomNodes && (typeof el == "string" || typeof el == "number")) {
sanitized.push(document.createTextNode(el))
continue
}
sanitized.push(el)
}
return sanitized
}
}
// arr didn't need to be sanitized, return it
return arr
}
var scroll = (function() {
var scrollRoot
return function(sprintObj, method, value) {
// define scroll root element on first run
if (!scrollRoot) {
var initialScrollPos = root.scrollTop
root.scrollTop = initialScrollPos + 1
var updatedScrollPos = root.scrollTop
root.scrollTop = initialScrollPos
scrollRoot = updatedScrollPos > initialScrollPos
? root // spec-compliant browsers (like FF34 and IE11)
: document.body // naughty boys (like Chrome 39 and Safari 8)
}
// get scroll position
if (value == null) {
var el = sprintObj.get(0)
if (!el) return
if (el == window || el == document) {
el = scrollRoot
}
return el[method]
}
// set scroll position
return sprintObj.each(function() {
var el = this
if (el == window || el == document) {
el = scrollRoot
}
el[method] = value
})
}
}())
var selectAdjacentSiblings = function(sprintObj, direction, selector, until) {
var dom = []
var prop = direction + "ElementSibling"
sprintObj.each(function() {
var el = this
while (el = el[prop]) {
if (until && sprintObj.is(until, el)) break
if (selector && !sprintObj.is(selector, el)) continue
dom.push(el)
}
})
return Sprint(removeDuplicates(dom))
}
var selectImmediateAdjacentSibling = function(sprintObj, direction, selector) {
var prop = direction + "ElementSibling"
return sprintObj.map(function() {
var el = this[prop]
if (!el || (selector && !sprintObj.is(selector, el))) return
return el
}, false)
}
var selectElements = function(selector, context) {
context = context || document
// class, id, tag name or universal selector
if (/^[\#.]?[\w-]+$/.test(selector)) {
var firstChar = selector[0]
if (firstChar == ".") {
return toArray(context.getElementsByClassName(selector.slice(1)))
}
if (firstChar == "#") {
var el = context.getElementById(selector.slice(1))
return el ? [el] : []
}
if (selector == "body") {
return [document.body]
}
return toArray(context.getElementsByTagName(selector))
}
return toArray(context.querySelectorAll(selector))
}
var splitNamespaces = function(event) {
return sanitize(event.split("."))
}
var toArray = function(obj) {
var arr = []
var i = obj.length
while (i--) {
arr[i] = obj[i]
}
return arr
}
var wrap = (function() {
var callback = function(wrappingElement, variant) {
var wrap = Sprint(wrappingElement).clone(true).get(0)
var innerWrap = wrap
if (!wrap || this.nodeType > 1) return
while (innerWrap.firstChild) {
innerWrap = innerWrap.firstChild
}
if (variant == "inner") {
while (this.firstChild) {
innerWrap.appendChild(this.firstChild)
}
this.appendChild(wrap)
}
else {
var el = variant == "all" ? this.get(0) : this
var prt = el.parentNode
var next = el.nextSibling
variant == "all"
? this.each(function() { innerWrap.appendChild(this) })
: innerWrap.appendChild(el)
prt.insertBefore(wrap, next)
}
}
return function(wrappingElement, variant) {
if (typeof wrappingElement == "function") {
this.each(function(i) {
Sprint(this)[variant == "inner" ? "wrapInner" : "wrap"](wrappingElement.call(this, i))
})
}
else {
variant == "all"
? callback.call(this, wrappingElement, variant)
: this.each(function() { callback.call(this, wrappingElement, variant) })
}
return this
}
}())
var wrapMap = {
legend: {
intro: "