comparison default/node_modules/shoestring/dist/shoestring-dev.js @ 0:1d038bc9b3d2 default tip

Up:default
author Liny <dev@neowd.com>
date Sat, 31 May 2025 09:21:51 +0800
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:1d038bc9b3d2
1 /*! Shoestring - v2.0.1 - 2017-05-24
2 * http://github.com/filamentgroup/shoestring/
3 * Copyright (c) 2017 Scott Jehl, Filament Group, Inc; Licensed MIT & GPLv2 */
4 (function( factory ) {
5 if( typeof define === 'function' && define.amd ) {
6 // AMD. Register as an anonymous module.
7 define( [ 'shoestring' ], factory );
8 } else if (typeof module === 'object' && module.exports) {
9 // Node/CommonJS
10 module.exports = factory();
11 } else {
12 // Browser globals
13 factory();
14 }
15 }(function () {
16 var win = typeof window !== "undefined" ? window : this;
17 var doc = win.document;
18
19
20 /**
21 * The shoestring object constructor.
22 *
23 * @param {string,object} prim The selector to find or element to wrap.
24 * @param {object} sec The context in which to match the `prim` selector.
25 * @returns shoestring
26 * @this window
27 */
28 function shoestring( prim, sec ){
29 var pType = typeof( prim ),
30 ret = [],
31 sel;
32
33 // return an empty shoestring object
34 if( !prim ){
35 return new Shoestring( ret );
36 }
37
38 // ready calls
39 if( prim.call ){
40 return shoestring.ready( prim );
41 }
42
43 // handle re-wrapping shoestring objects
44 if( prim.constructor === Shoestring && !sec ){
45 return prim;
46 }
47
48 // if string starting with <, make html
49 if( pType === "string" && prim.indexOf( "<" ) === 0 ){
50 var dfrag = doc.createElement( "div" );
51
52 dfrag.innerHTML = prim;
53
54 // TODO depends on children (circular)
55 return shoestring( dfrag ).children().each(function(){
56 dfrag.removeChild( this );
57 });
58 }
59
60 // if string, it's a selector, use qsa
61 if( pType === "string" ){
62 if( sec ){
63 return shoestring( sec ).find( prim );
64 }
65
66 try {
67 sel = doc.querySelectorAll( prim );
68 } catch( e ) {
69 shoestring.error( 'queryselector', prim );
70 }
71
72 return new Shoestring( sel, prim );
73 }
74
75 // array like objects or node lists
76 if( Object.prototype.toString.call( pType ) === '[object Array]' ||
77 (win.NodeList && prim instanceof win.NodeList) ){
78
79 return new Shoestring( prim, prim );
80 }
81
82 // if it's an array, use all the elements
83 if( prim.constructor === Array ){
84 return new Shoestring( prim, prim );
85 }
86
87 // otherwise assume it's an object the we want at an index
88 return new Shoestring( [prim], prim );
89 }
90
91 var Shoestring = function( ret, prim ) {
92 this.length = 0;
93 this.selector = prim;
94 shoestring.merge(this, ret);
95 };
96
97 // TODO only required for tests
98 Shoestring.prototype.reverse = [].reverse;
99
100 // For adding element set methods
101 shoestring.fn = Shoestring.prototype;
102
103 shoestring.Shoestring = Shoestring;
104
105 // For extending objects
106 // TODO move to separate module when we use prototypes
107 shoestring.extend = function( first, second ){
108 for( var i in second ){
109 if( second.hasOwnProperty( i ) ){
110 first[ i ] = second[ i ];
111 }
112 }
113
114 return first;
115 };
116
117 // taken directly from jQuery
118 shoestring.merge = function( first, second ) {
119 var len, j, i;
120
121 len = +second.length,
122 j = 0,
123 i = first.length;
124
125 for ( ; j < len; j++ ) {
126 first[ i++ ] = second[ j ];
127 }
128
129 first.length = i;
130
131 return first;
132 };
133
134 // expose
135 win.shoestring = shoestring;
136
137
138
139 shoestring.enUS = {
140 errors: {
141 "prefix": "Shoestring does not support",
142
143 "ajax-url-query": "data with urls that have existing query params",
144 "children-selector" : "passing selectors into .child, try .children().filter( selector )",
145 "click": "the click method. Try using .on( 'click', function(){}) or .trigger( 'click' ) instead.",
146 "css-get" : "getting computed attributes from the DOM.",
147 "data-attr-alias": "the data method aliased to `data-` DOM attributes.",
148 "each-length": "objects without a length passed into each",
149 "has-class" : "the hasClass method. Try using .is( '.klassname' ) instead.",
150 "html-function" : "passing a function into .html. Try generating the html you're passing in an outside function",
151 "index-shoestring-object": "an index call with a shoestring object argument. Use .get(0) on the argument instead.",
152 "live-delegate" : "the .live or .delegate methods. Use .bind or .on instead.",
153 "map": "the map method. Try using .each to make a new object.",
154 "next-selector" : "passing selectors into .next, try .next().filter( selector )",
155 "off-delegate" : ".off( events, selector, handler ) or .off( events, selector ). Use .off( eventName, callback ) instead.",
156 "next-until" : "the .nextUntil method. Use .next in a loop until you reach the selector, don't include the selector",
157 "on-delegate" : "the .on method with three or more arguments. Using .on( eventName, callback ) instead.",
158 "outer-width": "the outerWidth method. Try combining .width() with .css for padding-left, padding-right, and the border of the left and right side.",
159 "prev-selector" : "passing selectors into .prev, try .prev().filter( selector )",
160 "prevall-selector" : "passing selectors into .prevAll, try .prevAll().filter( selector )",
161 "queryselector": "all CSS selectors on querySelector (varies per browser support). Specifically, this failed: ",
162 "siblings-selector": "passing selector into siblings not supported, try .siblings().find( ... )",
163 "show-hide": "the show or hide methods. Use display: block (or whatever you'd like it to be) or none instead",
164 "text-setter": "setting text via the .text method.",
165 "toggle-class" : "the toggleClass method. Try using addClass or removeClass instead.",
166 "trim": "the trim method. Use String.prototype.trim."
167 }
168 };
169
170 shoestring.error = function( id, str ) {
171 var errors = shoestring.enUS.errors;
172 throw new Error( errors.prefix + " " + errors[id] + ( str ? " " + str : "" ) );
173 };
174
175
176
177 /**
178 * Make an HTTP request to a url.
179 *
180 * **NOTE** the following options are supported:
181 *
182 * - *method* - The HTTP method used with the request. Default: `GET`.
183 * - *data* - Raw object with keys and values to pass with request as query params. Default `null`.
184 * - *headers* - Set of request headers to add. Default `{}`.
185 * - *async* - Whether the opened request is asynchronouse. Default `true`.
186 * - *success* - Callback for successful request and response. Passed the response data.
187 * - *error* - Callback for failed request and response.
188 * - *cancel* - Callback for cancelled request and response.
189 *
190 * @param {string} url The url to request.
191 * @param {object} options The options object, see Notes.
192 * @return shoestring
193 * @this shoestring
194 */
195
196 shoestring.ajax = function( url, options ) {
197 var params = "", req = new XMLHttpRequest(), settings, key;
198
199 settings = shoestring.extend( {}, shoestring.ajax.settings );
200
201 if( options ){
202 shoestring.extend( settings, options );
203 }
204
205 if( !url ){
206 url = settings.url;
207 }
208
209 if( !req || !url ){
210 return;
211 }
212
213 // create parameter string from data object
214 if( settings.data ){
215 for( key in settings.data ){
216 if( settings.data.hasOwnProperty( key ) ){
217 if( params !== "" ){
218 params += "&";
219 }
220 params += encodeURIComponent( key ) + "=" +
221 encodeURIComponent( settings.data[key] );
222 }
223 }
224 }
225
226 // append params to url for GET requests
227 if( settings.method === "GET" && params ){
228 if( url.indexOf("?") >= 0 ){
229 shoestring.error( 'ajax-url-query' );
230 }
231
232 url += "?" + params;
233 }
234
235 req.open( settings.method, url, settings.async );
236
237 if( req.setRequestHeader ){
238 req.setRequestHeader( "X-Requested-With", "XMLHttpRequest" );
239
240 // Set 'Content-type' header for POST requests
241 if( settings.method === "POST" && params ){
242 req.setRequestHeader( "Content-type", "application/x-www-form-urlencoded" );
243 }
244
245 for( key in settings.headers ){
246 if( settings.headers.hasOwnProperty( key ) ){
247 req.setRequestHeader(key, settings.headers[ key ]);
248 }
249 }
250 }
251
252 req.onreadystatechange = function () {
253 if( req.readyState === 4 ){
254 // Trim the whitespace so shoestring('<div>') works
255 var res = (req.responseText || '').replace(/^\s+|\s+$/g, '');
256 if( req.status.toString().indexOf( "0" ) === 0 ){
257 return settings.cancel( res, req.status, req );
258 }
259 else if ( req.status.toString().match( /^(4|5)/ ) && RegExp.$1 ){
260 return settings.error( res, req.status, req );
261 }
262 else if (settings.success) {
263 return settings.success( res, req.status, req );
264 }
265 }
266 };
267
268 if( req.readyState === 4 ){
269 return req;
270 }
271
272 // Send request
273 if( settings.method === "POST" && params ){
274 req.send( params );
275 } else {
276 req.send();
277 }
278
279 return req;
280 };
281
282 shoestring.ajax.settings = {
283 success: function(){},
284 error: function(){},
285 cancel: function(){},
286 method: "GET",
287 async: true,
288 data: null,
289 headers: {}
290 };
291
292
293
294 /**
295 * Helper function wrapping a call to [ajax](ajax.js.html) using the `GET` method.
296 *
297 * @param {string} url The url to GET from.
298 * @param {function} callback Callback to invoke on success.
299 * @return shoestring
300 * @this shoestring
301 */
302 shoestring.get = function( url, callback ){
303 return shoestring.ajax( url, { success: callback } );
304 };
305
306
307
308 /**
309 * Load the HTML response from `url` into the current set of elements.
310 *
311 * @param {string} url The url to GET from.
312 * @param {function} callback Callback to invoke after HTML is inserted.
313 * @return shoestring
314 * @this shoestring
315 */
316 shoestring.fn.load = function( url, callback ){
317 var self = this,
318 args = arguments,
319 intCB = function( data ){
320 self.each(function(){
321 shoestring( this ).html( data );
322 });
323
324 if( callback ){
325 callback.apply( self, args );
326 }
327 };
328
329 shoestring.ajax( url, { success: intCB } );
330 return this;
331 };
332
333
334
335 /**
336 * Helper function wrapping a call to [ajax](ajax.js.html) using the `POST` method.
337 *
338 * @param {string} url The url to POST to.
339 * @param {object} data The data to send.
340 * @param {function} callback Callback to invoke on success.
341 * @return shoestring
342 * @this shoestring
343 */
344 shoestring.post = function( url, data, callback ){
345 return shoestring.ajax( url, { data: data, method: "POST", success: callback } );
346 };
347
348
349
350 /**
351 * Iterates over `shoestring` collections.
352 *
353 * @param {function} callback The callback to be invoked on each element and index
354 * @return shoestring
355 * @this shoestring
356 */
357 shoestring.fn.each = function( callback ){
358 return shoestring.each( this, callback );
359 };
360
361 shoestring.each = function( collection, callback ) {
362 var val;
363 if( !( "length" in collection ) ) {
364 shoestring.error( 'each-length' );
365 }
366 for( var i = 0, il = collection.length; i < il; i++ ){
367 val = callback.call( collection[i], i, collection[i] );
368 if( val === false ){
369 break;
370 }
371 }
372
373 return collection;
374 };
375
376
377
378 /**
379 * Check for array membership.
380 *
381 * @param {object} needle The thing to find.
382 * @param {object} haystack The thing to find the needle in.
383 * @return {boolean}
384 * @this window
385 */
386 shoestring.inArray = function( needle, haystack ){
387 var isin = -1;
388 for( var i = 0, il = haystack.length; i < il; i++ ){
389 if( haystack.hasOwnProperty( i ) && haystack[ i ] === needle ){
390 isin = i;
391 }
392 }
393 return isin;
394 };
395
396
397
398 /**
399 * Bind callbacks to be run when the DOM is "ready".
400 *
401 * @param {function} fn The callback to be run
402 * @return shoestring
403 * @this shoestring
404 */
405 shoestring.ready = function( fn ){
406 if( ready && fn ){
407 fn.call( doc );
408 }
409 else if( fn ){
410 readyQueue.push( fn );
411 }
412 else {
413 runReady();
414 }
415
416 return [doc];
417 };
418
419 // TODO necessary?
420 shoestring.fn.ready = function( fn ){
421 shoestring.ready( fn );
422 return this;
423 };
424
425 // Empty and exec the ready queue
426 var ready = false,
427 readyQueue = [],
428 runReady = function(){
429 if( !ready ){
430 while( readyQueue.length ){
431 readyQueue.shift().call( doc );
432 }
433 ready = true;
434 }
435 };
436
437 // If DOM is already ready at exec time, depends on the browser.
438 // From: https://github.com/mobify/mobifyjs/blob/526841be5509e28fc949038021799e4223479f8d/src/capture.js#L128
439 if (doc.attachEvent ? doc.readyState === "complete" : doc.readyState !== "loading") {
440 runReady();
441 } else {
442 doc.addEventListener( "DOMContentLoaded", runReady, false );
443 doc.addEventListener( "readystatechange", runReady, false );
444 win.addEventListener( "load", runReady, false );
445 }
446
447
448
449 /**
450 * Checks the current set of elements against the selector, if one matches return `true`.
451 *
452 * @param {string} selector The selector to check.
453 * @return {boolean}
454 * @this {shoestring}
455 */
456 shoestring.fn.is = function( selector ){
457 var ret = false, self = this, parents, check;
458
459 // assume a dom element
460 if( typeof selector !== "string" ){
461 // array-like, ie shoestring objects or element arrays
462 if( selector.length && selector[0] ){
463 check = selector;
464 } else {
465 check = [selector];
466 }
467
468 return _checkElements(this, check);
469 }
470
471 parents = this.parent();
472
473 if( !parents.length ){
474 parents = shoestring( doc );
475 }
476
477 parents.each(function( i, e ) {
478 var children;
479
480 try {
481 children = e.querySelectorAll( selector );
482 } catch( e ) {
483 shoestring.error( 'queryselector', selector );
484 }
485
486 ret = _checkElements( self, children );
487 });
488
489 return ret;
490 };
491
492 function _checkElements(needles, haystack){
493 var ret = false;
494
495 needles.each(function() {
496 var j = 0;
497
498 while( j < haystack.length ){
499 if( this === haystack[j] ){
500 ret = true;
501 }
502
503 j++;
504 }
505 });
506
507 return ret;
508 }
509
510
511
512 /**
513 * Get data attached to the first element or set data values on all elements in the current set.
514 *
515 * @param {string} name The data attribute name.
516 * @param {any} value The value assigned to the data attribute.
517 * @return {any|shoestring}
518 * @this shoestring
519 */
520 shoestring.fn.data = function( name, value ){
521 if( name !== undefined ){
522 if( value !== undefined ){
523 return this.each(function(){
524 if( !this.shoestringData ){
525 this.shoestringData = {};
526 }
527
528 this.shoestringData[ name ] = value;
529 });
530 }
531 else {
532 if( this[ 0 ] ) {
533 if( this[ 0 ].shoestringData ) {
534 return this[ 0 ].shoestringData[ name ];
535 }
536 if( shoestring( this[ 0 ] ).is( "[data-" + name + "]" ) ){
537 shoestring.error( 'data-attr-alias' );
538 }
539 }
540 }
541 }
542 else {
543 return this[ 0 ] ? this[ 0 ].shoestringData || {} : undefined;
544 }
545 };
546
547
548 /**
549 * Remove data associated with `name` or all the data, for each element in the current set.
550 *
551 * @param {string} name The data attribute name.
552 * @return shoestring
553 * @this shoestring
554 */
555 shoestring.fn.removeData = function( name ){
556 return this.each(function(){
557 if( name !== undefined && this.shoestringData ){
558 this.shoestringData[ name ] = undefined;
559 delete this.shoestringData[ name ];
560 } else {
561 this[ 0 ].shoestringData = {};
562 }
563 });
564 };
565
566
567
568 /**
569 * An alias for the `shoestring` constructor.
570 */
571 win.$ = shoestring;
572
573
574
575 /**
576 * Add a class to each DOM element in the set of elements.
577 *
578 * @param {string} className The name of the class to be added.
579 * @return shoestring
580 * @this shoestring
581 */
582 shoestring.fn.addClass = function( className ){
583 var classes = className.replace(/^\s+|\s+$/g, '').split( " " );
584
585 return this.each(function(){
586 for( var i = 0, il = classes.length; i < il; i++ ){
587 if( this.className !== undefined &&
588 (this.className === "" ||
589 !this.className.match( new RegExp( "(^|\\s)" + classes[ i ] + "($|\\s)"))) ){
590 this.className += " " + classes[ i ];
591 }
592 }
593 });
594 };
595
596
597
598 /**
599 * Add elements matching the selector to the current set.
600 *
601 * @param {string} selector The selector for the elements to add from the DOM
602 * @return shoestring
603 * @this shoestring
604 */
605 shoestring.fn.add = function( selector ){
606 var ret = [];
607 this.each(function(){
608 ret.push( this );
609 });
610
611 shoestring( selector ).each(function(){
612 ret.push( this );
613 });
614
615 return shoestring( ret );
616 };
617
618
619
620 /**
621 * Insert an element or HTML string after each element in the current set.
622 *
623 * @param {string|HTMLElement} fragment The HTML or HTMLElement to insert.
624 * @return shoestring
625 * @this shoestring
626 */
627 shoestring.fn.after = function( fragment ){
628 if( typeof( fragment ) === "string" || fragment.nodeType !== undefined ){
629 fragment = shoestring( fragment );
630 }
631
632 if( fragment.length > 1 ){
633 fragment = fragment.reverse();
634 }
635 return this.each(function( i ){
636 for( var j = 0, jl = fragment.length; j < jl; j++ ){
637 var insertEl = i > 0 ? fragment[ j ].cloneNode( true ) : fragment[ j ];
638 this.parentNode.insertBefore( insertEl, this.nextSibling );
639 }
640 });
641 };
642
643
644
645 /**
646 * Insert an element or HTML string as the last child of each element in the set.
647 *
648 * @param {string|HTMLElement} fragment The HTML or HTMLElement to insert.
649 * @return shoestring
650 * @this shoestring
651 */
652 shoestring.fn.append = function( fragment ){
653 if( typeof( fragment ) === "string" || fragment.nodeType !== undefined ){
654 fragment = shoestring( fragment );
655 }
656
657 return this.each(function( i ){
658 for( var j = 0, jl = fragment.length; j < jl; j++ ){
659 this.appendChild( i > 0 ? fragment[ j ].cloneNode( true ) : fragment[ j ] );
660 }
661 });
662 };
663
664
665
666 /**
667 * Insert the current set as the last child of the elements matching the selector.
668 *
669 * @param {string} selector The selector after which to append the current set.
670 * @return shoestring
671 * @this shoestring
672 */
673 shoestring.fn.appendTo = function( selector ){
674 return this.each(function(){
675 shoestring( selector ).append( this );
676 });
677 };
678
679
680
681 /**
682 * Get the value of the first element of the set or set the value of all the elements in the set.
683 *
684 * @param {string} name The attribute name.
685 * @param {string} value The new value for the attribute.
686 * @return {shoestring|string|undefined}
687 * @this {shoestring}
688 */
689 shoestring.fn.attr = function( name, value ){
690 var nameStr = typeof( name ) === "string";
691
692 if( value !== undefined || !nameStr ){
693 return this.each(function(){
694 if( nameStr ){
695 this.setAttribute( name, value );
696 } else {
697 for( var i in name ){
698 if( name.hasOwnProperty( i ) ){
699 this.setAttribute( i, name[ i ] );
700 }
701 }
702 }
703 });
704 } else {
705 return this[ 0 ] ? this[ 0 ].getAttribute( name ) : undefined;
706 }
707 };
708
709
710
711 /**
712 * Insert an element or HTML string before each element in the current set.
713 *
714 * @param {string|HTMLElement} fragment The HTML or HTMLElement to insert.
715 * @return shoestring
716 * @this shoestring
717 */
718 shoestring.fn.before = function( fragment ){
719 if( typeof( fragment ) === "string" || fragment.nodeType !== undefined ){
720 fragment = shoestring( fragment );
721 }
722
723 return this.each(function( i ){
724 for( var j = 0, jl = fragment.length; j < jl; j++ ){
725 this.parentNode.insertBefore( i > 0 ? fragment[ j ].cloneNode( true ) : fragment[ j ], this );
726 }
727 });
728 };
729
730
731
732 /**
733 * Get the children of the current collection.
734 * @return shoestring
735 * @this shoestring
736 */
737 shoestring.fn.children = function(){
738 if( arguments.length > 0 ){
739 shoestring.error( 'children-selector' );
740 }
741 var ret = [],
742 childs,
743 j;
744 this.each(function(){
745 childs = this.children;
746 j = -1;
747
748 while( j++ < childs.length-1 ){
749 if( shoestring.inArray( childs[ j ], ret ) === -1 ){
750 ret.push( childs[ j ] );
751 }
752 }
753 });
754 return shoestring(ret);
755 };
756
757
758
759 /**
760 * Clone and return the current set of nodes into a new `shoestring` object.
761 *
762 * @return shoestring
763 * @this shoestring
764 */
765 shoestring.fn.clone = function() {
766 var ret = [];
767
768 this.each(function() {
769 ret.push( this.cloneNode( true ) );
770 });
771
772 return shoestring( ret );
773 };
774
775
776
777 /**
778 * Find an element matching the selector in the set of the current element and its parents.
779 *
780 * @param {string} selector The selector used to identify the target element.
781 * @return shoestring
782 * @this shoestring
783 */
784 shoestring.fn.closest = function( selector ){
785 var ret = [];
786
787 if( !selector ){
788 return shoestring( ret );
789 }
790
791 this.each(function(){
792 var element, $self = shoestring( element = this );
793
794 if( $self.is(selector) ){
795 ret.push( this );
796 return;
797 }
798
799 while( element.parentElement ) {
800 if( shoestring(element.parentElement).is(selector) ){
801 ret.push( element.parentElement );
802 break;
803 }
804
805 element = element.parentElement;
806 }
807 });
808
809 return shoestring( ret );
810 };
811
812
813
814 shoestring.cssExceptions = {
815 'float': [ 'cssFloat' ]
816 };
817
818
819
820 (function() {
821 var cssExceptions = shoestring.cssExceptions;
822
823 // marginRight instead of margin-right
824 function convertPropertyName( str ) {
825 return str.replace( /\-([A-Za-z])/g, function ( match, character ) {
826 return character.toUpperCase();
827 });
828 }
829
830 function _getStyle( element, property ) {
831 return win.getComputedStyle( element, null ).getPropertyValue( property );
832 }
833
834 var vendorPrefixes = [ '', '-webkit-', '-ms-', '-moz-', '-o-', '-khtml-' ];
835
836 /**
837 * Private function for getting the computed style of an element.
838 *
839 * **NOTE** Please use the [css](../css.js.html) method instead.
840 *
841 * @method _getStyle
842 * @param {HTMLElement} element The element we want the style property for.
843 * @param {string} property The css property we want the style for.
844 */
845 shoestring._getStyle = function( element, property ) {
846 var convert, value, j, k;
847
848 if( cssExceptions[ property ] ) {
849 for( j = 0, k = cssExceptions[ property ].length; j < k; j++ ) {
850 value = _getStyle( element, cssExceptions[ property ][ j ] );
851
852 if( value ) {
853 return value;
854 }
855 }
856 }
857
858 for( j = 0, k = vendorPrefixes.length; j < k; j++ ) {
859 convert = convertPropertyName( vendorPrefixes[ j ] + property );
860
861 // VendorprefixKeyName || key-name
862 value = _getStyle( element, convert );
863
864 if( convert !== property ) {
865 value = value || _getStyle( element, property );
866 }
867
868 if( vendorPrefixes[ j ] ) {
869 // -vendorprefix-key-name
870 value = value || _getStyle( element, vendorPrefixes[ j ] + property );
871 }
872
873 if( value ) {
874 return value;
875 }
876 }
877
878 return undefined;
879 };
880 })();
881
882
883
884 (function() {
885 var cssExceptions = shoestring.cssExceptions;
886
887 // marginRight instead of margin-right
888 function convertPropertyName( str ) {
889 return str.replace( /\-([A-Za-z])/g, function ( match, character ) {
890 return character.toUpperCase();
891 });
892 }
893
894 /**
895 * Private function for setting the style of an element.
896 *
897 * **NOTE** Please use the [css](../css.js.html) method instead.
898 *
899 * @method _setStyle
900 * @param {HTMLElement} element The element we want to style.
901 * @param {string} property The property being used to style the element.
902 * @param {string} value The css value for the style property.
903 */
904 shoestring._setStyle = function( element, property, value ) {
905 var convertedProperty = convertPropertyName(property);
906
907 element.style[ property ] = value;
908
909 if( convertedProperty !== property ) {
910 element.style[ convertedProperty ] = value;
911 }
912
913 if( cssExceptions[ property ] ) {
914 for( var j = 0, k = cssExceptions[ property ].length; j<k; j++ ) {
915 element.style[ cssExceptions[ property ][ j ] ] = value;
916 }
917 }
918 };
919 })();
920
921
922
923 /**
924 * Get the compute style property of the first element or set the value of a style property
925 * on all elements in the set.
926 *
927 * @method _setStyle
928 * @param {string} property The property being used to style the element.
929 * @param {string|undefined} value The css value for the style property.
930 * @return {string|shoestring}
931 * @this shoestring
932 */
933 shoestring.fn.css = function( property, value ){
934 if( !this[0] ){
935 return;
936 }
937
938 if( typeof property === "object" ) {
939 return this.each(function() {
940 for( var key in property ) {
941 if( property.hasOwnProperty( key ) ) {
942 shoestring._setStyle( this, key, property[key] );
943 }
944 }
945 });
946 } else {
947 // assignment else retrieve first
948 if( value !== undefined ){
949 return this.each(function(){
950 shoestring._setStyle( this, property, value );
951 });
952 }
953
954 return shoestring._getStyle( this[0], property );
955 }
956 };
957
958
959
960 /**
961 * Returns the indexed element wrapped in a new `shoestring` object.
962 *
963 * @param {integer} index The index of the element to wrap and return.
964 * @return shoestring
965 * @this shoestring
966 */
967 shoestring.fn.eq = function( index ){
968 if( this[index] ){
969 return shoestring( this[index] );
970 }
971
972 return shoestring([]);
973 };
974
975
976
977 /**
978 * Filter out the current set if they do *not* match the passed selector or
979 * the supplied callback returns false
980 *
981 * @param {string,function} selector The selector or boolean return value callback used to filter the elements.
982 * @return shoestring
983 * @this shoestring
984 */
985 shoestring.fn.filter = function( selector ){
986 var ret = [];
987
988 this.each(function( index ){
989 var wsel;
990
991 if( typeof selector === 'function' ) {
992 if( selector.call( this, index ) !== false ) {
993 ret.push( this );
994 }
995 // document node
996 } else if( this.nodeType === 9 ){
997 if( this === selector ) {
998 ret.push( this );
999 }
1000 } else {
1001 if( !this.parentNode ){
1002 var context = shoestring( doc.createDocumentFragment() );
1003
1004 context[ 0 ].appendChild( this );
1005 wsel = shoestring( selector, context );
1006 } else {
1007 wsel = shoestring( selector, this.parentNode );
1008 }
1009
1010 if( shoestring.inArray( this, wsel ) > -1 ){
1011 ret.push( this );
1012 }
1013 }
1014 });
1015
1016 return shoestring( ret );
1017 };
1018
1019
1020
1021 /**
1022 * Find descendant elements of the current collection.
1023 *
1024 * @param {string} selector The selector used to find the children
1025 * @return shoestring
1026 * @this shoestring
1027 */
1028 shoestring.fn.find = function( selector ){
1029 var ret = [],
1030 finds;
1031 this.each(function(){
1032 try {
1033 finds = this.querySelectorAll( selector );
1034 } catch( e ) {
1035 shoestring.error( 'queryselector', selector );
1036 }
1037
1038 for( var i = 0, il = finds.length; i < il; i++ ){
1039 ret = ret.concat( finds[i] );
1040 }
1041 });
1042 return shoestring( ret );
1043 };
1044
1045
1046
1047 /**
1048 * Returns the first element of the set wrapped in a new `shoestring` object.
1049 *
1050 * @return shoestring
1051 * @this shoestring
1052 */
1053 shoestring.fn.first = function(){
1054 return this.eq( 0 );
1055 };
1056
1057
1058
1059 /**
1060 * Returns the raw DOM node at the passed index.
1061 *
1062 * @param {integer} index The index of the element to wrap and return.
1063 * @return {HTMLElement|undefined|array}
1064 * @this shoestring
1065 */
1066 shoestring.fn.get = function( index ){
1067
1068 // return an array of elements if index is undefined
1069 if( index === undefined ){
1070 var elements = [];
1071
1072 for( var i = 0; i < this.length; i++ ){
1073 elements.push( this[ i ] );
1074 }
1075
1076 return elements;
1077 } else {
1078 return this[ index ];
1079 }
1080 };
1081
1082
1083
1084 /**
1085 * Private function for setting/getting the offset property for height/width.
1086 *
1087 * **NOTE** Please use the [width](width.js.html) or [height](height.js.html) methods instead.
1088 *
1089 * @param {shoestring} set The set of elements.
1090 * @param {string} name The string "height" or "width".
1091 * @param {float|undefined} value The value to assign.
1092 * @return shoestring
1093 * @this window
1094 */
1095 shoestring._dimension = function( set, name, value ){
1096 var offsetName;
1097
1098 if( value === undefined ){
1099 offsetName = name.replace(/^[a-z]/, function( letter ) {
1100 return letter.toUpperCase();
1101 });
1102
1103 return set[ 0 ][ "offset" + offsetName ];
1104 } else {
1105 // support integer values as pixels
1106 value = typeof value === "string" ? value : value + "px";
1107
1108 return set.each(function(){
1109 this.style[ name ] = value;
1110 });
1111 }
1112 };
1113
1114
1115
1116 /**
1117 * Gets the height value of the first element or sets the height for the whole set.
1118 *
1119 * @param {float|undefined} value The value to assign.
1120 * @return shoestring
1121 * @this shoestring
1122 */
1123 shoestring.fn.height = function( value ){
1124 return shoestring._dimension( this, "height", value );
1125 };
1126
1127
1128
1129 var set = function( html ){
1130 if( typeof html === "string" || typeof html === "number" ){
1131 return this.each(function(){
1132 this.innerHTML = "" + html;
1133 });
1134 } else {
1135 var h = "";
1136 if( typeof html.length !== "undefined" ){
1137 for( var i = 0, l = html.length; i < l; i++ ){
1138 h += html[i].outerHTML;
1139 }
1140 } else {
1141 h = html.outerHTML;
1142 }
1143 return this.each(function(){
1144 this.innerHTML = h;
1145 });
1146 }
1147 };
1148 /**
1149 * Gets or sets the `innerHTML` from all the elements in the set.
1150 *
1151 * @param {string|undefined} html The html to assign
1152 * @return {string|shoestring}
1153 * @this shoestring
1154 */
1155 shoestring.fn.html = function( html ){
1156 if( !!html && typeof html === "function" ){
1157 shoestring.error( 'html-function' );
1158 }
1159 if( typeof html !== "undefined" ){
1160 return set.call( this, html );
1161 } else { // get
1162 var pile = "";
1163
1164 this.each(function(){
1165 pile += this.innerHTML;
1166 });
1167
1168 return pile;
1169 }
1170 };
1171
1172
1173
1174 (function() {
1175 function _getIndex( set, test ) {
1176 var i, result, element;
1177
1178 for( i = result = 0; i < set.length; i++ ) {
1179 element = set.item ? set.item(i) : set[i];
1180
1181 if( test(element) ){
1182 return result;
1183 }
1184
1185 // ignore text nodes, etc
1186 // NOTE may need to be more permissive
1187 if( element.nodeType === 1 ){
1188 result++;
1189 }
1190 }
1191
1192 return -1;
1193 }
1194
1195 /**
1196 * Find the index in the current set for the passed selector.
1197 * Without a selector it returns the index of the first node within the array of its siblings.
1198 *
1199 * @param {string|undefined} selector The selector used to search for the index.
1200 * @return {integer}
1201 * @this {shoestring}
1202 */
1203 shoestring.fn.index = function( selector ){
1204 var self, children;
1205
1206 self = this;
1207
1208 // no arg? check the children, otherwise check each element that matches
1209 if( selector === undefined ){
1210 children = ( ( this[ 0 ] && this[0].parentNode ) || doc.documentElement).childNodes;
1211
1212 // check if the element matches the first of the set
1213 return _getIndex(children, function( element ) {
1214 return self[0] === element;
1215 });
1216 } else {
1217 if( selector.constructor === shoestring.Shoestring ) {
1218 shoestring.error( "index-shoestring-object" );
1219 }
1220
1221 // check if the element matches the first selected node from the parent
1222 return _getIndex(self, function( element ) {
1223 return element === (shoestring( selector, element.parentNode )[ 0 ]);
1224 });
1225 }
1226 };
1227 })();
1228
1229
1230
1231 /**
1232 * Insert the current set after the elements matching the selector.
1233 *
1234 * @param {string} selector The selector after which to insert the current set.
1235 * @return shoestring
1236 * @this shoestring
1237 */
1238 shoestring.fn.insertAfter = function( selector ){
1239 return this.each(function(){
1240 shoestring( selector ).after( this );
1241 });
1242 };
1243
1244
1245
1246 /**
1247 * Insert the current set before the elements matching the selector.
1248 *
1249 * @param {string} selector The selector before which to insert the current set.
1250 * @return shoestring
1251 * @this shoestring
1252 */
1253 shoestring.fn.insertBefore = function( selector ){
1254 return this.each(function(){
1255 shoestring( selector ).before( this );
1256 });
1257 };
1258
1259
1260
1261 /**
1262 * Returns the last element of the set wrapped in a new `shoestring` object.
1263 *
1264 * @return shoestring
1265 * @this shoestring
1266 */
1267 shoestring.fn.last = function(){
1268 return this.eq( this.length - 1 );
1269 };
1270
1271
1272
1273 /**
1274 * Returns a `shoestring` object with the set of siblings of each element in the original set.
1275 *
1276 * @return shoestring
1277 * @this shoestring
1278 */
1279 shoestring.fn.next = function(){
1280 if( arguments.length > 0 ){
1281 shoestring.error( 'next-selector' );
1282 }
1283
1284 var result = [];
1285
1286 // TODO need to implement map
1287 this.each(function() {
1288 var children, item, found;
1289
1290 // get the child nodes for this member of the set
1291 children = shoestring( this.parentNode )[0].childNodes;
1292
1293 for( var i = 0; i < children.length; i++ ){
1294 item = children.item( i );
1295
1296 // found the item we needed (found) which means current item value is
1297 // the next node in the list, as long as it's viable grab it
1298 // NOTE may need to be more permissive
1299 if( found && item.nodeType === 1 ){
1300 result.push( item );
1301 break;
1302 }
1303
1304 // find the current item and mark it as found
1305 if( item === this ){
1306 found = true;
1307 }
1308 }
1309 });
1310
1311 return shoestring( result );
1312 };
1313
1314
1315
1316 /**
1317 * Removes elements from the current set.
1318 *
1319 * @param {string} selector The selector to use when removing the elements.
1320 * @return shoestring
1321 * @this shoestring
1322 */
1323 shoestring.fn.not = function( selector ){
1324 var ret = [];
1325
1326 this.each(function(){
1327 var found = shoestring( selector, this.parentNode );
1328
1329 if( shoestring.inArray(this, found) === -1 ){
1330 ret.push( this );
1331 }
1332 });
1333
1334 return shoestring( ret );
1335 };
1336
1337
1338
1339 /**
1340 * Returns an object with the `top` and `left` properties corresponging to the first elements offsets.
1341 *
1342 * @return object
1343 * @this shoestring
1344 */
1345 shoestring.fn.offset = function(){
1346 return {
1347 top: this[ 0 ].offsetTop,
1348 left: this[ 0 ].offsetLeft
1349 };
1350 };
1351
1352
1353
1354 /**
1355 * Returns the set of first parents for each element in the current set.
1356 *
1357 * @return shoestring
1358 * @this shoestring
1359 */
1360 shoestring.fn.parent = function(){
1361 var ret = [],
1362 parent;
1363
1364 this.each(function(){
1365 // no parent node, assume top level
1366 // jQuery parent: return the document object for <html> or the parent node if it exists
1367 parent = (this === doc.documentElement ? doc : this.parentNode);
1368
1369 // if there is a parent and it's not a document fragment
1370 if( parent && parent.nodeType !== 11 ){
1371 ret.push( parent );
1372 }
1373 });
1374
1375 return shoestring(ret);
1376 };
1377
1378
1379
1380 /**
1381 * Returns the set of all parents matching the selector if provided for each element in the current set.
1382 *
1383 * @param {string} selector The selector to check the parents with.
1384 * @return shoestring
1385 * @this shoestring
1386 */
1387 shoestring.fn.parents = function( selector ){
1388 var ret = [];
1389
1390 this.each(function(){
1391 var curr = this, match;
1392
1393 while( curr.parentElement && !match ){
1394 curr = curr.parentElement;
1395
1396 if( selector ){
1397 if( curr === shoestring( selector )[0] ){
1398 match = true;
1399
1400 if( shoestring.inArray( curr, ret ) === -1 ){
1401 ret.push( curr );
1402 }
1403 }
1404 } else {
1405 if( shoestring.inArray( curr, ret ) === -1 ){
1406 ret.push( curr );
1407 }
1408 }
1409 }
1410 });
1411
1412 return shoestring(ret);
1413 };
1414
1415
1416
1417 /**
1418 * Add an HTML string or element before the children of each element in the current set.
1419 *
1420 * @param {string|HTMLElement} fragment The HTML string or element to add.
1421 * @return shoestring
1422 * @this shoestring
1423 */
1424 shoestring.fn.prepend = function( fragment ){
1425 if( typeof( fragment ) === "string" || fragment.nodeType !== undefined ){
1426 fragment = shoestring( fragment );
1427 }
1428
1429 return this.each(function( i ){
1430
1431 for( var j = 0, jl = fragment.length; j < jl; j++ ){
1432 var insertEl = i > 0 ? fragment[ j ].cloneNode( true ) : fragment[ j ];
1433 if ( this.firstChild ){
1434 this.insertBefore( insertEl, this.firstChild );
1435 } else {
1436 this.appendChild( insertEl );
1437 }
1438 }
1439 });
1440 };
1441
1442
1443
1444 /**
1445 * Add each element of the current set before the children of the selected elements.
1446 *
1447 * @param {string} selector The selector for the elements to add the current set to..
1448 * @return shoestring
1449 * @this shoestring
1450 */
1451 shoestring.fn.prependTo = function( selector ){
1452 return this.each(function(){
1453 shoestring( selector ).prepend( this );
1454 });
1455 };
1456
1457
1458
1459 /**
1460 * Returns a `shoestring` object with the set of *one* siblingx before each element in the original set.
1461 *
1462 * @return shoestring
1463 * @this shoestring
1464 */
1465 shoestring.fn.prev = function(){
1466 if( arguments.length > 0 ){
1467 shoestring.error( 'prev-selector' );
1468 }
1469
1470 var result = [];
1471
1472 // TODO need to implement map
1473 this.each(function() {
1474 var children, item, found;
1475
1476 // get the child nodes for this member of the set
1477 children = shoestring( this.parentNode )[0].childNodes;
1478
1479 for( var i = children.length -1; i >= 0; i-- ){
1480 item = children.item( i );
1481
1482 // found the item we needed (found) which means current item value is
1483 // the next node in the list, as long as it's viable grab it
1484 // NOTE may need to be more permissive
1485 if( found && item.nodeType === 1 ){
1486 result.push( item );
1487 break;
1488 }
1489
1490 // find the current item and mark it as found
1491 if( item === this ){
1492 found = true;
1493 }
1494 }
1495 });
1496
1497 return shoestring( result );
1498 };
1499
1500
1501
1502 /**
1503 * Returns a `shoestring` object with the set of *all* siblings before each element in the original set.
1504 *
1505 * @return shoestring
1506 * @this shoestring
1507 */
1508 shoestring.fn.prevAll = function(){
1509 if( arguments.length > 0 ){
1510 shoestring.error( 'prevall-selector' );
1511 }
1512
1513 var result = [];
1514
1515 this.each(function() {
1516 var $previous = shoestring( this ).prev();
1517
1518 while( $previous.length ){
1519 result.push( $previous[0] );
1520 $previous = $previous.prev();
1521 }
1522 });
1523
1524 return shoestring( result );
1525 };
1526
1527
1528
1529 // Property normalization, a subset taken from jQuery src
1530 shoestring.propFix = {
1531 "class": "className",
1532 contenteditable: "contentEditable",
1533 "for": "htmlFor",
1534 readonly: "readOnly",
1535 tabindex: "tabIndex"
1536 };
1537
1538
1539
1540 /**
1541 * Gets the property value from the first element or sets the property value on all elements of the currrent set.
1542 *
1543 * @param {string} name The property name.
1544 * @param {any} value The property value.
1545 * @return {any|shoestring}
1546 * @this shoestring
1547 */
1548 shoestring.fn.prop = function( name, value ){
1549 if( !this[0] ){
1550 return;
1551 }
1552
1553 name = shoestring.propFix[ name ] || name;
1554
1555 if( value !== undefined ){
1556 return this.each(function(){
1557 this[ name ] = value;
1558 });
1559 } else {
1560 return this[ 0 ][ name ];
1561 }
1562 };
1563
1564
1565
1566 /**
1567 * Remove an attribute from each element in the current set.
1568 *
1569 * @param {string} name The name of the attribute.
1570 * @return shoestring
1571 * @this shoestring
1572 */
1573 shoestring.fn.removeAttr = function( name ){
1574 return this.each(function(){
1575 this.removeAttribute( name );
1576 });
1577 };
1578
1579
1580
1581 /**
1582 * Remove a class from each DOM element in the set of elements.
1583 *
1584 * @param {string} className The name of the class to be removed.
1585 * @return shoestring
1586 * @this shoestring
1587 */
1588 shoestring.fn.removeClass = function( cname ){
1589 var classes = cname.replace(/^\s+|\s+$/g, '').split( " " );
1590
1591 return this.each(function(){
1592 var newClassName, regex;
1593
1594 for( var i = 0, il = classes.length; i < il; i++ ){
1595 if( this.className !== undefined ){
1596 regex = new RegExp( "(^|\\s)" + classes[ i ] + "($|\\s)", "gmi" );
1597 newClassName = this.className.replace( regex, " " );
1598
1599 this.className = newClassName.replace(/^\s+|\s+$/g, '');
1600 }
1601 }
1602 });
1603 };
1604
1605
1606
1607 /**
1608 * Remove the current set of elements from the DOM.
1609 *
1610 * @return shoestring
1611 * @this shoestring
1612 */
1613 shoestring.fn.remove = function(){
1614 return this.each(function(){
1615 if( this.parentNode ) {
1616 this.parentNode.removeChild( this );
1617 }
1618 });
1619 };
1620
1621
1622
1623 /**
1624 * Remove a proprety from each element in the current set.
1625 *
1626 * @param {string} name The name of the property.
1627 * @return shoestring
1628 * @this shoestring
1629 */
1630 shoestring.fn.removeProp = function( property ){
1631 var name = shoestring.propFix[ property ] || property;
1632
1633 return this.each(function(){
1634 this[ name ] = undefined;
1635 delete this[ name ];
1636 });
1637 };
1638
1639
1640
1641 /**
1642 * Replace each element in the current set with that argument HTML string or HTMLElement.
1643 *
1644 * @param {string|HTMLElement} fragment The value to assign.
1645 * @return shoestring
1646 * @this shoestring
1647 */
1648 shoestring.fn.replaceWith = function( fragment ){
1649 if( typeof( fragment ) === "string" ){
1650 fragment = shoestring( fragment );
1651 }
1652
1653 var ret = [];
1654
1655 if( fragment.length > 1 ){
1656 fragment = fragment.reverse();
1657 }
1658 this.each(function( i ){
1659 var clone = this.cloneNode( true ),
1660 insertEl;
1661 ret.push( clone );
1662
1663 // If there is no parentNode, this is pointless, drop it.
1664 if( !this.parentNode ){ return; }
1665
1666 if( fragment.length === 1 ){
1667 insertEl = i > 0 ? fragment[ 0 ].cloneNode( true ) : fragment[ 0 ];
1668 this.parentNode.replaceChild( insertEl, this );
1669 } else {
1670 for( var j = 0, jl = fragment.length; j < jl; j++ ){
1671 insertEl = i > 0 ? fragment[ j ].cloneNode( true ) : fragment[ j ];
1672 this.parentNode.insertBefore( insertEl, this.nextSibling );
1673 }
1674 this.parentNode.removeChild( this );
1675 }
1676 });
1677
1678 return shoestring( ret );
1679 };
1680
1681
1682
1683 shoestring.inputTypes = [
1684 "text",
1685 "hidden",
1686 "password",
1687 "color",
1688 "date",
1689 "datetime",
1690 // "datetime\-local" matched by datetime
1691 "email",
1692 "month",
1693 "number",
1694 "range",
1695 "search",
1696 "tel",
1697 "time",
1698 "url",
1699 "week"
1700 ];
1701
1702 shoestring.inputTypeTest = new RegExp( shoestring.inputTypes.join( "|" ) );
1703
1704
1705 /**
1706 * Serialize child input element values into an object.
1707 *
1708 * @return shoestring
1709 * @this shoestring
1710 */
1711 shoestring.fn.serialize = function(){
1712 var data = {};
1713
1714 shoestring( "input, select", this ).each(function(){
1715 var type = this.type, name = this.name, value = this.value;
1716
1717 if( shoestring.inputTypeTest.test( type ) ||
1718 ( type === "checkbox" || type === "radio" ) &&
1719 this.checked ){
1720
1721 data[ name ] = value;
1722 } else if( this.nodeName === "SELECT" ){
1723 data[ name ] = this.options[ this.selectedIndex ].nodeValue;
1724 }
1725 });
1726
1727 return data;
1728 };
1729
1730
1731
1732 /**
1733 * Get all of the sibling elements for each element in the current set.
1734 *
1735 * @return shoestring
1736 * @this shoestring
1737 */
1738 shoestring.fn.siblings = function(){
1739 if( arguments.length > 0 ) {
1740 shoestring.error( 'siblings-selector' );
1741 }
1742
1743 if( !this.length ) {
1744 return shoestring( [] );
1745 }
1746
1747 var sibs = [], el = this[ 0 ].parentNode.firstChild;
1748
1749 do {
1750 if( el.nodeType === 1 && el !== this[ 0 ] ) {
1751 sibs.push( el );
1752 }
1753
1754 el = el.nextSibling;
1755 } while( el );
1756
1757 return shoestring( sibs );
1758 };
1759
1760
1761
1762 var getText = function( elem ){
1763 var node,
1764 ret = "",
1765 i = 0,
1766 nodeType = elem.nodeType;
1767
1768 if ( !nodeType ) {
1769 // If no nodeType, this is expected to be an array
1770 while ( (node = elem[i++]) ) {
1771 // Do not traverse comment nodes
1772 ret += getText( node );
1773 }
1774 } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) {
1775 // Use textContent for elements
1776 // innerText usage removed for consistency of new lines (jQuery #11153)
1777 if ( typeof elem.textContent === "string" ) {
1778 return elem.textContent;
1779 } else {
1780 // Traverse its children
1781 for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) {
1782 ret += getText( elem );
1783 }
1784 }
1785 } else if ( nodeType === 3 || nodeType === 4 ) {
1786 return elem.nodeValue;
1787 }
1788 // Do not include comment or processing instruction nodes
1789
1790 return ret;
1791 };
1792
1793 /**
1794 * Recursively retrieve the text content of the each element in the current set.
1795 *
1796 * @return shoestring
1797 * @this shoestring
1798 */
1799 shoestring.fn.text = function() {
1800 if( arguments.length > 0 ){
1801 shoestring.error( 'text-setter' );
1802 }
1803
1804 return getText( this );
1805 };
1806
1807
1808
1809
1810 /**
1811 * Get the value of the first element or set the value of all elements in the current set.
1812 *
1813 * @param {string} value The value to set.
1814 * @return shoestring
1815 * @this shoestring
1816 */
1817 shoestring.fn.val = function( value ){
1818 var el;
1819 if( value !== undefined ){
1820 return this.each(function(){
1821 if( this.tagName === "SELECT" ){
1822 var optionSet, option,
1823 options = this.options,
1824 values = [],
1825 i = options.length,
1826 newIndex;
1827
1828 values[0] = value;
1829 while ( i-- ) {
1830 option = options[ i ];
1831 if ( (option.selected = shoestring.inArray( option.value, values ) >= 0) ) {
1832 optionSet = true;
1833 newIndex = i;
1834 }
1835 }
1836 // force browsers to behave consistently when non-matching value is set
1837 if ( !optionSet ) {
1838 this.selectedIndex = -1;
1839 } else {
1840 this.selectedIndex = newIndex;
1841 }
1842 } else {
1843 this.value = value;
1844 }
1845 });
1846 } else {
1847 el = this[0];
1848
1849 if( el.tagName === "SELECT" ){
1850 if( el.selectedIndex < 0 ){ return ""; }
1851 return el.options[ el.selectedIndex ].value;
1852 } else {
1853 return el.value;
1854 }
1855 }
1856 };
1857
1858
1859
1860 /**
1861 * Gets the width value of the first element or sets the width for the whole set.
1862 *
1863 * @param {float|undefined} value The value to assign.
1864 * @return shoestring
1865 * @this shoestring
1866 */
1867 shoestring.fn.width = function( value ){
1868 return shoestring._dimension( this, "width", value );
1869 };
1870
1871
1872
1873 /**
1874 * Wraps the child elements in the provided HTML.
1875 *
1876 * @param {string} html The wrapping HTML.
1877 * @return shoestring
1878 * @this shoestring
1879 */
1880 shoestring.fn.wrapInner = function( html ){
1881 return this.each(function(){
1882 var inH = this.innerHTML;
1883
1884 this.innerHTML = "";
1885 shoestring( this ).append( shoestring( html ).html( inH ) );
1886 });
1887 };
1888
1889
1890
1891 function initEventCache( el, evt ) {
1892 if ( !el.shoestringData ) {
1893 el.shoestringData = {};
1894 }
1895 if ( !el.shoestringData.events ) {
1896 el.shoestringData.events = {};
1897 }
1898 if ( !el.shoestringData.loop ) {
1899 el.shoestringData.loop = {};
1900 }
1901 if ( !el.shoestringData.events[ evt ] ) {
1902 el.shoestringData.events[ evt ] = [];
1903 }
1904 }
1905
1906 function addToEventCache( el, evt, eventInfo ) {
1907 var obj = {};
1908 obj.isCustomEvent = eventInfo.isCustomEvent;
1909 obj.callback = eventInfo.callfunc;
1910 obj.originalCallback = eventInfo.originalCallback;
1911 obj.namespace = eventInfo.namespace;
1912
1913 el.shoestringData.events[ evt ].push( obj );
1914
1915 if( eventInfo.customEventLoop ) {
1916 el.shoestringData.loop[ evt ] = eventInfo.customEventLoop;
1917 }
1918 }
1919
1920 /**
1921 * Bind a callback to an event for the currrent set of elements.
1922 *
1923 * @param {string} evt The event(s) to watch for.
1924 * @param {object,function} data Data to be included with each event or the callback.
1925 * @param {function} originalCallback Callback to be invoked when data is define.d.
1926 * @return shoestring
1927 * @this shoestring
1928 */
1929 shoestring.fn.bind = function( evt, data, originalCallback ){
1930
1931 if( arguments.length > 3 ){
1932 shoestring.error( 'on-delegate' );
1933 }
1934 if( typeof data === "string" ){
1935 shoestring.error( 'on-delegate' );
1936 }
1937 if( typeof data === "function" ){
1938 originalCallback = data;
1939 data = null;
1940 }
1941
1942 var evts = evt.split( " " );
1943
1944 // NOTE the `triggeredElement` is purely for custom events from IE
1945 function encasedCallback( e, namespace, triggeredElement ){
1946 var result;
1947
1948 if( e._namespace && e._namespace !== namespace ) {
1949 return;
1950 }
1951
1952 e.data = data;
1953 e.namespace = e._namespace;
1954
1955 var returnTrue = function(){
1956 return true;
1957 };
1958
1959 e.isDefaultPrevented = function(){
1960 return false;
1961 };
1962
1963 var originalPreventDefault = e.preventDefault;
1964 var preventDefaultConstructor = function(){
1965 if( originalPreventDefault ) {
1966 return function(){
1967 e.isDefaultPrevented = returnTrue;
1968 originalPreventDefault.call(e);
1969 };
1970 } else {
1971 return function(){
1972 e.isDefaultPrevented = returnTrue;
1973 e.returnValue = false;
1974 };
1975 }
1976 };
1977
1978 // thanks https://github.com/jonathantneal/EventListener
1979 e.target = triggeredElement || e.target || e.srcElement;
1980 e.preventDefault = preventDefaultConstructor();
1981 e.stopPropagation = e.stopPropagation || function () {
1982 e.cancelBubble = true;
1983 };
1984
1985 result = originalCallback.apply(this, [ e ].concat( e._args ) );
1986
1987 if( result === false ){
1988 e.preventDefault();
1989 e.stopPropagation();
1990 }
1991
1992 return result;
1993 }
1994
1995 return this.each(function(){
1996 var domEventCallback,
1997 customEventCallback,
1998 customEventLoop,
1999 oEl = this;
2000
2001 for( var i = 0, il = evts.length; i < il; i++ ){
2002 var split = evts[ i ].split( "." ),
2003 evt = split[ 0 ],
2004 namespace = split.length > 0 ? split[ 1 ] : null;
2005
2006 domEventCallback = function( originalEvent ) {
2007 if( oEl.ssEventTrigger ) {
2008 originalEvent._namespace = oEl.ssEventTrigger._namespace;
2009 originalEvent._args = oEl.ssEventTrigger._args;
2010
2011 oEl.ssEventTrigger = null;
2012 }
2013 return encasedCallback.call( oEl, originalEvent, namespace );
2014 };
2015 customEventCallback = null;
2016 customEventLoop = null;
2017
2018 initEventCache( this, evt );
2019
2020 this.addEventListener( evt, domEventCallback, false );
2021
2022 addToEventCache( this, evt, {
2023 callfunc: customEventCallback || domEventCallback,
2024 isCustomEvent: !!customEventCallback,
2025 customEventLoop: customEventLoop,
2026 originalCallback: originalCallback,
2027 namespace: namespace
2028 });
2029 }
2030 });
2031 };
2032
2033 shoestring.fn.on = shoestring.fn.bind;
2034
2035 shoestring.fn.live = function(){
2036 shoestring.error( 'live-delegate' );
2037 };
2038
2039 shoestring.fn.delegate = function(){
2040 shoestring.error( 'live-delegate' );
2041 };
2042
2043
2044
2045 /**
2046 * Unbind a previous bound callback for an event.
2047 *
2048 * @param {string} event The event(s) the callback was bound to..
2049 * @param {function} callback Callback to unbind.
2050 * @return shoestring
2051 * @this shoestring
2052 */
2053 shoestring.fn.unbind = function( event, callback ){
2054
2055 if( arguments.length >= 3 || typeof callback === "string" ){
2056 shoestring.error( 'off-delegate' );
2057 }
2058
2059 var evts = event ? event.split( " " ) : [];
2060
2061 return this.each(function(){
2062 if( !this.shoestringData || !this.shoestringData.events ) {
2063 return;
2064 }
2065
2066 if( !evts.length ) {
2067 unbindAll.call( this );
2068 } else {
2069 var split, evt, namespace;
2070 for( var i = 0, il = evts.length; i < il; i++ ){
2071 split = evts[ i ].split( "." ),
2072 evt = split[ 0 ],
2073 namespace = split.length > 0 ? split[ 1 ] : null;
2074
2075 if( evt ) {
2076 unbind.call( this, evt, namespace, callback );
2077 } else {
2078 unbindAll.call( this, namespace, callback );
2079 }
2080 }
2081 }
2082 });
2083 };
2084
2085 function unbind( evt, namespace, callback ) {
2086 var bound = this.shoestringData.events[ evt ];
2087 if( !(bound && bound.length) ) {
2088 return;
2089 }
2090
2091 var matched = [], j, jl;
2092 for( j = 0, jl = bound.length; j < jl; j++ ) {
2093 if( !namespace || namespace === bound[ j ].namespace ) {
2094 if( callback === undefined || callback === bound[ j ].originalCallback ) {
2095 this.removeEventListener( evt, bound[ j ].callback, false );
2096 matched.push( j );
2097 }
2098 }
2099 }
2100
2101 for( j = 0, jl = matched.length; j < jl; j++ ) {
2102 this.shoestringData.events[ evt ].splice( j, 1 );
2103 }
2104 }
2105
2106 function unbindAll( namespace, callback ) {
2107 for( var evtKey in this.shoestringData.events ) {
2108 unbind.call( this, evtKey, namespace, callback );
2109 }
2110 }
2111
2112 shoestring.fn.off = shoestring.fn.unbind;
2113
2114
2115 /**
2116 * Bind a callback to an event for the currrent set of elements, unbind after one occurence.
2117 *
2118 * @param {string} event The event(s) to watch for.
2119 * @param {function} callback Callback to invoke on the event.
2120 * @return shoestring
2121 * @this shoestring
2122 */
2123 shoestring.fn.one = function( event, callback ){
2124 var evts = event.split( " " );
2125
2126 return this.each(function(){
2127 var thisevt, cbs = {}, $t = shoestring( this );
2128
2129 for( var i = 0, il = evts.length; i < il; i++ ){
2130 thisevt = evts[ i ];
2131
2132 cbs[ thisevt ] = function( e ){
2133 var $t = shoestring( this );
2134
2135 for( var j in cbs ) {
2136 $t.unbind( j, cbs[ j ] );
2137 }
2138
2139 return callback.apply( this, [ e ].concat( e._args ) );
2140 };
2141
2142 $t.bind( thisevt, cbs[ thisevt ] );
2143 }
2144 });
2145 };
2146
2147
2148
2149 /**
2150 * Trigger an event on the first element in the set, no bubbling, no defaults.
2151 *
2152 * @param {string} event The event(s) to trigger.
2153 * @param {object} args Arguments to append to callback invocations.
2154 * @return shoestring
2155 * @this shoestring
2156 */
2157 shoestring.fn.triggerHandler = function( event, args ){
2158 var e = event.split( " " )[ 0 ],
2159 el = this[ 0 ],
2160 ret;
2161
2162 // See this.fireEvent( 'on' + evts[ i ], document.createEventObject() ); instead of click() etc in trigger.
2163 if( doc.createEvent && el.shoestringData && el.shoestringData.events && el.shoestringData.events[ e ] ){
2164 var bindings = el.shoestringData.events[ e ];
2165 for (var i in bindings ){
2166 if( bindings.hasOwnProperty( i ) ){
2167 event = doc.createEvent( "Event" );
2168 event.initEvent( e, true, true );
2169 event._args = args;
2170 args.unshift( event );
2171
2172 ret = bindings[ i ].originalCallback.apply( event.target, args );
2173 }
2174 }
2175 }
2176
2177 return ret;
2178 };
2179
2180
2181
2182 /**
2183 * Trigger an event on each of the DOM elements in the current set.
2184 *
2185 * @param {string} event The event(s) to trigger.
2186 * @param {object} args Arguments to append to callback invocations.
2187 * @return shoestring
2188 * @this shoestring
2189 */
2190 shoestring.fn.trigger = function( event, args ){
2191 var evts = event.split( " " );
2192
2193 return this.each(function(){
2194 var split, evt, namespace;
2195 for( var i = 0, il = evts.length; i < il; i++ ){
2196 split = evts[ i ].split( "." ),
2197 evt = split[ 0 ],
2198 namespace = split.length > 0 ? split[ 1 ] : null;
2199
2200 if( evt === "click" ){
2201 if( this.tagName === "INPUT" && this.type === "checkbox" && this.click ){
2202 this.click();
2203 return false;
2204 }
2205 }
2206
2207 if( doc.createEvent ){
2208 var event = doc.createEvent( "Event" );
2209 event.initEvent( evt, true, true );
2210 event._args = args;
2211 event._namespace = namespace;
2212
2213 this.dispatchEvent( event );
2214 }
2215 }
2216 });
2217 };
2218
2219
2220
2221
2222 shoestring.fn.hasClass = function(){
2223 shoestring.error( 'has-class' );
2224 };
2225
2226
2227
2228 shoestring.fn.hide = function(){
2229 shoestring.error( 'show-hide' );
2230 };
2231
2232
2233
2234 shoestring.fn.outerWidth = function(){
2235 shoestring.error( 'outer-width' );
2236 };
2237
2238
2239
2240 shoestring.fn.show = function(){
2241 shoestring.error( 'show-hide' );
2242 };
2243
2244
2245
2246 shoestring.fn.click = function(){
2247 shoestring.error( 'click' );
2248 };
2249
2250
2251
2252 shoestring.map = function(){
2253 shoestring.error( 'map' );
2254 };
2255
2256
2257
2258 shoestring.fn.map = function(){
2259 shoestring.error( 'map' );
2260 };
2261
2262
2263
2264 shoestring.trim = function(){
2265 shoestring.error( 'trim' );
2266 };
2267
2268
2269
2270 (function() {
2271 shoestring.trackedMethodsKey = "shoestringMethods";
2272
2273 // simple check for localStorage from Modernizr - https://github.com/Modernizr/Modernizr/blob/master/feature-detects/storage/localstorage.js
2274 function supportsStorage() {
2275 var mod = "modernizr";
2276 try {
2277 localStorage.setItem(mod, mod);
2278 localStorage.removeItem(mod);
2279 return true;
2280 } catch(e) {
2281 return false;
2282 }
2283 }
2284
2285 // return a new function closed over the old implementation
2286 function recordProxy( old, name ) {
2287 return function() {
2288 var tracked;
2289 try {
2290 tracked = JSON.parse(win.localStorage.getItem( shoestring.trackedMethodsKey ) || "{}");
2291 } catch (e) {
2292 if( e instanceof SyntaxError) {
2293 tracked = {};
2294 }
2295 }
2296
2297 tracked[ name ] = true;
2298 win.localStorage.setItem( shoestring.trackedMethodsKey, JSON.stringify(tracked) );
2299
2300 return old.apply(this, arguments);
2301 };
2302 }
2303
2304 // proxy each of the methods defined on fn
2305 if( supportsStorage() ){
2306 for( var method in shoestring.fn ){
2307 if( shoestring.fn.hasOwnProperty(method) ) {
2308 shoestring.fn[ method ] = recordProxy(shoestring.fn[ method ], method);
2309 }
2310 }
2311 }
2312 })();
2313
2314
2315
2316 return shoestring;
2317 }));