├── .gitignore ├── .jshintrc ├── .npmignore ├── README.md ├── bower.json ├── chibi-min.js ├── chibi.js ├── gulpfile.js ├── package.json └── tests └── runner.html /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | package-lock.json 3 | npm-debug.log 4 | tests/server.js 5 | tests/sandpit.html 6 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | { 2 | "maxerr" : 50, // {int} Maximum error before stopping 3 | 4 | // Enforcing 5 | "bitwise" : true, // true: Prohibit bitwise operators (&, |, ^, etc.) 6 | "camelcase" : false, // true: Identifiers must be in camelCase 7 | "curly" : false, // true: Require {} for every new block or scope 8 | "eqeqeq" : true, // true: Require triple equals (===) for comparison 9 | "forin" : false, // true: Require filtering for..in loops with obj.hasOwnProperty() 10 | "immed" : true, // true: Require immediate invocations to be wrapped in parens e.g. `(function () { } ());` 11 | "indent" : 2, // {int} Number of spaces to use for indentation 12 | "latedef" : true, // true: Require variables/functions to be defined before being used 13 | "newcap" : true, // true: Require capitalization of all constructor functions e.g. `new F()` 14 | "noarg" : true, // true: Prohibit use of `arguments.caller` and `arguments.callee` 15 | "noempty" : true, // true: Prohibit use of empty blocks 16 | "nonew" : true, // true: Prohibit use of constructors for side-effects (without assignment) 17 | "plusplus" : true, // true: Prohibit use of `++` & `--` 18 | "quotmark" : "single", // Quotation mark consistency: 19 | // false : do nothing (default) 20 | // true : ensure whatever is used is consistent 21 | // "single" : require single quotes 22 | // "double" : require double quotes 23 | "undef" : true, // true: Require all non-global variables to be declared (prevents global leaks) 24 | "unused" : true, // true: Require all defined variables be used 25 | "strict" : true, // true: Requires all functions run in ES5 Strict Mode 26 | "maxparams" : false, // {int} Max number of formal params allowed per function 27 | "maxdepth" : false, // {int} Max depth of nested blocks (within functions) 28 | "maxstatements" : false, // {int} Max number statements per function 29 | "maxcomplexity" : false, // {int} Max cyclomatic complexity per function 30 | "maxlen" : false, // {int} Max number of characters per line 31 | 32 | // Relaxing 33 | "asi" : false, // true: Tolerate Automatic Semicolon Insertion (no semicolons) 34 | "boss" : false, // true: Tolerate assignments where comparisons would be expected 35 | "debug" : false, // true: Allow debugger statements e.g. browser breakpoints. 36 | "eqnull" : false, // true: Tolerate use of `== null` 37 | "es5" : false, // true: Allow ES5 syntax (ex: getters and setters) 38 | "esnext" : false, // true: Allow ES.next (ES6) syntax (ex: `const`) 39 | "moz" : false, // true: Allow Mozilla specific syntax (extends and overrides esnext features) 40 | // (ex: `for each`, multiple try/catch, function expression…) 41 | "evil" : false, // true: Tolerate use of `eval` and `new Function()` 42 | "expr" : false, // true: Tolerate `ExpressionStatement` as Programs 43 | "funcscope" : false, // true: Tolerate defining variables inside control statements 44 | "globalstrict" : false, // true: Allow global "use strict" (also enables 'strict') 45 | "iterator" : false, // true: Tolerate using the `__iterator__` property 46 | "lastsemic" : false, // true: Tolerate omitting a semicolon for the last statement of a 1-line block 47 | "laxbreak" : false, // true: Tolerate possibly unsafe line breakings 48 | "laxcomma" : false, // true: Tolerate comma-first style coding 49 | "loopfunc" : false, // true: Tolerate functions being defined in loops 50 | "multistr" : false, // true: Tolerate multi-line strings 51 | "proto" : false, // true: Tolerate using the `__proto__` property 52 | "scripturl" : false, // true: Tolerate script-targeted URLs 53 | "shadow" : false, // true: Allows re-define variables later in code e.g. `var x=1; x=2;` 54 | "sub" : false, // true: Tolerate using `[]` notation when it can still be expressed in dot notation 55 | "supernew" : false, // true: Tolerate `new function () { ... };` and `new Object;` 56 | "validthis" : false, // true: Tolerate using this in a non-constructor function 57 | 58 | // Environments 59 | "browser" : true, // Web Browser (window, document, etc) 60 | "couch" : false, // CouchDB 61 | "devel" : true, // Development/debugging (alert, confirm, etc) 62 | "dojo" : false, // Dojo Toolkit 63 | "jquery" : false, // jQuery 64 | "mootools" : false, // MooTools 65 | "node" : false, // Node.js 66 | "nonstandard" : false, // Widely adopted globals (escape, unescape, etc) 67 | "prototypejs" : false, // Prototype and Scriptaculous 68 | "rhino" : false, // Rhino 69 | "worker" : false, // Web Workers 70 | "wsh" : true, // Windows Scripting Host 71 | "yui" : false, // Yahoo User Interface 72 | 73 | // Custom Globals 74 | "globals": { 75 | "angular" : false, 76 | "define" : false, 77 | "require" : false, 78 | "exports" : false, 79 | "module" : false, 80 | "describe" : false, 81 | "before" : false, 82 | "beforeEach" : false, 83 | "after" : false, 84 | "afterEach" : false, 85 | "it" : false, 86 | "inject" : false, 87 | "expect" : false, 88 | "spyOn" : false 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | tests/sandpit.html 2 | tests/server.js 3 | bower.json 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Chibi v3.0.9 2 | 3 | #### A tiny JavaScript micro-library 4 | 5 | Think it's OK to serve up 30KB over 3G just to manipulate a couple of DOM elements? Of course you don't because that's an asshat move and you're no asshat. You'll probably instead use a couple of lines of vanilla JavaScript, perhaps a little CSS `:active` with transitions, all while riding a unicorn bareback through a double rainbow, no hands. 6 | 7 | Working on something a wee bit more complex? Unlike fat, grown-up frameworks and libraries, Chibi focuses on just the essentials, melted down and mixed with optimisation rainbows to create a really light micro-library that allows you to do awesome things, asshatory free. 8 | 9 | ### The sweet, juicy bits 10 | 11 | * Chibi is really tiny: 7KB minified, 3KB gzipped, small enough to stick inline on single page web apps, saving an extra HTTP request. 12 | * Supports modern desktop and mobile browsers including Chrome, Firefox, Internet Explorer, Opera and Safari (see Browser Support below). 13 | * Even supports creaky old browsers like IE6, I don't know why you would do this. 14 | * No animation cruft, instead use CSS transitions like a nice person. 15 | * In modern browsers, Chibi typically executes DOM manipulation 20% to 50% faster than grown-up libraries. 16 | 17 | ### The lumpy, chewy bits 18 | 19 | * Chibi's polyfill for `document.querySelectorAll()` is limited to browser CSS support and is not as fast as some dedicated selector engines. This means no `input[type=text]` or `p:nth-child(even)` selectors with IE6. Fortunately modern browser don't need this polyfill. 20 | * Ancient browsers that support neither `document.querySelectorAll()` nor `window.getComputedStyle` can bugger off. 21 | 22 | **Version 3 is a major update with many breaking changes. If it's difficult to embrace change, version 1 is still available [here](https://github.com/kylebarrow/chibi/tree/1.1.5).** 23 | 24 | ### Browser Support 25 | 26 | Chibi has been tested with and supports the following browsers: 27 | 28 | * Android Browser 2.1 or higher 29 | * Blackberry Browser 6 or higher 30 | * Chrome 31 | * Chrome Android 32 | * Firefox 3.5 or higher 33 | * Firefox Mobile 34 | * Internet Explorer 6 or higher 35 | * Internet Explorer Mobile 9 or higher 36 | * Opera 10 or higher 37 | * Opera Mini 38 | * Opera Mobile 10 or higher 39 | * Safari 3.2 or higher 40 | * Safari Mobile 3.2 or higher 41 | * Symbian^3 Browser or higher 42 | 43 | Chibi should also work with any other browser that supports `document.querySelectorAll()`. 44 | 45 | ### Installation 46 | 47 | Grab it from [here](https://github.com/kylebarrow/chibi/) or 48 | 49 | ```shell 50 | npm install chibijs 51 | ``` 52 | 53 | ### Using Chibi 54 | 55 | Chibi syntax is similar to that pioneered by jQuery: `$(selector).method()`. It intentionally uses the same `$` namespace as jQuery because micro-libraries and grown-up libraries should never mix. 56 | 57 | Chibi's supports standard CSS selectors but you can also pass in DOM elements directly: 58 | 59 | ##### CSS selector 60 | 61 | ```js 62 | $("p") // Returns an array of all paragraph elements 63 | $("p").hide() // Hides all paragraphs 64 | $("#foo").show() // Shows element with id equal to "foo" 65 | $(".foo").hide() // Hides elements with "foo" CSS class 66 | ``` 67 | 68 | ##### A DOM element selector, pointless 69 | 70 | ```js 71 | $(document.getElementsByTagName('p')).hide() // Hides all paragraphs 72 | ``` 73 | 74 | ##### A more interesting DOM element selector 75 | 76 | ```js 77 | $($('p')[0]).hide() // Hides first paragraph 78 | ``` 79 | 80 | ### Methods 81 | 82 | Chibi supports method chaining `$(selector).method().anothermethod().evenmoremethods()` of any method not returning a value (string, boolean, etc.). 83 | 84 | #### $().ready(handler) 85 | *Fires handler when the DOM is ready.* 86 | 87 | Use to fire a function when the DOM is ready. Including a selector makes no sense for this method, don't do it. 88 | 89 | ```js 90 | $().ready(function(){ 91 | // Do awesome 92 | }); 93 | ``` 94 | or perhaps 95 | 96 | ```js 97 | function foo() { 98 | // Do awesome 99 | } 100 | 101 | $().ready(foo); 102 | ``` 103 | 104 | #### $().loaded(handler) 105 | *Fires handler when the page is loaded.* 106 | 107 | Use to fire a function when the page is loaded. Including a selector makes no sense for this method, don't do it. 108 | 109 | ```js 110 | function foo() { 111 | // Do awesome 112 | } 113 | 114 | $().loaded(foo); 115 | ``` 116 | 117 | #### $(selector).each(function) 118 | *Executes a function on each matching element* 119 | 120 | **each** passes each matching element to the specified function. 121 | 122 | ```html 123 | 124 | 125 | 126 | 127 | 128 | 129 |

Foo

130 |

Bar

131 | 145 | 146 | 147 | ``` 148 | 149 | #### $(selector).first() 150 | *Finds the first matching element.* 151 | 152 | **first** will return an array containing the first matching element, useful when working with crappy browsers like IE6 with weak CSS pseudo support, especially when combined with method chaining. 153 | 154 | #### $(selector).last() 155 | *Finds the last matching element.* 156 | 157 | **last** will return an array containing the last matching element. 158 | 159 | #### $(selector).odd() 160 | *Finds matching odd elements.* 161 | 162 | **odd** will return an array containing matching odd elements. 163 | 164 | #### $(selector).even() 165 | *Finds matching even elements.* 166 | 167 | **even** will return an array containing matching even elements. 168 | 169 | 170 | ```html 171 | 172 | 173 | 174 | 175 | 176 | 177 |

Foo

178 |

Bar

179 |

Foo

180 |

Bar

181 | 187 | 188 | 189 | ``` 190 | 191 | #### $(selector).hide() 192 | *Hides matching elements.* 193 | 194 | ```html 195 | 196 | 197 | 198 | 199 | 200 | 201 |

Foo

202 |

Bar

203 | 206 | 207 | 208 | ``` 209 | 210 | #### $(selector).show() 211 | *Shows matching elements.* 212 | 213 | ```html 214 | 215 | 216 | 217 | 220 | 221 | 222 | 223 |

Foo

224 |

Bar

225 | 228 | 229 | 230 | ``` 231 | 232 | #### $(selector).toggle() 233 | *Toggles visibility of matching elements.* 234 | 235 | ```html 236 | 237 | 238 | 239 | 240 | 241 | 242 |

Foo

243 |

Bar

244 | 247 | 248 | 249 | ``` 250 | 251 | #### $(selector).remove() 252 | *Removes matching elements from the DOM tree.* 253 | 254 | ```html 255 | 256 | 257 | 258 | 259 | 260 | 261 |

Foo

262 |

Bar

263 | 266 | 267 | 268 | ``` 269 | 270 | #### $(selector).css(property, value) 271 | *Gets or optionally sets the CSS property for matching elements.* 272 | 273 | **css** with no *value* will return the CSS property string of the first matching element found. **css** will return the computed property value if the property isn't explicitly set which can vary between browsers. For example, an element with no explicit font weight will return 'normal' in Opera and Webkit browsers but '400' in Firefox and Internet Explorer browsers. 274 | 275 | *value* will set the value of the CSS property for all matching elements. 276 | 277 | ```html 278 | 279 | 280 | 281 | 284 | 285 | 286 | 287 |

Foo

288 |

Bar

289 | 293 | 294 | 295 | ``` 296 | 297 | #### $(selector).getClass() 298 | *Gets class for first matching element found.* 299 | 300 | ```html 301 | 302 | 303 | 304 | 309 | 310 | 311 | 312 |
Foo
313 |
Bar
314 |

Foo

315 |

Bar

316 | 320 | 321 | 322 | ``` 323 | 324 | #### $(selector).setClass(class) 325 | *Sets the class of all matching elements replacing any existing element class with this class.* 326 | 327 | ```html 328 | 329 | 330 | 331 | 336 | 337 | 338 | 339 |

Foo

340 |

Bar

341 | 344 | 345 | 346 | ``` 347 | 348 | #### $(selector).addClass(class) 349 | *Adds a class to all matching elements.* 350 | 351 | ```html 352 | 353 | 354 | 355 | 360 | 361 | 362 | 363 |

Foo

364 |

Bar

365 | 368 | 369 | 370 | ``` 371 | 372 | #### $(selector).removeClass(class) 373 | *Removes class from all matching elements.* 374 | 375 | ```html 376 | 377 | 378 | 379 | 384 | 385 | 386 | 387 |

Foo

388 |

Bar

389 | 392 | 393 | 394 | ``` 395 | 396 | #### $(selector).toggleClass(class) 397 | *Toggles class for matching elements.* 398 | 399 | ```html 400 | 401 | 402 | 403 | 408 | 409 | 410 | 411 |

Foo

412 |

Bar

413 | 416 | 417 | 418 | ``` 419 | 420 | #### $(selector).hasClass(class) 421 | *Returns true if first matching element found includes the class.* 422 | 423 | ```html 424 | 425 | 426 | 427 | 432 | 433 | 434 | 435 |
Foo
436 |
Bar
437 |

Foo

438 |

Bar

439 | 443 | 444 | 445 | ``` 446 | 447 | #### $(selector).html(html) 448 | *Gets or optionally sets the inner HTML of matching elements.* 449 | 450 | **html** with no arguments will return the HTML string of the first matching element found. 451 | 452 | If the *html* argument is specified, this will replace the inner HTML of all matching elements. 453 | 454 | ```html 455 | 456 | 457 | 458 | 459 | 460 | 461 |

Foo

462 |

Bar

463 | 467 | 468 | 469 | ``` 470 | 471 | #### $(selector).htmlBefore(value) 472 | *Inserts html before all matching elements.* 473 | 474 | ```html 475 | 476 | 477 | 478 | 479 | 480 | 481 |

Foo

482 |

Bar

483 | 486 | 487 | 488 | ``` 489 | 490 | #### $(selector).htmlAfter(value) 491 | *Inserts html after all matching elements.* 492 | 493 | ```html 494 | 495 | 496 | 497 | 498 | 499 | 500 |

Foo

501 |

Bar

502 | 505 | 506 | 507 | ``` 508 | 509 | #### $(selector).htmlAppend(value) 510 | *Inserts html after all matching elements inner elements.* 511 | 512 | ```html 513 | 514 | 515 | 516 | 517 | 518 | 519 |

Foo

520 |

Bar

521 | 524 | 525 | 526 | ``` 527 | 528 | #### $(selector).htmlPrepend(value) 529 | *Inserts html before all matching elements inner elements.* 530 | 531 | ```html 532 | 533 | 534 | 535 | 536 | 537 | 538 |

Foo

539 |

Bar

540 | 543 | 544 | 545 | ``` 546 | 547 | #### $(selector).attr(property, value) 548 | *Gets or optionally sets the property for all matching elements.* 549 | 550 | **attr** with no value argument will return the property string of the first matching element found. 551 | 552 | *value* will set the value of the property for all matching elements. 553 | 554 | ```html 555 | 556 | 557 | 558 | 559 | 560 | 561 |

Foobar

562 | 566 | 567 | 568 | ``` 569 | 570 | #### $(selector).data(key, value) 571 | *Gets or optionally sets the data key value for all matching elements.* 572 | 573 | **data** with no *value* argument will return the data key value of the first matching element found. 574 | 575 | *value* will set the value of the data key for all matching elements. 576 | 577 | ```html 578 | 579 | 580 | 581 | 582 | 583 | 584 |

585 |

586 | 590 | 591 | 592 | ``` 593 | 594 | #### $(selector).val(value) 595 | *Gets or optionally sets the value of matching form elements.* 596 | 597 | **val** with no arguments will return the value string of the first matching form element found. For select lists, Chibi will return the selected option value string, if any. For select lists with multiple selects, Chibi will return an array of selected option value strings, if any. 598 | 599 | *value* will set the value of matching form field elements. For select lists, this will select the option matching this value. For select lists with multiple selects, passing an array of values will select all options in the select list matching these values. 600 | 601 | 602 | ```html 603 | 604 | 605 | 606 | 607 | 608 | 609 |
610 | 611 | 615 |
616 | 623 | 624 | 625 | ``` 626 | 627 | #### $(selector).checked(boolean) 628 | *Gets or optionally sets checked status of checkbox or radio elements.* 629 | 630 | **checked** with no arguments will return the checked boolean of the first matching element found. 631 | 632 | *boolean* will set the checked status of matching checkbox or radio elements. 633 | 634 | ```html 635 | 636 | 637 | 638 | 639 | 640 | 641 |
642 | 643 |
644 | 650 | 651 | 652 | ``` 653 | 654 | #### $(selector).on(event, listener) 655 | *Adds an event listener to all matching elements.* 656 | 657 | **on** adds an event listener to all matching elements. There is no need to use the HTML event format ('on' + event) as Chibi will automatically prefix the event as required. **on** also supports passing `window` and `document` as the selector. 658 | 659 | ```html 660 | 661 | 662 | 663 | 664 | 665 | 666 |

Foo

667 |

Bar

668 | 675 | 676 | 677 | ``` 678 | 679 | #### $(selector).off(event, listener) 680 | *Removed an event listener from all matching elements.* 681 | 682 | **off** removed an event listener from all matching elements. There is no need to use the HTML event format ('off' + event) as Chibi will automatically prefix the event as required. **off** also supports passing `window` and `document` as the selector. 683 | 684 | ```html 685 | 686 | 687 | 688 | 689 | 690 | 691 |

Foo

692 |

Bar

693 | 701 | 702 | 703 | ``` 704 | 705 | #### $(selector).get(url, callback, nocache, nojsonp) 706 | *Sends a GET AJAX request, optionally firing a callback with the XHR `responseText` and `status`. Alias of $(selector).ajax with GET method* 707 | 708 | When *nocache* is true, a `_ts` time stamp is added to the URL to prevent caching, yes, I'm looking at you Android Browser and iOS 6. 709 | 710 | **get** supports JSON as a selector ({name:value}), useful for when you want to send data without using form elements. 711 | 712 | For cross-domain requests, **get** uses JSONP by default but this is overridden when *nojsonp* is true. JSONP requests will apply any *callback* to `callback=?` or similar in the **get** url. 713 | 714 | ```html 715 | 716 | 717 | 718 | 719 | 720 | 721 |
722 | 723 |
724 | 732 | 733 | 734 | ``` 735 | ```html 736 | 737 | 738 | 739 | 740 | 741 | 742 | 753 | 754 | 755 | ``` 756 | 757 | #### $(selector).post(url, callback, nocache) 758 | *Sends a POST AJAX request, optionally firing a callback with the XHR `responseText` and `status`. Alias of $(selector).ajax with POST method* 759 | 760 | When *nocache* is true, a `_ts` time stamp is added to the URL to prevent caching. 761 | 762 | **post** supports JSON as a selector ({name:value}), useful for when you want to send data without using form elements. 763 | 764 | ```html 765 | 766 | 767 | 768 | 769 | 770 | 771 |
772 | 773 |
774 | 783 | 784 | 785 | ``` 786 | 787 | #### $(selector).ajax(url, method, callback, nocache, nojsonp) 788 | *Sends an AJAX request, optionally firing a callback with the XHR `responseText` and `status`* 789 | 790 | **ajax** uses the GET method if none is specified. When *nocache* is true, a `_ts` time stamp is added to the URL to prevent caching. 791 | 792 | **ajax** supports JSON as a selector ({name:value}), useful for when you want to send data without using form elements. 793 | 794 | For cross-domain requests, **ajax** uses JSONP by default but this is overridden when *nojsonp* is true. JSONP requests will apply any *callback* to `callback=?` or similar in the **ajax** url. The *method* is obviously always `GET` for JSONP requests. 795 | 796 | ```html 797 | 798 | 799 | 800 | 801 | 802 | 803 |
804 | 805 |
806 | 818 | 819 | 820 | ``` 821 | ```html 822 | 823 | 824 | 825 | 826 | 827 | 828 | 839 | 840 | 841 | ``` 842 | 843 | ### Modify, build, contribute 844 | 845 | ```shell 846 | npm install 847 | gulp 848 | ``` 849 | 850 | ##### FIN 851 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "chibi", 3 | "description": "A tiny JavaScript micro-library", 4 | "main": "chibi-min.js", 5 | "license": "MIT", 6 | "ignore": [ 7 | "node_modules", 8 | "tests", 9 | ".*", 10 | "gulpfile.js", 11 | "package.json" 12 | ], 13 | "keywords": [ 14 | "browser", 15 | "chibi", 16 | "dom", 17 | "library", 18 | "microlibrary" 19 | ], 20 | "authors": [ 21 | "Kyle Barrow (https://barrow.io)" 22 | ], 23 | "homepage": "https://github.com/kylebarrow/chibi", 24 | "repository": { 25 | "type": "git", 26 | "url": "https://github.com/kylebarrow/chibi.git" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /chibi-min.js: -------------------------------------------------------------------------------- 1 | /*!chibi 3.0.9, Copyright 2012-2017 Kyle Barrow, released under MIT license */ 2 | !function(){"use strict";function e(){var e;for(h=!0,e=0;e=0;n-=1)e(t[n])}function r(e){return e.replace(/-\w/g,function(e){return e.charAt(1).toUpperCase()})}function a(e,t){return e.currentStyle?e.currentStyle[r(t)]:v.getComputedStyle?v.getComputedStyle(e,null).getPropertyValue(t):null}function o(e,t){return encodeURIComponent(e).replace(/%20/g,"+")+"="+encodeURIComponent(t).replace(/%20/g,"+")}function c(e,t,n){try{e.style[r(t)]=n}catch(e){console.error('Could not set css style property "'+t+'".')}}function s(e){e.style.display="","none"===a(e,"display")&&(e.style.display="block")}function i(e){var t,r,a,c="";if(e.constructor===Object){for(t in e)if(e.hasOwnProperty(t))if(e[t].constructor===Array)for(r=0;r0&&(c+="&"+o(t.name,t.value));break;case"select-multiple":for(a=0;a0?c.substring(1):""}function u(e,t,r){var a,o,c,s,i=!1;return e&&(a=e.split(/\s+/),n(function(e){for(s=0;s0)return T[0].className.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,"").replace(/\s+/," ")},t.setClass=function(e){return(e||""===e)&&n(function(t){t.className=e},T),t},t.addClass=function(e){return e&&n(function(t){t.className+=" "+e},T),t},t.removeClass=function(e){return u(e,"remove",T),t},t.toggleClass=function(e){return u(e,"toggle",T),t},t.hasClass=function(e){return u(e,"has",T)},t.html=function(e){return e||""===e?(n(function(t){t.innerHTML=e},T),t):T[0]?T[0].innerHTML:void 0},t.htmlBefore=function(e){return l(e,"before",T),t},t.htmlAfter=function(e){return l(e,"after",T),t},t.htmlAppend=function(e){return l(e,"append",T),t},t.htmlPrepend=function(e){return l(e,"prepend",T),t},t.attr=function(e,r){if(e){if(e=e.toLowerCase(),r||""===r)return n(function(t){"style"===e?t.style.cssText=r:"class"===e?t.className=r:t.setAttribute(e,r)},T),t;if(T[0])if("style"===e){if(T[0].style.cssText)return T[0].style.cssText}else if("class"===e){if(T[0].className)return T[0].className}else if(T[0].getAttribute(e))return T[0].getAttribute(e)}},t.data=function(e,n){if(e)return t.attr("data-"+e,n)},t.val=function(e){var r,a,o;if(e||""===e)return n(function(t){switch(t.nodeName){case"SELECT":for("string"!=typeof e&&"number"!=typeof e||(e=[e]),a=0;a1?r:r[0];case"INPUT":case"TEXTAREA":case"BUTTON":return T[0].value}},t.checked=function(e){return"boolean"==typeof e?(n(function(t){"INPUT"!==t.nodeName||"checkbox"!==t.type&&"radio"!==t.type||(t.checked=e)},T),t):!T[0]||"INPUT"!==T[0].nodeName||"checkbox"!==T[0].type&&"radio"!==T[0].type?void 0:!!T[0].checked},t.on=function(r,a){return e!==v&&e!==g||(T=[e]),n(function(e){g.addEventListener?e.addEventListener(r,a,!1):g.attachEvent&&(e[r+a]=function(){return a.apply(e,arguments)},e.attachEvent("on"+r,e[r+a]))},T),t},t.off=function(r,a){return e!==v&&e!==g||(T=[e]),n(function(e){g.addEventListener?e.removeEventListener(r,a,!1):g.attachEvent&&(e.detachEvent("on"+r,e[r+a]),e[r+a]=null)},T),t},t.ajax=function(e,n,r,a,o){var c,s,u=i(T),l=n?n.toUpperCase():"GET",f=new RegExp("http[s]?://(.*?)/","gi").exec(e),d="_ts="+ +new Date,p=g.getElementsByTagName("head")[0],h="chibi"+ +new Date+(m+=1);return!u||"GET"!==l&&"DELETE"!==l||(e+=-1===e.indexOf("?")?"?"+u:"&"+u,u=null),"GET"===l&&!o&&f&&v.location.host!==f[1]?(a&&(e+=-1===e.indexOf("?")?"?"+d:"&"+d),e=e.replace("=%3F","=?"),r&&-1!==e.indexOf("=?")&&(e=e.replace("=?","="+h),v[h]=function(e){try{r(e,200)}catch(e){}v[h]=void 0}),(s=document.createElement("script")).async=!0,s.src=e,s.onload=function(){p.removeChild(s)},p.appendChild(s)):(v.XMLHttpRequest?c=new XMLHttpRequest:v.ActiveXObject&&(c=new ActiveXObject("Microsoft.XMLHTTP")),c&&(a&&(e+=-1===e.indexOf("?")?"?"+d:"&"+d),c.open(l,e,!0),c.onreadystatechange=function(){4===c.readyState&&r&&r(c.responseText,c.status)},c.setRequestHeader("X-Requested-With","XMLHttpRequest"),"POST"!==l&&"PUT"!==l||c.setRequestHeader("Content-Type","application/x-www-form-urlencoded"),c.send(u))),t},t.get=function(e,n,r,a){return t.ajax(e,"get",n,r,a)},t.post=function(e,n,r){return t.ajax(e,"post",n,r)},t}var d=[],p=[],h=!1,y=!1,m=0,g=document,v=window;g.addEventListener?(g.addEventListener("DOMContentLoaded",e,!1),v.addEventListener("load",t,!1)):g.attachEvent?(g.attachEvent("onreadystatechange",e),v.attachEvent("onload",t)):v.onload=t,v.$=f}(); -------------------------------------------------------------------------------- /chibi.js: -------------------------------------------------------------------------------- 1 | /*!chibi 3.0.9, Copyright 2012-2017 Kyle Barrow, released under MIT license */ 2 | (function () { 3 | 'use strict'; 4 | 5 | var readyfn = [], 6 | loadedfn = [], 7 | domready = false, 8 | pageloaded = false, 9 | jsonpcount = 0, 10 | d = document, 11 | w = window; 12 | 13 | // Fire any function calls on ready event 14 | function fireReady() { 15 | var i; 16 | domready = true; 17 | for (i = 0; i < readyfn.length; i += 1) { 18 | readyfn[i](); 19 | } 20 | readyfn = []; 21 | } 22 | 23 | // Fire any function calls on loaded event 24 | function fireLoaded() { 25 | var i; 26 | pageloaded = true; 27 | // For browsers with no DOM loaded support 28 | if (!domready) { 29 | fireReady(); 30 | } 31 | for (i = 0; i < loadedfn.length; i += 1) { 32 | loadedfn[i](); 33 | } 34 | loadedfn = []; 35 | } 36 | 37 | // Check DOM ready, page loaded 38 | if (d.addEventListener) { 39 | // Standards 40 | d.addEventListener('DOMContentLoaded', fireReady, false); 41 | w.addEventListener('load', fireLoaded, false); 42 | } else if (d.attachEvent) { 43 | // IE 44 | d.attachEvent('onreadystatechange', fireReady); 45 | // IE < 9 46 | w.attachEvent('onload', fireLoaded); 47 | } else { 48 | // Anything else 49 | w.onload = fireLoaded; 50 | } 51 | 52 | // Utility functions 53 | 54 | // Loop through node array 55 | function nodeLoop(fn, nodes) { 56 | var i; 57 | // Good idea to walk up the DOM 58 | for (i = nodes.length - 1; i >= 0; i -= 1) { 59 | fn(nodes[i]); 60 | } 61 | } 62 | 63 | // Convert to camel case 64 | function cssCamel(property) { 65 | return property.replace(/-\w/g, function (result) {return result.charAt(1).toUpperCase(); }); 66 | } 67 | 68 | // Get computed style 69 | function computeStyle(elm, property) { 70 | // IE, everything else or null 71 | return (elm.currentStyle) ? elm.currentStyle[cssCamel(property)] : (w.getComputedStyle) ? w.getComputedStyle(elm, null).getPropertyValue(property) : null; 72 | 73 | } 74 | 75 | // Returns URI encoded query string pair 76 | function queryPair(name, value) { 77 | return encodeURIComponent(name).replace(/%20/g, '+') + '=' + encodeURIComponent(value).replace(/%20/g, '+'); 78 | } 79 | 80 | // Set CSS, important to wrap in try to prevent error thrown on unsupported property 81 | function setCss(elm, property, value) { 82 | try { 83 | elm.style[cssCamel(property)] = value; 84 | } catch (e) { 85 | console.error('Could not set css style property "' + property + '".'); 86 | } 87 | } 88 | 89 | // Show CSS 90 | function showCss(elm) { 91 | elm.style.display = ''; 92 | // For elements still hidden by style block 93 | if (computeStyle(elm, 'display') === 'none') { 94 | elm.style.display = 'block'; 95 | } 96 | } 97 | 98 | // Serialize form & JSON values 99 | function serializeData(nodes) { 100 | var querystring = '', subelm, i, j; 101 | if (nodes.constructor === Object) { // Serialize JSON data 102 | for (subelm in nodes) { 103 | if (nodes.hasOwnProperty(subelm)) { 104 | if (nodes[subelm].constructor === Array) { 105 | for (i = 0; i < nodes[subelm].length; i += 1) { 106 | querystring += '&' + queryPair(subelm, nodes[subelm][i]); 107 | } 108 | } else { 109 | querystring += '&' + queryPair(subelm, nodes[subelm]); 110 | } 111 | } 112 | } 113 | } else { // Serialize node data 114 | nodeLoop(function (elm) { 115 | if (elm.nodeName === 'FORM') { 116 | for (i = 0; i < elm.elements.length; i += 1) { 117 | subelm = elm.elements[i]; 118 | 119 | if (!subelm.disabled) { 120 | switch (subelm.type) { 121 | // Ignore buttons, unsupported XHR 1 form fields 122 | case 'button': 123 | case 'image': 124 | case 'file': 125 | case 'submit': 126 | case 'reset': 127 | break; 128 | 129 | case 'select-one': 130 | if (subelm.length > 0) { 131 | querystring += '&' + queryPair(subelm.name, subelm.value); 132 | } 133 | break; 134 | 135 | case 'select-multiple': 136 | for (j = 0; j < subelm.length; j += 1) { 137 | if (subelm[j].selected) { 138 | querystring += '&' + queryPair(subelm.name, subelm[j].value); 139 | } 140 | } 141 | break; 142 | 143 | case 'checkbox': 144 | case 'radio': 145 | if (subelm.checked) { 146 | querystring += '&' + queryPair(subelm.name, subelm.value); 147 | } 148 | break; 149 | 150 | // Everything else including shinny new HTML5 input types 151 | default: 152 | querystring += '&' + queryPair(subelm.name, subelm.value); 153 | } 154 | } 155 | } 156 | } 157 | }, nodes); 158 | } 159 | // Tidy up first & 160 | return (querystring.length > 0) ? querystring.substring(1) : ''; 161 | } 162 | 163 | // Class helper 164 | function classHelper(classes, action, nodes) { 165 | var classarray, search, replace, i, has = false; 166 | if (classes) { 167 | // Trim any whitespace 168 | classarray = classes.split(/\s+/); 169 | nodeLoop(function (elm) { 170 | for (i = 0; i < classarray.length; i += 1) { 171 | search = new RegExp('\\b' + classarray[i] + '\\b', 'g'); 172 | replace = new RegExp(' *' + classarray[i] + '\\b', 'g'); 173 | if (action === 'remove') { 174 | elm.className = elm.className.replace(replace, ''); 175 | } else if (action === 'toggle') { 176 | elm.className = (elm.className.match(search)) ? elm.className.replace(replace, '') : elm.className + ' ' + classarray[i]; 177 | } else if (action === 'has') { 178 | if (elm.className.match(search)) { 179 | has = true; 180 | break; 181 | } 182 | } 183 | } 184 | }, nodes); 185 | } 186 | return has; 187 | } 188 | 189 | // HTML insertion helper 190 | function insertHtml(value, position, nodes) { 191 | var tmpnodes, tmpnode; 192 | if (value) { 193 | nodeLoop(function (elm) { 194 | // No insertAdjacentHTML support for FF < 8 and IE doesn't allow insertAdjacentHTML table manipulation, so use this instead 195 | // Convert string to node. We can't innerHTML on a document fragment 196 | tmpnodes = d.createElement('div'); 197 | tmpnodes.innerHTML = value; 198 | while ((tmpnode = tmpnodes.lastChild) !== null) { 199 | // Catch error in unlikely case elm has been removed 200 | try { 201 | if (position === 'before') { 202 | elm.parentNode.insertBefore(tmpnode, elm); 203 | } else if (position === 'after') { 204 | elm.parentNode.insertBefore(tmpnode, elm.nextSibling); 205 | } else if (position === 'append') { 206 | elm.appendChild(tmpnode); 207 | } else if (position === 'prepend') { 208 | elm.insertBefore(tmpnode, elm.firstChild); 209 | } 210 | } catch (e) {break; } 211 | } 212 | }, nodes); 213 | } 214 | } 215 | 216 | // Get nodes and return chibi 217 | function chibi(selector) { 218 | var cb, nodes = [], json = false, nodelist, i; 219 | 220 | if (selector) { 221 | 222 | // Element node, would prefer to use (selector instanceof HTMLElement) but no IE support 223 | if (selector.nodeType && selector.nodeType === 1) { 224 | nodes = [selector]; // return element as node list 225 | } else if (typeof selector === 'object') { 226 | // JSON, document object or node list, would prefer to use (selector instanceof NodeList) but no IE support 227 | json = (typeof selector.length !== 'number'); 228 | nodes = selector; 229 | } else if (typeof selector === 'string') { 230 | 231 | // 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) 232 | 233 | // IE < 8 234 | if (!d.querySelectorAll) { 235 | // Polyfill querySelectorAll 236 | d.querySelectorAll = function (selector) { 237 | 238 | var style, head = d.getElementsByTagName('head')[0], allnodes, selectednodes = [], i; 239 | 240 | style = d.createElement('STYLE'); 241 | style.type = 'text/css'; 242 | 243 | if (style.styleSheet) { 244 | style.styleSheet.cssText = selector + ' {a:b}'; 245 | 246 | head.appendChild(style); 247 | 248 | allnodes = d.getElementsByTagName('*'); 249 | 250 | for (i = 0; i < allnodes.length; i += 1) { 251 | if (computeStyle(allnodes[i], 'a') === 'b') { 252 | selectednodes.push(allnodes[i]); 253 | } 254 | } 255 | 256 | head.removeChild(style); 257 | } 258 | 259 | return selectednodes; 260 | }; 261 | } 262 | 263 | nodelist = d.querySelectorAll(selector); 264 | 265 | // Convert node list to array so results have full access to array methods 266 | // Array.prototype.slice.call not supported in IE < 9 and often slower than loop anyway 267 | for (i = 0; i < nodelist.length; i += 1) { 268 | nodes[i] = nodelist[i]; 269 | } 270 | 271 | } 272 | } 273 | 274 | // Only attach nodes if not JSON 275 | cb = json ? {} : nodes; 276 | 277 | // Public functions 278 | 279 | // Fire on DOM ready 280 | cb.ready = function (fn) { 281 | if (fn) { 282 | if (domready) { 283 | fn(); 284 | return cb; 285 | } else { 286 | readyfn.push(fn); 287 | } 288 | } 289 | }; 290 | // Fire on page loaded 291 | cb.loaded = function (fn) { 292 | if (fn) { 293 | if (pageloaded) { 294 | fn(); 295 | return cb; 296 | } else { 297 | loadedfn.push(fn); 298 | } 299 | } 300 | }; 301 | // Executes a function on nodes 302 | cb.each = function (fn) { 303 | if (typeof fn === 'function') { 304 | nodeLoop(function (elm) { 305 | // <= IE 8 loses scope so need to apply 306 | return fn.apply(elm, arguments); 307 | }, nodes); 308 | } 309 | return cb; 310 | }; 311 | // Find first 312 | cb.first = function () { 313 | return chibi(nodes.shift()); 314 | }; 315 | // Find last 316 | cb.last = function () { 317 | return chibi(nodes.pop()); 318 | }; 319 | // Find odd 320 | cb.odd = function () { 321 | var odds = [], i; 322 | for (i = 0; i < nodes.length; i += 2) { 323 | odds.push(nodes[i]); 324 | } 325 | return chibi(odds); 326 | }; 327 | // Find even 328 | cb.even = function () { 329 | var evens = [], i; 330 | for (i = 1; i < nodes.length; i += 2) { 331 | evens.push(nodes[i]); 332 | } 333 | return chibi(evens); 334 | }; 335 | // Hide node 336 | cb.hide = function () { 337 | nodeLoop(function (elm) { 338 | elm.style.display = 'none'; 339 | }, nodes); 340 | return cb; 341 | }; 342 | // Show node 343 | cb.show = function () { 344 | nodeLoop(function (elm) { 345 | showCss(elm); 346 | }, nodes); 347 | return cb; 348 | }; 349 | // Toggle node display 350 | cb.toggle = function () { 351 | nodeLoop(function (elm) { 352 | // computeStyle instead of style.display == 'none' catches elements that are hidden via style block 353 | if (computeStyle(elm, 'display') === 'none') { 354 | showCss(elm); 355 | } else { 356 | elm.style.display = 'none'; 357 | } 358 | 359 | }, nodes); 360 | return cb; 361 | }; 362 | // Remove node 363 | cb.remove = function () { 364 | nodeLoop(function (elm) { 365 | // Catch error in unlikely case elm has been removed 366 | try { 367 | elm.parentNode.removeChild(elm); 368 | } catch (e) {} 369 | }, nodes); 370 | return chibi(); 371 | }; 372 | // Get/Set CSS 373 | cb.css = function (property, value) { 374 | if (property) { 375 | if (value || value === '') { 376 | nodeLoop(function (elm) { 377 | setCss(elm, property, value); 378 | }, nodes); 379 | return cb; 380 | } 381 | if (nodes[0]) { 382 | if (nodes[0].style[cssCamel(property)]) { 383 | return nodes[0].style[cssCamel(property)]; 384 | } 385 | if (computeStyle(nodes[0], property)) { 386 | return computeStyle(nodes[0], property); 387 | } 388 | } 389 | } 390 | }; 391 | // Get class(es) 392 | cb.getClass = function () { 393 | if (nodes[0] && nodes[0].className.length > 0) { 394 | // Weak IE trim support 395 | return nodes[0].className.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, '').replace(/\s+/,' '); 396 | } 397 | }; 398 | // Set (replaces) classes 399 | cb.setClass = function (classes) { 400 | if (classes || classes === '') { 401 | nodeLoop(function (elm) { 402 | elm.className = classes; 403 | }, nodes); 404 | } 405 | return cb; 406 | }; 407 | // Add class 408 | cb.addClass = function (classes) { 409 | if (classes) { 410 | nodeLoop(function (elm) { 411 | elm.className += ' ' + classes; 412 | }, nodes); 413 | } 414 | return cb; 415 | }; 416 | // Remove class 417 | cb.removeClass = function (classes) { 418 | classHelper(classes, 'remove', nodes); 419 | return cb; 420 | }; 421 | // Toggle class 422 | cb.toggleClass = function (classes) { 423 | classHelper(classes, 'toggle', nodes); 424 | return cb; 425 | }; 426 | // Has class 427 | cb.hasClass = function (classes) { 428 | return classHelper(classes, 'has', nodes); 429 | }; 430 | // Get/set HTML 431 | cb.html = function (value) { 432 | if (value || value === '') { 433 | nodeLoop(function (elm) { 434 | elm.innerHTML = value; 435 | }, nodes); 436 | return cb; 437 | } 438 | if (nodes[0]) { 439 | return nodes[0].innerHTML; 440 | } 441 | }; 442 | // Insert HTML before selector 443 | cb.htmlBefore = function (value) { 444 | insertHtml(value, 'before', nodes); 445 | return cb; 446 | }; 447 | // Insert HTML after selector 448 | cb.htmlAfter = function (value) { 449 | insertHtml(value, 'after', nodes); 450 | return cb; 451 | }; 452 | // Insert HTML after selector innerHTML 453 | cb.htmlAppend = function (value) { 454 | insertHtml(value, 'append', nodes); 455 | return cb; 456 | }; 457 | // Insert HTML before selector innerHTML 458 | cb.htmlPrepend = function (value) { 459 | insertHtml(value, 'prepend', nodes); 460 | return cb; 461 | }; 462 | // Get/Set HTML attributes 463 | cb.attr = function (property, value) { 464 | if (property) { 465 | property = property.toLowerCase(); 466 | // IE < 9 doesn't allow style or class via get/setAttribute so switch. cssText returns prettier CSS anyway 467 | if (value || value === '') { 468 | nodeLoop(function (elm) { 469 | if (property === 'style') { 470 | elm.style.cssText = value; 471 | } else if (property === 'class') { 472 | elm.className = value; 473 | } else { 474 | elm.setAttribute(property, value); 475 | } 476 | }, nodes); 477 | return cb; 478 | } 479 | if (nodes[0]) { 480 | if (property === 'style') { 481 | if (nodes[0].style.cssText) { 482 | return nodes[0].style.cssText; 483 | } 484 | } else if (property === 'class') { 485 | if (nodes[0].className) { 486 | return nodes[0].className; 487 | } 488 | } else { 489 | if (nodes[0].getAttribute(property)) { 490 | return nodes[0].getAttribute(property); 491 | } 492 | } 493 | } 494 | } 495 | }; 496 | // Get/Set HTML data property 497 | cb.data = function (key, value) { 498 | if (key) { 499 | return cb.attr('data-'+key, value); 500 | } 501 | }; 502 | // Get/Set form element values 503 | cb.val = function (value) { 504 | var values, i, j; 505 | if (value || value === '') { 506 | nodeLoop(function (elm) { 507 | switch (elm.nodeName) { 508 | case 'SELECT': 509 | if (typeof value === 'string' || typeof value === 'number') { 510 | value = [value]; 511 | } 512 | for (i = 0; i < elm.length; i += 1) { 513 | // Multiple select 514 | for (j = 0; j < value.length; j += 1) { 515 | elm[i].selected = ''; 516 | if (elm[i].value === value[j]) { 517 | elm[i].selected = 'selected'; 518 | break; 519 | } 520 | } 521 | } 522 | break; 523 | case 'INPUT': 524 | case 'TEXTAREA': 525 | case 'BUTTON': 526 | elm.value = value; 527 | break; 528 | } 529 | }, nodes); 530 | 531 | return cb; 532 | } 533 | if (nodes[0]) { 534 | switch (nodes[0].nodeName) { 535 | case 'SELECT': 536 | values = []; 537 | for (i = 0; i < nodes[0].length; i += 1) { 538 | if (nodes[0][i].selected) { 539 | values.push(nodes[0][i].value); 540 | } 541 | } 542 | return (values.length > 1) ? values : values[0]; 543 | case 'INPUT': 544 | case 'TEXTAREA': 545 | case 'BUTTON': 546 | return nodes[0].value; 547 | } 548 | } 549 | }; 550 | // Return matching checked checkbox or radios 551 | cb.checked = function (check) { 552 | if (typeof check === 'boolean') { 553 | nodeLoop(function (elm) { 554 | if (elm.nodeName === 'INPUT' && (elm.type === 'checkbox' || elm.type === 'radio')) { 555 | elm.checked = check; 556 | } 557 | }, nodes); 558 | return cb; 559 | } 560 | if (nodes[0] && nodes[0].nodeName === 'INPUT' && (nodes[0].type === 'checkbox' || nodes[0].type === 'radio')) { 561 | return (!!nodes[0].checked); 562 | } 563 | }; 564 | // Add event handler 565 | cb.on = function (event, fn) { 566 | if (selector === w || selector === d) { 567 | nodes = [selector]; 568 | } 569 | nodeLoop(function (elm) { 570 | if (d.addEventListener) { 571 | elm.addEventListener(event, fn, false); 572 | } else if (d.attachEvent) { 573 | // <= IE 8 loses scope so need to apply, we add this to object so we can detach later (can't detach anonymous functions) 574 | elm[event + fn] = function () { return fn.apply(elm, arguments); }; 575 | elm.attachEvent('on' + event, elm[event + fn]); 576 | } 577 | }, nodes); 578 | return cb; 579 | }; 580 | // Remove event handler 581 | cb.off = function (event, fn) { 582 | if (selector === w || selector === d) { 583 | nodes = [selector]; 584 | } 585 | nodeLoop(function (elm) { 586 | if (d.addEventListener) { 587 | elm.removeEventListener(event, fn, false); 588 | } else if (d.attachEvent) { 589 | elm.detachEvent('on' + event, elm[event + fn]); 590 | // Tidy up 591 | elm[event + fn] = null; 592 | } 593 | }, nodes); 594 | return cb; 595 | }; 596 | // Basic XHR 1, no file support. Shakes fist at IE 597 | cb.ajax = function (url, method, callback, nocache, nojsonp) { 598 | var xhr, 599 | query = serializeData(nodes), 600 | type = (method) ? method.toUpperCase() : 'GET', 601 | hostsearch = new RegExp('http[s]?://(.*?)/', 'gi'), 602 | domain = hostsearch.exec(url), 603 | timestamp = '_ts=' + (+new Date()), 604 | head = d.getElementsByTagName('head')[0], 605 | jsonpcallback = 'chibi' + (+new Date()) + (jsonpcount += 1), 606 | script; 607 | 608 | if (query && (type === 'GET' || type === 'DELETE')) { 609 | url += (url.indexOf('?') === -1) ? '?' + query : '&' + query; 610 | query = null; 611 | } 612 | 613 | // JSONP if cross domain url 614 | if (type === 'GET' && !nojsonp && domain && w.location.host !== domain[1]) { 615 | 616 | if (nocache) { 617 | url += (url.indexOf('?') === -1) ? '?' + timestamp : '&' + timestamp; 618 | } 619 | 620 | // Replace possible encoded ? 621 | url = url.replace('=%3F', '=?'); 622 | 623 | // Replace jsonp ? with callback 624 | if (callback && url.indexOf('=?') !== -1) { 625 | 626 | url = url.replace('=?', '=' + jsonpcallback); 627 | 628 | w[jsonpcallback] = function (data) { 629 | try { 630 | callback(data, 200); 631 | } catch (e) {} 632 | 633 | // Tidy up 634 | w[jsonpcallback] = undefined; 635 | }; 636 | } 637 | 638 | // JSONP 639 | script = document.createElement('script'); 640 | script.async = true; 641 | script.src = url; 642 | 643 | // Tidy up 644 | script.onload = function () { 645 | head.removeChild(script); 646 | }; 647 | 648 | head.appendChild(script); 649 | 650 | } else { 651 | 652 | if (w.XMLHttpRequest) { 653 | xhr = new XMLHttpRequest(); 654 | } else if (w.ActiveXObject) { 655 | xhr = new ActiveXObject('Microsoft.XMLHTTP'); // IE < 9 656 | } 657 | 658 | if (xhr) { 659 | 660 | if (nocache) { 661 | url += (url.indexOf('?') === -1) ? '?' + timestamp : '&' + timestamp; 662 | } 663 | 664 | // Douglas Crockford: "Synchronous programming is disrespectful and should not be employed in applications which are used by people" 665 | xhr.open(type, url, true); 666 | 667 | xhr.onreadystatechange = function () { 668 | if (xhr.readyState === 4) { 669 | if (callback) { 670 | callback(xhr.responseText, xhr.status); 671 | } 672 | } 673 | }; 674 | 675 | xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); 676 | 677 | if (type === 'POST' || type === 'PUT') { 678 | xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); 679 | } 680 | 681 | xhr.send(query); 682 | 683 | } 684 | } 685 | return cb; 686 | }; 687 | // Alias to cb.ajax(url, 'get', callback, nocache, nojsonp) 688 | cb.get = function (url, callback, nocache, nojsonp) { 689 | return cb.ajax(url, 'get', callback, nocache, nojsonp); 690 | }; 691 | // Alias to cb.ajax(url, 'post', callback, nocache) 692 | cb.post = function (url, callback, nocache) { 693 | return cb.ajax(url, 'post', callback, nocache); 694 | }; 695 | 696 | return cb; 697 | } 698 | 699 | // Set Chibi's global namespace here ($) 700 | w.$ = chibi; 701 | 702 | }()); 703 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'), 2 | replace = require('gulp-replace'), 3 | jshint = require('gulp-jshint'), 4 | rename = require('gulp-rename'), 5 | uglify = require('gulp-uglify'), 6 | qunit = require('gulp-qunit'), 7 | package = require('./package.json'); 8 | 9 | var paths = { 10 | source: ['chibi.js'], 11 | readme: ['README.md'], 12 | test: ['tests/runner.html'] 13 | }; 14 | 15 | var handleError = function(err) { 16 | process.exit(1); 17 | } 18 | 19 | gulp.task('update', function(callback) { 20 | gulp.src(paths.source) 21 | .pipe(replace(/(\/\*!)(.*)(\*\/)/g, '$1chibi '+package.version+', Copyright 2012-'+(new Date()).getUTCFullYear()+' '+package.author.name+', released under '+package.license+' license $3')) 22 | .pipe(gulp.dest('')) 23 | gulp.src(paths.readme) 24 | .pipe(replace(/(\# Chibi v)([\d\.]*)/g, '$1'+package.version)) 25 | .pipe(gulp.dest('')) 26 | .on('end', callback); 27 | }); 28 | 29 | gulp.task('lint', function() { 30 | gulp.src(paths.source) 31 | .pipe(jshint()) 32 | .pipe(jshint.reporter('default')) 33 | .pipe(jshint.reporter('fail')) 34 | .on('error', handleError); 35 | }); 36 | 37 | gulp.task('compress', function() { 38 | gulp.src(paths.source) 39 | .pipe(rename('chibi-min.js')) 40 | .pipe(uglify({output: {comments: /^!/i}})) 41 | .pipe(gulp.dest('')); 42 | }); 43 | 44 | gulp.task('test', function() { 45 | gulp.src(paths.test) 46 | .pipe(qunit()) 47 | .on('error', handleError); 48 | }); 49 | 50 | gulp.task('watch', function() { 51 | gulp.watch(paths.source, ['lint', 'compress']); 52 | }); 53 | 54 | gulp.task('default', ['lint', 'compress', 'test']); 55 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "chibijs", 3 | "version": "3.0.9", 4 | "description": "A tiny JavaScript micro-library", 5 | "keywords": [ 6 | "browser", 7 | "chibi", 8 | "dom", 9 | "library", 10 | "microlibrary" 11 | ], 12 | "homepage": "https://github.com/kylebarrow/chibi", 13 | "bugs": { 14 | "url": "https://github.com/kylebarrow/chibi/issues" 15 | }, 16 | "license": "MIT", 17 | "author": { 18 | "name": "Kyle Barrow", 19 | "email": "kyle@barrow.io", 20 | "url": "https://barrow.io" 21 | }, 22 | "main": "chibi-min.js", 23 | "repository": { 24 | "type": "git", 25 | "url": "https://github.com/kylebarrow/chibi.git" 26 | }, 27 | "devDependencies": { 28 | "gulp": "^3.9.1", 29 | "gulp-jshint": "^2.0.4", 30 | "gulp-qunit": "^1.5.0", 31 | "gulp-rename": "^1.2.2", 32 | "gulp-replace": "^0.5.4", 33 | "gulp-uglify": "^3.0.0", 34 | "jshint": "^2.9.4", 35 | "qunitjs": "^2.3.3" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /tests/runner.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Chibi Unit Tests 5 | 6 | 7 | 25 | 26 | 27 | 461 | 462 | 463 |
464 |
465 | Element 1 466 | Element 2 467 | Element 3 468 | Element 4 469 | Element 5 470 | 471 |
472 | 473 | 477 | 482 | 483 | 484 |
485 |
486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 |
496 |
497 | 498 | 499 |
500 |
501 | 502 |
503 |
504 |
505 | 506 | 507 |
508 | 509 | 510 | 511 | --------------------------------------------------------------------------------