Mercurial > nebulaweb3
diff default/node_modules/shoestring/dist/shoestring.js @ 0:1d038bc9b3d2 default tip
Up:default
author | Liny <dev@neowd.com> |
---|---|
date | Sat, 31 May 2025 09:21:51 +0800 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/default/node_modules/shoestring/dist/shoestring.js Sat May 31 09:21:51 2025 +0800 @@ -0,0 +1,2123 @@ +/*! Shoestring - v2.0.1 - 2017-05-24 +* http://github.com/filamentgroup/shoestring/ +* Copyright (c) 2017 Scott Jehl, Filament Group, Inc; Licensed MIT & GPLv2 */ +(function( factory ) { + if( typeof define === 'function' && define.amd ) { + // AMD. Register as an anonymous module. + define( [ 'shoestring' ], factory ); + } else if (typeof module === 'object' && module.exports) { + // Node/CommonJS + module.exports = factory(); + } else { + // Browser globals + factory(); + } +}(function () { + var win = typeof window !== "undefined" ? window : this; + var doc = win.document; + + + /** + * The shoestring object constructor. + * + * @param {string,object} prim The selector to find or element to wrap. + * @param {object} sec The context in which to match the `prim` selector. + * @returns shoestring + * @this window + */ + function shoestring( prim, sec ){ + var pType = typeof( prim ), + ret = [], + sel; + + // return an empty shoestring object + if( !prim ){ + return new Shoestring( ret ); + } + + // ready calls + if( prim.call ){ + return shoestring.ready( prim ); + } + + // handle re-wrapping shoestring objects + if( prim.constructor === Shoestring && !sec ){ + return prim; + } + + // if string starting with <, make html + if( pType === "string" && prim.indexOf( "<" ) === 0 ){ + var dfrag = doc.createElement( "div" ); + + dfrag.innerHTML = prim; + + // TODO depends on children (circular) + return shoestring( dfrag ).children().each(function(){ + dfrag.removeChild( this ); + }); + } + + // if string, it's a selector, use qsa + if( pType === "string" ){ + if( sec ){ + return shoestring( sec ).find( prim ); + } + + sel = doc.querySelectorAll( prim ); + + return new Shoestring( sel, prim ); + } + + // array like objects or node lists + if( Object.prototype.toString.call( pType ) === '[object Array]' || + (win.NodeList && prim instanceof win.NodeList) ){ + + return new Shoestring( prim, prim ); + } + + // if it's an array, use all the elements + if( prim.constructor === Array ){ + return new Shoestring( prim, prim ); + } + + // otherwise assume it's an object the we want at an index + return new Shoestring( [prim], prim ); + } + + var Shoestring = function( ret, prim ) { + this.length = 0; + this.selector = prim; + shoestring.merge(this, ret); + }; + + // TODO only required for tests + Shoestring.prototype.reverse = [].reverse; + + // For adding element set methods + shoestring.fn = Shoestring.prototype; + + shoestring.Shoestring = Shoestring; + + // For extending objects + // TODO move to separate module when we use prototypes + shoestring.extend = function( first, second ){ + for( var i in second ){ + if( second.hasOwnProperty( i ) ){ + first[ i ] = second[ i ]; + } + } + + return first; + }; + + // taken directly from jQuery + shoestring.merge = function( first, second ) { + var len, j, i; + + len = +second.length, + j = 0, + i = first.length; + + for ( ; j < len; j++ ) { + first[ i++ ] = second[ j ]; + } + + first.length = i; + + return first; + }; + + // expose + win.shoestring = shoestring; + + + + /** + * Make an HTTP request to a url. + * + * **NOTE** the following options are supported: + * + * - *method* - The HTTP method used with the request. Default: `GET`. + * - *data* - Raw object with keys and values to pass with request as query params. Default `null`. + * - *headers* - Set of request headers to add. Default `{}`. + * - *async* - Whether the opened request is asynchronouse. Default `true`. + * - *success* - Callback for successful request and response. Passed the response data. + * - *error* - Callback for failed request and response. + * - *cancel* - Callback for cancelled request and response. + * + * @param {string} url The url to request. + * @param {object} options The options object, see Notes. + * @return shoestring + * @this shoestring + */ + + shoestring.ajax = function( url, options ) { + var params = "", req = new XMLHttpRequest(), settings, key; + + settings = shoestring.extend( {}, shoestring.ajax.settings ); + + if( options ){ + shoestring.extend( settings, options ); + } + + if( !url ){ + url = settings.url; + } + + if( !req || !url ){ + return; + } + + // create parameter string from data object + if( settings.data ){ + for( key in settings.data ){ + if( settings.data.hasOwnProperty( key ) ){ + if( params !== "" ){ + params += "&"; + } + params += encodeURIComponent( key ) + "=" + + encodeURIComponent( settings.data[key] ); + } + } + } + + // append params to url for GET requests + if( settings.method === "GET" && params ){ + + url += "?" + params; + } + + req.open( settings.method, url, settings.async ); + + if( req.setRequestHeader ){ + req.setRequestHeader( "X-Requested-With", "XMLHttpRequest" ); + + // Set 'Content-type' header for POST requests + if( settings.method === "POST" && params ){ + req.setRequestHeader( "Content-type", "application/x-www-form-urlencoded" ); + } + + for( key in settings.headers ){ + if( settings.headers.hasOwnProperty( key ) ){ + req.setRequestHeader(key, settings.headers[ key ]); + } + } + } + + req.onreadystatechange = function () { + if( req.readyState === 4 ){ + // Trim the whitespace so shoestring('<div>') works + var res = (req.responseText || '').replace(/^\s+|\s+$/g, ''); + if( req.status.toString().indexOf( "0" ) === 0 ){ + return settings.cancel( res, req.status, req ); + } + else if ( req.status.toString().match( /^(4|5)/ ) && RegExp.$1 ){ + return settings.error( res, req.status, req ); + } + else if (settings.success) { + return settings.success( res, req.status, req ); + } + } + }; + + if( req.readyState === 4 ){ + return req; + } + + // Send request + if( settings.method === "POST" && params ){ + req.send( params ); + } else { + req.send(); + } + + return req; + }; + + shoestring.ajax.settings = { + success: function(){}, + error: function(){}, + cancel: function(){}, + method: "GET", + async: true, + data: null, + headers: {} + }; + + + + /** + * Helper function wrapping a call to [ajax](ajax.js.html) using the `GET` method. + * + * @param {string} url The url to GET from. + * @param {function} callback Callback to invoke on success. + * @return shoestring + * @this shoestring + */ + shoestring.get = function( url, callback ){ + return shoestring.ajax( url, { success: callback } ); + }; + + + + /** + * Load the HTML response from `url` into the current set of elements. + * + * @param {string} url The url to GET from. + * @param {function} callback Callback to invoke after HTML is inserted. + * @return shoestring + * @this shoestring + */ + shoestring.fn.load = function( url, callback ){ + var self = this, + args = arguments, + intCB = function( data ){ + self.each(function(){ + shoestring( this ).html( data ); + }); + + if( callback ){ + callback.apply( self, args ); + } + }; + + shoestring.ajax( url, { success: intCB } ); + return this; + }; + + + + /** + * Helper function wrapping a call to [ajax](ajax.js.html) using the `POST` method. + * + * @param {string} url The url to POST to. + * @param {object} data The data to send. + * @param {function} callback Callback to invoke on success. + * @return shoestring + * @this shoestring + */ + shoestring.post = function( url, data, callback ){ + return shoestring.ajax( url, { data: data, method: "POST", success: callback } ); + }; + + + + /** + * Iterates over `shoestring` collections. + * + * @param {function} callback The callback to be invoked on each element and index + * @return shoestring + * @this shoestring + */ + shoestring.fn.each = function( callback ){ + return shoestring.each( this, callback ); + }; + + shoestring.each = function( collection, callback ) { + var val; + for( var i = 0, il = collection.length; i < il; i++ ){ + val = callback.call( collection[i], i, collection[i] ); + if( val === false ){ + break; + } + } + + return collection; + }; + + + + /** + * Check for array membership. + * + * @param {object} needle The thing to find. + * @param {object} haystack The thing to find the needle in. + * @return {boolean} + * @this window + */ + shoestring.inArray = function( needle, haystack ){ + var isin = -1; + for( var i = 0, il = haystack.length; i < il; i++ ){ + if( haystack.hasOwnProperty( i ) && haystack[ i ] === needle ){ + isin = i; + } + } + return isin; + }; + + + + /** + * Bind callbacks to be run when the DOM is "ready". + * + * @param {function} fn The callback to be run + * @return shoestring + * @this shoestring + */ + shoestring.ready = function( fn ){ + if( ready && fn ){ + fn.call( doc ); + } + else if( fn ){ + readyQueue.push( fn ); + } + else { + runReady(); + } + + return [doc]; + }; + + // TODO necessary? + shoestring.fn.ready = function( fn ){ + shoestring.ready( fn ); + return this; + }; + + // Empty and exec the ready queue + var ready = false, + readyQueue = [], + runReady = function(){ + if( !ready ){ + while( readyQueue.length ){ + readyQueue.shift().call( doc ); + } + ready = true; + } + }; + + // If DOM is already ready at exec time, depends on the browser. + // From: https://github.com/mobify/mobifyjs/blob/526841be5509e28fc949038021799e4223479f8d/src/capture.js#L128 + if (doc.attachEvent ? doc.readyState === "complete" : doc.readyState !== "loading") { + runReady(); + } else { + doc.addEventListener( "DOMContentLoaded", runReady, false ); + doc.addEventListener( "readystatechange", runReady, false ); + win.addEventListener( "load", runReady, false ); + } + + + + /** + * Checks the current set of elements against the selector, if one matches return `true`. + * + * @param {string} selector The selector to check. + * @return {boolean} + * @this {shoestring} + */ + shoestring.fn.is = function( selector ){ + var ret = false, self = this, parents, check; + + // assume a dom element + if( typeof selector !== "string" ){ + // array-like, ie shoestring objects or element arrays + if( selector.length && selector[0] ){ + check = selector; + } else { + check = [selector]; + } + + return _checkElements(this, check); + } + + parents = this.parent(); + + if( !parents.length ){ + parents = shoestring( doc ); + } + + parents.each(function( i, e ) { + var children; + + children = e.querySelectorAll( selector ); + + ret = _checkElements( self, children ); + }); + + return ret; + }; + + function _checkElements(needles, haystack){ + var ret = false; + + needles.each(function() { + var j = 0; + + while( j < haystack.length ){ + if( this === haystack[j] ){ + ret = true; + } + + j++; + } + }); + + return ret; + } + + + + /** + * Get data attached to the first element or set data values on all elements in the current set. + * + * @param {string} name The data attribute name. + * @param {any} value The value assigned to the data attribute. + * @return {any|shoestring} + * @this shoestring + */ + shoestring.fn.data = function( name, value ){ + if( name !== undefined ){ + if( value !== undefined ){ + return this.each(function(){ + if( !this.shoestringData ){ + this.shoestringData = {}; + } + + this.shoestringData[ name ] = value; + }); + } + else { + if( this[ 0 ] ) { + if( this[ 0 ].shoestringData ) { + return this[ 0 ].shoestringData[ name ]; + } + } + } + } + else { + return this[ 0 ] ? this[ 0 ].shoestringData || {} : undefined; + } + }; + + + /** + * Remove data associated with `name` or all the data, for each element in the current set. + * + * @param {string} name The data attribute name. + * @return shoestring + * @this shoestring + */ + shoestring.fn.removeData = function( name ){ + return this.each(function(){ + if( name !== undefined && this.shoestringData ){ + this.shoestringData[ name ] = undefined; + delete this.shoestringData[ name ]; + } else { + this[ 0 ].shoestringData = {}; + } + }); + }; + + + + /** + * An alias for the `shoestring` constructor. + */ + win.$ = shoestring; + + + + /** + * Add a class to each DOM element in the set of elements. + * + * @param {string} className The name of the class to be added. + * @return shoestring + * @this shoestring + */ + shoestring.fn.addClass = function( className ){ + var classes = className.replace(/^\s+|\s+$/g, '').split( " " ); + + return this.each(function(){ + for( var i = 0, il = classes.length; i < il; i++ ){ + if( this.className !== undefined && + (this.className === "" || + !this.className.match( new RegExp( "(^|\\s)" + classes[ i ] + "($|\\s)"))) ){ + this.className += " " + classes[ i ]; + } + } + }); + }; + + + + /** + * Add elements matching the selector to the current set. + * + * @param {string} selector The selector for the elements to add from the DOM + * @return shoestring + * @this shoestring + */ + shoestring.fn.add = function( selector ){ + var ret = []; + this.each(function(){ + ret.push( this ); + }); + + shoestring( selector ).each(function(){ + ret.push( this ); + }); + + return shoestring( ret ); + }; + + + + /** + * Insert an element or HTML string after each element in the current set. + * + * @param {string|HTMLElement} fragment The HTML or HTMLElement to insert. + * @return shoestring + * @this shoestring + */ + shoestring.fn.after = function( fragment ){ + if( typeof( fragment ) === "string" || fragment.nodeType !== undefined ){ + fragment = shoestring( fragment ); + } + + if( fragment.length > 1 ){ + fragment = fragment.reverse(); + } + return this.each(function( i ){ + for( var j = 0, jl = fragment.length; j < jl; j++ ){ + var insertEl = i > 0 ? fragment[ j ].cloneNode( true ) : fragment[ j ]; + this.parentNode.insertBefore( insertEl, this.nextSibling ); + } + }); + }; + + + + /** + * Insert an element or HTML string as the last child of each element in the set. + * + * @param {string|HTMLElement} fragment The HTML or HTMLElement to insert. + * @return shoestring + * @this shoestring + */ + shoestring.fn.append = function( fragment ){ + if( typeof( fragment ) === "string" || fragment.nodeType !== undefined ){ + fragment = shoestring( fragment ); + } + + return this.each(function( i ){ + for( var j = 0, jl = fragment.length; j < jl; j++ ){ + this.appendChild( i > 0 ? fragment[ j ].cloneNode( true ) : fragment[ j ] ); + } + }); + }; + + + + /** + * Insert the current set as the last child of the elements matching the selector. + * + * @param {string} selector The selector after which to append the current set. + * @return shoestring + * @this shoestring + */ + shoestring.fn.appendTo = function( selector ){ + return this.each(function(){ + shoestring( selector ).append( this ); + }); + }; + + + + /** + * Get the value of the first element of the set or set the value of all the elements in the set. + * + * @param {string} name The attribute name. + * @param {string} value The new value for the attribute. + * @return {shoestring|string|undefined} + * @this {shoestring} + */ + shoestring.fn.attr = function( name, value ){ + var nameStr = typeof( name ) === "string"; + + if( value !== undefined || !nameStr ){ + return this.each(function(){ + if( nameStr ){ + this.setAttribute( name, value ); + } else { + for( var i in name ){ + if( name.hasOwnProperty( i ) ){ + this.setAttribute( i, name[ i ] ); + } + } + } + }); + } else { + return this[ 0 ] ? this[ 0 ].getAttribute( name ) : undefined; + } + }; + + + + /** + * Insert an element or HTML string before each element in the current set. + * + * @param {string|HTMLElement} fragment The HTML or HTMLElement to insert. + * @return shoestring + * @this shoestring + */ + shoestring.fn.before = function( fragment ){ + if( typeof( fragment ) === "string" || fragment.nodeType !== undefined ){ + fragment = shoestring( fragment ); + } + + return this.each(function( i ){ + for( var j = 0, jl = fragment.length; j < jl; j++ ){ + this.parentNode.insertBefore( i > 0 ? fragment[ j ].cloneNode( true ) : fragment[ j ], this ); + } + }); + }; + + + + /** + * Get the children of the current collection. + * @return shoestring + * @this shoestring + */ + shoestring.fn.children = function(){ + var ret = [], + childs, + j; + this.each(function(){ + childs = this.children; + j = -1; + + while( j++ < childs.length-1 ){ + if( shoestring.inArray( childs[ j ], ret ) === -1 ){ + ret.push( childs[ j ] ); + } + } + }); + return shoestring(ret); + }; + + + + /** + * Clone and return the current set of nodes into a new `shoestring` object. + * + * @return shoestring + * @this shoestring + */ + shoestring.fn.clone = function() { + var ret = []; + + this.each(function() { + ret.push( this.cloneNode( true ) ); + }); + + return shoestring( ret ); + }; + + + + /** + * Find an element matching the selector in the set of the current element and its parents. + * + * @param {string} selector The selector used to identify the target element. + * @return shoestring + * @this shoestring + */ + shoestring.fn.closest = function( selector ){ + var ret = []; + + if( !selector ){ + return shoestring( ret ); + } + + this.each(function(){ + var element, $self = shoestring( element = this ); + + if( $self.is(selector) ){ + ret.push( this ); + return; + } + + while( element.parentElement ) { + if( shoestring(element.parentElement).is(selector) ){ + ret.push( element.parentElement ); + break; + } + + element = element.parentElement; + } + }); + + return shoestring( ret ); + }; + + + + shoestring.cssExceptions = { + 'float': [ 'cssFloat' ] + }; + + + + (function() { + var cssExceptions = shoestring.cssExceptions; + + // marginRight instead of margin-right + function convertPropertyName( str ) { + return str.replace( /\-([A-Za-z])/g, function ( match, character ) { + return character.toUpperCase(); + }); + } + + function _getStyle( element, property ) { + return win.getComputedStyle( element, null ).getPropertyValue( property ); + } + + var vendorPrefixes = [ '', '-webkit-', '-ms-', '-moz-', '-o-', '-khtml-' ]; + + /** + * Private function for getting the computed style of an element. + * + * **NOTE** Please use the [css](../css.js.html) method instead. + * + * @method _getStyle + * @param {HTMLElement} element The element we want the style property for. + * @param {string} property The css property we want the style for. + */ + shoestring._getStyle = function( element, property ) { + var convert, value, j, k; + + if( cssExceptions[ property ] ) { + for( j = 0, k = cssExceptions[ property ].length; j < k; j++ ) { + value = _getStyle( element, cssExceptions[ property ][ j ] ); + + if( value ) { + return value; + } + } + } + + for( j = 0, k = vendorPrefixes.length; j < k; j++ ) { + convert = convertPropertyName( vendorPrefixes[ j ] + property ); + + // VendorprefixKeyName || key-name + value = _getStyle( element, convert ); + + if( convert !== property ) { + value = value || _getStyle( element, property ); + } + + if( vendorPrefixes[ j ] ) { + // -vendorprefix-key-name + value = value || _getStyle( element, vendorPrefixes[ j ] + property ); + } + + if( value ) { + return value; + } + } + + return undefined; + }; + })(); + + + + (function() { + var cssExceptions = shoestring.cssExceptions; + + // marginRight instead of margin-right + function convertPropertyName( str ) { + return str.replace( /\-([A-Za-z])/g, function ( match, character ) { + return character.toUpperCase(); + }); + } + + /** + * Private function for setting the style of an element. + * + * **NOTE** Please use the [css](../css.js.html) method instead. + * + * @method _setStyle + * @param {HTMLElement} element The element we want to style. + * @param {string} property The property being used to style the element. + * @param {string} value The css value for the style property. + */ + shoestring._setStyle = function( element, property, value ) { + var convertedProperty = convertPropertyName(property); + + element.style[ property ] = value; + + if( convertedProperty !== property ) { + element.style[ convertedProperty ] = value; + } + + if( cssExceptions[ property ] ) { + for( var j = 0, k = cssExceptions[ property ].length; j<k; j++ ) { + element.style[ cssExceptions[ property ][ j ] ] = value; + } + } + }; + })(); + + + + /** + * Get the compute style property of the first element or set the value of a style property + * on all elements in the set. + * + * @method _setStyle + * @param {string} property The property being used to style the element. + * @param {string|undefined} value The css value for the style property. + * @return {string|shoestring} + * @this shoestring + */ + shoestring.fn.css = function( property, value ){ + if( !this[0] ){ + return; + } + + if( typeof property === "object" ) { + return this.each(function() { + for( var key in property ) { + if( property.hasOwnProperty( key ) ) { + shoestring._setStyle( this, key, property[key] ); + } + } + }); + } else { + // assignment else retrieve first + if( value !== undefined ){ + return this.each(function(){ + shoestring._setStyle( this, property, value ); + }); + } + + return shoestring._getStyle( this[0], property ); + } + }; + + + + /** + * Returns the indexed element wrapped in a new `shoestring` object. + * + * @param {integer} index The index of the element to wrap and return. + * @return shoestring + * @this shoestring + */ + shoestring.fn.eq = function( index ){ + if( this[index] ){ + return shoestring( this[index] ); + } + + return shoestring([]); + }; + + + + /** + * Filter out the current set if they do *not* match the passed selector or + * the supplied callback returns false + * + * @param {string,function} selector The selector or boolean return value callback used to filter the elements. + * @return shoestring + * @this shoestring + */ + shoestring.fn.filter = function( selector ){ + var ret = []; + + this.each(function( index ){ + var wsel; + + if( typeof selector === 'function' ) { + if( selector.call( this, index ) !== false ) { + ret.push( this ); + } + // document node + } else if( this.nodeType === 9 ){ + if( this === selector ) { + ret.push( this ); + } + } else { + if( !this.parentNode ){ + var context = shoestring( doc.createDocumentFragment() ); + + context[ 0 ].appendChild( this ); + wsel = shoestring( selector, context ); + } else { + wsel = shoestring( selector, this.parentNode ); + } + + if( shoestring.inArray( this, wsel ) > -1 ){ + ret.push( this ); + } + } + }); + + return shoestring( ret ); + }; + + + + /** + * Find descendant elements of the current collection. + * + * @param {string} selector The selector used to find the children + * @return shoestring + * @this shoestring + */ + shoestring.fn.find = function( selector ){ + var ret = [], + finds; + this.each(function(){ + finds = this.querySelectorAll( selector ); + + for( var i = 0, il = finds.length; i < il; i++ ){ + ret = ret.concat( finds[i] ); + } + }); + return shoestring( ret ); + }; + + + + /** + * Returns the first element of the set wrapped in a new `shoestring` object. + * + * @return shoestring + * @this shoestring + */ + shoestring.fn.first = function(){ + return this.eq( 0 ); + }; + + + + /** + * Returns the raw DOM node at the passed index. + * + * @param {integer} index The index of the element to wrap and return. + * @return {HTMLElement|undefined|array} + * @this shoestring + */ + shoestring.fn.get = function( index ){ + + // return an array of elements if index is undefined + if( index === undefined ){ + var elements = []; + + for( var i = 0; i < this.length; i++ ){ + elements.push( this[ i ] ); + } + + return elements; + } else { + return this[ index ]; + } + }; + + + + /** + * Private function for setting/getting the offset property for height/width. + * + * **NOTE** Please use the [width](width.js.html) or [height](height.js.html) methods instead. + * + * @param {shoestring} set The set of elements. + * @param {string} name The string "height" or "width". + * @param {float|undefined} value The value to assign. + * @return shoestring + * @this window + */ + shoestring._dimension = function( set, name, value ){ + var offsetName; + + if( value === undefined ){ + offsetName = name.replace(/^[a-z]/, function( letter ) { + return letter.toUpperCase(); + }); + + return set[ 0 ][ "offset" + offsetName ]; + } else { + // support integer values as pixels + value = typeof value === "string" ? value : value + "px"; + + return set.each(function(){ + this.style[ name ] = value; + }); + } + }; + + + + /** + * Gets the height value of the first element or sets the height for the whole set. + * + * @param {float|undefined} value The value to assign. + * @return shoestring + * @this shoestring + */ + shoestring.fn.height = function( value ){ + return shoestring._dimension( this, "height", value ); + }; + + + + var set = function( html ){ + if( typeof html === "string" || typeof html === "number" ){ + return this.each(function(){ + this.innerHTML = "" + html; + }); + } else { + var h = ""; + if( typeof html.length !== "undefined" ){ + for( var i = 0, l = html.length; i < l; i++ ){ + h += html[i].outerHTML; + } + } else { + h = html.outerHTML; + } + return this.each(function(){ + this.innerHTML = h; + }); + } + }; + /** + * Gets or sets the `innerHTML` from all the elements in the set. + * + * @param {string|undefined} html The html to assign + * @return {string|shoestring} + * @this shoestring + */ + shoestring.fn.html = function( html ){ + if( typeof html !== "undefined" ){ + return set.call( this, html ); + } else { // get + var pile = ""; + + this.each(function(){ + pile += this.innerHTML; + }); + + return pile; + } + }; + + + + (function() { + function _getIndex( set, test ) { + var i, result, element; + + for( i = result = 0; i < set.length; i++ ) { + element = set.item ? set.item(i) : set[i]; + + if( test(element) ){ + return result; + } + + // ignore text nodes, etc + // NOTE may need to be more permissive + if( element.nodeType === 1 ){ + result++; + } + } + + return -1; + } + + /** + * Find the index in the current set for the passed selector. + * Without a selector it returns the index of the first node within the array of its siblings. + * + * @param {string|undefined} selector The selector used to search for the index. + * @return {integer} + * @this {shoestring} + */ + shoestring.fn.index = function( selector ){ + var self, children; + + self = this; + + // no arg? check the children, otherwise check each element that matches + if( selector === undefined ){ + children = ( ( this[ 0 ] && this[0].parentNode ) || doc.documentElement).childNodes; + + // check if the element matches the first of the set + return _getIndex(children, function( element ) { + return self[0] === element; + }); + } else { + + // check if the element matches the first selected node from the parent + return _getIndex(self, function( element ) { + return element === (shoestring( selector, element.parentNode )[ 0 ]); + }); + } + }; + })(); + + + + /** + * Insert the current set after the elements matching the selector. + * + * @param {string} selector The selector after which to insert the current set. + * @return shoestring + * @this shoestring + */ + shoestring.fn.insertAfter = function( selector ){ + return this.each(function(){ + shoestring( selector ).after( this ); + }); + }; + + + + /** + * Insert the current set before the elements matching the selector. + * + * @param {string} selector The selector before which to insert the current set. + * @return shoestring + * @this shoestring + */ + shoestring.fn.insertBefore = function( selector ){ + return this.each(function(){ + shoestring( selector ).before( this ); + }); + }; + + + + /** + * Returns the last element of the set wrapped in a new `shoestring` object. + * + * @return shoestring + * @this shoestring + */ + shoestring.fn.last = function(){ + return this.eq( this.length - 1 ); + }; + + + + /** + * Returns a `shoestring` object with the set of siblings of each element in the original set. + * + * @return shoestring + * @this shoestring + */ + shoestring.fn.next = function(){ + + var result = []; + + // TODO need to implement map + this.each(function() { + var children, item, found; + + // get the child nodes for this member of the set + children = shoestring( this.parentNode )[0].childNodes; + + for( var i = 0; i < children.length; i++ ){ + item = children.item( i ); + + // found the item we needed (found) which means current item value is + // the next node in the list, as long as it's viable grab it + // NOTE may need to be more permissive + if( found && item.nodeType === 1 ){ + result.push( item ); + break; + } + + // find the current item and mark it as found + if( item === this ){ + found = true; + } + } + }); + + return shoestring( result ); + }; + + + + /** + * Removes elements from the current set. + * + * @param {string} selector The selector to use when removing the elements. + * @return shoestring + * @this shoestring + */ + shoestring.fn.not = function( selector ){ + var ret = []; + + this.each(function(){ + var found = shoestring( selector, this.parentNode ); + + if( shoestring.inArray(this, found) === -1 ){ + ret.push( this ); + } + }); + + return shoestring( ret ); + }; + + + + /** + * Returns an object with the `top` and `left` properties corresponging to the first elements offsets. + * + * @return object + * @this shoestring + */ + shoestring.fn.offset = function(){ + return { + top: this[ 0 ].offsetTop, + left: this[ 0 ].offsetLeft + }; + }; + + + + /** + * Returns the set of first parents for each element in the current set. + * + * @return shoestring + * @this shoestring + */ + shoestring.fn.parent = function(){ + var ret = [], + parent; + + this.each(function(){ + // no parent node, assume top level + // jQuery parent: return the document object for <html> or the parent node if it exists + parent = (this === doc.documentElement ? doc : this.parentNode); + + // if there is a parent and it's not a document fragment + if( parent && parent.nodeType !== 11 ){ + ret.push( parent ); + } + }); + + return shoestring(ret); + }; + + + + /** + * Returns the set of all parents matching the selector if provided for each element in the current set. + * + * @param {string} selector The selector to check the parents with. + * @return shoestring + * @this shoestring + */ + shoestring.fn.parents = function( selector ){ + var ret = []; + + this.each(function(){ + var curr = this, match; + + while( curr.parentElement && !match ){ + curr = curr.parentElement; + + if( selector ){ + if( curr === shoestring( selector )[0] ){ + match = true; + + if( shoestring.inArray( curr, ret ) === -1 ){ + ret.push( curr ); + } + } + } else { + if( shoestring.inArray( curr, ret ) === -1 ){ + ret.push( curr ); + } + } + } + }); + + return shoestring(ret); + }; + + + + /** + * Add an HTML string or element before the children of each element in the current set. + * + * @param {string|HTMLElement} fragment The HTML string or element to add. + * @return shoestring + * @this shoestring + */ + shoestring.fn.prepend = function( fragment ){ + if( typeof( fragment ) === "string" || fragment.nodeType !== undefined ){ + fragment = shoestring( fragment ); + } + + return this.each(function( i ){ + + for( var j = 0, jl = fragment.length; j < jl; j++ ){ + var insertEl = i > 0 ? fragment[ j ].cloneNode( true ) : fragment[ j ]; + if ( this.firstChild ){ + this.insertBefore( insertEl, this.firstChild ); + } else { + this.appendChild( insertEl ); + } + } + }); + }; + + + + /** + * Add each element of the current set before the children of the selected elements. + * + * @param {string} selector The selector for the elements to add the current set to.. + * @return shoestring + * @this shoestring + */ + shoestring.fn.prependTo = function( selector ){ + return this.each(function(){ + shoestring( selector ).prepend( this ); + }); + }; + + + + /** + * Returns a `shoestring` object with the set of *one* siblingx before each element in the original set. + * + * @return shoestring + * @this shoestring + */ + shoestring.fn.prev = function(){ + + var result = []; + + // TODO need to implement map + this.each(function() { + var children, item, found; + + // get the child nodes for this member of the set + children = shoestring( this.parentNode )[0].childNodes; + + for( var i = children.length -1; i >= 0; i-- ){ + item = children.item( i ); + + // found the item we needed (found) which means current item value is + // the next node in the list, as long as it's viable grab it + // NOTE may need to be more permissive + if( found && item.nodeType === 1 ){ + result.push( item ); + break; + } + + // find the current item and mark it as found + if( item === this ){ + found = true; + } + } + }); + + return shoestring( result ); + }; + + + + /** + * Returns a `shoestring` object with the set of *all* siblings before each element in the original set. + * + * @return shoestring + * @this shoestring + */ + shoestring.fn.prevAll = function(){ + + var result = []; + + this.each(function() { + var $previous = shoestring( this ).prev(); + + while( $previous.length ){ + result.push( $previous[0] ); + $previous = $previous.prev(); + } + }); + + return shoestring( result ); + }; + + + + // Property normalization, a subset taken from jQuery src + shoestring.propFix = { + "class": "className", + contenteditable: "contentEditable", + "for": "htmlFor", + readonly: "readOnly", + tabindex: "tabIndex" + }; + + + + /** + * Gets the property value from the first element or sets the property value on all elements of the currrent set. + * + * @param {string} name The property name. + * @param {any} value The property value. + * @return {any|shoestring} + * @this shoestring + */ + shoestring.fn.prop = function( name, value ){ + if( !this[0] ){ + return; + } + + name = shoestring.propFix[ name ] || name; + + if( value !== undefined ){ + return this.each(function(){ + this[ name ] = value; + }); + } else { + return this[ 0 ][ name ]; + } + }; + + + + /** + * Remove an attribute from each element in the current set. + * + * @param {string} name The name of the attribute. + * @return shoestring + * @this shoestring + */ + shoestring.fn.removeAttr = function( name ){ + return this.each(function(){ + this.removeAttribute( name ); + }); + }; + + + + /** + * Remove a class from each DOM element in the set of elements. + * + * @param {string} className The name of the class to be removed. + * @return shoestring + * @this shoestring + */ + shoestring.fn.removeClass = function( cname ){ + var classes = cname.replace(/^\s+|\s+$/g, '').split( " " ); + + return this.each(function(){ + var newClassName, regex; + + for( var i = 0, il = classes.length; i < il; i++ ){ + if( this.className !== undefined ){ + regex = new RegExp( "(^|\\s)" + classes[ i ] + "($|\\s)", "gmi" ); + newClassName = this.className.replace( regex, " " ); + + this.className = newClassName.replace(/^\s+|\s+$/g, ''); + } + } + }); + }; + + + + /** + * Remove the current set of elements from the DOM. + * + * @return shoestring + * @this shoestring + */ + shoestring.fn.remove = function(){ + return this.each(function(){ + if( this.parentNode ) { + this.parentNode.removeChild( this ); + } + }); + }; + + + + /** + * Remove a proprety from each element in the current set. + * + * @param {string} name The name of the property. + * @return shoestring + * @this shoestring + */ + shoestring.fn.removeProp = function( property ){ + var name = shoestring.propFix[ property ] || property; + + return this.each(function(){ + this[ name ] = undefined; + delete this[ name ]; + }); + }; + + + + /** + * Replace each element in the current set with that argument HTML string or HTMLElement. + * + * @param {string|HTMLElement} fragment The value to assign. + * @return shoestring + * @this shoestring + */ + shoestring.fn.replaceWith = function( fragment ){ + if( typeof( fragment ) === "string" ){ + fragment = shoestring( fragment ); + } + + var ret = []; + + if( fragment.length > 1 ){ + fragment = fragment.reverse(); + } + this.each(function( i ){ + var clone = this.cloneNode( true ), + insertEl; + ret.push( clone ); + + // If there is no parentNode, this is pointless, drop it. + if( !this.parentNode ){ return; } + + if( fragment.length === 1 ){ + insertEl = i > 0 ? fragment[ 0 ].cloneNode( true ) : fragment[ 0 ]; + this.parentNode.replaceChild( insertEl, this ); + } else { + for( var j = 0, jl = fragment.length; j < jl; j++ ){ + insertEl = i > 0 ? fragment[ j ].cloneNode( true ) : fragment[ j ]; + this.parentNode.insertBefore( insertEl, this.nextSibling ); + } + this.parentNode.removeChild( this ); + } + }); + + return shoestring( ret ); + }; + + + + shoestring.inputTypes = [ + "text", + "hidden", + "password", + "color", + "date", + "datetime", + // "datetime\-local" matched by datetime + "email", + "month", + "number", + "range", + "search", + "tel", + "time", + "url", + "week" + ]; + + shoestring.inputTypeTest = new RegExp( shoestring.inputTypes.join( "|" ) ); + + + /** + * Serialize child input element values into an object. + * + * @return shoestring + * @this shoestring + */ + shoestring.fn.serialize = function(){ + var data = {}; + + shoestring( "input, select", this ).each(function(){ + var type = this.type, name = this.name, value = this.value; + + if( shoestring.inputTypeTest.test( type ) || + ( type === "checkbox" || type === "radio" ) && + this.checked ){ + + data[ name ] = value; + } else if( this.nodeName === "SELECT" ){ + data[ name ] = this.options[ this.selectedIndex ].nodeValue; + } + }); + + return data; + }; + + + + /** + * Get all of the sibling elements for each element in the current set. + * + * @return shoestring + * @this shoestring + */ + shoestring.fn.siblings = function(){ + + if( !this.length ) { + return shoestring( [] ); + } + + var sibs = [], el = this[ 0 ].parentNode.firstChild; + + do { + if( el.nodeType === 1 && el !== this[ 0 ] ) { + sibs.push( el ); + } + + el = el.nextSibling; + } while( el ); + + return shoestring( sibs ); + }; + + + + var getText = function( elem ){ + var node, + ret = "", + i = 0, + nodeType = elem.nodeType; + + if ( !nodeType ) { + // If no nodeType, this is expected to be an array + while ( (node = elem[i++]) ) { + // Do not traverse comment nodes + ret += getText( node ); + } + } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { + // Use textContent for elements + // innerText usage removed for consistency of new lines (jQuery #11153) + if ( typeof elem.textContent === "string" ) { + return elem.textContent; + } else { + // Traverse its children + for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { + ret += getText( elem ); + } + } + } else if ( nodeType === 3 || nodeType === 4 ) { + return elem.nodeValue; + } + // Do not include comment or processing instruction nodes + + return ret; + }; + + /** + * Recursively retrieve the text content of the each element in the current set. + * + * @return shoestring + * @this shoestring + */ + shoestring.fn.text = function() { + + return getText( this ); + }; + + + + + /** + * Get the value of the first element or set the value of all elements in the current set. + * + * @param {string} value The value to set. + * @return shoestring + * @this shoestring + */ + shoestring.fn.val = function( value ){ + var el; + if( value !== undefined ){ + return this.each(function(){ + if( this.tagName === "SELECT" ){ + var optionSet, option, + options = this.options, + values = [], + i = options.length, + newIndex; + + values[0] = value; + while ( i-- ) { + option = options[ i ]; + if ( (option.selected = shoestring.inArray( option.value, values ) >= 0) ) { + optionSet = true; + newIndex = i; + } + } + // force browsers to behave consistently when non-matching value is set + if ( !optionSet ) { + this.selectedIndex = -1; + } else { + this.selectedIndex = newIndex; + } + } else { + this.value = value; + } + }); + } else { + el = this[0]; + + if( el.tagName === "SELECT" ){ + if( el.selectedIndex < 0 ){ return ""; } + return el.options[ el.selectedIndex ].value; + } else { + return el.value; + } + } + }; + + + + /** + * Gets the width value of the first element or sets the width for the whole set. + * + * @param {float|undefined} value The value to assign. + * @return shoestring + * @this shoestring + */ + shoestring.fn.width = function( value ){ + return shoestring._dimension( this, "width", value ); + }; + + + + /** + * Wraps the child elements in the provided HTML. + * + * @param {string} html The wrapping HTML. + * @return shoestring + * @this shoestring + */ + shoestring.fn.wrapInner = function( html ){ + return this.each(function(){ + var inH = this.innerHTML; + + this.innerHTML = ""; + shoestring( this ).append( shoestring( html ).html( inH ) ); + }); + }; + + + + function initEventCache( el, evt ) { + if ( !el.shoestringData ) { + el.shoestringData = {}; + } + if ( !el.shoestringData.events ) { + el.shoestringData.events = {}; + } + if ( !el.shoestringData.loop ) { + el.shoestringData.loop = {}; + } + if ( !el.shoestringData.events[ evt ] ) { + el.shoestringData.events[ evt ] = []; + } + } + + function addToEventCache( el, evt, eventInfo ) { + var obj = {}; + obj.isCustomEvent = eventInfo.isCustomEvent; + obj.callback = eventInfo.callfunc; + obj.originalCallback = eventInfo.originalCallback; + obj.namespace = eventInfo.namespace; + + el.shoestringData.events[ evt ].push( obj ); + + if( eventInfo.customEventLoop ) { + el.shoestringData.loop[ evt ] = eventInfo.customEventLoop; + } + } + + /** + * Bind a callback to an event for the currrent set of elements. + * + * @param {string} evt The event(s) to watch for. + * @param {object,function} data Data to be included with each event or the callback. + * @param {function} originalCallback Callback to be invoked when data is define.d. + * @return shoestring + * @this shoestring + */ + shoestring.fn.bind = function( evt, data, originalCallback ){ + + if( typeof data === "function" ){ + originalCallback = data; + data = null; + } + + var evts = evt.split( " " ); + + // NOTE the `triggeredElement` is purely for custom events from IE + function encasedCallback( e, namespace, triggeredElement ){ + var result; + + if( e._namespace && e._namespace !== namespace ) { + return; + } + + e.data = data; + e.namespace = e._namespace; + + var returnTrue = function(){ + return true; + }; + + e.isDefaultPrevented = function(){ + return false; + }; + + var originalPreventDefault = e.preventDefault; + var preventDefaultConstructor = function(){ + if( originalPreventDefault ) { + return function(){ + e.isDefaultPrevented = returnTrue; + originalPreventDefault.call(e); + }; + } else { + return function(){ + e.isDefaultPrevented = returnTrue; + e.returnValue = false; + }; + } + }; + + // thanks https://github.com/jonathantneal/EventListener + e.target = triggeredElement || e.target || e.srcElement; + e.preventDefault = preventDefaultConstructor(); + e.stopPropagation = e.stopPropagation || function () { + e.cancelBubble = true; + }; + + result = originalCallback.apply(this, [ e ].concat( e._args ) ); + + if( result === false ){ + e.preventDefault(); + e.stopPropagation(); + } + + return result; + } + + return this.each(function(){ + var domEventCallback, + customEventCallback, + customEventLoop, + oEl = this; + + for( var i = 0, il = evts.length; i < il; i++ ){ + var split = evts[ i ].split( "." ), + evt = split[ 0 ], + namespace = split.length > 0 ? split[ 1 ] : null; + + domEventCallback = function( originalEvent ) { + if( oEl.ssEventTrigger ) { + originalEvent._namespace = oEl.ssEventTrigger._namespace; + originalEvent._args = oEl.ssEventTrigger._args; + + oEl.ssEventTrigger = null; + } + return encasedCallback.call( oEl, originalEvent, namespace ); + }; + customEventCallback = null; + customEventLoop = null; + + initEventCache( this, evt ); + + this.addEventListener( evt, domEventCallback, false ); + + addToEventCache( this, evt, { + callfunc: customEventCallback || domEventCallback, + isCustomEvent: !!customEventCallback, + customEventLoop: customEventLoop, + originalCallback: originalCallback, + namespace: namespace + }); + } + }); + }; + + shoestring.fn.on = shoestring.fn.bind; + + + + + /** + * Unbind a previous bound callback for an event. + * + * @param {string} event The event(s) the callback was bound to.. + * @param {function} callback Callback to unbind. + * @return shoestring + * @this shoestring + */ + shoestring.fn.unbind = function( event, callback ){ + + + var evts = event ? event.split( " " ) : []; + + return this.each(function(){ + if( !this.shoestringData || !this.shoestringData.events ) { + return; + } + + if( !evts.length ) { + unbindAll.call( this ); + } else { + var split, evt, namespace; + for( var i = 0, il = evts.length; i < il; i++ ){ + split = evts[ i ].split( "." ), + evt = split[ 0 ], + namespace = split.length > 0 ? split[ 1 ] : null; + + if( evt ) { + unbind.call( this, evt, namespace, callback ); + } else { + unbindAll.call( this, namespace, callback ); + } + } + } + }); + }; + + function unbind( evt, namespace, callback ) { + var bound = this.shoestringData.events[ evt ]; + if( !(bound && bound.length) ) { + return; + } + + var matched = [], j, jl; + for( j = 0, jl = bound.length; j < jl; j++ ) { + if( !namespace || namespace === bound[ j ].namespace ) { + if( callback === undefined || callback === bound[ j ].originalCallback ) { + this.removeEventListener( evt, bound[ j ].callback, false ); + matched.push( j ); + } + } + } + + for( j = 0, jl = matched.length; j < jl; j++ ) { + this.shoestringData.events[ evt ].splice( j, 1 ); + } + } + + function unbindAll( namespace, callback ) { + for( var evtKey in this.shoestringData.events ) { + unbind.call( this, evtKey, namespace, callback ); + } + } + + shoestring.fn.off = shoestring.fn.unbind; + + + /** + * Bind a callback to an event for the currrent set of elements, unbind after one occurence. + * + * @param {string} event The event(s) to watch for. + * @param {function} callback Callback to invoke on the event. + * @return shoestring + * @this shoestring + */ + shoestring.fn.one = function( event, callback ){ + var evts = event.split( " " ); + + return this.each(function(){ + var thisevt, cbs = {}, $t = shoestring( this ); + + for( var i = 0, il = evts.length; i < il; i++ ){ + thisevt = evts[ i ]; + + cbs[ thisevt ] = function( e ){ + var $t = shoestring( this ); + + for( var j in cbs ) { + $t.unbind( j, cbs[ j ] ); + } + + return callback.apply( this, [ e ].concat( e._args ) ); + }; + + $t.bind( thisevt, cbs[ thisevt ] ); + } + }); + }; + + + + /** + * Trigger an event on the first element in the set, no bubbling, no defaults. + * + * @param {string} event The event(s) to trigger. + * @param {object} args Arguments to append to callback invocations. + * @return shoestring + * @this shoestring + */ + shoestring.fn.triggerHandler = function( event, args ){ + var e = event.split( " " )[ 0 ], + el = this[ 0 ], + ret; + + // See this.fireEvent( 'on' + evts[ i ], document.createEventObject() ); instead of click() etc in trigger. + if( doc.createEvent && el.shoestringData && el.shoestringData.events && el.shoestringData.events[ e ] ){ + var bindings = el.shoestringData.events[ e ]; + for (var i in bindings ){ + if( bindings.hasOwnProperty( i ) ){ + event = doc.createEvent( "Event" ); + event.initEvent( e, true, true ); + event._args = args; + args.unshift( event ); + + ret = bindings[ i ].originalCallback.apply( event.target, args ); + } + } + } + + return ret; + }; + + + + /** + * Trigger an event on each of the DOM elements in the current set. + * + * @param {string} event The event(s) to trigger. + * @param {object} args Arguments to append to callback invocations. + * @return shoestring + * @this shoestring + */ + shoestring.fn.trigger = function( event, args ){ + var evts = event.split( " " ); + + return this.each(function(){ + var split, evt, namespace; + for( var i = 0, il = evts.length; i < il; i++ ){ + split = evts[ i ].split( "." ), + evt = split[ 0 ], + namespace = split.length > 0 ? split[ 1 ] : null; + + if( evt === "click" ){ + if( this.tagName === "INPUT" && this.type === "checkbox" && this.click ){ + this.click(); + return false; + } + } + + if( doc.createEvent ){ + var event = doc.createEvent( "Event" ); + event.initEvent( evt, true, true ); + event._args = args; + event._namespace = namespace; + + this.dispatchEvent( event ); + } + } + }); + }; + + + + return shoestring; +}));