PageRenderTime 51ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/ajax/libs/chibi/1.1.5/chibi.js

https://gitlab.com/Mirros/cdnjs
JavaScript | 777 lines | 563 code | 142 blank | 72 comment | 149 complexity | 151dd032a39709858adee24a57296ca3 MD5 | raw file
  1. /*!chibi 1.1.5, Copyright 2012-2015 Kyle Barrow, released under MIT license */
  2. (function () {
  3. 'use strict';
  4. var readyfn = [],
  5. loadedfn = [],
  6. domready = false,
  7. pageloaded = false,
  8. jsonpcount = 0,
  9. d = document,
  10. w = window;
  11. // Fire any function calls on ready event
  12. function fireReady() {
  13. var i;
  14. domready = true;
  15. for (i = 0; i < readyfn.length; i += 1) {
  16. readyfn[i]();
  17. }
  18. readyfn = [];
  19. }
  20. // Fire any function calls on loaded event
  21. function fireLoaded() {
  22. var i;
  23. pageloaded = true;
  24. // For browsers with no DOM loaded support
  25. if (!domready) {
  26. fireReady();
  27. }
  28. for (i = 0; i < loadedfn.length; i += 1) {
  29. loadedfn[i]();
  30. }
  31. loadedfn = [];
  32. }
  33. // Check DOM ready, page loaded
  34. if (d.addEventListener) {
  35. // Standards
  36. d.addEventListener('DOMContentLoaded', fireReady, false);
  37. w.addEventListener('load', fireLoaded, false);
  38. } else if (d.attachEvent) {
  39. // IE
  40. d.attachEvent('onreadystatechange', fireReady);
  41. // IE < 9
  42. w.attachEvent('onload', fireLoaded);
  43. } else {
  44. // Anything else
  45. w.onload = fireLoaded;
  46. }
  47. // Utility functions
  48. // Loop through node array
  49. function nodeLoop(fn, nodes) {
  50. var i;
  51. // Good idea to walk up the DOM
  52. for (i = nodes.length - 1; i >= 0; i -= 1) {
  53. fn(nodes[i]);
  54. }
  55. }
  56. // Convert to camel case
  57. function cssCamel(property) {
  58. return property.replace(/-\w/g, function (result) {return result.charAt(1).toUpperCase(); });
  59. }
  60. // Get computed style
  61. function computeStyle(elm, property) {
  62. // IE, everything else or null
  63. return (elm.currentStyle) ? elm.currentStyle[cssCamel(property)] : (w.getComputedStyle) ? w.getComputedStyle(elm, null).getPropertyValue(property) : null;
  64. }
  65. // Returns URI encoded query string pair
  66. function queryPair(name, value) {
  67. return encodeURIComponent(name).replace(/%20/g, '+') + '=' + encodeURIComponent(value).replace(/%20/g, '+');
  68. }
  69. // Set CSS, important to wrap in try to prevent error thown on unsupported property
  70. function setCss(elm, property, value) {
  71. try {
  72. elm.style[cssCamel(property)] = value;
  73. } catch (e) {}
  74. }
  75. // Show CSS
  76. function showCss(elm) {
  77. elm.style.display = '';
  78. // For elements still hidden by style block
  79. if (computeStyle(elm, 'display') === 'none') {
  80. elm.style.display = 'block';
  81. }
  82. }
  83. // Handle standard method value returns
  84. function returnValues(values) {
  85. values = values.reverse();
  86. // Return string for singles
  87. if (values.length === 1) {
  88. values = values[0];
  89. }
  90. return values;
  91. }
  92. // Serialize form & JSON values
  93. function serializeData(nodes) {
  94. var querystring = '', subelm, i, j;
  95. if (nodes.constructor === Object) { // Serialize JSON data
  96. for (subelm in nodes) {
  97. if (nodes.hasOwnProperty(subelm)) {
  98. if (nodes[subelm].constructor === Array) {
  99. for (i = 0; i < nodes[subelm].length; i += 1) {
  100. querystring += '&' + queryPair(subelm, nodes[subelm][i]);
  101. }
  102. } else {
  103. querystring += '&' + queryPair(subelm, nodes[subelm]);
  104. }
  105. }
  106. }
  107. } else { // Serialize node data
  108. nodeLoop(function (elm) {
  109. if (elm.nodeName === 'FORM') {
  110. for (i = 0; i < elm.elements.length; i += 1) {
  111. subelm = elm.elements[i];
  112. if (!subelm.disabled) {
  113. switch (subelm.type) {
  114. // Ignore buttons, unsupported XHR 1 form fields
  115. case 'button':
  116. case 'image':
  117. case 'file':
  118. case 'submit':
  119. case 'reset':
  120. break;
  121. case 'select-one':
  122. if (subelm.length > 0) {
  123. querystring += '&' + queryPair(subelm.name, subelm.value);
  124. }
  125. break;
  126. case 'select-multiple':
  127. for (j = 0; j < subelm.length; j += 1) {
  128. if (subelm[j].selected) {
  129. querystring += '&' + queryPair(subelm.name, subelm[j].value);
  130. }
  131. }
  132. break;
  133. case 'checkbox':
  134. case 'radio':
  135. if (subelm.checked) {
  136. querystring += '&' + queryPair(subelm.name, subelm.value);
  137. }
  138. break;
  139. // Everything else including shinny new HTML5 input types
  140. default:
  141. querystring += '&' + queryPair(subelm.name, subelm.value);
  142. }
  143. }
  144. }
  145. }
  146. }, nodes);
  147. }
  148. // Tidy up first &
  149. return (querystring.length > 0) ? querystring.substring(1) : '';
  150. }
  151. // Get nodes and return chibi
  152. function chibi(selector) {
  153. var cb, nodes = [], json = false, nodelist, i;
  154. if (selector) {
  155. // Element node, would prefer to use (selector instanceof HTMLElement) but no IE support
  156. if (selector.nodeType && selector.nodeType === 1) {
  157. nodes = [selector]; // return element as node list
  158. } else if (typeof selector === 'object') {
  159. // JSON, document object or node list, would prefer to use (selector instanceof NodeList) but no IE support
  160. json = (typeof selector.length !== 'number');
  161. nodes = selector;
  162. } else if (typeof selector === 'string') {
  163. // 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)
  164. // IE < 8
  165. if (!d.querySelectorAll) {
  166. // Polyfill querySelectorAll
  167. d.querySelectorAll = function (selector) {
  168. var style, head = d.getElementsByTagName('head')[0], allnodes, selectednodes = [], i;
  169. style = d.createElement('STYLE');
  170. style.type = 'text/css';
  171. if (style.styleSheet) {
  172. style.styleSheet.cssText = selector + ' {a:b}';
  173. head.appendChild(style);
  174. allnodes = d.getElementsByTagName('*');
  175. for (i = 0; i < allnodes.length; i += 1) {
  176. if (computeStyle(allnodes[i], 'a') === 'b') {
  177. selectednodes.push(allnodes[i]);
  178. }
  179. }
  180. head.removeChild(style);
  181. }
  182. return selectednodes;
  183. };
  184. }
  185. nodelist = d.querySelectorAll(selector);
  186. // Convert node list to array so results have full access to array methods
  187. // Array.prototype.slice.call not supported in IE < 9 and often slower than loop anyway
  188. for (i = 0; i < nodelist.length; i += 1) {
  189. nodes[i] = nodelist[i];
  190. }
  191. }
  192. }
  193. // Only attach nodes if not JSON
  194. cb = json ? {} : nodes;
  195. // Public functions
  196. // Fire on DOM ready
  197. cb.ready = function (fn) {
  198. if (fn) {
  199. if (domready) {
  200. fn();
  201. } else {
  202. readyfn.push(fn);
  203. }
  204. }
  205. };
  206. // Fire on page loaded
  207. cb.loaded = function (fn) {
  208. if (fn) {
  209. if (pageloaded) {
  210. fn();
  211. } else {
  212. loadedfn.push(fn);
  213. }
  214. }
  215. };
  216. // Executes a function on nodes
  217. cb.loop = function (fn) {
  218. if (typeof fn === 'function') {
  219. nodeLoop(function (elm) {
  220. // <= IE 8 loses scope so need to apply
  221. return fn.apply(elm, arguments);
  222. }, nodes);
  223. }
  224. };
  225. // Find nodes
  226. cb.find = function (filter) {
  227. if (filter) {
  228. var temp = [], i;
  229. switch (filter) {
  230. case 'first':
  231. if (nodes.length > 0) {
  232. nodes = [nodes.shift()];
  233. }
  234. break;
  235. case 'last':
  236. if (nodes.length > 0) {
  237. nodes = [nodes.pop()];
  238. }
  239. break;
  240. case 'odd':
  241. case 'even':
  242. for (i = (filter === 'odd') ? 0 : 1; i < nodes.length; i += 2) {
  243. temp.push(nodes[i]);
  244. }
  245. nodes = temp;
  246. break;
  247. }
  248. }
  249. return (nodes.length > 0) ? (nodes.length === 1) ? nodes[0] : nodes : false;
  250. };
  251. // Hide node
  252. cb.hide = function () {
  253. nodeLoop(function (elm) {
  254. elm.style.display = 'none';
  255. }, nodes);
  256. };
  257. // Show node
  258. cb.show = function () {
  259. nodeLoop(function (elm) {
  260. showCss(elm);
  261. }, nodes);
  262. };
  263. // Toggle node display
  264. cb.toggle = function () {
  265. nodeLoop(function (elm) {
  266. // computeStyle instead of style.display == 'none' catches elements that are hidden via style block
  267. if (computeStyle(elm, 'display') === 'none') {
  268. showCss(elm);
  269. } else {
  270. elm.style.display = 'none';
  271. }
  272. }, nodes);
  273. };
  274. // Remove node
  275. cb.remove = function () {
  276. nodeLoop(function (elm) {
  277. // Catch error in unlikely case elm has been removed
  278. try {
  279. elm.parentNode.removeChild(elm);
  280. } catch (e) {}
  281. }, nodes);
  282. // Clear nodes after remove
  283. nodes = [];
  284. };
  285. // Get/Set CSS
  286. cb.css = function (property, value) {
  287. var values = [];
  288. nodeLoop(function (elm) {
  289. if (value) {
  290. setCss(elm, property, value);
  291. } else if (elm.style[cssCamel(property)]) {
  292. values.push(elm.style[cssCamel(property)]);
  293. } else if (computeStyle(elm, property)) {
  294. values.push(computeStyle(elm, property));
  295. } else {
  296. values.push(null);
  297. }
  298. }, nodes);
  299. // Get CSS property: return values
  300. if (values.length > 0) {
  301. return returnValues(values);
  302. }
  303. };
  304. // Get/Set/Add/Remove class
  305. cb.cls = function (classes, action) {
  306. var values = [], classarray, classname, search, has, i;
  307. if (classes) {
  308. // Trim any whitespace
  309. classarray = classes.split(/\s+/);
  310. action = action || 'replace';
  311. }
  312. nodeLoop(function (elm) {
  313. classname = elm.className;
  314. if (classes) {
  315. switch (action) {
  316. case 'add':
  317. elm.className = classname + ' ' + classes;
  318. break;
  319. case 'replace':
  320. elm.className = classes;
  321. break;
  322. case 'has':
  323. case 'toggle':
  324. case 'remove':
  325. has = true;
  326. for (i = 0; i < classarray.length; i += 1) {
  327. search = new RegExp('\\b' + classarray[i] + '\\b', 'g');
  328. if (action === 'has') {
  329. if (!classname.match(search)) {
  330. has = false;
  331. break;
  332. }
  333. } else if (action === 'toggle') {
  334. elm.className = (elm.className.match(search)) ? elm.className.replace(search, '') : elm.className + ' ' + classarray[i];
  335. } else { // replace
  336. elm.className = elm.className.replace(search, '');
  337. }
  338. }
  339. if (action === 'has') {
  340. values.push(has);
  341. }
  342. break;
  343. }
  344. elm.className = elm.className.replace(/^\s+|\s+$/g, '');
  345. } else {
  346. values.push(classname);
  347. }
  348. }, nodes);
  349. if (values.length > 0) {
  350. return returnValues(values);
  351. }
  352. };
  353. // Get/Set innerHTML optionally before/after
  354. cb.html = function (value, location) {
  355. var values = [], tmpnodes, tmpnode;
  356. nodeLoop(function (elm) {
  357. if (location) {
  358. // No insertAdjacentHTML support for FF < 8 and IE doesn't allow insertAdjacentHTML table manipulation, so use this instead
  359. // Convert string to node. We can't innerHTML on a document fragment
  360. tmpnodes = d.createElement('div');
  361. tmpnodes.innerHTML = value;
  362. while ((tmpnode = tmpnodes.lastChild) !== null) {
  363. // Catch error in unlikely case elm has been removed
  364. try {
  365. if (location === 'before') {
  366. elm.parentNode.insertBefore(tmpnode, elm);
  367. } else if (location === 'after') {
  368. elm.parentNode.insertBefore(tmpnode, elm.nextSibling);
  369. } else if (location === 'append') {
  370. elm.appendChild(tmpnode);
  371. } else if (location === 'prepend') {
  372. elm.insertBefore(tmpnode, elm.firstChild);
  373. }
  374. } catch (e) {break; }
  375. }
  376. } else {
  377. if (value) {
  378. elm.innerHTML = value;
  379. } else {
  380. values.push(elm.innerHTML);
  381. }
  382. }
  383. }, nodes);
  384. if (values.length > 0) {
  385. return returnValues(values);
  386. }
  387. };
  388. // Get/Set HTML attributes
  389. cb.attr = function (name, value) {
  390. var values = [];
  391. nodeLoop(function (elm) {
  392. if (name) {
  393. name = name.toLowerCase();
  394. switch (name) {
  395. // IE < 9 doesn't allow style or class via get/setAttribute so switch. cssText returns prettier CSS anyway
  396. case 'style':
  397. if (value) {
  398. elm.style.cssText = value;
  399. } else if (elm.style.cssText) {
  400. values.push(elm.style.cssText);
  401. } else {
  402. values.push(null);
  403. }
  404. break;
  405. case 'class':
  406. if (value) {
  407. elm.className = value;
  408. } else if (elm.className) {
  409. values.push(elm.className);
  410. } else {
  411. values.push(null);
  412. }
  413. break;
  414. default:
  415. if (value) {
  416. elm.setAttribute(name, value);
  417. } else if (elm.getAttribute(name)) {
  418. values.push(elm.getAttribute(name));
  419. } else {
  420. values.push(null);
  421. }
  422. }
  423. }
  424. }, nodes);
  425. if (values.length > 0) {
  426. return returnValues(values);
  427. }
  428. };
  429. // Get/Set form element values
  430. cb.val = function (replacement) {
  431. var radiogroup = [], values = [], i, j, grouped, active;
  432. if (typeof replacement === 'string') {
  433. replacement = [replacement];
  434. }
  435. nodeLoop(function (elm) {
  436. if (replacement) {
  437. switch (elm.nodeName) {
  438. case 'SELECT':
  439. for (i = 0; i < elm.length; i += 1) {
  440. // Multiple select
  441. for (j = 0; j < replacement.length; j += 1) {
  442. elm[i].selected = '';
  443. if (elm[i].value === replacement[j]) {
  444. elm[i].selected = 'selected';
  445. break;
  446. }
  447. }
  448. }
  449. break;
  450. case 'INPUT':
  451. switch (elm.type) {
  452. case 'checkbox':
  453. case 'radio':
  454. elm.checked = '';
  455. for (i = 0; i < replacement.length; i += 1) {
  456. if (elm.value === replacement[i]) {
  457. elm.checked = 'checked';
  458. break;
  459. }
  460. }
  461. break;
  462. default:
  463. elm.value = replacement[0];
  464. }
  465. break;
  466. case 'TEXTAREA':
  467. case 'BUTTON':
  468. elm.value = replacement[0];
  469. break;
  470. }
  471. } else {
  472. switch (elm.nodeName) {
  473. case 'SELECT':
  474. active = values.length;
  475. values.push([]);
  476. for (i = 0; i < elm.length; i += 1) {
  477. if (elm[i].selected) {
  478. values[active].push(elm[i].value);
  479. }
  480. }
  481. switch (values[active].length) {
  482. case 0:
  483. values[active] = null;
  484. break;
  485. case 1:
  486. values[active] = values[active][0];
  487. break;
  488. }
  489. break;
  490. case 'INPUT':
  491. switch (elm.type) {
  492. case 'checkbox':
  493. if (elm.checked) {
  494. values.push(elm.value);
  495. } else {
  496. values.push(null);
  497. }
  498. break;
  499. case 'radio':
  500. grouped = false;
  501. for (i = 0; i < radiogroup.length; i += 1) {
  502. if (radiogroup[i][0] === elm.name) {
  503. if (elm.checked) {
  504. values[radiogroup[i][1]] = elm.value;
  505. }
  506. grouped = true;
  507. }
  508. }
  509. if (!grouped) {
  510. radiogroup.push([elm.name, values.length]);
  511. if (elm.checked) {
  512. values.push(elm.value);
  513. } else {
  514. values.push(null);
  515. }
  516. }
  517. break;
  518. // Everything else including shinny new HTML5 input types
  519. default:
  520. values.push(elm.value);
  521. }
  522. break;
  523. case 'TEXTAREA':
  524. case 'BUTTON':
  525. values.push(elm.value);
  526. break;
  527. }
  528. }
  529. }, nodes);
  530. if (values.length > 0) {
  531. return returnValues(values);
  532. }
  533. };
  534. // Event handler
  535. cb.on = function (event, fn, clear) {
  536. if (selector === w || selector === d) {
  537. nodes = [selector];
  538. }
  539. nodeLoop(function (elm) {
  540. if (d.addEventListener) {
  541. if (clear) {
  542. elm.removeEventListener(event, fn, false);
  543. } else {
  544. elm.addEventListener(event, fn, false);
  545. }
  546. } else if (d.attachEvent) {
  547. if (clear) {
  548. elm.detachEvent('on' + event, elm[event + fn]);
  549. // Tidy up
  550. elm[event + fn] = null;
  551. } else {
  552. // <= IE 8 loses scope so need to apply, we add this to object so we can detach later (can't detach anonymous functions)
  553. elm[event + fn] = function () { return fn.apply(elm, arguments); };
  554. elm.attachEvent('on' + event, elm[event + fn]);
  555. }
  556. }
  557. }, nodes);
  558. };
  559. // Basic XHR 1, no file support. Shakes fist at IE
  560. cb.ajax = function (url, method, callback, nocache, nojsonp) {
  561. var xhr,
  562. query = serializeData(nodes),
  563. querystart = (url.indexOf('?') === -1) ? '?' : '&',
  564. hostsearch = new RegExp('http[s]?://(.*?)/', 'gi'),
  565. domain = hostsearch.exec(url),
  566. timestamp = '_ts=' + (+new Date()),
  567. head = d.getElementsByTagName('head')[0],
  568. jsonpcallback = 'chibi' + (+new Date()) + (jsonpcount += 1),
  569. script;
  570. // JSONP if cross domain url
  571. if (!nojsonp && domain && w.location.host !== domain[1]) {
  572. url += querystart + query;
  573. if (nocache) {
  574. url += '&' + timestamp;
  575. }
  576. // Replace possible encoded ?
  577. url = url.replace('=%3F', '=?');
  578. // Replace jsonp ? with callback
  579. if (callback && url.indexOf('=?') !== -1) {
  580. url = url.replace('=?', '=' + jsonpcallback);
  581. w[jsonpcallback] = function (data) {
  582. try {
  583. callback(data, 200);
  584. } catch (e) {}
  585. // Tidy up
  586. w[jsonpcallback] = undefined;
  587. };
  588. }
  589. // JSONP
  590. script = document.createElement('script');
  591. script.async = true;
  592. script.src = url;
  593. // Tidy up
  594. script.onload = function () {
  595. head.removeChild(script);
  596. };
  597. head.appendChild(script);
  598. } else {
  599. method = method || 'GET';
  600. if (w.XMLHttpRequest) {
  601. xhr = new XMLHttpRequest();
  602. } else if (w.ActiveXObject) {
  603. xhr = new ActiveXObject('Microsoft.XMLHTTP'); // IE < 9
  604. }
  605. if (xhr) {
  606. method = method.toUpperCase();
  607. if (method === 'GET') {
  608. url += querystart + query;
  609. query = null;
  610. }
  611. if (nocache) {
  612. url += (method === 'POST') ? querystart + timestamp : '&' + timestamp;
  613. }
  614. // Douglas Crockford: "Synchronous programming is disrespectful and should not be employed in applications which are used by people"
  615. xhr.open(method, url, true);
  616. xhr.onreadystatechange = function () {
  617. if (xhr.readyState === 4) {
  618. if (callback) {
  619. callback(xhr.responseText, xhr.status);
  620. }
  621. }
  622. };
  623. xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
  624. if (method === 'POST') {
  625. xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
  626. }
  627. xhr.send(query);
  628. }
  629. }
  630. };
  631. return cb;
  632. }
  633. // Set Chibi's global namespace here ($)
  634. w.$ = chibi;
  635. }());