/ajax/libs/chibi/1.1.5/chibi.js
JavaScript | 777 lines | 563 code | 142 blank | 72 comment | 149 complexity | 151dd032a39709858adee24a57296ca3 MD5 | raw file
- /*!chibi 1.1.5, Copyright 2012-2015 Kyle Barrow, released under MIT license */
- (function () {
- 'use strict';
- var readyfn = [],
- loadedfn = [],
- domready = false,
- pageloaded = false,
- jsonpcount = 0,
- d = document,
- w = window;
- // Fire any function calls on ready event
- function fireReady() {
- var i;
- domready = true;
- for (i = 0; i < readyfn.length; i += 1) {
- readyfn[i]();
- }
- readyfn = [];
- }
- // Fire any function calls on loaded event
- function fireLoaded() {
- var i;
- pageloaded = true;
- // For browsers with no DOM loaded support
- if (!domready) {
- fireReady();
- }
- for (i = 0; i < loadedfn.length; i += 1) {
- loadedfn[i]();
- }
- loadedfn = [];
- }
- // Check DOM ready, page loaded
- if (d.addEventListener) {
- // Standards
- d.addEventListener('DOMContentLoaded', fireReady, false);
- w.addEventListener('load', fireLoaded, false);
- } else if (d.attachEvent) {
- // IE
- d.attachEvent('onreadystatechange', fireReady);
- // IE < 9
- w.attachEvent('onload', fireLoaded);
- } else {
- // Anything else
- w.onload = fireLoaded;
- }
- // Utility functions
- // Loop through node array
- function nodeLoop(fn, nodes) {
- var i;
- // Good idea to walk up the DOM
- for (i = nodes.length - 1; i >= 0; i -= 1) {
- fn(nodes[i]);
- }
- }
- // Convert to camel case
- function cssCamel(property) {
- return property.replace(/-\w/g, function (result) {return result.charAt(1).toUpperCase(); });
- }
- // Get computed style
- function computeStyle(elm, property) {
- // IE, everything else or null
- return (elm.currentStyle) ? elm.currentStyle[cssCamel(property)] : (w.getComputedStyle) ? w.getComputedStyle(elm, null).getPropertyValue(property) : null;
- }
- // Returns URI encoded query string pair
- function queryPair(name, value) {
- return encodeURIComponent(name).replace(/%20/g, '+') + '=' + encodeURIComponent(value).replace(/%20/g, '+');
- }
- // Set CSS, important to wrap in try to prevent error thown on unsupported property
- function setCss(elm, property, value) {
- try {
- elm.style[cssCamel(property)] = value;
- } catch (e) {}
- }
- // Show CSS
- function showCss(elm) {
- elm.style.display = '';
- // For elements still hidden by style block
- if (computeStyle(elm, 'display') === 'none') {
- elm.style.display = 'block';
- }
- }
- // Handle standard method value returns
- function returnValues(values) {
- values = values.reverse();
- // Return string for singles
- if (values.length === 1) {
- values = values[0];
- }
- return values;
- }
- // Serialize form & JSON values
- function serializeData(nodes) {
- var querystring = '', subelm, i, j;
- if (nodes.constructor === Object) { // Serialize JSON data
- for (subelm in nodes) {
- if (nodes.hasOwnProperty(subelm)) {
- if (nodes[subelm].constructor === Array) {
- for (i = 0; i < nodes[subelm].length; i += 1) {
- querystring += '&' + queryPair(subelm, nodes[subelm][i]);
- }
- } else {
- querystring += '&' + queryPair(subelm, nodes[subelm]);
- }
- }
- }
- } else { // Serialize node data
- nodeLoop(function (elm) {
- if (elm.nodeName === 'FORM') {
- for (i = 0; i < elm.elements.length; i += 1) {
- subelm = elm.elements[i];
- if (!subelm.disabled) {
- switch (subelm.type) {
- // Ignore buttons, unsupported XHR 1 form fields
- case 'button':
- case 'image':
- case 'file':
- case 'submit':
- case 'reset':
- break;
- case 'select-one':
- if (subelm.length > 0) {
- querystring += '&' + queryPair(subelm.name, subelm.value);
- }
- break;
- case 'select-multiple':
- for (j = 0; j < subelm.length; j += 1) {
- if (subelm[j].selected) {
- querystring += '&' + queryPair(subelm.name, subelm[j].value);
- }
- }
- break;
- case 'checkbox':
- case 'radio':
- if (subelm.checked) {
- querystring += '&' + queryPair(subelm.name, subelm.value);
- }
- break;
- // Everything else including shinny new HTML5 input types
- default:
- querystring += '&' + queryPair(subelm.name, subelm.value);
- }
- }
- }
- }
- }, nodes);
- }
- // Tidy up first &
- return (querystring.length > 0) ? querystring.substring(1) : '';
- }
- // Get nodes and return chibi
- function chibi(selector) {
- var cb, nodes = [], json = false, nodelist, i;
- if (selector) {
- // Element node, would prefer to use (selector instanceof HTMLElement) but no IE support
- if (selector.nodeType && selector.nodeType === 1) {
- nodes = [selector]; // return element as node list
- } else if (typeof selector === 'object') {
- // JSON, document object or node list, would prefer to use (selector instanceof NodeList) but no IE support
- json = (typeof selector.length !== 'number');
- nodes = selector;
- } else if (typeof selector === 'string') {
- // A very light querySelectorAll polyfill for IE < 8. It suits my needs but is restricted to IE CSS support, is no speed demon, and does leave older mobile browsers in the cold (that support neither querySelectorAll nor currentStyle/getComputedStyle). If you want to use a fuller featured selector engine like Qwery, Sizzle et al, just return results to the nodes array: nodes = altselectorengine(selector)
- // IE < 8
- if (!d.querySelectorAll) {
- // Polyfill querySelectorAll
- d.querySelectorAll = function (selector) {
- var style, head = d.getElementsByTagName('head')[0], allnodes, selectednodes = [], i;
- style = d.createElement('STYLE');
- style.type = 'text/css';
- if (style.styleSheet) {
- style.styleSheet.cssText = selector + ' {a:b}';
- head.appendChild(style);
- allnodes = d.getElementsByTagName('*');
- for (i = 0; i < allnodes.length; i += 1) {
- if (computeStyle(allnodes[i], 'a') === 'b') {
- selectednodes.push(allnodes[i]);
- }
- }
- head.removeChild(style);
- }
- return selectednodes;
- };
- }
- nodelist = d.querySelectorAll(selector);
- // Convert node list to array so results have full access to array methods
- // Array.prototype.slice.call not supported in IE < 9 and often slower than loop anyway
- for (i = 0; i < nodelist.length; i += 1) {
- nodes[i] = nodelist[i];
- }
- }
- }
- // Only attach nodes if not JSON
- cb = json ? {} : nodes;
- // Public functions
- // Fire on DOM ready
- cb.ready = function (fn) {
- if (fn) {
- if (domready) {
- fn();
- } else {
- readyfn.push(fn);
- }
- }
- };
- // Fire on page loaded
- cb.loaded = function (fn) {
- if (fn) {
- if (pageloaded) {
- fn();
- } else {
- loadedfn.push(fn);
- }
- }
- };
- // Executes a function on nodes
- cb.loop = function (fn) {
- if (typeof fn === 'function') {
- nodeLoop(function (elm) {
- // <= IE 8 loses scope so need to apply
- return fn.apply(elm, arguments);
- }, nodes);
- }
- };
- // Find nodes
- cb.find = function (filter) {
- if (filter) {
- var temp = [], i;
- switch (filter) {
- case 'first':
- if (nodes.length > 0) {
- nodes = [nodes.shift()];
- }
- break;
- case 'last':
- if (nodes.length > 0) {
- nodes = [nodes.pop()];
- }
- break;
- case 'odd':
- case 'even':
- for (i = (filter === 'odd') ? 0 : 1; i < nodes.length; i += 2) {
- temp.push(nodes[i]);
- }
- nodes = temp;
- break;
- }
- }
- return (nodes.length > 0) ? (nodes.length === 1) ? nodes[0] : nodes : false;
- };
- // Hide node
- cb.hide = function () {
- nodeLoop(function (elm) {
- elm.style.display = 'none';
- }, nodes);
- };
- // Show node
- cb.show = function () {
- nodeLoop(function (elm) {
- showCss(elm);
- }, nodes);
- };
- // Toggle node display
- cb.toggle = function () {
- nodeLoop(function (elm) {
- // computeStyle instead of style.display == 'none' catches elements that are hidden via style block
- if (computeStyle(elm, 'display') === 'none') {
- showCss(elm);
- } else {
- elm.style.display = 'none';
- }
- }, nodes);
- };
- // Remove node
- cb.remove = function () {
- nodeLoop(function (elm) {
- // Catch error in unlikely case elm has been removed
- try {
- elm.parentNode.removeChild(elm);
- } catch (e) {}
- }, nodes);
- // Clear nodes after remove
- nodes = [];
- };
- // Get/Set CSS
- cb.css = function (property, value) {
- var values = [];
- nodeLoop(function (elm) {
- if (value) {
- setCss(elm, property, value);
- } else if (elm.style[cssCamel(property)]) {
- values.push(elm.style[cssCamel(property)]);
- } else if (computeStyle(elm, property)) {
- values.push(computeStyle(elm, property));
- } else {
- values.push(null);
- }
- }, nodes);
- // Get CSS property: return values
- if (values.length > 0) {
- return returnValues(values);
- }
- };
- // Get/Set/Add/Remove class
- cb.cls = function (classes, action) {
- var values = [], classarray, classname, search, has, i;
- if (classes) {
- // Trim any whitespace
- classarray = classes.split(/\s+/);
- action = action || 'replace';
- }
- nodeLoop(function (elm) {
- classname = elm.className;
- if (classes) {
- switch (action) {
- case 'add':
- elm.className = classname + ' ' + classes;
- break;
- case 'replace':
- elm.className = classes;
- break;
- case 'has':
- case 'toggle':
- case 'remove':
- has = true;
- for (i = 0; i < classarray.length; i += 1) {
- search = new RegExp('\\b' + classarray[i] + '\\b', 'g');
- if (action === 'has') {
- if (!classname.match(search)) {
- has = false;
- break;
- }
- } else if (action === 'toggle') {
- elm.className = (elm.className.match(search)) ? elm.className.replace(search, '') : elm.className + ' ' + classarray[i];
- } else { // replace
- elm.className = elm.className.replace(search, '');
- }
- }
- if (action === 'has') {
- values.push(has);
- }
- break;
- }
- elm.className = elm.className.replace(/^\s+|\s+$/g, '');
- } else {
- values.push(classname);
- }
- }, nodes);
- if (values.length > 0) {
- return returnValues(values);
- }
- };
- // Get/Set innerHTML optionally before/after
- cb.html = function (value, location) {
- var values = [], tmpnodes, tmpnode;
- nodeLoop(function (elm) {
- if (location) {
- // No insertAdjacentHTML support for FF < 8 and IE doesn't allow insertAdjacentHTML table manipulation, so use this instead
- // Convert string to node. We can't innerHTML on a document fragment
- tmpnodes = d.createElement('div');
- tmpnodes.innerHTML = value;
- while ((tmpnode = tmpnodes.lastChild) !== null) {
- // Catch error in unlikely case elm has been removed
- try {
- if (location === 'before') {
- elm.parentNode.insertBefore(tmpnode, elm);
- } else if (location === 'after') {
- elm.parentNode.insertBefore(tmpnode, elm.nextSibling);
- } else if (location === 'append') {
- elm.appendChild(tmpnode);
- } else if (location === 'prepend') {
- elm.insertBefore(tmpnode, elm.firstChild);
- }
- } catch (e) {break; }
- }
- } else {
- if (value) {
- elm.innerHTML = value;
- } else {
- values.push(elm.innerHTML);
- }
- }
- }, nodes);
- if (values.length > 0) {
- return returnValues(values);
- }
- };
- // Get/Set HTML attributes
- cb.attr = function (name, value) {
- var values = [];
- nodeLoop(function (elm) {
- if (name) {
- name = name.toLowerCase();
- switch (name) {
- // IE < 9 doesn't allow style or class via get/setAttribute so switch. cssText returns prettier CSS anyway
- case 'style':
- if (value) {
- elm.style.cssText = value;
- } else if (elm.style.cssText) {
- values.push(elm.style.cssText);
- } else {
- values.push(null);
- }
- break;
- case 'class':
- if (value) {
- elm.className = value;
- } else if (elm.className) {
- values.push(elm.className);
- } else {
- values.push(null);
- }
- break;
- default:
- if (value) {
- elm.setAttribute(name, value);
- } else if (elm.getAttribute(name)) {
- values.push(elm.getAttribute(name));
- } else {
- values.push(null);
- }
- }
- }
- }, nodes);
- if (values.length > 0) {
- return returnValues(values);
- }
- };
- // Get/Set form element values
- cb.val = function (replacement) {
- var radiogroup = [], values = [], i, j, grouped, active;
- if (typeof replacement === 'string') {
- replacement = [replacement];
- }
- nodeLoop(function (elm) {
- if (replacement) {
- switch (elm.nodeName) {
- case 'SELECT':
- for (i = 0; i < elm.length; i += 1) {
- // Multiple select
- for (j = 0; j < replacement.length; j += 1) {
- elm[i].selected = '';
- if (elm[i].value === replacement[j]) {
- elm[i].selected = 'selected';
- break;
- }
- }
- }
- break;
- case 'INPUT':
- switch (elm.type) {
- case 'checkbox':
- case 'radio':
- elm.checked = '';
- for (i = 0; i < replacement.length; i += 1) {
- if (elm.value === replacement[i]) {
- elm.checked = 'checked';
- break;
- }
- }
- break;
- default:
- elm.value = replacement[0];
- }
- break;
- case 'TEXTAREA':
- case 'BUTTON':
- elm.value = replacement[0];
- break;
- }
- } else {
- switch (elm.nodeName) {
- case 'SELECT':
- active = values.length;
- values.push([]);
- for (i = 0; i < elm.length; i += 1) {
- if (elm[i].selected) {
- values[active].push(elm[i].value);
- }
- }
- switch (values[active].length) {
- case 0:
- values[active] = null;
- break;
- case 1:
- values[active] = values[active][0];
- break;
- }
- break;
- case 'INPUT':
- switch (elm.type) {
- case 'checkbox':
- if (elm.checked) {
- values.push(elm.value);
- } else {
- values.push(null);
- }
- break;
- case 'radio':
- grouped = false;
- for (i = 0; i < radiogroup.length; i += 1) {
- if (radiogroup[i][0] === elm.name) {
- if (elm.checked) {
- values[radiogroup[i][1]] = elm.value;
- }
- grouped = true;
- }
- }
- if (!grouped) {
- radiogroup.push([elm.name, values.length]);
- if (elm.checked) {
- values.push(elm.value);
- } else {
- values.push(null);
- }
- }
- break;
- // Everything else including shinny new HTML5 input types
- default:
- values.push(elm.value);
- }
- break;
- case 'TEXTAREA':
- case 'BUTTON':
- values.push(elm.value);
- break;
- }
- }
- }, nodes);
- if (values.length > 0) {
- return returnValues(values);
- }
- };
- // Event handler
- cb.on = function (event, fn, clear) {
- if (selector === w || selector === d) {
- nodes = [selector];
- }
- nodeLoop(function (elm) {
- if (d.addEventListener) {
- if (clear) {
- elm.removeEventListener(event, fn, false);
- } else {
- elm.addEventListener(event, fn, false);
- }
- } else if (d.attachEvent) {
- if (clear) {
- elm.detachEvent('on' + event, elm[event + fn]);
- // Tidy up
- elm[event + fn] = null;
- } else {
- // <= IE 8 loses scope so need to apply, we add this to object so we can detach later (can't detach anonymous functions)
- elm[event + fn] = function () { return fn.apply(elm, arguments); };
- elm.attachEvent('on' + event, elm[event + fn]);
- }
- }
- }, nodes);
- };
- // Basic XHR 1, no file support. Shakes fist at IE
- cb.ajax = function (url, method, callback, nocache, nojsonp) {
- var xhr,
- query = serializeData(nodes),
- querystart = (url.indexOf('?') === -1) ? '?' : '&',
- hostsearch = new RegExp('http[s]?://(.*?)/', 'gi'),
- domain = hostsearch.exec(url),
- timestamp = '_ts=' + (+new Date()),
- head = d.getElementsByTagName('head')[0],
- jsonpcallback = 'chibi' + (+new Date()) + (jsonpcount += 1),
- script;
- // JSONP if cross domain url
- if (!nojsonp && domain && w.location.host !== domain[1]) {
- url += querystart + query;
- if (nocache) {
- url += '&' + timestamp;
- }
- // Replace possible encoded ?
- url = url.replace('=%3F', '=?');
- // Replace jsonp ? with callback
- if (callback && url.indexOf('=?') !== -1) {
- url = url.replace('=?', '=' + jsonpcallback);
- w[jsonpcallback] = function (data) {
- try {
- callback(data, 200);
- } catch (e) {}
- // Tidy up
- w[jsonpcallback] = undefined;
- };
- }
- // JSONP
- script = document.createElement('script');
- script.async = true;
- script.src = url;
- // Tidy up
- script.onload = function () {
- head.removeChild(script);
- };
- head.appendChild(script);
- } else {
- method = method || 'GET';
- if (w.XMLHttpRequest) {
- xhr = new XMLHttpRequest();
- } else if (w.ActiveXObject) {
- xhr = new ActiveXObject('Microsoft.XMLHTTP'); // IE < 9
- }
- if (xhr) {
- method = method.toUpperCase();
- if (method === 'GET') {
- url += querystart + query;
- query = null;
- }
- if (nocache) {
- url += (method === 'POST') ? querystart + timestamp : '&' + timestamp;
- }
- // Douglas Crockford: "Synchronous programming is disrespectful and should not be employed in applications which are used by people"
- xhr.open(method, url, true);
- xhr.onreadystatechange = function () {
- if (xhr.readyState === 4) {
- if (callback) {
- callback(xhr.responseText, xhr.status);
- }
- }
- };
- xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
- if (method === 'POST') {
- xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
- }
- xhr.send(query);
- }
- }
- };
- return cb;
- }
- // Set Chibi's global namespace here ($)
- w.$ = chibi;
- }());