0/*!1 * kimbo v1.0.4 - 2013-12-112 * http://kimbojs.com3 * Copyright (c) 2013 Denis Ciccale (@tdecs)4 * Released under the MIT license5 * https://github.com/dciccale/kimbo.js/blob/master/LICENSE.txt6 */7(function (window, document) {89 'use strict';1011 /*\12 * $13 [ object ]14 * Global namespace for using Kimbo functions15 \*/1617 // Kimbo modules18 var modules = {};1920 // Common helpers21 var _ = {2223 // Array methods24 push: Array.prototype.push,25 slice: Array.prototype.slice,2627 // Reference to the current document28 document: document,29 rootContext: document,3031 // Creates and returns a new Kimbo object32 kimbo: function (element) {33 return new Kimbo(element);34 }35 };3637 /*\38 * $(…)39 * Kimbo object collection.40 * All methods called from a Kimbo collection affects all elements in it.41 \*/42 var Kimbo = function (selector, context) {43 var match;4445 // Auto create a new instance of Kimbo if needed46 if (!(this instanceof Kimbo)) {47 return new Kimbo(selector, context);48 }4950 // No selector, return empty Kimbo object51 if (!selector) {52 return this;53 }5455 // Asume a css selector, query the dom56 if (typeof selector === 'string') {5758 // Handle faster $('#id');59 match = /^#([\w\-]+)$/.exec(selector);60 if (match && match[1]) {61 match = document.getElementById(match[1]);6263 if (match) {64 this[0] = match;65 this.length = 1;66 }6768 return this;69 }7071 // All other selectors72 context = context ? _.kimbo(context) : _.rootContext;7374 return context.find(selector);75 }7677 // Already a dom element78 if (selector.nodeType) {79 this[0] = selector;80 this.length = 1;81 return this;82 }8384 // Is a function, call it when DOM is ready85 if (Kimbo.isFunction(selector)) {86 return _.rootContext.ready(selector);87 }8889 // Handle kimbo object, plain objects or other objects90 return Kimbo.makeArray(selector, this);91 };9293 Kimbo.require = function (module) {94 return modules[module];95 };9697 Kimbo.define = function (module, fn) {98 modules[module] = fn(_);99 };100101 /*102 * Kimbo prototype aliased as fn103 */104 Kimbo.prototype = Kimbo.fn = {105106 /*\107 * $(…).length108 [ property ]109 * The length of the current Kimbo collection.110 = (number) Integer representing the lenght of the current collection.111 > Usage112 * Having these paragraphs:113 | <p>one</p>114 | <p>two</p>115 | <p>three</p>116 * Grab them and check for the length property:117 | $('p').length; // 3118 \*/119 length: 0,120121 /*\122 * $(…).ready123 [ method ]124 * Execute a callback after de DOM is completely loaded125 > Parameters126 - callback (function) A function to call after de DOM is ready127 = (object) The original element128 > Usage129 | $(document).ready(function () {130 | console.log('the DOM is loaded!');131 | });132 * Or using the shortcut (recommended)133 | $(function () {134 | console.log('the DOM is loaded!);135 | });136 \*/137 ready: function (callback) {138 var completed;139140 // First check if already loaded, interactive or complete state so the t is enough141 if (/t/.test(document.readyState)) {142143 // Execute the callback144 callback.call(document);145146 // If not listen for when it loads147 } else {148 completed = function () {149150 // When completed remove the listener151 document.removeEventListener('DOMContentLoaded', completed, false);152153 // Execute the callback154 callback.call(document);155 };156157 // Register the event158 document.addEventListener('DOMContentLoaded', completed, false);159 }160161 // Return the Kimbo wrapped document162 return _.rootContext;163 },164165 /*\166 * $(…).get167 [ method ]168 * Retrieve native DOM elements from the current collection169 > Parameters170 - index (number) #optional A zero-based integer indicating which element to retrieve, supports going backwards with negative index.171 = (array|object) An array of the native DOM elements or the specified element by index matched by Kimbo.172 > Usage173 | <ul>174 | <li id="foo"></li>175 | <li id="bar"></li>176 | </ul>177 * Get standard array of elements178 | $('li').get(); // [<li id="foo">, <li id="bar">]179 * Get the first element180 | $('li').get(0); // <li id="foo">181 * Get the last element182 | $('li').get(-1); // <li id="bar">183 \*/184 get: function (index) {185 if (!this.length) {186 return;187 }188189 // If no index specified return a new set190 // Else return the element in the specified positive index or backwards if negative191 return (!arguments.length) ? _.slice.call(this) :192 (index < 0 ? this[this.length + index] : this[index]);193 },194195 // Needed to have an array-like object196 splice: Array.prototype.splice197 };198199 /*\200 * $.forEach201 [ method ]202 * Iterator function that can be used to seamlessly iterate over both objects and arrays.203 > Parameters204 - obj (object) Object or array to iterate205 - callback (function) Function that will be executed on each iteration206 = (object) The original array or object207 > Usage208 | // Iterating array209 | $.forEach(['a', 'b', 'c'], function (index, value) {210 | alert(index + ': ' + value);211 | });212 |213 | // Iterating object214 | $.forEach({name: 'Denis', surname: 'Ciccale'}, function (key, value) {215 | alert(key + ': ' + value);216 | });217 \*/218 Kimbo.forEach = function (obj, callback) {219 var l = obj.length;220 var isObj = l === undefined || typeof obj === 'function';221 var i;222223 if (isObj) {224 for (i in obj) {225 if (obj.hasOwnProperty(i) && callback.call(obj[i], i, obj[i], obj) === false) {226 break;227 }228 }229 } else {230 for (i = 0; i < l; i++) {231 if (callback.call(obj[i], obj[i], i, obj) === false) {232 break;233 }234 }235 }236237 // Return original obj238 return obj;239 };240241242 /*\243 * $.extend244 [ method ]245 * Merge the contents of two or more objects together into the first object.246 > Parameters247 - target (object|boolean) Object that will receive the merged properties if objects are passed, or target will extend Kimbo object. If target is `true` the passed objects will be recursively merged.248 - objectN (object) #optional One or more objects with additional properties.249 = (object) The extended target or a new copy if target is an empty object.250 > Usage251 * When passing two or more objects, all properties will be merged into the target object.252 | var obj1 = { msg: 'hi', info: { from: 'Denis' }};253 | var obj2 = { msg: 'Hi!', info: { time: '22:00PM' }};254 |255 | // Merge obj1 into obj2256 | $.extend(obj1, obj2);257 |258 | // Now obj1 is equal to:259 | { msg: 'Hi!', info: { time: '22:00PM' }}260 * If an empty target object is passed, none of the other objects will be directly modified261 | // Pass an empty target262 | var obj3 = $.extend({}, obj1, obj);263 * To do a recursive merge, pass true as first argument, then the objects to merge264 | $.extend(true, obj1, obj2);265 | // Obj1 will be:266 | { msg: 'Hi!', info: { from: 'Denis', time: '22:00PM' }}267 \*/268 Kimbo.extend = Kimbo.fn.extend = function () {269 var objs = arguments;270 var target = objs[0] || {};271 var deep = (target === true);272 var cut = 1;273274 // Check for deep copy275 if (deep) {276 target = objs[1] || {};277 cut = 2;278279 // Extend Kimbo itself if only one argument is passed280 } else if (objs.length === 1) {281 target = this;282 cut = 0;283 }284285 // Make an array from the arguments removing the target and/or the deep boolean286 objs = _.slice.call(objs, cut);287288 // Loop through the objects289 Kimbo.forEach(objs, function (source) {290291 // Populate target from source292 Kimbo.forEach(source, function (key, value) {293 var src;294295 if (deep && (Kimbo.isObject(value) || Kimbo.isArray(value))) {296 src = target[key];297 target[key] = Kimbo.extend(deep, src, value);298299 } else if (value !== undefined) {300 target[key] = value;301 }302 });303 });304305 return target;306 };307308 // Unique reference for the current instance of Kimbo309 Kimbo.ref = 'kimbo' + ('1' + Math.random()).replace(/\D/g, '');310311 // Expose Kimbo as an AMD module312 if (typeof window.define === 'function' && window.define.amd) {313 window.define('kimbo', [], function () {314 return Kimbo;315 });316 }317318 // Expose Kimbo to global object319 window.Kimbo = window.$ = Kimbo;320321}(window, window.document));322323324Kimbo.define('query', function (_) {325326 'use strict';327328 // Selector regexes329 var ID_RE = /^#([\w\-]+)$/;330 var CLASS_RE = /^\.([\w\-]+)$/;331 var TAG_RE = /^[\w\-]+$/;332 var NAME_RE = /^\[name=["']?([\w\-]+)["']?\]$/;333334 // Use querySelectoAll but optimize for id, class, tagName and name335 var _find = function (element, selector) {336 var els = [], sel;337338 // #id339 if (element === _.document && (sel = ID_RE.exec(selector))) {340 els = element.getElementById(sel[1]);341342 // .class343 } else if ((sel = CLASS_RE.exec(selector))) {344 els = element.getElementsByClassName(sel[1]);345346 // tag347 } else if (TAG_RE.test(selector)) {348 els = element.getElementsByTagName(selector);349350 // [name=val]351 } else if ((sel = NAME_RE.exec(selector))) {352 els = element.getElementsByName(sel[1]);353354 // Other CSS selectors355 } else {356 els = element.querySelectorAll(selector);357 }358359 // Return NodeList/Node as an array360 return _.slice.call(els);361 };362363 // DOM Level 4 contains improved364 var _contains = function (el1, el2) {365 return (el1 === window && (el2 && (el2 === window || el2.nodeType))) ||366 ((el1 && el1.nodeType) &&367 (el2 && el2.nodeType)) ? (el1.contains ? el1.contains(el2) : true) : false;368 };369370 return {371 find: _find,372 contains: _contains373 };374});375376377Kimbo.define('data', function () {378379 'use strict';380381 var cache = {};382 var dataId = 1;383384 var data = {385 get: function (el, key) {386 var dataCache = cache[el._dataId];387 var value;388389 // Look first in cached data390 if (dataCache) {391 value = dataCache[key];392393 // If none, try dataset394 } else {395 value = el.dataset[key];396397 // Cache the value398 if (value) {399 this.set(el, key, value);400 }401 }402403 return value;404 },405406 set: function (el, key, value) {407 var elementId = el._dataId || (el._dataId = dataId++);408 var dataCache = cache[elementId];409410 // Create data cache for the current element if necessary411 if (!dataCache) {412 dataCache = cache[elementId] = {};413 }414415 dataCache[key] = value;416 },417418 remove: function (el, key) {419 if (key === undefined) {420 cache[el._dataId] = {};421 } else {422 delete cache[el._dataId][key];423 }424 }425 };426427 Kimbo.fn.extend({428 /*\429 * $(…).data430 [ method ]431 * Store or retrieve elements dataset.432 > Parameters433 - key (string) Key of the data attribute to to set.434 - value (string) #optional Value to store in dataset.435 = (object) Original matched collection.436 > Usage437 | <div id="panel"></div>438 * Set some data to the panel:439 | $('#panel').data('isOpen', true);440 * No a data-* attribute was added441 | <div id="panel" data-isOpen="true"></div>442 * We can retrieve the data443 | $('#panel').data('isOpen'); // 'true'444 \*/445 data: function (key, value) {446 if (!this.length || !Kimbo.isString(key)) {447 return this;448 }449450 key = Kimbo.camelCase(key);451452 // Get453 if (value === undefined) {454 return data.get(this[0], key);455456 // Set457 } else {458 return this.each(function (el) {459 data.set(el, key, value);460 });461 }462 },463464 /*\465 * $(…).removeData466 [ method ]467 * Remove data from the element dataset.468 > Parameters469 - key (string) Key of the data attribute to to remove.470 = (object) Original matched collection.471 > Usage472 | <div id="panel" data-isOpen="true"></div>473 * Remove data associated to the panel div:474 | $('#panel').removeData('isOpen');475 * Data attribute and value was removed:476 | <div id="panel"></div>477 * data-isOpen is undefined478 | $('#panel').data('isOpen'); // Undefined479 \*/480 removeData: function (key) {481 if (!this.length || !Kimbo.isString(key)) {482 return this;483 }484485 key = Kimbo.camelCase(key);486487 return this.each(function (el) {488 data.remove(el, key);489 });490 }491 });492493 return data;494});495496497Kimbo.define('css', function (_) {498499 'use strict';500501 // Properties without 'px' at the end502 var CSS_NO_PX = {503 fontWeight: true,504 lineHeight: true,505 opacity: true,506 zIndex: true507 };508509 // Wrap native to extend behavoiur510 var _getComputedStyle = function (element, property) {511512 // Support both camelCase and dashed property names513 property = property.replace(/([A-Z])/g, '-$1').toLowerCase();514515 return window.getComputedStyle(element, null).getPropertyValue(property);516 };517518 var iframe = null;519520 var createIframe = function () {521 iframe = _.document.createElement('iframe');522 _.document.documentElement.appendChild(iframe);523 return iframe;524 };525526 var getActualDisplay = function (nodeName, doc) {527 doc = doc || _.document;528529 var elem, display;530531 // Create and append the node532 elem = doc.createElement(nodeName);533 doc.body.appendChild(elem);534535 // Get display536 display = _getComputedStyle(elem, 'display');537538 // Remove it from the dom539 elem.parentNode.removeChild(elem);540541 return display;542 };543544 var elementsDisplay = {};545546 Kimbo.fn.extend({547 /*\548 * $(…).show549 [ method ]550 * Display all matched elements.551 = (object) Original matched collection.552 > Usage553 | <p style="display: none;">Lorem</p>554 * Show it555 | $('p').show();556 * Now it's visible557 | <p style="display: block;">Lorem</p>558 \*/559 show: function () {560 return this.each(function (el) {561 var nodeName = el.nodeName;562 var display = elementsDisplay[nodeName];563 var doc;564565 if (!display) {566 display = getActualDisplay(nodeName);567568 // If still fails for some css rule try creating the element in an isolated iframe569 if (display === 'none' || !display) {570571 // Use the already-created iframe if possible572 iframe = (iframe || createIframe());573574 doc = (iframe.contentWindow || iframe.contentDocument).document;575 doc.write('<!doctype html><html><body>');576 doc.close();577 display = getActualDisplay(nodeName, doc);578 iframe.parentNode.removeChild(iframe);579 }580581 // Save the default display for this element582 elementsDisplay[nodeName] = display;583 }584585 el.style.display = display || 'block';586 });587 },588589 /*\590 * $(…).hide591 [ method ]592 * Hide all matched elements.593 = (object) Original matched collection.594 > Usage595 | <p>Lorem</p>596 * Hide it597 | $('p').hide();598 * Now it's hidden599 | <p style="display: none;">Lorem</p>600 \*/601 hide: function () {602 return this.each(function (el) {603 var nodeName = el.nodeName;604 var display = elementsDisplay[nodeName];605606 if (!display) {607 display = _getComputedStyle(el, 'display');608 elementsDisplay[nodeName] = display;609 } else {610 display = el.style.display;611 }612613 // Only hide if not already hidden614 if (display !== 'none') {615 el.style.display = 'none';616 }617 });618 },619620 /*\621 * $(…).css622 [ method ]623 * Get the css value of a property from one element or set a css property to all matched elements.624 > Parameters625 - property (string|object) Name of the css property.626 - value (string|number) #optional Value for the property.627 = (object) Original matched collection.628 > Usage629 | <p>Hello dude!</p>630 * Modify some styles631 | $('p').css('color', 'red'); // Now the text is red632 * The HTML will look like this:633 | <p style="color: red;">Hello dude!</p>634 * You can also pass an object for setting multiple properties at the same time635 | $('p').css({636 | color: 'red',637 | 'font-size': '30px',638 | backgroundColor: 'blue'639 | });640 * Properties can be dash-separated (add quotes) or camelCase.641 | <p style="color: red; font-size: 30px; background-color: blue">Hello dude!</p>642 \*/643 css: function (property, value) {644 var that = this;645 var setCss;646647 if (!this.length || (!Kimbo.isString(property) && !Kimbo.isObject(property))) {648 return this;649 }650651 setCss = function (name, value) {652653 // If it's a number add 'px' except for some properties654 if (Kimbo.isNumeric(value) && !CSS_NO_PX[Kimbo.camelCase(name)]) {655 value += 'px';656 }657658 // Apply styles to all elements in the set659 that.each(function (el) {660 el.style[name] = value;661 });662 };663664 // Setting one property665 if (Kimbo.isString(property)) {666667 // Get668 if (value === undefined) {669 return _getComputedStyle(this[0], property);670671 // Set672 } else {673 setCss(property, value);674 }675676 // Multiple properties with an object677 } else if (Kimbo.isObject(property)) {678 Kimbo.forEach(property, setCss);679 }680681 return this;682 }683 });684});685686687Kimbo.define('manipulation', function (_) {688689 'use strict';690691 var SPACE_RE = /\s+/;692693 // Browser native classList694 var _hasClass = function (el, name) {695 return (el.nodeType === 1 && el.classList.contains(name));696 };697698 /*\699 * $(…).text700 [ method ]701 * Get the text of the first element in the set or set the text of all the matched elements.702 > Parameters703 - value (string) #optional A string of text to set as the text content of all matched elements.704 = (string) string value of the text content of the element if no parameter passed.705 = (object) current Kimbo object.706 > Usage707 * Get the text content of an element708 | <p>Demo text</p>709 * without passing any parameter to the function:710 | $('p').text(); // 'Demo text'711 * To replace the text of the paragraph pass a string parameter to the function:712 | $('p').text('Another text');713 * Now the text content was replaced:714 | <p>Another text</p>715 \*/716717 /*\718 * $(…).html719 [ method ]720 * Get the HTML content of the first element in the set or set the HTML content of all the matched elements.721 > Parameters722 - value (string) #optional A string of HTML to set as the HTML content of all matched elements.723 = (string) a string value of the HTML content of the element if no parameter passed.724 = (object) current Kimbo object.725 > Usage726 * Get the HTML content of an element727 | <p><span>Demo text<span></p>728 * without passing any parameter to the function:729 | $('p').html(); // '<span>Demo tetxt</span>'730 * To replace the HTML content of the paragraph pass a string parameter to the function:731 | $('p').html('<strong>Another content</strong>');732 * Now the text content was replaced:733 | <p><strong>Another content</strong></p>734 \*/735736 /*\737 * $(…).val738 [ method ]739 * Get the current value of the first element in the set or set the value of all the matched elements.740 > Parameters741 - value (string) #optional A string of text to set as the value of all matched elements.742 = (string) the current value of the first element if no parameter passed743 = (object) current Kimbo object744 > Usage745 * Get the value of a form element:746 | <input type="text" name="color" value="red" />747 * without passing any parameter to the function:748 | $('input').val(); // 'red'749 * To change the value of the input pass a string parameter to the function:750 | $('input').val('blue');751 * Now the value was changed:752 | $('input').val(); // 'blue'753 \*/754 Kimbo.forEach({755 text: 'textContent',756 html: 'innerHTML',757 val: 'value'758 }, function (method, prop) {759 Kimbo.fn[method] = function (value) {760761 // No element762 if (!this.length) {763 return undefined;764 }765766 // Get767 if (value === undefined) {768 return this[0][prop];769770 // Set771 } else {772 return this.each(function (el) {773 el[prop] = value;774 });775 }776 };777 });778779 /*\780 * $(…).addClass781 [ method ]782 * Adds a class to all matched elements.783 > Parameters784 - name (string) Name of the class to add.785 = (object) Original matched collection.786 > Usage787 | <p>I want to be green</p>788 | <script>789 | $('p').addClass('green');790 | </script>791 * Now it's green792 | <p class="green">I want to be green</p>793 * You can add multiple classes separated by a space794 | <p>Add classes to me</p>795 | <script>796 | $('p').addClass('green big width100');797 | </script>798 * All classes added and they won't be repetead if you try to add an existing one799 | <p class="green big width100">Add classes to me</p>800 \*/801802 /*\803 * $(…).removeClass804 [ method ]805 * Removes a class to all matched elements.806 > Parameters807 - name (string) Name of the class to remove.808 = (object) Original matched collection.809 > Usage810 | <p class="big green title float-rigth">Lorem ipsum</p>811 * Remove a specific class from the paragraph:812 | $('p').removeClass('green');813 * The specified class was removed:814 | <p class="big title float-right">Lorem ipsum</p>815 * You can remove multiple classes separating them by a space when calling the function:816 | $('p').removeClass('big title');817 * Now only one class left:818 | <p class="float-right">Lorem ipsum</p>819 * You can remove all classes just calling .removeClass() without parameters820 | $('p').removeClass();821 * All classes were removed including the class attribute:822 | <p>Lorem ipsum</p>823 \*/824825 // Generate addClass and removeClass methods826 // Use native classList827 // Mdn: https://developer.mozilla.org/en-US/docs/DOM/element.classList828 // Spec: http://www.whatwg.org/specs/web-apps/current-work/multipage/elements.html#dom-classlist829 Kimbo.forEach(['add', 'remove'], function (method, i) {830 var isRemove = i > 0;831832 Kimbo.fn[method + 'Class'] = function (name) {833 var classNames;834835 if (name && Kimbo.isString(name)) {836 classNames = name.split(SPACE_RE);837 this.each(function (el) {838839 // Skip comments, text, etc840 if (el.nodeType === 1) {841842 // Iterate through all class names passed843 Kimbo.forEach(classNames, function (className) {844 el.classList[method](className);845 });846 }847 });848849 // Remove all element classes if no classname specified850 } else if (!name && isRemove) {851 this.removeAttr('class');852 }853854 return this;855 };856 });857858 /*\859 * $(…).append860 [ method ]861 * Insert content to the end of all elements matched by Kimbo.862 > Parameters863 - value (string|object) HTML string, DOM element, or Kimbo object to insert.864 = (object) Original matched collection.865 > Usage866 | <div class="container">867 | <p class="lorem">Lorem </p>868 | <p class="lorem">Lorem </p>869 | </div>870 * Insert content871 | $('.lorem').append('<em>ipsum</em>')872 * Each element gets the content873 | <div class="container">874 | <p class="lorem">Lorem <em>ipsum</em></p>875 | <p class="lorem">Lorem <em>ipsum</em></p>876 | </div>877 * You can also get an element and insert it elsewhere878 | $('.container').append($('.ipsum'))879 * The selected element will be moved, not cloned.880 | <div class="container">881 | <p class="lorem">Lorem</p>882 | <p class="lorem">Lorem</p>883 | <p class="ipsum">Ipsum</p>884 | </div>885 \*/886887 /*\888 * $(…).prepend889 [ method ]890 * Insert content to the beginning of all elements matched by Kimbo.891 > Parameters892 - value (string|object) HTML string, DOM element, or Kimbo object to insert.893 = (object) Original matched collection.894 > Usage895 | <div class="container">896 | <p class="lorem"> Lorem</p>897 | <p class="lorem"> Lorem</p>898 | </div>899 * Insert content900 | $('.lorem').prepend('<em>ipsum</em>')901 * Each element gets the content902 | <div class="container">903 | <p class="lorem"><em>ipsum</em> Lorem</p>904 | <p class="lorem"><em>ipsum</em> Lorem</p>905 | </div>906 * You can also get an element and insert it elsewhere907 | $('.container').prepend($('.ipsum'))908 * The selected element will be moved, not cloned.909 | <div class="container">910 | <p class="ipsum">Ipsum</p>911 | <p class="lorem">Lorem</p>912 | <p class="lorem">Lorem</p>913 | </div>914 \*/915916 // Generate append and prepend methods917 Kimbo.forEach(['append', 'prepend'], function (method, i) {918 var isPrepend = i > 0;919920 Kimbo.fn[method] = function (value) {921 var div;922923 // Exit if no value passed924 if (!this.length || !value) {925 return this;926 }927928 // Handle html string929 if (Kimbo.isString(value)) {930931 // Placeholder element932 div = document.createElement('div');933 div.innerHTML = value;934 value = div.firstChild;935 }936937 // Already a dom node or kimbo collection, just insert it938 if (value.nodeType || (value instanceof Kimbo)) {939 return this.each(function (el) {940941 // Be sure we can append/prepend to the element942 if (el.nodeType === 1 || el.nodeType === 11) {943 _.kimbo(value).each(function (_el) {944 el.insertBefore(_el, isPrepend ? el.firstChild : null);945 });946 }947 });948 }949 };950 });951952 Kimbo.fn.extend({953 /*\954 * $(…).empty955 [ method ]956 * Remove all child nodes from the DOM of the elements in the collection.957 = (object) Original matched collection.958 > Usage959 | <div class="container">960 | <p class="lorem">Lorem</p>961 | <p class="lorem">Lorem</p>962 | </div>963 * Empty .container964 | $('.container').empty();965 * All elements inside ".container" are removed from the DOM966 | <div class="container"></div>967 \*/968 empty: function () {969 return this.each(function (el) {970 while (el.hasChildNodes()) {971 el.removeChild(el.childNodes[0]);972 }973 });974 },975976 /*\977 * $(…).remove978 [ method ]979 * Remove all matched elements from the DOM.980 * Similar to @$(…).empty but .remove() removes the element itself981 = (object) Original matched collection.982 > Usage983 | <div class="container">984 | <p class="lorem">Lorem</p>985 | <p>Lorem</p>986 | </div>987 * Remove one element988 | $('.lorem').remove();989 * The result element is:990 | <div class="container">991 | <p>Lorem</p>992 | </div>993 \*/994 remove: function () {995 return this.each(function (el) {996 if (el.parentNode) {997 el.parentNode.removeChild(el);998 }999 });1000 },10011002 // Todo: extend to accept objects and functions to set values1003 /*\1004 * $(…).attr1005 [ method ]1006 * Get an attribute value from one element or set attributes to all matched elements.1007 > Parameters1008 - name (string) Name of the attribute.1009 - value (string) #optional Value for the attribute.1010 = (string) Attribute value, only if name was passed.1011 = (object) Original matched collection when setting a value.1012 > Usage1013 | <a href="http://kimbojs.com">Go to Kimbojs.com</a>1014 * Get href attribute1015 | $('a').attr('href'); // Http://kimbojs.com1016 * Set a new attribute1017 | $('a').attr('title', 'Go to Kimbojs.com');1018 * Now element has a title attribute1019 | <a href="http://kimbojs.com" title="Go to Kimbojs.com">Go to Kimbojs.com</a>1020 \*/1021 attr: function (name, value) {1022 if (!this.length) {1023 return this;1024 }10251026 if (Kimbo.isString(name) && value === undefined) {1027 return this[0].getAttribute(name);1028 } else {1029 return this.each(function (el) {1030 el.setAttribute(name, value);1031 });1032 }1033 },10341035 /*\1036 * $(…).removeAttr1037 [ method ]1038 * Removes an attribute from all matched elements.1039 > Parameters1040 - name (string) Name of the attribute to remove.1041 = (object) Original matched collection.1042 > Usage1043 | <a href="http://kimbojs.com" title="Go to Kimbojs.com">Go to Kimbojs.com</a>1044 * Remove the title attribute1045 | $('a').removeAttr('title');1046 * Now the element has no title1047 | <a href="http://kimbojs.com">Go to Kimbojs.com</a>1048 \*/1049 removeAttr: function (name) {1050 return this.each(function (el) {1051 el.removeAttribute(name);1052 });1053 },10541055 /*\1056 * $(…).toggleClass1057 [ method ]1058 * Removes a class to all matched elements.1059 > Parameters1060 - name (string) Name of the class to toggle.1061 - state (boolean) #optional If state is true the class will be added, if false, removed.1062 = (object) Original matched collection.1063 > Usage1064 | <p class="foo">Lorem ipsum.</p>1065 | <script>1066 | $('p').toggleClass('foo');1067 | </script>1068 * The `p` element has the class foo soo it will be removed1069 | <p>Lorem ipsum.</p>1070 \*/1071 toggleClass: function (name, state) {1072 var classNames;10731074 if (this.length && name && Kimbo.isString(name)) {10751076 classNames = name.split(SPACE_RE);10771078 this.each(function (el) {1079 Kimbo.forEach(classNames, function (name) {10801081 // Use custom toggle (anyway it uses classList.add/remove)1082 state = Kimbo.isBoolean(state) ? state : !_hasClass(el, name);1083 _.kimbo(el)[state ? 'addClass' : 'removeClass'](name);1084 });1085 });1086 }10871088 return this;1089 },10901091 /*\1092 * $(…).hasClass1093 [ method ]1094 * Determine whether any matched elements has the given class.1095 > Parameters1096 - name (string) Name of the class to search for.1097 = (object) Original matched collection.1098 > Usage1099 | <p class="asd foo qwe">Lorem ipsum.</p>1100 * Check if the element has the class 'foo'1101 | $('p').hasClass('foo'); // True1102 * You could also check if it has multiple classes1103 | $('p').hasClass('qwe asd'); // True1104 \*/1105 hasClass: function (name) {1106 var has = false;1107 var classNames;11081109 if (this.length && name && Kimbo.isString(name)) {11101111 classNames = name.trim().split(SPACE_RE);11121113 this.each(function (el) {11141115 // Classlist.contains only accepts one class parameter1116 Kimbo.forEach(classNames, function (name) {1117 has = _hasClass(el, name);11181119 // When only one is missing break the loop1120 if (!has) {1121 return false;1122 }1123 });1124 });1125 }11261127 return has;1128 },11291130 /*\1131 * $(…).clone1132 [ method ]1133 * Clones a DOM node.1134 > Parameters1135 = (object) Original matched collection.1136 > Usage1137 | <p class="asd foo qwe">Lorem ipsum.</p>1138 | var p1 = $('p'); // Grab the p element1139 | var p2 = p1.clone(); // Clone p1 into p21140 | console.log(p2 === p1); // False1141 \*/1142 clone: function () {1143 return this.each(function (el) {1144 return el.cloneNode(true);1145 });1146 }1147 });11481149 // Generate get/set .width() and .height() methods1150 /*\1151 * $(…).width1152 [ method ]1153 * Get the current width of the first element or set the width of all matched elements.1154 > Parameters1155 - value (number|string) #optional An integer indicating the width of the element or a string width a unit of measure.1156 = (number) the actual width of the element if no parameter passed.1157 = (object) Kimbo object.1158 > Usage1159 | <style>1160 | div { width: 100px; }1161 | </style>1162 | <div>Actual width is 100px</div>1163 * Get the width:1164 | $('div').width(); // 1001165 * Change the width:1166 | $('div').width(200); // Now its width is 2001167 * Or passing a specific unit:1168 | $('div').width('50%'); // Now its width is 50%1169 \*/11701171 /*\1172 * $(…).height1173 [ method ]1174 * Get the current height of the first element or set the height of all matched elements.1175 > Parameters1176 - value (number|string) #optional An integer indicating the height of the element or a string height a unit of measure.1177 = (number) the actual height of the element if no parameter passed.1178 = (object) Kimbo object.1179 > Usage1180 | <style>1181 | div { height: 100px; }1182 | </style>1183 | <div>Actual height is 100px</div>1184 * Get the height:1185 | $('div').height(); // 1001186 * Change the height:1187 | $('div').height(200); // Now its height is 2001188 * Or passing a specific unit:1189 | $('div').height('50%'); // Now its height is 50%1190 \*/1191 Kimbo.forEach(['width', 'height'], function (dimension) {1192 Kimbo.fn[dimension] = function (value) {1193 if (!value) {1194 return parseInt(this.css(dimension), 10);1195 }11961197 return this.css(dimension, value);1198 };1199 });1200});120112021203Kimbo.define('traversing', function (_) {12041205 'use strict';12061207 var query = Kimbo.require('query');12081209 var _filter = Array.prototype.filter;12101211 var IS_UNIQUE = {1212 children: true,1213 contents: true,1214 next: true,1215 prev: true1216 };12171218 // Use native matchesSelector1219 var _matchesSelector = _.document.documentElement.webkitMatchesSelector ||1220 _.document.documentElement.mozMatchesSelector ||1221 _.document.documentElement.oMatchesSelector ||1222 _.document.documentElement.matchesSelector;12231224 var _matches = function (elem, selector) {1225 return (!elem || elem.nodeType !== 1) ? false : _matchesSelector.call(elem, selector);1226 };12271228 // Remove duplicates from an array1229 var _unique = function (array) {1230 return array.filter(function (item, index) {1231 return array.indexOf(item) === index;1232 });1233 };12341235 var _sibling = function (node, elem) {1236 var result = [];1237 for (; node; node = node.nextSibling) {1238 if (node.nodeType === 1 && node !== elem ) {1239 result.push(node);1240 }1241 }1242 return result;1243 };12441245 var _singleSibling = function (node, prop) {1246 do {1247 node = node[prop];1248 } while (node && node.nodeType !== 1);12491250 return node;1251 };12521253 Kimbo.fn.extend({1254 /*\1255 * $(…).filter1256 [ method ]1257 * Filter element collection by the passed argument.1258 > Parameters1259 - selector (string|object|function) The argument by which the collection will be filtered.1260 = (object) Filtered elements collection.1261 > Usage1262 | <ul>1263 | <li>One</li>1264 | <li>Two</li>1265 | <li>Three</li>1266 | <li>Four</li>1267 | </ul>1268 * Get even items.1269 | $('li').filter(':nth-child(even)').addClass('even');1270 * Only even items were affected.1271 | <ul>1272 | <li>One</li>1273 | <li class="even">Two</li>1274 | <li>Three</li>1275 | <li class="even">Four</li>1276 | </ul>1277 > Using a function(index, element)1278 * You can also filter the collection using a function, receiving the current index and element in the collection.1279 | $('li').filter(function (index, element) {1280 | return index % 3 == 2;1281 | }).addClass('red');1282 * This will add a 'red' class to the third, sixth, ninth elements and so on...1283 > Filter by DOM or Kimbo object1284 * You can also filter by a DOM or Kimbo object.1285 | $('li').filter(document.getElementById('id'));1286 | // Or a Kimbo object1287 | $('li').filter($('#id'));1288 \*/1289 filter: function (selector) {1290 var result;12911292 // Filter collection1293 result = _filter.call(this, function (elem, i) {1294 var ret;12951296 if (Kimbo.isFunction(selector)) {1297 ret = !!selector.call(elem, i, elem);1298 } else if (Kimbo.isString(selector)) {1299 ret = _matches(elem, selector);1300 } else if (selector.nodeType) {1301 ret = elem === selector;1302 } else if (selector instanceof Kimbo) {1303 ret = elem === selector[0];1304 }13051306 return ret;1307 });13081309 return _.kimbo(result);1310 },13111312 /*\1313 * $(…).eq1314 [ method ]1315 * Reduce the matched elements collection to the one at the specified index.1316 > Parameters1317 - index (number) An integer indicating the position of the element. Use a negative number to go backwards in the collection.1318 = (object) Kimbo object at specified index.1319 > Usage1320 | <ul>1321 | <li>Item 1</li>1322 | <li>Item 2</li>1323 | <li>Item 3</li>1324 | <li>Item 4</li>1325 | </ul>1326 * Get 3rd element, index always start at 0, so to get the 3rd we need to pass the number 2.1327 | $('li').eq(2); // Item 31328 \*/1329 eq: function (i) {1330 return this.length && i === -1 ? this.slice(i) : this.slice(i, i + 1);1331 },13321333 /*\1334 * $(…).first1335 [ method ]1336 * Reduce the matched elements collection to the first in the set.1337 = (object) First Kimbo object of the collection1338 > Usage1339 | <ul>1340 | <li>Item 1</li>1341 | <li>Item 2</li>1342 | <li>Item 3</li>1343 | </ul>1344 * Get only the first element1345 | $('li').first(); // Item 11346 \*/1347 first: function () {1348 return this.eq(0);1349 },13501351 /*\1352 * $(…).last1353 [ method ]1354 * Reduce the matched elements collection to the last in the set.1355 = (object) Last Kimbo object of the collection1356 > Usage1357 | <ul>1358 | <li>Item 1</li>1359 | <li>Item 2</li>1360 | <li>Item 3</li>1361 | </ul>1362 * Get only the last element1363 | $('li').last(); // Item 31364 \*/1365 last: function () {1366 return this.eq(-1);1367 },13681369 /*\1370 * $(…).slice1371 [ method ]1372 * Reduce the matched elements collection to a subset specified by a range of indices1373 > Parameters1374 - start (number) An integer indicating the position at which the elements begin to be selected. Use a negative number to go backwards in the collection.1375 - end (number) #optional An integer indicating the position at which the elements stop being selected. Use a negative number to start at the end of the collection. If ommited, the range continues to the end.1376 = (object) Reduced Kimbo object collection in the range specified1377 > Usage1378 | <ul>1379 | <li>Item 1</li>1380 | <li>Item 2</li>1381 | <li>Item 3</li>1382 | <li>Item 4</li>1383 | <li>Item 5</li>1384 | </ul>1385 * This will add the class selected to Item 3, 4 and 5, as the index starts at 01386 | $('li').slice(2).addClass('selected');1387 * This will select only Items 3 and 41388 | $('li').slice(2, 4).addClass('selected');1389 > Negative index1390 * Here only Item 4 will be selected since is the only between -2 (Item 3) and -1 (Item 5)1391 | $('li').slice(-2, -1).addClass('selected');1392 \*/1393 slice: function () {1394 return this.length && _.kimbo(_.slice.apply(this, arguments));1395 },13961397 /*\1398 * $(…).each1399 [ method ]1400 * Iterate over a Kimbo objct, executing a function for each element.1401 > Parameters1402 - callback (function) A function to call for each element in the collection.1403 = (object) Kimbo object1404 > Usage1405 * Here we have an unordered list:1406 | <ul>1407 | <li>Item 1</li>1408 | <li>Item 2</li>1409 | <li>Item 3</li>1410 | </ul>1411 * You can iterate over all the list items and execute a function1412 | $('li').each(function (el, index, collection) {1413 | console.log('index of ' + $(this).text() + ' is: ' + index);1414 | });1415 * This will log the following message1416 *1417 * index of Item 1 is: 01418 *1419 * index of Item 2 is: 11420 *1421 * index of Item 3 is: 21422 \*/1423 each: function (callback) {1424 return Kimbo.forEach(this, callback);1425 },14261427 /*\1428 * $(…).map1429 [ method ]1430 * Execute a function for each element in the collection, producing a new Kimbo set with the returned values1431 > Parameters1432 - callback (function) A function to call for each element in the collection.1433 = (object) Kimbo object1434 > Usage1435 * Here we have an unordered list:1436 | <ul>1437 | <li id="item1">Item 1</li>1438 | <li id="item2">Item 2</li>1439 | <li id="item3">Item 3</li>1440 | </ul>1441 * You can call a function for each element to create a new Kimbo object1442 | $('li').map(function (el, index) {1443 | return this.id;1444 | }).get().join();1445 * This will produce a list of the item ids.1446 | "item1,item2,item3"1447 \*/1448 map: function (callback) {1449 return _.kimbo(Kimbo.map(this, function (elem, i) {1450 return callback.call(elem, elem, i);1451 }));1452 },14531454 /*\1455 * $(…).find1456 [ method ]1457 * Find descendant elements for each element in the current collection.1458 > Parameters1459 - selector (string) A string selector to match elements.1460 = (object) Kimbo object1461 > Usage1462 * Here we have some HTML1463 | <div id="container">1464 | <p>Demo</p>1465 | <div class="article">1466 | <p>This is an article</p>1467 | <p>with some paragraphs</p>1468 | </div>1469 | </div>1470 * You can find all paragraph elements inside the article:1471 | $('.article').find('p');1472 \*/1473 find: function (selector) {1474 var i, l, length, n, r, result, elems;14751476 // Make new empty kimbo collection1477 result = _.kimbo();14781479 // Could use Kimbo.forEach, but this is a bit faster..1480 for (i = 0, l = this.length; i < l; i++) {1481 length = result.length;14821483 // Get elements1484 elems = query.find(this[i], selector);14851486 // Push them to current kimbo collection1487 _.push.apply(result, elems);14881489 if (i) {14901491 // Make results unique1492 for (n = length; n < result.length; n++) {1493 for (r = 0; r < length; r++) {1494 if (result[r] === result[n]) {1495 result.splice(n--, 1);1496 break;1497 }1498 }1499 }1500 }1501 }15021503 return result;1504 },15051506 /*\1507 * $(…).closest1508 [ method ]1509 * Get a Kimbo collection that matches the closest selector1510 > Parameters1511 - selector (string) A string selector to match elements.1512 - context (string) #optional A DOM element within which matching elements may be found.1513 = (object) Kimbo object1514 > Usage1515 * Here we have a nested unordered lists:1516 | <ul>1517 | <li>1518 | Item 11519 | <ul class="ul-level-2">1520 | <li class="item-1-1">Item 1.1</li>1521 | <li class="item-1-2">Item 1.2</li>1522 | </ul>1523 | </li>1524 | <li>Item 2</li>1525 | </ul>1526 * You can find the containing ul of the items in the nested ul:1527 | $('.item-1-1').closest('ul');1528 * This will return `ul.level-2` element1529 \*/1530 closest: function (selector, context) {1531 var l = this.length;1532 var result = [];1533 var closest = function (node) {15341535 // Check selector match and grab the element1536 while (node && !_matches(node, selector)) {1537 node = node !== context && node !== _.document && node.parentNode;1538 }1539 return node;1540 };15411542 if (!l) {1543 return this;15441545 // Get closest only for one element1546 } else if (l === 1) {1547 result = closest(this[0]);15481549 // Get closest from all elements in the set1550 } else {1551 Kimbo.forEach(this, function (node) {1552 node = closest(node);1553 if (node) {1554 result.push(node);1555 }1556 });15571558 // Only unique results1559 result = result.length > 1 ? _unique(result) : result;1560 }15611562 return _.kimbo(result);1563 },15641565 /*\1566 * $(…).contains1567 [ method ]1568 * Determine whether an element is contained by the current matched element.1569 > Parameters1570 - element (string|object) Selector of the element or the actual DOM or Kimbo object.1571 = (boolean) true if it is contained, false if not.1572 > Usage1573 | <div id="container">1574 | <p id="inside">Inside paragraph</p>1575 | </div>1576 | <p id="outside">Outside paragraph</p>1577 * The paragraph with id "inside" is actually contained by "#container"1578 | $('#container').contains('#inside'); // True1579 * The paragraph ourside is not contained1580 | var outside_p = $('#outside');1581 | $('#container').contains(outside_p); // False1582 \*/1583 contains: function (element) {1584 element = (element instanceof Kimbo) ? element[0] :1585 (Kimbo.isString(element) ? this.find(element)[0] : element);15861587 return query.contains(this[0], element);1588 },15891590 /*\1591 * $(…).add1592 [ method ]1593 * Add elements to the current Kimbo collection.1594 > Parameters1595 - selector (string|object) Selector of the element or the actual DOM or Kimbo object.1596 - context (string|object) #optional Selector of the context element or the actual DOM or Kimbo object.1597 = (object) The merged Kimbo collection.1598 > Usage1599 | <ul id="menu1">1600 | <li>Apple</li>1601 | <li>Orange</li>1602 | </ul>1603 |1604 | <ul id="menu2">1605 | <li>Lemon</li>1606 | <li>Banana</li>1607 | </ul>1608 * Get the items from the #menu1 and add the ones from #menu2, all the following ways will produce the same collection1609 | $('#menu1 li').add('#menu2 li');1610 * or1611 | $('#menu1 li').add('li', '#menu2');1612 * or1613 | $('#menu1 li').add($('#menu2 li'));1614 \*/1615 add: function (selector, context) {1616 var set = Kimbo.isString(selector) ? _.kimbo(selector, context) :1617 Kimbo.makeArray(selector && selector.nodeType ? [selector] : selector);16181619 var all = Kimbo.merge(this, set);16201621 return _.kimbo(all);1622 },16231624 /*\1625 * $(…).is1626 [ method ]1627 * Check the current elements collection against a selector, object or function.1628 - selector (string|object|function) The argument by which the collection will be matched against.1629 = (boolean)1630 > Usage1631 | <ul>1632 | <li>Click the <em>Apple</em></li>1633 | <li><span>Click the Orange</span></li>1634 | <li>Or the Banana</li>1635 | </ul>1636 * Test if the current clicked element is an `<li>` element.1637 | $('ul').click(function (event) {1638 | var $target = $(event.target);1639 | if ($target.is('li')) {1640 | $target.css('background-color', 'red');1641 | }1642 | });1643 \*/1644 is: function (selector) {1645 return !!(this.length && this.filter(selector).length);1646 }1647 });16481649 Kimbo.forEach({1650 /*\1651 * $(…).parent1652 [ method ]1653 * Get the parent of each element matched in the current collection.1654 * If a selector is specified, it will return the parent element only if it matches that selector.1655 - selector (string) #optional A string containing a selector expression to match elements against1656 = (object) Kimbo object1657 > Usage1658 * Suppose a page with this HTML:1659 | <ul>1660 | <li class="item-a">Item 1</li>1661 | <li class="item-b">Item 2</li>1662 | </ul>1663 * Get the parent element of `.item-a`1664 | $('.item-a').parent(); // Ul1665 \*/1666 parent: function (elem) {1667 var parent = elem.parentNode;16681669 return parent && parent.nodeType !== 11 ? parent : null;1670 },16711672 /*\1673 * $(…).next1674 [ method ]1675 * Get the immedeately following sibling of each element in the current collection.1676 * If a selector is specified, it will return the element only if it matches that selector.1677 - selector (string) #optional A string containing a selector expression to match elements against1678 = (object) Kimbo object1679 > Usage1680 * Suppose a page with this HTML:1681 | <ul>1682 | <li class="item-a">Item 1</li>1683 | <li class="item-b">Item 2</li>1684 | </ul>1685 * Get the parent element of `.item-a`1686 | $('.item-a').next(); // .item-b1687 \*/1688 next: function (elem) {1689 return _singleSibling(elem, 'nextSibling');1690 },16911692 /*\1693 * $(…).prev1694 [ method ]1695 * Get the immedeately previous sibling of each element in the current collection.1696 * If a selector is specified, it will return the element only if it matches that selector.1697 - selector (string) #optional A string containing a selector expression to match elements against1698 = (object) Kimbo object1699 > Usage1700 * Suppose a page with this HTML:1701 | <ul>1702 | <li class="item-a">Item 1</li>1703 | <li class="item-b">Item 2</li>1704 | </ul>1705 * Get the parent element of `.item-a`1706 | $('.item-b').prev(); // .item-a1707 \*/1708 prev: function (elem) {1709 return _singleSibling(elem, 'previousSibling');1710 },17111712 /*\1713 * $(…).sibling1714 [ method ]1715 * Get the immedeately previous sibling of each element in the current collection.1716 * If a selector is specified, it will return the element only if it matches that selector.1717 - selector (string) #optional A string containing a selector expression to match elements against1718 = (object) Kimbo object1719 > Usage1720 * Suppose a page with this HTML:1721 | <ul>1722 | <li class="item-a">Item 1</li>1723 | <li class="item-b">Item 2</li>1724 | </ul>1725 * Get the parent element of `.item-a`1726 | $('.item-b').prev(); // .item-a1727 \*/1728 siblings: function (elem) {1729 return _sibling((elem.parentNode || {}).firstChild, elem);1730 },17311732 /*\1733 * $(…).children1734 [ method ]1735 * Get the children of all matched elements, optionally filtered by a selector.1736 - selector (string) #optional A string selector to match elements against.1737 = (object) Kimbo object1738 > Usage1739 * Suppose a page with the following HTML:1740 | <div class="demo">1741 | <p>This</p>1742 | <p>is</p>1743 | <p>a</p>1744 | <p>demo.</p>1745 | </div>1746 * Get all children of `.demo`:1747 | $('.demo').children(); // Al <p> tags inside .demo div1748 * Another example passing an specific selector:1749 | <form>1750 | <input type="text" name="name" />1751 | <input type="text" name="last" />1752 | <input type="submit" value="Send" />1753 | </form>1754 * Get only the children that are text type elements:1755 | $('form').children('input[type="text"]'); // Only name and last inputs1756 \*/1757 children: function (elem) {1758 return _sibling(elem.firstChild);1759 },17601761 /*\1762 * $(…).contents1763 [ method ]1764 * Get the HTML content of an iframe1765 = (object) Kimbo object1766 > Usage1767 * Suppose an iframe loading an external page:1768 | <iframe src="http://api.kimbojs.com"></iframe>1769 * Find the body element of the contents of that iframe:1770 | $('iframe').contents().find('body');1771 \*/1772 contents: function (elem) {1773 return elem.nodeName.toLowerCase() === 'iframe' ? elem.contentDocument || elem.contentWindow[_.document] : Kimbo.makeArray(elem.childNodes);1774 }1775 }, function (name, fn) {1776 Kimbo.fn[name] = function (selector) {1777 var ret;17781779 if (!this.length) {1780 return this;1781 }17821783 ret = Kimbo.map(this, fn);17841785 // Clean collection1786 ret = this.length > 1 && !IS_UNIQUE[name] ? _unique(ret) : ret;17871788 if (Kimbo.isString(selector)) {1789 ret = _.kimbo(ret).filter(selector);1790 }17911792 return _.kimbo(ret);1793 };1794 });1795});179617971798Kimbo.define('utilities', function (_) {17991800 'use strict';18011802 // Mobile userAgent escaped regexes1803 var ANDROID_RE = '(Android)\\s+([\\d.]+)';1804 var BLACKBERRY_RE = '(BlackBerry|BB10|Playbook).*Version\/([\\d.]+)';1805 var FIREFOXOS_RE = '(Mozilla).*Mobile[^\/]*\/([\\d.]*)';1806 var IPAD_RE = '(iPad).*OS\\s([\\d_]+)';1807 var IPHONE_RE = '(iPhone\\sOS)\\s([\\d_]+)';1808 var WEBOS = '(web|hpw)OS[\\s\/]([\\d.]+)';18091810 // Full regexp to test the userAgent1811 var MOBILE_OS_RE = new RegExp(1812 ANDROID_RE + '|' +1813 BLACKBERRY_RE + '|' +1814 FIREFOXOS_RE + '|' +1815 IPHONE_RE + '|' +1816 IPAD_RE + '|' +1817 WEBOS1818 );18191820 var isMobile = null;18211822 var objectTypes = {};18231824 // Map object types1825 Kimbo.forEach([ 'Array', 'Boolean', 'Date', 'Error', 'Function',1826 'Number', 'Object', 'RegExp', 'String'1827 ], function (type) {1828 objectTypes['[object ' + type + ']'] = type.toLowerCase();1829 }1830 );18311832 Kimbo.extend({1833 /*\1834 * $.typeOf1835 [ method ]1836 * Determine the internal JavaScript [[Class]] of an object.1837 > Parameters1838 - obj (object) Object to get its [[Class]] type.1839 = (string) Type of the object.1840 > Usage1841 | $.typeOf('i am a string'); // 'string'1842 | $.typeOf(/(\.regexp?)/); // 'regexp'1843 | $.typeOf(null); // 'null'1844 | $.typeOf(undefined); // 'undefined'1845 | $.typeOf(window.notDefined); // 'undefined'1846 | $.typeOf(function () {}); // 'function'1847 | $.typeOf({key: 'value'}); // 'object'1848 | $.typeOf(true); // 'boolean'1849 | $.typeOf([]); // 'array'1850 | $.typeOf(new Date()); // 'date'1851 | $.typeOf(3); // 'number'1852 \*/1853 typeOf: function (obj) {1854 var type;18551856 if (obj === null || obj === undefined) {1857 type = String(obj);18581859 } else {1860 type = (objectTypes[Object.prototype.toString.call(obj)] || 'object');1861 }18621863 return type;1864 },18651866 /*\1867 * $.isArray1868 [ method ]1869 * Determine if the parameter passed is an array object.1870 > Parameters1871 - obj (object) Object to test if its an array.1872 = (boolean) According wether or not it is an array object.1873 > Usage1874 | $.isArray([]); // True1875 | $.isArray({}); // False1876 | $.isArray('test'); // False1877 \*/1878 isArray: Array.isArray,18791880 /*\1881 * $.isNumeric1882 [ method ]1883 * Determine if the parameter passed is an number.1884 > Parameters1885 - obj (object) Object to test if its a number.1886 = (boolean) According wether or not it is a number.1887 > Usage1888 | $.isNumeric(3); // True1889 | $.isNumeric('3'); // False1890 \*/1891 isNumeric: function (obj) {1892 return !isNaN(parseFloat(obj)) && isFinite(obj);1893 },18941895 /*\1896 * $.isWindow1897 [ method ]1898 * Determine if the parameter passed is the window object.1899 > Parameters1900 - obj (object) Object to test if its the window object.1901 = (boolean) According wether or not it is the window object.1902 > Usage1903 | $.isWindow(window); // True1904 | $.isWindow({ window: window }); // False1905 \*/1906 isWindow: function (obj) {1907 return obj && obj === obj.window;1908 },19091910 /*\1911 * $.isEmptyObject1912 [ method ]1913 * Determine if the parameter passed is an empty object.1914 > Parameters1915 - obj (object) Object to test if its an empty object.1916 = (boolean) According wether or not it is an empty object.1917 > Usage1918 | $.isEmptyObject({}); // True1919 | $.isEmptyObject([]); // True1920 | $.isEmptyObject([1, 2]); // False1921 \*/1922 isEmptyObject: function (obj) {1923 var key;19241925 for (key in obj) {1926 if (obj.hasOwnProperty(key)) {1927 return false;1928 }1929 }19301931 return true;1932 },19331934 /*\1935 * $.isMobile1936 [ method ]1937 * Determine if the current platform is a mobile device, (otherwise is a desktop browser).1938 > Parameters1939 = (boolean) According wether or not is a mobile device.1940 > Usage1941 | $.isMobile(); // False1942 \*/1943 isMobile: function () {19441945 // Check only once for the current browser1946 if (isMobile === null) {1947 isMobile = MOBILE_OS_RE.test(navigator.userAgent);1948 }19491950 return isMobile;1951 },19521953 /*\1954 * $.parseJSON1955 [ method ]1956 * Parses a well-formed JSON string and returns the resulting JavaScript object.1957 > Parameters1958 - data (string) The JSON string to parse.1959 = (object) A JavaScript object.1960 > Usage1961 | var obj = $.parseJSON('{"name":"Denis"}');1962 | console.log(obj.name === 'Denis'); // True1963 \*/1964 parseJSON: function (data) {19651966 // Use native JSON parser1967 if (data && Kimbo.isString(data)) {1968 return window.JSON.parse(data);1969 }1970 },19711972 /*\1973 * $.parseXML1974 [ method ]1975 * Parses a string into an XML document.1976 > Parameters1977 - data (string) The JSON string to parse.1978 = (object) A JavaScript object.1979 > Usage1980 | var xml = "<rss version='2.0'><channel><title>RSS Title</title></channel></rss>"1981 | var xmlDoc = $.parseXML(xml);1982 | $(xmlDoc).find('title'); // 'RSS Title'1983 \*/1984 parseXML: function (data) {19851986 // Use native XML (DOM) parser1987 var domparser;1988 var xml;19891990 if (data && Kimbo.isString(data)) {1991 domparser = new window.DOMParser();1992 xml = domparser.parseFromString(data, 'text/xml');19931994 if (xml.getElementsByTagName('parsererror').length) {1995 throw new Error('Invalid XML: ' + data);1996 }19971998 return xml;1999 }2000 },20012002 /*\2003 * $.map2004 [ method ]2005 * Translate all items in an array or object to new array of items.2006 > Parameters2007 - obj (array|object) The Array or Object to translate.2008 = (array) A new array.2009 > Usage2010 | var arr = ['a', 'b', 'c'];2011 | arr = $.map(arr, function (element, index) {2012 | return element.toUpperCase() + index;2013 | });2014 | console.log(arr); // ['A0', 'B1', 'C2']2015 * Or wit an object2016 | var obj = {a: 'a', b: 'b', c: 'c'};2017 | obj = $.map(arr, function (key, value) {2018 | return key + ': ' + value.toUpperCase();2019 | });2020 | console.log(obj); // ['a: A', 'b: B', 'c: C']2021 \*/2022 map: function (obj, callback) {2023 var values = [];20242025 if (obj) {2026 Kimbo.forEach(obj, function (key, val) {2027 var value = callback(key, val);20282029 if (value !== null && value !== undefined) {2030 values.push(value);2031 }2032 });2033 }20342035 // Flatten any nested arrays2036 return values.concat.apply([], values);2037 },20382039 /*\2040 * $.makeArray2041 [ method ]2042 * Create an Array from the given object2043 > Parameters2044 - obj (array|object) The Array or Object to make an array from.2045 = (array) A new array.2046 > Usage2047 | var lis = $('li'); // Kimbo object2048 | var arr = $.makeArray(lis);2049 |2050 | console.log($.isArray(lis)); // False2051 | console.log($.isArray(arr)); // True2052 \*/2053 makeArray: function (obj, results) {2054 results = results || [];20552056 if (obj) {2057 if (Kimbo.isArray(obj) || (obj instanceof Kimbo) || obj instanceof window.NodeList) {2058 results = Kimbo.merge(results, obj);2059 } else {2060 _.push.call(results, obj);2061 }2062 }20632064 return results;2065 },20662067 /*\2068 * $.merge2069 [ method ]2070 * Merge the contents of two arrays into the first array passed.2071 > Parameters2072 - first (array) The first array to merge the contents of the second.2073 - second (array|string) The second array to merge into the first.2074 = (array) The first array merged with the second.2075 > Usage2076 | $.merge(['a', 'b'], ['c', 'd']);2077 * Result:2078 | ['a', 'b', 'c', 'd']2079 \*/2080 merge: function (first, second) {20812082 // Concat is very fast, use it if we can2083 if (Kimbo.isArray(first)) {2084 first = first.concat(second);20852086 // Kimbo object, do a consecutive push2087 } else {2088 _.push.apply(first, _.slice.call(second));2089 }20902091 return first;2092 },20932094 /*\2095 * $.camelCase2096 [ method ]2097 * Camelize any dashed separated string2098 > Parameters2099 - str (string) A dashed separated string value to transform into camelCase.2100 = (string) camelCase string2101 > Usage2102 | $.camelCase('background-color');2103 * Result:2104 | 'backgroundColor'2105 \*/2106 camelCase: function (str) {2107 return str.replace(/-+(.)?/g, function (wholeMatch, character) {2108 return character.toUpperCase();2109 });2110 },21112112 /*\2113 * $.isFunction2114 [ method ]2115 * Determine if the parameter passed is a Javascript function object.2116 > Parameters2117 - obj (object) Object to test if its a function.2118 = (boolean) According wether or not it is a function.2119 > Usage2120 | var myFunction = function () {};2121 | $.isFunction(myFunction); // True2122 | var something = ['lala', 'jojo'];2123 | $.isFunction(something); // False2124 \*/2125 isFunction: function (obj) {2126 return Kimbo.typeOf(obj) === 'function';2127 },21282129 /*\2130 * $.isObject2131 [ method ]2132 * Determine if the parameter passed is a Javascript plain object.2133 > Parameters2134 - obj (object) Object to test if its a plain object.2135 = (boolean) According wether or not it is a plain object.2136 > Usage2137 | $.isObject({}); // True2138 | $.isObject([]); // False2139 | $.isObject('test'); // False2140 \*/2141 isObject: function (obj) {2142 return Kimbo.typeOf(obj) === 'object';2143 },21442145 /*\2146 * $.isString2147 [ method ]2148 * Determine if the parameter passed is a string.2149 > Parameters2150 - obj (object) Object to test if its a string.2151 = (boolean) According wether or not it is a string.2152 > Usage2153 | $.isString('test'); // True2154 | $.isString({ name: 'asd' }); // False2155 \*/2156 isString: function (obj) {2157 return Kimbo.typeOf(obj) === 'string';2158 },21592160 /*\2161 * $.isBoolean2162 [ method ]2163 * Determine if the parameter passed is boolean.2164 > Parameters2165 - obj (object) Object to test if its boolean..2166 = (boolean) According wether or not it is boolean.2167 > Usage2168 | $.isBoolean(false); // True2169 | $.isBoolean(3); // False2170 \*/2171 isBoolean: function (obj) {2172 return Kimbo.typeOf(obj) === 'boolean';2173 }2174 });21752176 // Save reference to Kimbo wrapped document as the default context2177 _.rootContext = _.kimbo(_.rootContext);2178});217921802181Kimbo.define('events', function (_) {21822183 'use strict';21842185 var query = Kimbo.require('query');21862187 var _guid = 1;21882189 var MOUSE_EVENT_RE = /^(?:mouse|menu)|click/;21902191 var KEY_EVENT_RE = /^key/;21922193 var DEFAULT_EVENT_PROPS = [2194 'altKey', 'bubbles', 'cancelable', 'ctrlKey', 'currentTarget', 'defaultPrevented', 'eventPhase',2195 'metaKey', 'relatedTarget', 'shiftKey', 'target', 'timeStamp', 'type', 'view', 'which'2196 ];21972198 var MOUSE_EVENT_PROPS = [2199 'button', 'buttons', 'clientX', 'clientY', 'fromElement',2200 'offsetX', 'offsetY', 'screenX', 'screenY', 'toElement'2201 ];22022203 var KEY_EVENT_PROPS = ['char', 'charCode', 'key', 'keyCode'];22042205 // Gestures fallback for not mobile environment2206 var GESTURES_FALLBACK = Kimbo.isMobile() ? {} : {2207 touchstart: 'mousedown',2208 touchmove: 'mousemove',2209 touchend: 'mouseup',2210 touch: 'click',2211 doubletap: 'dblclick',2212 orientationchange: 'resize'2213 };22142215 var handlersHash = {};2216 var fixEventProps = {};2217 var specialEvents = {};22182219 var _fixEvent = function (event) {2220 var originalEvent, eventProps, props;22212222 // Already fixed2223 if (event[Kimbo.ref]) {2224 return event;2225 }22262227 // Get event properties2228 originalEvent = event;2229 eventProps = fixEventProps[event.type] || [];2230 props = DEFAULT_EVENT_PROPS.concat(eventProps);22312232 // Create a new event writable custom event object2233 event = new Kimbo.Event(originalEvent);22342235 // Set event props to Kimbo.Event object2236 Kimbo.forEach(props, function (prop) {2237 event[prop] = originalEvent[prop];2238 });22392240 return event;2241 };22422243 // Return element id2244 var _getElementId = function (element) {2245 return element._guid || (element._guid = _guid++);2246 };22472248 // Get element handlers for the specified type2249 var _getHandlers = function (elementId, type) {2250 var events = ((handlersHash[elementId] || {}).events || {});22512252 return (type ? events[type] : events) || [];2253 };22542255 // Quick is() to check if event target matches when events are delegated2256 var _is = function (target, selector, element) {2257 return (target.nodeName.toLowerCase() === selector && _.kimbo(target).closest(selector, element)[0]);2258 };22592260 var _returnFalse = function () {2261 return false;2262 };22632264 var _returnTrue = function () {2265 return true;2266 };22672268 // Register events to dom elements2269 var _addEvent = function (element, type, callback, data, selector) {22702271 // TODO: element should use Kimbo.ref and the handler the _guid2272 var elementId = _getElementId(element);2273 var elementHandlers = handlersHash[elementId];2274 var origType = type;2275 var events, handlers, handleObj, handler;22762277 // Could be a special type like mouseenter/mouseleave2278 type = specialEvents[type] ? specialEvents[type].origType : type;22792280 // Create hash for this element if first init2281 if (!elementHandlers) {2282 handlersHash[elementId] = elementHandlers = {};2283 }22842285 // Create events object if first init2286 events = elementHandlers.events;2287 if (!events) {2288 elementHandlers.events = events = {};2289 }22902291 // Create the handler for this element if first init2292 handler = elementHandlers.handler;2293 if (!handler) {2294 elementHandlers.handler = handler = function () {2295 return _dispatchEvent.apply(element, arguments);2296 };2297 }22982299 // Create handler object2300 handleObj = {2301 type: type,2302 origType: origType,2303 data: data,2304 callback: callback,2305 selector: selector2306 };23072308 // Only add an event listener one time for each type of event2309 handlers = events[type];2310 if (!handlers) {23112312 // Array of events for the current type2313 handlers = events[type] = [];2314 handlers.delegateCount = 0;23152316 // Add event2317 if (element.addEventListener) {2318 element.addEventListener(type, handler, false);2319 }2320 }23212322 // Add to handlers hash, delegates first2323 if (selector) {2324 handlers.splice(handlers.delegateCount++, 0, handleObj);23252326 } else {2327 handlers.push(handleObj);2328 }2329 };23302331 // Unregister events from dom elements2332 var _removeEvent = function (element, type, callback, selector) {2333 var elementId = _getElementId(element);2334 var handleObj, handlers, name, i;23352336 if (!elementId) {2337 return;2338 }23392340 handlers = _getHandlers(elementId, type);23412342 // Return if no handlers for the current event type2343 if (type && !handlers.length) {2344 return;2345 }23462347 // Remove all handlers if no type provided2348 if (!type) {2349 for (name in handlers) {2350 if (handlers.hasOwnProperty(name)) {2351 _removeEvent(element, name, callback, selector);2352 }2353 }2354 }23552356 // Remove handlers that match2357 for (i = 0; i < handlers.length; i++) {2358 handleObj = handlers[i];2359 if ((!callback || callback === handleObj.callback) && (!selector || selector === handleObj.selector)) {23602361 // Remove current handler from stack2362 handlers.splice(i--, 1);23632364 // Decrement delegate count2365 if (handleObj.selector) {2366 handlers.delegateCount--;2367 }2368 }2369 }23702371 // If no more events for the current type delete its hash2372 if (!handlers.length) {23732374 // Remove event handler2375 element.removeEventListener(type, handlersHash[elementId].handler, false);2376 delete handlersHash[elementId].events[type];2377 }23782379 // Remove kimbo reference if element have no more events2380 // If (Kimbo.isEmptyObject(handlersHash[elementId].events)) {2381 // delete handlersHash[elementId];2382 // delete element._guid;2383 // }2384 };23852386 // Triggers a provided event type2387 var _triggerEvent = function (element, type, data) {23882389 /* jshint validthis: true */2390 var currentElement, lastElement, eventTree, elementId, event;23912392 // Don't do events if element is text or comment node2393 // Or if there is no event type at all or type is not a string2394 if ((element && (element.nodeType === 3 || element.nodeType === 8)) || !type || !Kimbo.isString(type)) {2395 return this;2396 }23972398 // Try triggering native focus and blur events2399 if (type === 'focus' || type === 'blur') {2400 try {2401 return element[type]();2402 } catch (e) {}2403 }24042405 // Create a new writable custom event object2406 event = new Kimbo.Event(type);24072408 // Triggered programatically2409 event.isTrigger = true;24102411 // Set the target2412 if (!event.target) {2413 event.target = element;2414 }24152416 // Include data if any2417 data = data ? Kimbo.makeArray(data) : [];2418 // Event goes first2419 data.unshift(event);24202421 // Generate a stack of [element, event] to be triggered2422 eventTree = [[element, type]];2423 if (!Kimbo.isWindow(element)) {24242425 // Get all parent elements to bubble event later2426 for (currentElement = element.parentNode; currentElement; currentElement = currentElement.parentNode) {2427 eventTree.push([currentElement, type]);2428 lastElement = currentElement;2429 }24302431 // Only add window object if we got to document (e.g., not plain obj or detached DOM)2432 if (lastElement && lastElement === element.ownerDocument) {2433 eventTree.push([window, type]);2434 }2435 }24362437 // Fire handlers up to the document (or the last element)2438 Kimbo.forEach(eventTree, function (branch) {24392440 // Element2441 currentElement = branch[0];24422443 // Type2444 event.type = branch[1];24452446 // Get element id2447 elementId = currentElement._guid;24482449 // If the current element has events of the specified type, dispatch them2450 if (elementId && _getHandlers(elementId, type)) {2451 handlersHash[elementId].handler.apply(currentElement, data);2452 }2453 });2454 };24552456 // Own defined dispatchEvent()2457 var _dispatchEvent = function (event) {2458 /* jshint -W040 */24592460 // Use own event object2461 event = _fixEvent(event);24622463 var elementId = _getElementId(this);2464 var handlers = _getHandlers(elementId, event.type);2465 var delegateCount = handlers.delegateCount;2466 var args = _.slice.call(arguments);2467 var handlerQueue = [];2468 var currentElement, ret, selMatch, matches, handleObj, selector, i;24692470 // Set the native event to be the fixed event2471 args[0] = event;24722473 // Save the delegate target element2474 event.delegateTarget = this;24752476 // Get delegated handlers if any2477 if (delegateCount) {24782479 // Go up to the dom finding the elements that matches the current selector from delegated event2480 for (currentElement = event.target; currentElement !== this; currentElement = currentElement.parentNode || this) {24812482 // Don't do events on disabled elements2483 if (currentElement.disabled !== true || event.type !== 'click') {2484 selMatch = {};2485 matches = [];24862487 // Loop throgh delegated events2488 for (i = 0; i < delegateCount; i++) {24892490 // Get its handler2491 handleObj = handlers[i];24922493 // Get its selector2494 selector = handleObj.selector;24952496 if (!selMatch[selector]) {2497 selMatch[selector] = _is(currentElement, selector, this);2498 }24992500 if (selMatch[selector]) {2501 matches.push(handleObj);2502 }2503 }25042505 if (matches.length) {2506 handlerQueue.push({elem: currentElement, matches: matches});2507 }2508 }2509 }2510 }25112512 // Add the remaining not delegated handlers2513 if (handlers.length > delegateCount) {2514 handlerQueue.push({elem: this, matches: handlers.slice(delegateCount)});2515 }25162517 // Fire callbacks queue2518 Kimbo.forEach(handlerQueue, function (handler) {25192520 // Only fire handler if event wasnt stopped2521 if (!event.isPropagationStopped()) {2522 event.currentTarget = handler.elem;25232524 Kimbo.forEach(handler.matches, function (handleObj) {25252526 // Only fire bubble if not stopped2527 if (!event.isImmediatePropagationStopped()) {2528 event.data = handleObj.data;2529 event.handleObj = handleObj;25302531 // Call original callback, check if its an special event first2532 ret = ((specialEvents[handleObj.origType] || {}).handle || handleObj.callback).apply(handler.elem, args);25332534 // If callback returns false, stop the event2535 if (ret === false) {2536 event.preventDefault();2537 event.stopPropagation();2538 }2539 }2540 });2541 }2542 });25432544 /* jshint +W040 */2545 };25462547 Kimbo.Event = function (event) {25482549 // Is event object2550 if (event && event.type) {2551 this.originalEvent = event;2552 this.type = event.type;25532554 // The event may have been prevented2555 // Check dom level 3 new attribute and set proper value2556 if (event.defaultPrevented) {2557 this.isDefaultPrevented = _returnTrue;2558 } else {2559 this.isDefaultPrevented = _returnFalse;2560 }25612562 // Is event type2563 } else {2564 this.type = event;2565 }25662567 // Create a timestamp if doesn't have one2568 this.timeStamp = (event && event.timeStamp) || Date.now();25692570 // Made by kimbo, yeah2571 this[Kimbo.ref] = true;2572 };25732574 // Dom-Level-3-Events compliant2575 Kimbo.Event.prototype = {2576 isDefaultPrevented: _returnFalse,2577 isPropagationStopped: _returnFalse,2578 isImmediatePropagationStopped: _returnFalse,25792580 preventDefault: function () {2581 this.isDefaultPrevented = _returnTrue;25822583 // Originalevent is not present when trigger is called2584 if (!this.isTrigger) {2585 this.originalEvent.preventDefault();2586 }2587 },25882589 stopPropagation: function () {2590 this.isPropagationStopped = _returnTrue;25912592 if (!this.isTrigger) {2593 this.originalEvent.stopPropagation();2594 }2595 },25962597 stopImmediatePropagation: function () {2598 this.isImmediatePropagationStopped = _returnTrue;25992600 if (!this.isTrigger) {2601 this.originalEvent.stopImmediatePropagation();2602 }2603 }2604 };26052606 Kimbo.fn.extend({2607 /*\2608 * $(…).on2609 [ method ]2610 * Add an event handler to the selected elements.2611 > Parameters2612 - type (string) A string name for the event to register.2613 - selector (string) #optional Delegate an event providing a string selector.2614 - data (any) #optional Data to be passed to the handler in `event.data` when an event is triggered.2615 - callback (function) A callback function to execute when the event is triggered.2616 > Usage2617 * Suppose a button element:2618 | <button id='btn'>click me</button>2619 * Register a click event handler2620 | $('#btn').on('click', function (event) {2621 | console.log('clicked!', event);2622 | });2623 * There are shorthands for all events for example:2624 | $('#btn').click(function (event) {2625 | console.log('clicked!', event);2626 | });2627 * Passing some data when registering the handler:2628 | $('#btn').on('click', { name: 'denis' }, function (event) {2629 | // Data passed is inside event.data2630 | console.log('name:', event.data.name);2631 | });2632 * Here is a list for all the shorthand methods available:2633 | 'blur', 'change', 'click', 'contextmenu', 'dblclick', 'error',2634 | 'focus', 'keydown', 'keypress', 'keyup', 'load', 'mousedown', 'mouseenter', 'mouseleave',2635 | 'mousemove', 'mouseout', 'mouseup', 'mouseover', 'resize', 'scroll', 'select', 'submit', 'unload'2636 \*/2637 on: function (type, selector, data, callback) {2638 // Prepare the arguments26392640 // (type, callback)2641 if (!data && !callback) {2642 callback = selector;2643 data = selector = undefined;26442645 // (type, selector, callback)2646 } else if (!callback) {2647 if (Kimbo.isString(selector)) {2648 callback = data;2649 data = undefined;26502651 // (type, data, callback)2652 } else {2653 callback = data;2654 data = selector;2655 selector = undefined;2656 }2657 }26582659 // Don't add events if no callback2660 if (!callback) {2661 return this;2662 }26632664 type = GESTURES_FALLBACK[type] || type;26652666 // Add the event2667 return this.each(function (el) {2668 _addEvent(el, type, callback, data, selector);2669 });2670 },26712672 /*\2673 * $(…).off2674 [ method ]2675 * Remove an event handler to the selected elements for the specified type, or all of them if no type defined.2676 > Parameters2677 - type (string) #optional A string name for the event to remove, or All if none specified.2678 - selector (string) #optional A string selector to undelegate an event from that element.2679 - callback (function) #optional A specific callback function if there are multiple registered under the same event type2680 > Usage2681 * Suppose a button element:2682 | <button id='btn'>click me</button>2683 * Register a click event handler2684 | $('#btn').on('click', function (event) {2685 | console.log('clicked!', event);2686 | });2687 * Remove the handler2688 | $('#btn').off('click');2689 * Also you could specify the handler for example:2690 | var firstFunction = function () { console.log('first fn'); };2691 | var secondFunction = function () { console.log('second fn'); };2692 | var btn = $('#btn');2693 | btn.click(firstFunction);2694 | btn.click(secondFunction);2695 * If you want to remove the click event only for the second function, do this:2696 | btn.off('click', secondFunction);2697 * Or if you want to remove All handlers (click and any other attached):2698 | btn.off();2699 \*/2700 off: function (type, selector, callback) {27012702 // Prepare the arguments27032704 // (type, callback)2705 if (Kimbo.isFunction(selector)) {2706 callback = selector;2707 selector = undefined;2708 }27092710 // Remove the event2711 return this.each(function (el) {2712 _removeEvent(el, type, callback, selector);2713 });2714 },27152716 /*\2717 * $(…).trigger2718 [ method ]2719 * Execute all handlers attached to the matched elements for the fiven event type.2720 > Parameters2721 - type (string) #optional A string name for the event to remove, or All if none specified.2722 - data (any) #optional Additional parameters to be passed to the handler in `event.data` when an event is triggered.2723 > Usage2724 * Suppose a button element:2725 | <button id='btn'>click me</button>2726 * Register a click event handler2727 | $('#btn').on('click', function (event, data) {2728 | console.log('name', data.name);2729 | });2730 * Trigger the event programatically passing some data:2731 | $('#btn').trigger('click', { name: 'denis' });2732 | // 'name denis'2733 * Allow the handler to recieve multiple data:2734 | $('#btn').on('click', function (event, name, last) {2735 | console.log('name', name);2736 | console.log('last', last);2737 | });2738 | $('#btn').trigger('click', ['denis', 'ciccale']);2739 | // Name denis2740 | // Last ciccale2741 \*/2742 trigger: function (type, data) {2743 return this.each(function (el) {2744 _triggerEvent(el, type, data);2745 });2746 },27472748 /*\2749 * $(…).hover2750 [ method ]2751 * A shortcut method to register moseenter and/or mouseleave event handlers to the matched elements.2752 > Parameters2753 - fnOver (function) A function to execute when the cursor enters the element.2754 - fnOut (function) #optional A function to execute when the cursor leaves the element.2755 > Usage2756 * Suppose a div element:2757 | <div id='box'></div>2758 * Register hover event2759 | var fnOver = function () { console.log('enter'); };2760 | var fnOut = function () { console.log('leave'); };2761 |2762 | $('.box').hover(fnOver, fnOut);2763 * When the cursor enters the `.box` element the console will log:2764 | 'enter'2765 * When the cursor leaves the `.box` element the console will log:2766 | 'leave'2767 \*/2768 hover: function (fnOver, fnOut) {2769 return this.mouseenter(fnOver).mouseleave(fnOut || fnOver);2770 }2771 });27722773 // Shortcut methods for each event type2774 Kimbo.forEach(['blur', 'change', 'click', 'contextmenu', 'dblclick', 'error',2775 'focus', 'keydown', 'keypress', 'keyup', 'load', 'mousedown', 'mouseenter', 'mouseleave', 'mousemove',2776 'mouseout', 'mouseup', 'mouseover', 'resize', 'scroll', 'select', 'submit', 'unload'], function (type) {27772778 Kimbo.fn[type] = function (data, callback) {2779 return arguments.length > 0 ? this.on(type, null, data, callback) : this.trigger(type);2780 };27812782 // Set event props for the specific type2783 fixEventProps[type] = KEY_EVENT_RE.test(type) ? KEY_EVENT_PROPS : MOUSE_EVENT_RE.test(type) ? MOUSE_EVENT_PROPS : null;2784 });27852786 // Fix mouseover and mouseout events to use mouseenter mouseleave2787 Kimbo.forEach({2788 mouseenter: 'mouseover',2789 mouseleave: 'mouseout'2790 }, function (orig, fix) {2791 specialEvents[orig] = {2792 origType: fix,27932794 handle: function (event) {2795 var target = this;2796 var related = event.relatedTarget;2797 var handleObj = event.handleObj;2798 var ret;27992800 if (!related || (related !== target && !query.contains(target, related))) {2801 event.type = handleObj.origType;2802 ret = handleObj.callback.apply(this, arguments);2803 event.type = fix;2804 }28052806 return ret;2807 }2808 };2809 });28102811});281228132814Kimbo.define('ajax', function (_) {28152816 'use strict';28172818 var JSONP_RE = /(\=)\?(&|$)|\?\?/i;28192820 var MIME_TYPES = {2821 html: 'text/html',2822 json: 'application/json',2823 script: 'text/javascript, application/javascript',2824 text: 'text/plain',2825 xml: 'application/xml, text/xml'2826 };28272828 var dataParse = {2829 json: Kimbo.parseJSON,2830 xml: Kimbo.parseXML2831 };28322833 var xhrCallbacks = {};28342835 // Success and error callbacks2836 Kimbo.forEach(['success', 'error'], function (type) {2837 xhrCallbacks[type] = function (res, msg, xhr, settings) {2838 settings = settings || xhr;2839 if (Kimbo.isFunction(settings[type])) {2840 settings[type].apply(settings.context, arguments);2841 }2842 };2843 });28442845 var _getResponse = function (response, type) {2846 return (dataParse[type] ? dataParse[type](response) : response);2847 };28482849 var _handleResponse = function (xhr, settings) {2850 var response, contentType;28512852 // Set dataType if missing2853 if (!settings.dataType) {2854 contentType = xhr.getResponseHeader('Content-Type');28552856 Kimbo.forEach(MIME_TYPES, function (name, type) {2857 if (type.match(contentType)) {2858 settings.dataType = name;2859 return false;2860 }2861 });28622863 // Fix settings headers2864 _setHeaders(settings);2865 }28662867 try {2868 response = _getResponse(xhr.responseText, settings.dataType);2869 } catch (e) {2870 response = false;2871 xhrCallbacks.error('parseerror', e, xhr, settings);2872 }28732874 return response;2875 };28762877 var _setHeaders = function (settings) {2878 if (!settings.crossDomain && !settings.headers['X-Requested-With']) {2879 settings.headers['X-Requested-With'] = 'XMLHttpRequest';2880 }28812882 if (settings.contentType) {2883 settings.headers['Content-Type'] = settings.contentType;2884 }28852886 settings.headers.Accept = MIME_TYPES[settings.dataType] || '*/*';2887 };28882889 var _timeout = function (xhr, settings) {2890 xhr.onreadystatechange = null;2891 xhr.abort();2892 xhrCallbacks.error('error', 'timeout', xhr, settings);2893 };28942895 var _createAbortTimeout = function (xhr, settings) {2896 return window.setTimeout(function () {2897 _timeout(xhr, settings);2898 }, settings.timeout);2899 };29002901 /*\2902 * $.ajaxSettings2903 [ property ]2904 * Default ajax settings object.2905 > Usage2906 * If you want to change the global and default ajax settings, change this object properties:2907 | $.ajaxSettings.error = function () {2908 | // Handle any failed ajax request in your app2909 | };2910 | $.ajaxSettings.timeout = 1000; // 1 second2911 \*/2912 Kimbo.ajaxSettings = {2913 type: 'GET',2914 async: true,2915 success: {},2916 error: {},2917 context: null,2918 headers: {},2919 data: null,2920 crossDomain: false,2921 timeout: 0,2922 contentType: 'application/x-www-form-urlencoded; charset=UTF-8',2923 xhr: function () {2924 return new window.XMLHttpRequest();2925 }2926 };29272928 /*\2929 * $.ajax2930 [ method ]2931 * Perform an asynchronous HTTP (Ajax) request.2932 > Parameters2933 - options (object) #optional An object with options2934 o {2935 o url (string) Url to make the request.2936 o type (string) #optional Type of request. Could be `'GET'` or `'POST'`. Default value is `'GET'`.2937 o async (boolean) #optional Default value is `true` if you want synchronous requests set option to `false`.2938 o success (function) #optional A function that will be called if the request succeeds. Recieving (response, responseMessage, xhr, settings).2939 o error (function) #optional A function that will be called if the request fails. Recieving (response, responseMessage, xhr, settings).2940 o context (object) #optional The context in which all ajax request are made. By default the context are the settings object. Could be any DOM element.2941 o headers (object) #optional An object with additional header key/value pairs to send along with the request.2942 o data (string|object) #optional Additional data to send with the request, if it is an object is converted to a query string.2943 o xhr (function) #optional A function that returns a `new XMLHttpRequest()` object by default.2944 o crossDomain (boolean) #optional Indicate wether you want to force crossDomain requests. `false` by defualt.2945 o timeout (number) #optional Set a default timeout in milliseconds for the request.2946 o contentType (string) #optional The default and finest contentType for most cases is `'application/x-www-form-urlencoded; charset=UTF-8'`.2947 o }2948 = (object) The native xhr object.2949 > Usage2950 * Get a username passing an id to the /users url2951 | $.ajax({2952 | url '/users',2953 | data: {2954 | id: 32955 | },2956 | success: function (response, responseMessage, xhr, settings) {2957 | // Success...2958 | },2959 | error: function (response, responseMessage, xhr, settings) {2960 | // Error...2961 | }2962 | });2963 \*/2964 Kimbo.ajax = function (options) {2965 var settings = Kimbo.extend({}, Kimbo.ajaxSettings, options);2966 var xhr, abortTimeout, callback;29672968 // Add data to url2969 if (settings.data) {2970 settings.url += (/\?/.test(settings.url) ? '&' : '?') +2971 Kimbo.param(settings.data);2972 delete settings.data;2973 }29742975 // Set default context2976 if (!settings.context) {2977 settings.context = settings;2978 }29792980 // Check if its jsonp2981 if (JSONP_RE.test(settings.url)) {2982 return _getJSONP(settings);2983 }29842985 // Create new instance2986 xhr = settings.xhr();29872988 // User specified timeout2989 if (settings.timeout > 0) {2990 abortTimeout = _createAbortTimeout(xhr, settings);2991 }29922993 // On complete callback2994 callback = function () {2995 var response, status;29962997 // Request complete2998 if (xhr.readyState === 4) {2999 status = xhr.status;30003001 // Clear timeout3002 window.clearTimeout(abortTimeout);30033004 // Scuccess3005 if ((status >= 200 && status < 300) || status === 304) {3006 if (settings.async) {3007 response = _handleResponse(xhr, settings);3008 if (response !== false) {3009 xhrCallbacks.success(response, xhr, settings);3010 }3011 }30123013 // Fail3014 } else {3015 xhrCallbacks.error('error', xhr.statusText, xhr, settings);3016 }3017 }3018 };30193020 // Listen for response3021 xhr.onreadystatechange = callback;30223023 // Init request3024 xhr.open(settings.type, settings.url, settings.async);30253026 // Set settings headers3027 _setHeaders(settings);30283029 // Set xhr headers3030 Kimbo.forEach(settings.headers, function (header, value) {3031 xhr.setRequestHeader(header, value);3032 });30333034 // Try to send request3035 xhr.send(settings.data);30363037 return (settings.async) ? xhr : callback();3038 };30393040 /*\3041 * $.get3042 [ method ]3043 * Load data from the server using HTTP GET request.3044 > Parameters3045 - url (string) A string containing the URL to which the request is sent.3046 - data (string|object) #optional An option string or object with data params to send to the server.3047 - callback (function) A callback function to execute if the request succeeds.3048 - type (string) #optional String with the type of the data to send (intelligent guess by default).3049 > Usage3050 | $.get('url/users.php', { id: '123' }, function (data) {3051 | // Success3052 | console.log('response:', data);3053 | });3054 * This method is a shorthand for the $.ajax3055 | $.ajax({3056 | url: url,3057 | data: data,3058 | success: success,3059 | dataType: dataType3060 | });3061 \*/30623063 /*\3064 * $.post3065 [ method ]3066 * Load data from the server using HTTP POST request.3067 > Parameters3068 - url (string) A string containing the URL to which the request is sent.3069 - data (string|object) #optional An option string or object with data params to send to the server.3070 - callback (function) A callback function to execute if the request succeeds.3071 - type (string) #optional String with the type of the data to send (intelligent guess by default).3072 > Usage3073 | $.post('url/users.php', { user: 'denis', pass: '123' }, function (data) {3074 | // Success3075 | console.log('response:', data);3076 | });3077 * This method is a shorthand for the $.ajax3078 | $.ajax({3079 | type: 'POST',3080 | url: url,3081 | data: data,3082 | success: success,3083 | dataType: dataType3084 | });3085 \*/3086 Kimbo.forEach(['get', 'post'], function (method) {3087 Kimbo[method] = function (url, data, callback, type) {30883089 // Prepare arguments3090 if (Kimbo.isFunction(data)) {3091 type = type || callback;3092 callback = data;3093 data = null;3094 }30953096 // Call ajax3097 return Kimbo.ajax({3098 type: method.toUpperCase(),3099 url: url,3100 data: data,3101 success: callback,3102 dataType: type3103 });3104 };3105 });31063107 Kimbo.extend({3108 /*\3109 * $.getScript3110 [ method ]3111 * Load a JavaScript file from the server using a GET HTTP request, then execute it.3112 > Parameters3113 - url (string) A string containing the URL to which the request is sent.3114 - callback (function) A callback function to execute if the request succeeds.3115 > Usage3116 | $.getScript('url/script.js', function (data) {3117 | // Success3118 | console.log('response:', data);3119 | });3120 * This method is a shorthand for the $.ajax3121 | $.ajax({3122 | url: url,3123 | dataType: 'script',3124 | success: success3125 | });3126 \*/3127 getScript: function (url, callback) {3128 return Kimbo.get(url, callback, 'script');3129 },31303131 /*\3132 * $.getJSON3133 [ method ]3134 * Load data from the server using HTTP POST request.3135 > Parameters3136 - url (string) A string containing the URL to which the request is sent.3137 - data (string|object) #optional An option string or object with data params to send to the server.3138 - callback (function) A callback function to execute if the request succeeds.3139 - type (string) #optional String with the type of the data to send (intelligent guess by default).3140 > Usage3141 | $.getJSON('url/test.json', { id: '2' }, function (data) {3142 | // Success3143 | console.log('response:', data);3144 | });3145 * This method is a shorthand for the $.ajax3146 | $.ajax({3147 | url: url,3148 | dataType: 'json',3149 | success: success3150 | });3151 * To get json data with jsonp:3152 | $.getJSON('http://search.twitter.com/search.json?callback=?', 'q=#javascript', function (data) {3153 | console.log(data);3154 | });3155 \*/3156 getJSON: function (url, data, callback) {3157 return Kimbo.get(url, data, callback, 'json');3158 }3159 });31603161 // getJSONP internal use3162 var _getJSONP = function (settings) {3163 var jsonpCallback = Kimbo.ref + '_' + Date.now();3164 var script = _.document.createElement('script');3165 var head = _.document.head;3166 var xhr = {3167 abort: function () {3168 window.clearTimeout(abortTimeout);3169 head.removeChild(script);3170 delete window[jsonpCallback];3171 }3172 };3173 var abortTimeout;31743175 // User specified timeout3176 if (settings.timeout > 0) {3177 abortTimeout = _createAbortTimeout(xhr, settings);3178 }31793180 // Set url3181 script.src = settings.url.replace(JSONP_RE, '$1' + jsonpCallback + '$2');31823183 // Jsonp callback3184 window[jsonpCallback] = function (response) {31853186 // Remove script3187 xhr.abort();31883189 // Fake xhr3190 Kimbo.extend(xhr, {3191 statusText: 'OK',3192 status: 200,3193 response: response,3194 headers: settings.headers3195 });31963197 // Success3198 xhrCallbacks.success(response, xhr, settings);3199 };32003201 // Set settings headers3202 _setHeaders(settings);32033204 // Apend script to head to make the request3205 head.appendChild(script);32063207 // Return fake xhr object to abort manually3208 return xhr;3209 };32103211 /*\3212 * $.param3213 [ method ]3214 * Create a serialized representation of an array or object, suitable for use in a URL query string or Ajax request.3215 > Parameters3216 - data (string|object) A string or object to serialize.3217 > Usage3218 | var obj = { name: 'Denis', last: 'Ciccale' };3219 | var serialized = $.param(obj); // 'name=Denis&last=Ciccale'3220 \*/3221 Kimbo.param = function (data) {3222 var params = '';32233224 if (Kimbo.isObject(data)) {3225 Kimbo.forEach(data, function (name, value) {3226 params += name + '=' + value + '&';3227 });3228 } else {3229 params = data;3230 }32313232 return window.encodeURIComponent(params)3233 .replace(/%20/g, '+')3234 .replace(/%\d[D6F]/g, window.unescape)3235 .replace(/^\?|&$/g, '');3236 };3237});3238