File Manager

Current Directory: /home/astoriaah/www/old15/media/mod_zentools/js/responsive
Viewing File: /home/astoriaah/www/old15/media/mod_zentools/js/responsive/response.js
/*! * Response Responsive design toolkit * @link http://responsejs.com * @author Ryan Van Etten (c) 2011-2012 * @license MIT * @version 0.7.3 * @requires jQuery 1.7+ * -or- Jeesh (ender.no.de/#jeesh) * -or- elo (github.com/ryanve/elo) * -or- zepto 0.8+ (zeptojs.com) */ /*jslint browser: true, devel: true, node: true, passfail: false, bitwise: true , continue: true, debug: true, eqeq: true, es5: true, forin: true, newcap: true , nomen: true, plusplus: true, regexp: true, undef: true, sloppy: true, stupid: true , sub: true, white: true, indent: 4, maxerr: 180 */ (function ( root, name, factory ) { var dep = root['jQuery'] || root['Zepto'] || root['ender'] || root['elo']; if (typeof module != 'undefined' && module['exports']) { module['exports'] = factory(dep); } // node else { root[name] = factory(dep); } // browser // see @link github.com/ryanve/response.js/pull/9 // AMD @example `define(['jquery'], factory)` }(this, 'Response', function ($) { if ( typeof $ != 'function' ) {// Exit gracefully if dependency is missing: throw 'Response unable to run due to missing dep.'; } // Combine local vars/funcs into one statement: var root = this , Response , name = 'Response' , old = root[name] , initContentKey = 'init' + name // key for storing initial content , win = window , doc = document , docElem = doc.documentElement , ready = $.domReady || $ , $win = $(win) // cache selector , screen = win.screen , isArray = Array.isArray || function(ukn) { return ukn instanceof Array; } , owns = {}.hasOwnProperty , slice = [].slice , concat = [].concat , nativeMap = [].map , map = nativeMap ? function(ob, fn, scope) { return nativeMap.call(ob, fn, scope); } : function (ob, fn, scope) { var i, l = ob.length, ret = []; for (i = 0; i < l; i++) { i in ob && (ret[i] = fn.call(scope, ob[i], i, ob)); } return ret; } , defaultBreakpoints = { width: [0, 320, 481, 641, 961, 1025, 1281] // width | device-width (ideal for 960 grids) , height: [0, 481] // height | device-height (maybe add 801 too) , ratio: [1, 1.5, 2] // device-pixel-ratio (!omit trailing zeros!) } // these are defined later , Elemset, band, wave, device = {} , propTests = {} , isCustom = {} , sets = { all: [] } , suid = 1 // responsejs.com/labs/dimensions/#device // device dims stay the same regardless of viewport size or rotation , screenW = screen.width , screenH = screen.height , screenMax = screenW > screenH ? screenW : screenH , screenMin = screenW + screenH - screenMax , deviceW = function() { return screenW; } , deviceH = function() { return screenH; } // cache expressions , regexFunkyPunc = /[^a-z0-9_\-\.]/gi , regexTrimPunc = /^[\W\s]+|[\W\s]+$|/g , regexCamels = /([a-z])([A-Z])/g , regexDashB4 = /-(.)/g , regexDataPrefix = /^data-(.+)$/ // Response.media (normalized matchMedia) // @example Response.media("(orientation:landscape)").matches // If both versions are undefined, .matches will equal undefined // Also see: band / wave / device.band / device.wave / dpr , media = win.matchMedia || win.msMatchMedia || Object // Local version of Object.create with polyfill that supports only the first arg. // It creates an empty object whose prototype is set to the specified proto param. // developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/create // FYI there is a full polyfill @link github.com/kriskowal/es5-shim // This gets exposed as Response.object since 0.4.0 , objectCreate = Object.create || function (proto) { function Type () {} // Function to output empty object. Type.prototype = proto; // Set prototype property to the proto. return new Type(); // Instantiate the new object. } , namespaceIt = function(eventName, customNamespace) {// namepace defaults to 'Response' customNamespace = customNamespace || name; return eventName.replace(regexTrimPunc, '') + '.' + customNamespace.replace(regexTrimPunc, ''); } , event = {// Custom Events allLoaded: namespaceIt('allLoaded') // fires on lazy elemsets when all elems in a set have been loaded once //, update: namespaceIt('update') // fires on each elem in a set each time that elem is updated , crossover: namespaceIt('crossover') // fires on window each time dynamic breakpoint bands is crossed } ;//var // responsejs.com/labs/dimensions/#viewport function viewportW() { return docElem.clientWidth; } function viewportH() { return docElem.clientHeight; } function doError(msg) { // Error handling. (Throws exception.) // Use Ctrl+F to find specific @errors throw new TypeError(msg ? name + '.' + msg : name); } function isNumber (item) {// inlined @minification return typeof item == 'number' && item === item; // second part stuffs NaN } function ssvToArr(ukn) { // Convert space separated values to array. Always returns a compact array: return typeof ukn == 'string' ? sift(ukn.split(' ')) : isArray(ukn) ? sift(ukn) : []; } /** * Response.each() * @since 0.4.0 * Since version 0.6.2, this function omits checking `i in arr` and supports scope */ function each(ob, callback, scope) { if ( null == ob ) { return ob; } var i = 0, len = ob.length; while ( i < len ) { callback.call(scope || ob[i], ob[i], i++, ob); } return ob; // chainable } // revamped affix method reintroduced in version 0.4.0: // updated again in 0.6.2 to skip null|undef values function affix(arr, prefix, suffix) { // Return new array with prefix/suffix added to each value. // null|undefined values are not included in the new array var r = [], l = arr.length, i = 0, v; prefix = prefix || ''; suffix = suffix || ''; while ( i < l ) { v = arr[i++]; null == v || r.push(prefix + v + suffix); } return r; } /** * @param {Array|Object} ob is an array or collection to iterate over. * @param {(Function|string|*)=} fn is a callback or typestring - callbacks receive (v, i, ob) * @param {(Object|boolean|*)=} scope thisArg or invert */ /** * Response.sift Filter out array values that don't pass a callback, * or (if no callback provided) filter out falsey values. * Similar (but more capable that) jQuery.grep or the native [].filter * * @since 0.4.0 Updated in 0.6.2 to support scope and typestrings * @example Response.sift([5, 0, 'seven'], isFinite) // [5, 0] * @example Response.sift([5, 0, '', undefined, null]) // [5] * */ function sift(ob, fn, scope) { var l, u = 0, i = 0, v, ret = [], invert, isF = typeof fn == 'function'; if ( !ob ) { return ret; } scope = (invert = true === scope) ? null : scope; for ( l = ob.length; i < l; i++ ) { v = ob[i]; // save reference to value in case `fn` mutates `ob[i]` // Use `=== !` to ensure that the comparison is bool-to-bool invert === !(isF ? fn.call(scope, v, i, ob) : fn ? typeof v === fn : v) && (ret[u++] = v); } return ret; } /** * Response.merge * @since 0.3.0 * Generic method for merging objects and/or arrays. * Undefined values in `adds` are skipped. * When `adds` is array-like, this behaves similar to jQuery.merge(base, adds) * Otherwise it behaves similar to jQuery.extend(base, adds) * @param {Object|Array|Function|*} base * @param {Object|Array|Function|*} adds */ function merge (base, adds) { if ( !base || !adds) { return base; } var k, l = adds.length; if ( typeof adds != 'function' && isNumber(l) ) { for ( k = 0; k < l; k++ ) { void 0 === adds[k] || (base[k] = adds[k]); } if ( !(base.length > k) ) { base.length = k; // in case `base` was not array } } else { for ( k in adds ) { owns.call(adds, k) && void 0 !== adds[k] && (base[k] = adds[k]); } } return base; } /** * Response.route() Handler method for accepting args as arrays or singles, for * callbacks. Returns self for chaining. * * @since 0.3.0 (scope support added in 0.6.2) * * @param {*} item If `item` is an array or array-like object then `callback` gets called * on each member. Otherwise `callback` is called on the `item` itself. * @param {Function} fn The function to call on item(s). * @param {*=} scope thisArg (defaults to current item) */ function route(item, fn, scope) { // If item is array-like then call the callback on each item. Otherwise call directly on item. if ( null == item ) { return item; } // Skip null|undefined if ( typeof item == 'object' && !item.nodeType && isNumber(item.length) ) { each(item, fn, scope); } else { fn.call(scope || item, item); } return item; // chainable } // Handler for defining range comparison booleans: function ranger(fn) { // In previous versions we used inORout() for this but this // is better because the resulting functions are faster // because they don't require an extra function call. return function(min, max) { var bool, curr = fn(); bool = curr >= (min || 0); return !max ? bool : bool && curr <= max; }; } /** * Range comparison booleans * @link responsejs.com/#booleans */ band = ranger(viewportW); // Response.band wave = ranger(viewportH); // Response.wave device.band = ranger(deviceW); // Response.device.band device.wave = ranger(deviceH); // Response.device.wave /** * Response.dpr(decimal) Tests if a minimum device pixel ratio is active. * Or (version added in 0.3.0) returns the device-pixel-ratio * * * @param number decimal is the integer or float to test. * * @return boolean|number * @example Response.dpr(); // get the device-pixel-ratio (or 0 if undetectable) * @example Response.dpr(1.5); // true when device-pixel-ratio is 1.5+ * @example Response.dpr(2); // true when device-pixel-ratio is 2+ * @example Response.dpr(3/2); // [!] FAIL (Gotta be a decimal or integer) * */ function dpr(decimal) { var dPR = win.devicePixelRatio; if ( null == decimal ) {//Return exact value or kinda iterate for approx: return dPR || (dpr(2) ? 2 : dpr(1.5) ? 1.5 : dpr(1) ? 1 : 0); } if ( !isFinite(decimal) ) {// Shh. Actually allows numeric strings too. ;) return false; } // Use window.devicePixelRatio if supported - supported by Webkit // (Safari/Chrome/Android) and Presto 2.8+ (Opera) browsers. if ( dPR && dPR > 0 ) { return dPR >= decimal; } // Fallback to .matchMedia/.msMatchMedia. Supported by Gecko (FF6+) and more: // @link developer.mozilla.org/en/DOM/window.matchMedia // -webkit-min- and -o-min- omitted (Webkit/Opera supported above) // The generic min-device-pixel-ratio is expected to be added to the W3 spec. // Return false if neither method is available. decimal = 'only all and (min--moz-device-pixel-ratio:' + decimal + ')'; if ( media(decimal).matches ) { return true; } return !!media(decimal.replace('-moz-', '')).matches; }//dpr /** * Response.camelize Converts data-pulp-fiction to pulpFiction * via camelize @link github.com/ded/bonzo * Used in dataset methods. * * @example Response.camelize('data-casa-blanca') // casaBlanca */ function camelize(s) { // Remove data- prefix and convert remaining dashed string to camelCase: return s.replace(regexDataPrefix, '$1').replace(regexDashB4, function (m, m1) { return m1.toUpperCase(); }); } /** * Response.datatize Converts pulpFiction (or data-pulpFiction) to data-pulp-fiction * Adapted from decamelize @link github.com/ded/bonzo * Used in dataset methods. * * @example Response.datatize('casaBlanca') // data-casa-blanca */ function datatize(s) { // Make sure there's no data- already in s for it to work right in IE8. return 'data-' + (s ? s.replace(regexDataPrefix, '$1').replace(regexCamels, '$1-$2').toLowerCase() : s); } /** * Response.render Converts stringified primitives back to JavaScript. * Adapted from dataValue() @link github.com/ded/bonzo * @since 0.3.0 * * @param string|other s String to render back to its correct JavaScript value. * If s is not a string then it is returned unaffected. * @return converted data * */ function render(s) { var n; // < undefined return ( !s || typeof s != 'string' ? s // unchanged : 'true' === s ? true // convert "true" to true : 'false' === s ? false // convert "false" to false : 'undefined' === s ? n // convert "undefined" to undefined : 'null' === s ? null // convert "null" to null : (n = parseFloat(s)) === +n ? n // convert "1000" to 1000 : s // unchanged ); }//render // Isolate native element: function getNative(e) { // stackoverflow.com/questions/9119823/safest-way-to-detect-native-dom-element // See @link jsperf.com/get-native // If e is a native element then return it. If not check if index 0 exists and is // a native elem. If so then return that. Otherwise return false. return !e ? false : e.nodeType === 1 ? e : e[0] && e[0].nodeType === 1 ? e[0] : false; } /** * .dataset() Cross browser implementation of HTML5 dataset * The chainable syntax is disabled by default and can be * enabled by calling Response.chain() (See Response.chain) * * @since 0.3.0 * * Chainable form: $('div').dataset(key) // get (from first matched element) * $('div').dataset([key]) // get and render (See Response.render) * $('div').dataset(key, value) // set (sets all matched elems) * $('div').dataset({k1:val, k2:val}) // set multiple attrs at once (on all matched elems) * $('div').deletes(keys) // delete attrs (space-separated string) * * Non-chainable: Response.dataset(elem, key) // get (elem can be native or jQuery elem) * Response.dataset(elem, [key]) // get and render (See Response.render) * Response.dataset(elem, key, value) // set * Response.dataset(elem, {k1:val, k2:val}) // set multiple attrs at once * Response.deletes(elem, keys) // delete attrs (space-separated string) * */ function datasetChainable(key, value) { var numOfArgs = arguments.length , elem = getNative(this) , ret = {} , renderData = false , n; if ( numOfArgs ) { if ( isArray(key) ) { renderData = true; key = key[0]; } if ( typeof key === 'string' ) { // key || doError('dataset @key'); // Make sure key is not an empty string. key = datatize(key); if ( 1 === numOfArgs ) {//GET ret = elem.getAttribute(key); return renderData ? render(ret) : ret; } if ( this === elem || 2 > (n = this.length || 1) ) {//SET single elem elem.setAttribute(key, value); } else {//SET for group of selected elems while( n-- ) {// n starts as # of elems in selector and stops at 0 if (n in this) { datasetChainable.apply(this[n], arguments); } } } } else if ( key instanceof Object ) {//SET // Plain object containing key/value pairs: for (n in key) { if (key.hasOwnProperty(n)) { datasetChainable.call(this, n, key[n]); } } } return this; // chain }//1 or more args // ** Zero args ** // Return object containing all the data attributes. // Use the native dataset when available: if ( elem.dataset && DOMStringMap ) { return elem.dataset; } // adapted from Bonzo @link github.com/ded/bonzo/blob/master/bonzo.js each(elem.attributes, function(a) { if (a && (n = String(a.name).match(regexDataPrefix))) { ret[camelize(n[1])] = a.value; } }); return ret; // plain object }//datasetChainable /** * .deletes() * * * @since 0.3.0 */ function deletesChainable(keys) { // Could make this take a little less code using sending the space-separated string // straight to removeAttr but Zepto's removeAttr doesn't support space-separated keys // it'd have to be like: /* if ( 'string' === typeof keys ) { var $elems = selectOnce(this); each(ssvToArr(keys), function(key) { if (key) { $elems.removeAttr(datatize(key)); } }); }*/ // Or, just use native removeAttribute: if (this && typeof keys === 'string') { keys = ssvToArr(keys); route(this, function(el) { each(keys, function(key) { if (key) { el.removeAttribute(datatize(key)); } }); }); } return this; }//deletesChainable /** * Response.dataset() See datasetChainable above * This is the non-chainable version. It grabs the thisArg * and calls the chainable version * * @since 0.3.0 */ function dataset(elem, key, value) { return datasetChainable.apply(elem, slice.call(arguments, 1)); } /** * Response.deletes(elem, keys) Delete HTML5 data attributes (remove them from them DOM) * * @since 0.3.0 * Where native DOM dataset is supported you can do: `delete elem.dataset.foo` * * @param object elem is a native element or jQuery object e.g. document.body or $('body') * * @param string keys one or more space-separated data attribute keys (names) to delete (removed * from the DOM) Should be camelCased or lowercase. * * @example Response.deletes(document.body, 'casaBlanca movie'); // Removes data-casa-blanca and data-movie * // from the <body> element. * * @example Response.deletes($(div), 'casaBlanca movie') // Removes data-casa-blanca and data-movie * // from all divs. */ function deletes(elem, keys) { return deletesChainable.call(elem, keys); } function selectify(keys) { // Convert an array of data keys into a selector string // Converts ["a","b","c"] into "[data-a],[data-b],[data-c]" // Double-slash escapes periods so that attrs like data-density-1.5 will work // @link api.jquery.com/category/selectors/ // @link github.com/jquery/sizzle/issues/76 var k, r = [], i = 0, l = keys.length; while ( i < l ) { if ( k = keys[i++] ) { r.push('[' + datatize(k.replace(regexTrimPunc, '').replace('.', '\\.')) + ']'); } } return r.join(); } /** * Response.target() Get the corresponding data attributes for an array of data keys. * @since 0.1.9 * @param array keys is the array of data keys whose attributes you want to select. * @return object jQuery selector * @example Response.target(['a', 'b', 'c']) // $('[data-a],[data-b],[data-c]') * @example Response.target('a b c']) // $('[data-a],[data-b],[data-c]') */ function target(keys) { return $(selectify(ssvToArr(keys))); } // Cross-browser versions of window.scrollX and window.scrollY // Compatibiliy notes @link developer.mozilla.org/en/DOM/window.scrollY // Performance tests @link jsperf.com/scrollx-cross-browser-compatible // Using native here b/c Zepto doesn't support .scrollLeft() /scrollTop() // In jQuery you can do $(window).scrollLeft() and $(window).scrollTop() /** * Response.scrollX() Cross-browser version of window.scrollX * * @since 0.3.0 * @return integer */ function scrollX(){ return window.pageXOffset || docElem.scrollLeft; } /** * Response.scrollY() Cross-browser version of window.scrollY * * @since 0.3.0 * @return integer */ function scrollY(){ return window.pageYOffset || docElem.scrollTop; } /** * area methods inX/inY/inViewport * * In non-chainable contexts, these are booleans. * In chainable contexts, they are filters. * * @since 0.3.0 * * Inspired by @link appelsiini.net/projects/viewport * */ function rectangle(el, verge) { // Local handler for area methods: // adapted from github.com/ryanve/dime // The native object is read-only so we // have use a copy in order to modify it. var r = el.getBoundingClientRect ? el.getBoundingClientRect() : {}; verge = typeof verge == 'number' ? verge || 0 : 0; return { top: (r.top || 0) - verge , left: (r.left || 0) - verge , bottom: (r.bottom || 0) + verge , right: (r.right || 0) + verge }; } // The verge is the amount of pixels to act as a cushion around the viewport. It can be any // integer. If verge is zero, then the inX/inY/inViewport methods are exact. If verge is set to 100, // then those methods return true when for elements that are are in the viewport *or* near it, // with *near* being defined as within 100 pixels outside the viewport edge. Elements immediately // outside the viewport are 'on the verge' of being scrolled to. function inX(elem, verge) { var r = rectangle(getNative(elem), verge); return !!r && r.right >= 0 && r.left <= viewportW(); } function inY(elem, verge) { var r = rectangle(getNative(elem), verge); return !!r && r.bottom >= 0 && r.top <= viewportH(); } function inViewport(elem, verge) { // equiv to: inX(elem, verge) && inY(elem, verge) // But just manually do both to avoid calling rectangle() and getNative() twice. // It actually gzips smaller this way too: var r = rectangle(getNative(elem), verge); return !!r && r.bottom >= 0 && r.top <= viewportH() && r.right >= 0 && r.left <= viewportW(); } function detectMode(elem) { // Detect whether elem should act in src or markup mode. // // @param elem is a native dom element // @return boolean true (src mode) or false (markup mode) depending on whether there is a // src attr *and* whether the spec allows it on the elem in question. // // @link dev.w3.org/html5/spec-author-view/index.html#attributes-1 // @link stackoverflow.com/questions/8715689/check-if-element-legally-supports-the-src-attribute-or-innerhtml // // In jQuery you can also use $(elem).prop('tagName') to get the tagName. // This uses developer.mozilla.org/en/DOM/element.tagName // // In HTML5, element.tagName returns the tagName in uppercase. // We force the case here to make it compatible with XHTML. // These are the elems that can use src attr per the W3 spec: var srcElems = {img:1, input:1, source:3, embed:3, track:3, iframe:5, audio:5, video:5, script:5} , modeID = srcElems[elem.tagName.toLowerCase()] || -1 ; // -5 => markup mode for video/audio/iframe w/o src attr. // -1 => markup mode for any elem not in the array above. // 1 => src mode for img/input (empty content model). Images. // 3 => src mode for source/embed/track (empty content model). Media *or* time data. // 5 => src mode for audio/video/iframe/script *with* src attr. // If we at some point we need to differentiate <track> we'll use 4, but for now // it's grouped with the other non-image empty content elems that use src. // hasAttribute is not supported in IE7 so check typeof elem.getAttribute('src') return 4 > modeID ? modeID : typeof elem.getAttribute('src') === 'string' ? 5 : -5; // integer }//detectMode /** * Response.store() * @since 0.1.9 * * Store a data value on each elem targeted by a jQuery selector. We use this for storing an * elem's orig (no-js) state. This gives us the ability to return the elem to its orig state. * The data it stores is either the src attr or the innerHTML based on result of detectMode(). * * @param {Object} $elems DOM element | jQuery object | nodeList | array of elements * @param {string} key is the key to use to store the orig value w/ @link api.jquery.com/data/ * @param {string=} source (@since 0.6.2) an optional attribute name to read data from * */ function store($elems, key, source) { var valToStore; if ( !$elems || null == key) { doError('store'); } source = typeof source == 'string' && source; route($elems, function (el) { if ( source ) { valToStore = el.getAttribute(source); } else if ( 0 < detectMode(el) ) { valToStore = el.getAttribute('src'); } else { valToStore = el.innerHTML; } null == valToStore ? deletes(el, key) : dataset(el, key, valToStore); }); return Response; }//store /** * Response.access() Access data-* values for element from an array of data-* keys. * * @since 0.1.9 (added support for space-separated strings in 0.3.1) * * @param object elem is a native or jQuery element whose values to access. * @param array|string keys is an array or space-separated string of data keys whose * values you want to access. * * @return array of dataset values corresponding to each key. Since 0.4.0 if * the params are wrong then the return is an empty array. */ function access(elem, keys) { // elem becomes thisArg for datasetChainable: var ret = []; elem && keys && each(ssvToArr(keys), function (k, i) { ret.push(dataset(elem, k)); }, elem); return ret; } /** * Response.addTest * */ function addTest(prop, fn) { if (typeof prop == 'string' && typeof fn == 'function') { propTests[prop] = fn; isCustom[prop] = 1; } return Response; } /* * Elemset Prototype object for element sets used in Response.create * Each element in the set inherits this as well, so some of the * methods apply to the set, while others apply to single elements. */ Elemset = (function() { var crossover = event.crossover //, update = event.update , min = Math.min; // Techically data attributes names can contain uppercase in HTML, but, The DOM lowercases // attributes, so they must be lowercase regardless when we target them in jQuery. Force them // lowercase here to prevent issues. Removing all punc marks except for dashes, underscores, // and periods so that we don't have to worry about escaping anything crazy. // Rules @link dev.w3.org/html5/spec/Overview.html#custom-data-attribute // jQuery selectors @link api.jquery.com/category/selectors/ function sanitize (key) { // Allow lowercase alphanumerics, dashes, underscores, and periods: return typeof key == 'string' ? key.toLowerCase().replace(regexFunkyPunc, '') : ''; } return { $e: 0 // object jQuery object , mode: 0 // integer defined per element , breakpoints: null // array validated @ configure() , prefix: null // string validated @ configure() , prop: 'width' // string validated @ configure() , keys: [] // array defined @ configure() , dynamic: null // boolean defined @ configure() , custom: 0 // boolean see addTest() , values: [] // array available values , fn: 0 // callback the test fn, defined @ configure() , verge: null // integer defaults to Math.min(screenMax, 500) , newValue: 0 , currValue: 1 , aka: null , lazy: null , i: 0 // integer the index of the current highest active breakpoint min , uid: null , reset: function () {// Reset / fire crossover events: var subjects = this.breakpoints , i = subjects.length , tempIndex = 0; // This is similar to the decideValue loop while( !tempIndex && i-- ) { this.fn(subjects[i]) && (tempIndex = i); } // Fire the crossover event if crossover has occured: if (tempIndex !== this.i) { $win.trigger(crossover) // fires for each set .trigger(this.prop + crossover); // fires this.i = tempIndex || 0; } return this; // chainable } , configure: function (options) { merge(this, options); var i, prefix, aliases, aliasKeys, isNumeric = true, arr, prop = this.prop; this.uid = suid++; this.verge = isFinite(this.verge) ? this.verge : min(screenMax, 500); this.fn = propTests[prop] || doError('create @fn'); // If we get to here then we know the prop is one one our supported props: // 'width', 'height', 'device-width', 'device-height', 'device-pixel-ratio' // device- props => NOT dynamic if (typeof this.dynamic != 'boolean') { this.dynamic = !!('device' !== prop.substring(0, 6)); } this.custom = isCustom[prop]; prefix = this.prefix ? sift(map(ssvToArr(this.prefix), sanitize)) : ['min-' + prop + '-']; aliases = 1 < prefix.length ? prefix.slice(1) : 0; this.prefix = prefix[0]; arr = this.breakpoints; // Sort and validate (#valid8) custom breakpoints if supplied. // Must be done before keys are created so that the keys match: if ( isArray(arr) ) {// custom breakpoints each(arr, function (v) { if ( !v && v !== 0 ) { throw 'invalid breakpoint'; } // null|undefined|''|NaN isNumeric = isNumeric && isFinite(v); }); arr = isNumeric ? arr.sort(function (a, b) { return (a - b); // sort lowest to highest }) : arr; arr.length || doError('create @breakpoints'); } else {// default breakpoints: // The defaults are presorted so we can skip the need to sort when using the defaults. Omit // trailing decimal zeros b/c for example if you put 1.0 as a devicePixelRatio breakpoint, // then the target would be data-pre1 (NOT data-pre1.0) so drop the zeros. // If no breakpoints are supplied, then get the default breakpoints for the specified prop. // Supported props: 'width', 'height', 'device-width', 'device-height', 'device-pixel-ratio' arr = defaultBreakpoints[prop] || defaultBreakpoints[prop.split('-').pop()] || doError('create @prop'); } // Remove breakpoints that are above the device's max dimension, // in order to reduce the number of iterations needed later. this.breakpoints = isNumeric ? sift(arr, function(n) { return n <= screenMax; }) : arr; // Use the breakpoints array to create array of data keys: this.keys = affix(this.breakpoints, this.prefix); this.aka = null; // Reset to just in case a value was merged in. if ( aliases ) {// There may be one of more aliases: aliasKeys = []; i = aliases.length; while ( i-- ) { aliasKeys.push(affix(this.breakpoints, aliases[i])); } this.aka = aliasKeys; // this.aka is an array of arrays (one for each alias) this.keys = concat.apply(this.keys, aliasKeys); // flatten aliases into this.keys } sets.all = sets.all.concat(sets[this.uid] = this.keys); // combined keys ===> sets.all return this; // chainable } , target: function() {// Stuff that can't happen until the DOM is ready: this.$e = $(selectify(sets[this.uid])); // Cache jQuery object for the set. store(this.$e, initContentKey); // Store original (no-js) value to data key. this.keys.push(initContentKey); // Add key onto end of keys array. (# keys now equals # breakpoints + 1) return this; // chainable } // The rest of the methods are designed for use with single elements. // They are for use in a cloned instances within a loop. , decideValue: function() { // Return the first value from the values array that passes the boolean // test callback. If none pass the test, then return the fallback value. // this.breakpoints.length === this.values.length + 1 // The extra member in the values array is the initContentKey value. var val = null , subjects = this.breakpoints , sL = subjects.length , i = sL; while( val == null && i-- ) { this.fn(subjects[i]) && (val = this.values[i]); } this.newValue = typeof val === 'string' ? val : this.values[sL]; return this; // chainable } , prepareData: function(elem) { this.$e = $(elem); // jQuery selector this.mode = detectMode(elem); // Detect the mode of the element. this.values = access(this.$e, this.keys); // Access Response data- values for the element. if (this.aka) { var i = this.aka.length; // If there are alias keys then there may be alias values. Merge the values from // all the aliases into the values array. The merge method only merges in truthy values // and prevents falsey values from overwriting truthy ones. (See Response.merge) while ( i-- ) {// loops down and stops at index 0 // Each of the this.aka arrays has the same length as the this.values // array, so no new indexes will be added, just filled if there's truthy values. this.values = merge(this.values, access(this.$e, this.aka[i])); } } return this.decideValue(); // chainable } , updateDOM: function() { // Apply the method that performs the actual swap. When updateDOM called this.$e and this.e refer // to single elements. Only update the DOM when the new value is different than the current value. if (this.currValue === this.newValue) { return this; } this.currValue = this.newValue; if ( 0 < this.mode ) { this.$e[0].setAttribute('src', this.newValue); } else if ( null == this.newValue ) { this.$e.empty && this.$e.empty(); } else { if (this.$e.html) { this.$e.html(this.newValue); } else { this.$e.empty && this.$e.empty(); this.$e[0].innerHTML = this.newValue; } } // this.$e.trigger(update); // may add this event in future return this; } };//return }());//Elemset // The keys are the prop and the values are the method that tests that prop. // The props with dashes in them are added via array notation below. // Props marked as dynamic change when the viewport is resized: propTests['width'] = band; // dynamic propTests['height'] = wave; // dynamic propTests['device-width'] = device.band; propTests['device-height'] = device.wave; propTests['device-pixel-ratio'] = dpr; /** * Response.resize * */ function resize(fn) { $win.on('resize', fn); return Response; // chainable } /** * Response.crossover * */ function crossover(prop, fn) { var temp, eventToFire, eventCrossover = event.crossover; if (typeof prop == 'function') {// support args in reverse temp = fn; fn = prop; prop = temp; } eventToFire = prop ? '' + prop + eventCrossover : eventCrossover; $win.on(eventToFire, fn); return Response; // chainable } /** * Response.action A facade for calling functions on both the ready and resize events. * * @link http://responsejs.com/#action * @since 0.1.3 * @param callback|array action is the callback name or array of callback names to call. * * @example Response.action(myFunc1); // call myFunc1() on ready/resize * @example Response.action([myFunc1, myFunc2]); // call myFunc1(), myFunc2() ... */ function action(fnOrArr) { route(fnOrArr, function (fn) { ready(fn); resize(fn); }); return Response; // chainable } /** * Response.create() Create their own Response attribute sets, with custom * breakpoints and data-* names. * @since 0.1.9 * * @param object|array args is an options object or an array of options objects. * * @link http://responsejs.com/#create * * @example Ideally this method is only called once: * To create a single set, use the form: Response.create(object); * To create multiple sets, use the form: Response.create([object1, object2]); */ function create(args) { route(args, function (options) { typeof options == 'object' || doError('create @args'); var elemset = objectCreate(Elemset).configure(options) , lowestNonZeroBP , verge = elemset.verge , breakpoints = elemset.breakpoints , scrollName = namespaceIt('scroll') , resizeName = namespaceIt('resize') ; if ( !breakpoints.length ) { return; } // Quit if there are zero breakpoints. // Identify the lowest nonzero breakpoint. (They're already sorted low to high by now.) lowestNonZeroBP = breakpoints[0] || breakpoints[1] || false; ready(function() { // Ready. Yea mofo. var allLoaded = event.allLoaded , lazy = !!elemset.lazy; // Target elements containing this set's Response data attributes and chain into the // loop that occurs on ready. The selector is cached to elemset.$e for later use. each(elemset.target().$e, function(el, i) { elemset[i] = objectCreate(elemset).prepareData(el);// Inherit from elemset if (!lazy || inViewport(elemset[i].$e, verge)) { // If not lazy update all the elems in the set. If // lazy, only update elems in the current viewport. elemset[i].updateDOM(); } }); function resizeHandler() { // Only runs for dynamic props. elemset.reset(); each(elemset.$e, function(el, i) {// Reset and then loop thru the set. elemset[i].decideValue().updateDOM(); // Grab elem object from cache and update all. }).trigger(allLoaded); } // device-* props are static and only need to be tested once. The others are // dynamic, meaning they need to be tested on resize. Also if a device so small // that it doesn't support the lowestNonZeroBP then we don't need to listen for // resize events b/c we know the device can't resize beyond that breakpoint. if (elemset.dynamic && (elemset.custom || lowestNonZeroBP < screenMax)) { resize(resizeHandler, resizeName); } // We don't have to re-decide the content on scrolls because neither the viewport or device // properties change from a scroll. This setup minimizes the operations binded to the scroll // event. Once everything in the set has been swapped once, the scroll handler is deactivated // through the use of a custom event. if ( !lazy ) { return; } function scrollHandler() { each(elemset.$e, function(el, i) { if (inViewport(elemset[i].$e, verge)) { elemset[i].updateDOM(); } }); } $win.on(scrollName, scrollHandler); elemset.$e.one(allLoaded, function() { $win.off(scrollName, scrollHandler); }); });//ready });//route return Response; // chainable }//create function noConflict(callback) { if ( root[name] === Response ) { root[name] = old; } if (typeof callback == 'function') { callback.call(root, Response); } return Response; } // Handler for adding inx/inY/inViewport to $.fn (or another prototype). function exposeAreaFilters(engine, proto, force) { each(['inX', 'inY', 'inViewport'], function(methodName) { (force || !proto[methodName]) && (proto[methodName] = function(verge, invert) { return engine(sift(this, function(el) { return !!el && !invert === Response[methodName](el, verge); })); }); }); } /** * Response.bridge >>>> bridge * Bridges applicable methods into the specified host (e.g. jQuery). */ function bridge(host, force) { if (typeof host == 'function' && host.fn) { // Expose .dataset() and .deletes() to jQuery: if (force || void 0 === host.fn.dataset) { host.fn.dataset = datasetChainable; } if (force || void 0 === host.fn.deletes) { host.fn.deletes = deletesChainable; } // Expose .inX() .inY() .inViewport() exposeAreaFilters(host, host.fn, force); } return Response; // chainable } /** * Response.chain * @since 0.3.0 * @depreciated ( Use Response.bridge instead. ) */ function chain (host, force) { host = arguments.length ? host : $; return bridge(host, force); } Response = { // Expose these as props/methods on Response: deviceMin: function() { return screenMin; } , deviceMax: function() { return screenMax; } //, sets: function(prop) {// must be uid // return $(selectify(sets[prop] || sets.all)); //} , noConflict: noConflict , chain: chain , bridge: bridge , create: create , addTest: addTest , datatize: datatize , camelize: camelize , render: render , store: store , access: access , target: target , object: objectCreate , crossover: crossover , action: action , resize: resize , ready: ready , affix: affix , sift: sift , dpr: dpr , deletes: deletes , scrollX: scrollX , scrollY: scrollY , deviceW: deviceW , deviceH: deviceH , device: device , inX: inX , inY: inY , route: route , merge: merge , media: media , wave: wave , band: band , map: map , each: each , inViewport: inViewport , dataset: dataset , viewportH: viewportH , viewportW: viewportW };// Response /** * Initialize */ ready(function() { var nativeJSONParse, customData = dataset(doc.body, 'responsejs'); if ( customData ) { nativeJSONParse = !!win.JSON && JSON.parse; if ( nativeJSONParse ) { customData = nativeJSONParse(customData); } else if ( $.parseJSON ) { customData = $.parseJSON(customData); } if ( customData && customData.create ) { create(customData.create); } } // Remove .no-responsejs class from html tag (if it's there) and add .responsejs docElem.className = docElem.className.replace(/(^|\s)(no-)?responsejs(\s|$)/, '$1$3') + ' responsejs '; }); return Response; // Bam! })); // closure