/*
* 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 || {}));