italia2024
Version:
Italia 2024 assets
872 lines (792 loc) • 32.3 kB
JavaScript
/*
* https://github.com/ryanve/response.js
* @license CC0-1.0
*/
!function(root, name, make) {
var $ = root.jQuery || root.Zepto || root.ender || root.elo;
if (typeof module != 'undefined' && module.exports) module.exports = make($);
else root[name] = make($);
}(this || window, 'Response', function($) {
if (typeof $ != 'function') {
try {
return void console.warn('response.js aborted due to missing dependency');
} catch (e) {}
}
var Response
, Elemset
, root = this || window
, name = 'Response'
, old = root[name]
, initContentKey = 'init' + name
, win = window
, doc = document
, docElem = doc.documentElement
, ready = $.domReady || $
, $win = $(win)
, DMS = typeof DOMStringMap != 'undefined'
, AP = Array.prototype
, OP = Object.prototype
, push = AP.push
, concat = AP.concat
, toString = OP.toString
, owns = OP.hasOwnProperty
, isArray = Array.isArray || function(item) {
return '[object Array]' === toString.call(item);
}
, defaultPoints = {
width: [0, 320, 481, 641, 961, 1025, 1281]
, height: [0, 481]
, ratio: [1, 1.5, 2] //device-pixel-ratio
}
, propTests = {}
, isCustom = {}
, sets = { all: [] }
, suid = 1
, screenW = screen.width
, screenH = screen.height
, screenMax = screenW > screenH ? screenW : screenH
, screenMin = screenW + screenH - screenMax
, deviceW = function() { return screenW; }
, deviceH = function() { return screenH; }
, regexFunkyPunc = /[^a-z0-9_.-]/gi
, regexTrimPunc = /^[\W\s]+|[\W\s]+$|/g
, regexCamels = /([a-z])([A-Z])/g
, regexDashB4 = /-(.)/g
, regexDataPrefix = /^data-(.+)$/
, procreate = Object.create || function(parent) {
/** @constructor */
function Type() {}
Type.prototype = parent;
return new Type;
}
, namespaceIt = function(eventName, customNamespace) {
customNamespace = customNamespace || name;
return eventName.replace(regexTrimPunc, '') + '.' + customNamespace.replace(regexTrimPunc, '');
}
, event = {
allLoaded: namespaceIt('allLoaded') //fires on lazy elemsets when all elems in a set have 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
}
//normalized matchMedia
, matchMedia = win.matchMedia || win.msMatchMedia
, media = matchMedia ? bind(matchMedia, win) : function() {
return {};
}
, mq = matchMedia ? function(q) {
return !!matchMedia.call(win, q).matches;
} : function() {
return false;
}
//http://ryanve.com/lab/dimensions
//http://github.com/ryanve/verge/issues/13
, viewportW = function() {
var a = docElem.clientWidth, b = win.innerWidth;
return a < b ? b : a;
}
, viewportH = function() {
var a = docElem.clientHeight, b = win.innerHeight;
return a < b ? b : a;
}
, band = bind(between, viewportW)
, wave = bind(between, viewportH)
, device = {
'band': bind(between, deviceW)
, 'wave': bind(between, deviceH)
};
function isNumber(item) {
return item === +item;
}
/**
* @param {Function} fn
* @param {*=} scope
* @return {Function}
*/
function bind(fn, scope) {
return function() {
return fn.apply(scope, arguments);
};
}
/**
* @this {Function}
* @param {number} min
* @param {number=} max
* @return {boolean}
*/
function between(min, max) {
var point = this.call();
return point >= (min || 0) && (!max || point <= max);
}
/**
* @param {{length:number}} stack
* @param {Function} fn
* @param {*=} scope
* @return {Array}
*/
function map(stack, fn, scope) {
for (var r = [], l = stack.length, i = 0; i < l;) r[i] = fn.call(scope, stack[i], i++, stack);
return r;
}
/**
* @param {string|{length:number}} list
* @return {Array} new and compact
*/
function compact(list) {
return !list ? [] : sift(typeof list == 'string' ? list.split(' ') : list);
}
/**
* @since 0.4.0, supports scope and sparse-item iteration since 0.6.2
* @param {{length:number}} stack
* @param {Function} fn
* @param {*=} scope
*/
function each(stack, fn, scope) {
if (null == stack) return stack;
for (var l = stack.length, i = 0; i < l;) fn.call(scope || stack[i], stack[i], i++, stack);
return stack;
}
/**
* @since 0.4.0 skips null|undefined since 0.6.2, adds `0` since 0.7.11
* @param {{length:number}} stack
* @param {(string|number)=} prefix
* @param {(string|number)=} suffix
* @return {Array} new array of affixed strings or added numbers
*/
function affix(stack, prefix, suffix) {
if (null == prefix) prefix = '';
if (null == suffix) suffix = '';
for (var r = [], l = stack.length, i = 0; i < l; i++)
null == stack[i] || r.push(prefix + stack[i] + suffix);
return r;
}
/**
* @param {{length:number}} stack to iterate
* @param {(Function|string|*)=} fn callback or typestring
* @param {(Object|boolean|*)=} scope or inversion boolean
* @since 0.4.0, supports scope and typestrings since 0.6.2
* @example Response.sift([5, 0, 'str'], isFinite) //[5, 0]
* @example Response.sift([5, 0, 'str']) //[5, 'str']
*/
function sift(stack, fn, scope) {
var fail, l, v, r = [], u = 0, i = 0, run = typeof fn == 'function', not = true === scope;
for (l = stack && stack.length, scope = not ? null : scope; i < l; i++) {
v = stack[i];
fail = run ? !fn.call(scope, v, i, stack) : fn ? typeof v !== fn : !v;
fail === not && (r[u++] = v);
}
return r;
}
/**
* @since 0.3.0
* @param {Object|Array|Function|*} r receiver
* @param {Object|Array|Function|*} s supplier Undefined values are ignored.
* @return {Object|Array|Function|*} receiver
*/
function merge(r, s) {
if (null == r || null == s) return r;
if (typeof s == 'object' && isNumber(s.length)) push.apply(r, sift(s, 'undefined', true));
else for (var k in s) owns.call(s, k) && void 0 !== s[k] && (r[k] = s[k]);
return r;
}
/**
* @description Call `fn` on each stack value or directly on a non-stack item
* @since 0.3.0 scope support added in 0.6.2
* @param {*} item stack or non-stack item
* @param {Function} fn callback
* @param {*=} scope defaults to current item
*/
function route(item, fn, scope) {
if (null == item) return item;
if (typeof item == 'object' && !item.nodeType && isNumber(item.length)) each(item, fn, scope);
else fn.call(scope || item, item);
return item;
}
/**
* 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+
*/
function dpr(decimal) {
//Consider: github.com/ryanve/res
var dPR = win.devicePixelRatio;
if (null == decimal) return dPR || (dpr(2) ? 2 : dpr(1.5) ? 1.5 : dpr(1) ? 1 : 0); //approx
if (!isFinite(decimal)) 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 (mq(decimal)) return true;
return mq(decimal.replace('-moz-', ''));
}
/**
* Response.camelize
* @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
* @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);
}
/**
* Convert stringified primitives back to JavaScript.
* @param {string|*} s String to parse into a JavaScript value.
* @return {*}
*/
function parse(s) {
var n; //undefined, or becomes number
return typeof s != 'string' || !s ? s
: 'false' === s ? false
: 'true' === s ? true
: 'null' === s ? null
: 'undefined' === s || (n = (+s)) || 0 === n || 'NaN' === s ? n
: s;
}
/**
* @param {Element|{length:number}} e
* @return {Element|*}
*/
function first(e) {
return !e || e.nodeType ? e : e[0];
}
/**
* internal-use function to iterate a node's attributes
* @param {Element} el
* @param {Function} fn
* @param {(boolean|*)=} exp
*/
function eachAttr(el, fn, exp) {
var test, n, a, i, l;
if (!el.attributes) return;
test = typeof exp == 'boolean' ? /^data-/ : test;
for (i = 0, l = el.attributes.length; i < l;) {
if (a = el.attributes[i++]) {
n = '' + a.name;
test && test.test(n) !== exp || null == a.value || fn.call(el, a.value, n, a);
}
}
}
/**
* Get object containing an element's data attrs.
* @param {Element} el
* @return {DOMStringMap|Object|undefined}
*/
function getDataset(el) {
var ob;
if (!el || 1 !== el.nodeType) return; //undefined
if (ob = DMS && el.dataset) return ob; //native
ob = {}; //Fallback plain object cannot mutate the dataset via reference.
eachAttr(el, function(v, k) {
ob[camelize(k)] = '' + v;
}, true);
return ob;
}
/**
* @param {Element} el
* @param {Object} ob
* @param {Function} fn
*/
function setViaObject(el, ob, fn) {
for (var n in ob) owns.call(ob, n) && fn(el, n, ob[n]);
}
/**
* @param {Object|Array|Function} el
* @param {(string|Object|*)=} k
* @param {*=} v
*/
function dataset(el, k, v) {
el = first(el);
if (!el || !el.setAttribute) return;
if (void 0 === k && v === k) return getDataset(el);
var exact = isArray(k) && datatize(k[0]);
if (typeof k == 'object' && !exact) {
k && setViaObject(el, k, dataset);
} else {
k = exact || datatize(k);
if (!k) return;
if (void 0 === v) {
k = el.getAttribute(k); //repurpose
return null == k ? v : exact ? parse(k) : '' + k; //normalize
}
el.setAttribute(k, v = '' + v);
return v; //current value
}
}
/**
* Response.deletes(elem, keys) Delete HTML5 data attributes (remove them from them DOM)
* @since 0.3.0
* @param {Element|{length:number}} elem is a DOM element or stack of them
* @param {string|Array} ssv data attribute key(s) in camelCase or lowercase to delete
*/
function deletes(elem, ssv) {
ssv = compact(ssv);
route(elem, function(el) {
each(ssv, function(k) {
el.removeAttribute(datatize(k));
});
});
}
function sel(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
for (var k, r = [], i = 0, l = keys.length; i < l;) {
(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 stack
* @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 $(sel(compact(keys)));
}
/**
* @since 0.3.0
* @return {number} like jQuery(window).scrollLeft()
*/
function scrollX() {
return window.pageXOffset || docElem.scrollLeft;
}
/**
* @since 0.3.0
* @return {number} like $(window).scrollTop()
*/
function scrollY() {
return window.pageYOffset || docElem.scrollTop;
}
/**
* area methods inX/inY/inViewport
* @since 0.3.0
*/
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(first(elem), verge);
return !!r && r.right >= 0 && r.left <= viewportW();
}
function inY(elem, verge) {
var r = rectangle(first(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 first() twice.
//It actually gzips smaller this way too:
var r = rectangle(first(elem), verge);
return !!r && r.bottom >= 0 && r.top <= viewportH() && r.right >= 0 && r.left <= viewportW();
}
/**
* @description Detect whether elem should act in src or markup mode.
* @param {Element} elem
* @return {number}
*/
function detectMode(elem) {
//Normalize to lowercase to ensure compatibility across HTML/XHTML/XML.
//These are the elems that can use src attr per the W3 spec:
//dev.w3.org/html5/spec-author-view/index.html#attributes-1
//stackoverflow.com/q/8715689/770127
//stackoverflow.com/a/4878963/770127
var srcElems = { img:1, input:1, source:3, embed:3, track:3, iframe:5, audio:5, video:5, script:5 }
, modeID = srcElems[ elem.nodeName.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 elem.getAttribute('src')
return 4 > modeID ? modeID : null != elem.getAttribute('src') ? 5 : -5;
}
/**
* Response.store()
* 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().
* @since 0.1.9
* @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) throw new TypeError('@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;
}
/**
* 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 SSV string of data keys
* @return {Array} 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) {
var ret = [];
elem && keys && each(compact(keys), function(k) {
ret.push(dataset(elem, k));
}, elem);
return ret;
}
function addTest(prop, fn) {
if (typeof prop == 'string' && typeof fn == 'function') {
propTests[prop] = fn;
isCustom[prop] = 1;
}
return Response;
}
//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, '') : '';
}
function ascending(a, b) {
return a - b;
}
return {
$e: 0 //jQuery instance
, 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 uses default based on device size
, newValue: 0
, currValue: 1
, aka: null
, lazy: null
, i: 0 //integer, the index of the current highest active breakpoint min
, uid: null
, reset: function() {
var subjects = this.breakpoints, i = subjects.length, tempIndex = 0;
while (!tempIndex && i--) this.fn(subjects[i]) && (tempIndex = i);
if (tempIndex !== this.i) {
//Crossover occurred. Fire the crossover events:
$win.trigger(crossover).trigger(this.prop + crossover);
this.i = tempIndex || 0;
}
return this;
}
, configure: function(options) {
merge(this, options);
var i, points, prefix, aliases, aliasKeys, isNumeric = true, prop = this.prop;
this.uid = suid++;
if (null == this.verge) this.verge = min(screenMax, 500);
if (!(this.fn = propTests[prop])) throw new TypeError('@create');
//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'
if (null == this.dynamic) this.dynamic = 'device' !== prop.slice(0, 6);
this.custom = isCustom[prop];
prefix = this.prefix ? sift(map(compact(this.prefix), sanitize)) : ['min-' + prop + '-'];
aliases = 1 < prefix.length ? prefix.slice(1) : 0;
this.prefix = prefix[0];
points = this.breakpoints;
//Sort and validate (#valid8) custom breakpoints if supplied.
//Must be done before keys are created so that the keys match:
if (isArray(points)) {
each(points, function(v) {
if (!v && v !== 0) throw 'invalid breakpoint';
isNumeric = isNumeric && isFinite(v);
});
isNumeric && points.sort(ascending);
if (!points.length) throw new TypeError('.breakpoints');
} else {
//The default breakpoints are presorted.
points = defaultPoints[prop] || defaultPoints[prop.split('-').pop()];
if (!points) throw new TypeError('.prop');
}
this.breakpoints = points;
this.keys = affix(this.breakpoints, this.prefix); //Create array of data keys.
this.aka = null; //Reset just in case a value was merged in.
if (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;
}
, target: function() {
this.$e = $(sel(sets[this.uid])); //Cache selection. DOM must be ready.
store(this.$e, initContentKey); //Store original (no-js) value to data key.
this.keys.push(initContentKey); //#keys now equals #breakpoints+1
return this;
}
//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);
this.mode = detectMode(elem);
this.values = access(this.$e, this.keys);
if (this.aka) {
//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)
//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.
var i = this.aka.length;
while (i--) this.values = merge(this.values, access(this.$e, this.aka[i]));
}
return this.decideValue();
}
, 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;
}
};
}());
//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;
function resize(fn) {
$win.on('resize', fn);
return Response; //chain
}
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; //chain
}
/**
* Response.action A facade for calling functions on both the ready and resize events.
* @link http://responsejs.com/#action
* @since 0.1.3
* @param {Function|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;
}
/**
* 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 Response.create(object) //single
* @example Response.create([object1, object2]) //bulk
*/
function create(args) {
route(args, function(options) {
if (typeof options != 'object') throw new TypeError('@create');
var elemset = procreate(Elemset).configure(options)
, lowestNonZeroBP
, verge = elemset.verge
, breakpoints = elemset.breakpoints
, scrollName = namespaceIt('scroll')
, resizeName = namespaceIt('resize');
if (!breakpoints.length) return;
//Identify the lowest nonzero breakpoint. (They're already sorted low to high by now.)
lowestNonZeroBP = breakpoints[0] || breakpoints[1] || false;
ready(function() {
var allLoaded = event.allLoaded, lazy = !!elemset.lazy;
function resizeHandler() {
elemset.reset();
each(elemset.$e, function(el, i) {
elemset[i].decideValue().updateDOM();
}).trigger(allLoaded);
}
function scrollHandler() {
each(elemset.$e, function(el, i) {
inViewport(elemset[i].$e, verge) && elemset[i].updateDOM();
});
}
//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] = procreate(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();
}
});
//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;
$win.on(scrollName, scrollHandler);
elemset.$e.one(allLoaded, function() {
$win.off(scrollName, scrollHandler);
});
});
});
return Response;
}
function noConflict(callback) {
if (root[name] === Response) root[name] = old;
if (typeof callback == 'function') callback.call(root, Response);
return Response;
}
//Many methods are @deprecated (see issue #51)
Response = {
deviceMin: function() { return screenMin; }
, deviceMax: function() { return screenMax; }
//, sets: function(prop) {//must be uid
// return $(sel(sets[prop] || sets.all));
//}
, noConflict: noConflict
, create: create
, addTest: addTest
, datatize: datatize
, camelize: camelize
, render: parse
, store: store
, access: access
, target: target
, object: procreate
, 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
, mq: mq
, wave: wave
, band: band
, map: map
, each: each
, inViewport: inViewport
, dataset: dataset
, viewportH: viewportH
, viewportW: viewportW
};
//Initialize
ready(function() {
var settings = dataset(doc.body, 'responsejs'), parse = win.JSON && JSON.parse || $.parseJSON;
settings = settings && parse ? parse(settings) : settings;
settings && settings.create && create(settings.create);
//Remove .no-responsejs and add .responsejs
docElem.className = docElem.className.replace(/(^|\s)(no-)?responsejs(\s|$)/, '$1$3') + ' responsejs ';
});
return Response;
});