0
|
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 }));
|