/* * TODO: DOCS (private for dev) */ var _util = ScrollMagic._util = (function (window) { var U = {}, i; /** * ------------------------------ * internal helpers * ------------------------------ */ // parse float and fall back to 0. var floatval = function (number) { return parseFloat(number) || 0; }; // get current style IE safe (otherwise IE would return calculated values for 'auto') var _getComputedStyle = function (elem) { return elem.currentStyle ? elem.currentStyle : window.getComputedStyle(elem); }; // get element dimension (width or height) var _dimension = function (which, elem, outer, includeMargin) { elem = (elem === document) ? window : elem; if (elem === window) { includeMargin = false; } else if (!_type.DomElement(elem)) { return 0; } which = which.charAt(0).toUpperCase() + which.substr(1).toLowerCase(); var dimension = (outer ? elem['offset' + which] || elem['outer' + which] : elem['client' + which] || elem['inner' + which]) || 0; if (outer && includeMargin) { var style = _getComputedStyle(elem); dimension += which === 'Height' ? floatval(style.marginTop) + floatval(style.marginBottom) : floatval(style.marginLeft) + floatval(style.marginRight); } return dimension; }; // converts 'margin-top' into 'marginTop' var _camelCase = function (str) { return str.replace(/^[^a-z]+([a-z])/g, '$1').replace(/-([a-z])/g, function (g) { return g[1].toUpperCase(); }); }; /** * ------------------------------ * external helpers * ------------------------------ */ // extend obj – same as jQuery.extend({}, objA, objB) U.extend = function (obj) { obj = obj || {}; for (i = 1; i < arguments.length; i++) { if (!arguments[i]) { continue; } for (var key in arguments[i]) { if (arguments[i].hasOwnProperty(key)) { obj[key] = arguments[i][key]; } } } return obj; }; // check if a css display type results in margin-collapse or not U.isMarginCollapseType = function (str) { return ["block", "flex", "list-item", "table", "-webkit-box"].indexOf(str) > -1; }; // implementation of requestAnimationFrame // based on https://gist.github.com/paulirish/1579671 var lastTime = 0, vendors = ['ms', 'moz', 'webkit', 'o']; var _requestAnimationFrame = window.requestAnimationFrame; var _cancelAnimationFrame = window.cancelAnimationFrame; // try vendor prefixes if the above doesn't work for (i = 0; !_requestAnimationFrame && i < vendors.length; ++i) { _requestAnimationFrame = window[vendors[i] + 'RequestAnimationFrame']; _cancelAnimationFrame = window[vendors[i] + 'CancelAnimationFrame'] || window[vendors[i] + 'CancelRequestAnimationFrame']; } // fallbacks if (!_requestAnimationFrame) { _requestAnimationFrame = function (callback) { var currTime = new Date().getTime(), timeToCall = Math.max(0, 16 - (currTime - lastTime)), id = window.setTimeout(function () { callback(currTime + timeToCall); }, timeToCall); lastTime = currTime + timeToCall; return id; }; } if (!_cancelAnimationFrame) { _cancelAnimationFrame = function (id) { window.clearTimeout(id); }; } U.rAF = _requestAnimationFrame.bind(window); U.cAF = _cancelAnimationFrame.bind(window); // (BUILD) - REMOVE IN MINIFY - START var loglevels = ["error", "warn", "log"], console = window.console || {}; console.log = console.log || function(){}; // no console log, well - do nothing then... // make sure methods for all levels exist. for(i = 0; i<loglevels.length; i++) { var method = loglevels[i]; if (!console[method]) { console[method] = console.log; // prefer .log over nothing } } U.log = function (loglevel) { if (loglevel > loglevels.length || loglevel <= 0) loglevel = loglevels.length; var now = new Date(), time = ("0"+now.getHours()).slice(-2) + ":" + ("0"+now.getMinutes()).slice(-2) + ":" + ("0"+now.getSeconds()).slice(-2) + ":" + ("00"+now.getMilliseconds()).slice(-3), method = loglevels[loglevel-1], args = Array.prototype.splice.call(arguments, 1), func = Function.prototype.bind.call(console[method], console); args.unshift(time); func.apply(console, args); }; // (BUILD) - REMOVE IN MINIFY - END /** * ------------------------------ * type testing * ------------------------------ */ var _type = U.type = function (v) { return Object.prototype.toString.call(v).replace(/^\[object (.+)\]$/, "$1").toLowerCase(); }; _type.String = function (v) { return _type(v) === 'string'; }; _type.Function = function (v) { return _type(v) === 'function'; }; _type.Array = function (v) { return Array.isArray(v); }; _type.Number = function (v) { return !_type.Array(v) && (v - parseFloat(v) + 1) >= 0; }; _type.DomElement = function (o){ return ( typeof HTMLElement === "object" ? o instanceof HTMLElement : //DOM2 o && typeof o === "object" && o !== null && o.nodeType === 1 && typeof o.nodeName==="string" ); }; /** * ------------------------------ * DOM Element info * ------------------------------ */ // always returns a list of matching DOM elements, from a selector, a DOM element or an list of elements or even an array of selectors var _get = U.get = {}; _get.elements = function (selector) { var arr = []; if (_type.String(selector)) { try { selector = document.querySelectorAll(selector); } catch (e) { // invalid selector return arr; } } if (_type(selector) === 'nodelist' || _type.Array(selector)) { for (var i = 0, ref = arr.length = selector.length; i < ref; i++) { // list of elements var elem = selector[i]; arr[i] = _type.DomElement(elem) ? elem : _get.elements(elem); // if not an element, try to resolve recursively } } else if (_type.DomElement(selector) || selector === document || selector === window){ arr = [selector]; // only the element } return arr; }; // get scroll top value _get.scrollTop = function (elem) { return (elem && typeof elem.scrollTop === 'number') ? elem.scrollTop : window.pageYOffset || 0; }; // get scroll left value _get.scrollLeft = function (elem) { return (elem && typeof elem.scrollLeft === 'number') ? elem.scrollLeft : window.pageXOffset || 0; }; // get element height _get.width = function (elem, outer, includeMargin) { return _dimension('width', elem, outer, includeMargin); }; // get element width _get.height = function (elem, outer, includeMargin) { return _dimension('height', elem, outer, includeMargin); }; // get element position (optionally relative to viewport) _get.offset = function (elem, relativeToViewport) { var offset = {top: 0, left: 0}; if (elem && elem.getBoundingClientRect) { // check if available var rect = elem.getBoundingClientRect(); offset.top = rect.top; offset.left = rect.left; if (!relativeToViewport) { // clientRect is by default relative to viewport... offset.top += _get.scrollTop(); offset.left += _get.scrollLeft(); } } return offset; }; /** * ------------------------------ * DOM Element manipulation * ------------------------------ */ U.addClass = function(elem, classname) { if (classname) { if (elem.classList) elem.classList.add(classname); else elem.className += ' ' + classname; } }; U.removeClass = function(elem, classname) { if (classname) { if (elem.classList) elem.classList.remove(classname); else elem.className = elem.className.replace(new RegExp('(^|\\b)' + classname.split(' ').join('|') + '(\\b|$)', 'gi'), ' '); } }; // if options is string -> returns css value // if options is array -> returns object with css value pairs // if options is object -> set new css values U.css = function (elem, options) { if (_type.String(options)) { return _getComputedStyle(elem)[_camelCase(options)]; } else if (_type.Array(options)) { var obj = {}, style = _getComputedStyle(elem); options.forEach(function(option, key) { obj[option] = style[_camelCase(option)]; }); return obj; } else { for (var option in options) { var val = options[option]; if (val == parseFloat(val)) { // assume pixel for seemingly numerical values val += 'px'; } elem.style[_camelCase(option)] = val; } } }; return U; }(window || {}));