/*!
* VERSION: 0.6.1
* DATE: 2019-10-30
* UPDATES AND DOCS AT: https://chris-moody.github.io/mkr
*
* @license copyright 2017 Christopher C. Moody
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in the
* Software without restriction, including without limitation the rights to use, copy,
* modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to the
* following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* @author: Christopher C. Moody, chris@moodydigital.com
*/
(function(global, className){
var _instances={}, _count=-1;
/**
* @class mkr
* @classdesc Lightweight library focused on javascript-based content creation
* @description Initializes a new mkr instance.
<ul>
<li>creates a div container, and appends it to the specified parent. @see container</li>
<li>creates a TimelineMax instance</li>
</ul>
* @param {Object} options - A set of attributes and css properties used to create the container. A few special properties are documented below.
* @param {Element} [options.parent=document.body] - Element the mkr instance container is appended to
* @param {Boolean} [options.preload=false] - When true, delays loading img elements until the instance's load function in called
* @param {String=} [options.revealKey='isi'] - Optional dom id and URL query param for screen capture
* @param {Object=} options.css - CSS properties to apply to the container element.
* @param {Object=} options.attr - Attributes to apply to the container element.
* @param {String} [options.attr.class='mkr-container'] - Class string applied to the container element. Applied classes will always include 'mkr-container'
* @param {Object=} options.tmln - options passed to the built-in TimelineMax instance.
* @requires {@link https://greensock.com/tweenmax TweenMax}
* @returns {mkr} A new mkr instance.
*/
var mkr = function(options) {
_count++;
var id=_count;
this._elements = [];
this._states = {};
this._count=0;
this._tag = 'mkr-'+id+'-index';
options = options || {};
options.tmln = options.tmln || {};
this._tmln = new TimelineMax(options.tmln);
delete options.tmln;
var parent = mkr.getDefault(options, 'parent', document.body);
delete options.parent;
if(typeof parent === 'string') parent = mkr.query(parent);
this._preload = options.preload;
delete options.preload;
this._images = [];
this._loadCallback = null;
this._loadContext = null;
var _loadedImages = 0;
mkr.setDefault(options, 'attr', {});
var classes = 'mkr-container mkr-'+id;
'class' in options.attr ? options.attr.class += ' '+classes : options.attr.class = classes;
/**
* @name mkr#container
* @type HTMLElement
* @description the default container element used to hold all elements added to/created by the mkr instance
**/
this.container = this.create("div", options, parent, true);
_instances[id] = this;
var revealKey = mkr.setDefault(options, 'revealKey', null);
var revealFlag = window.location.href.indexOf("?"+revealKey) > -1;
var _imageLoaded = function() {
_loadedImages++;
var self = _instances[id];
if(_loadedImages == self._images.length) {
if(self._loadCallback) {
self._loadCallback.apply(self._loadContext);
}
if (revealFlag) {
var _reveal = document.getElementById(revealKey);
console.log(revealKey, _reveal);
TweenLite.set(document.getElementsByTagName('div'), {autoAlpha:0});
TweenLite.set(_reveal.getElementsByTagName('div'), {autoAlpha:1});
TweenLite.set(_reveal, {
x: 0,
y: 0,
autoAlpha: 1,
height: "auto",
overflow:"visible"
});
mkr.add(_reveal, document.body);
}
_preload = false;
}
}
/**
* @name mkr#id
* @public
* @readonly
* @type int
* @description internal id of this mkr instance
**/
Object.defineProperty(this, 'id', {
get: function() {
return id;
}
});
/**
* @function load
* @memberof mkr
* @instance
* @description When preloading is enabled, this function begins loading the instances img tags
* @param {function} callback - A callback function to call once loading is complete
* @param {Object=} context - Context on which the callback is executed (object that should represent the `this` variable inside callback function)
**/
this.load = function(callback, context) {
this._loadCallback = callback;
this._loadContext = context;
var i = this._images.length;
if(i <= 0) {
this._loadCallback.apply(this._loadContext);
return;
}
while(i--) {
this._images[i].img.onload = _imageLoaded;
}
i = this._images.length;
while(i--) {
this._images[i].img.src = this._images[i].src;
}
};
};
/**
* @function makeDC
* @memberof mkr
* @static
* @description Factory method for quickly creating creatives that conform to DCS standards. Differs from the standard constructor in that it adds a border to the container, and sets a few default properties documented below.
* @param {Number} width - The width of the creative
* @param {Number} height - The height of the creative
* @param {Object} options - A set of attributes and css properties used to create the container. A few special properties and default values are documented below.
* @param {Element} [options.parent=document.body] - Element the mkr instance container is appended to
* @param {Boolean} [options.preload=false] - When true, delays loading img elements until the instance's load function in called
* @param {String=} [options.revealKey='isi'] - Optional dom id and URL query param for screen capture
* @param {Object=} [options.border] - A set of attributes and css properties used to create the border
* @param {Object=} options.border.attr - Attributes to apply to the border element.
* @param {String} [options.border.attr.class='mkr.border'] - Class string applied to the border element. Applied classes will always include 'mkr-border'
* @param {Object=} options.border.css - CSS properties to apply to the border element.
* @param {Number} [options.border.css.left=0] - CSS left property
* @param {Number} [options.border.css.top=0] - CSS top property
* @param {int} [options.border.css.zIndex=10] - CSS z-index property
* @param {String} [options.border.css.borderWidth='1px'] - CSS border-width property
* @param {String} [options.border.css.borderStyle='solid'] - CSS border-style property
* @param {String} [options.border.css.borderColor='#666666'] - CSS border-color
* @param {String} [options.border.css.pointerEvents='none'] - CSS pointer-events
* @param {Number} [options.border.css.width=width] - CSS width
* @param {Number} [options.border.css.height=height] - CSS height
* @returns {mkr} The new mkr instance
**/
mkr.makeDC = function(width, height, options) {
options = options || {};
mkr.setDefault(options, 'revealKey', 'isi');
mkr.setDefault(options, 'border', {});
mkr.setDefault(options.border, 'css', {});
mkr.setDefault(options.border.css, 'top', 0);
mkr.setDefault(options.border.css, 'left', 0);
mkr.setDefault(options.border.css, 'zIndex', 10);
mkr.setDefault(options.border.css, 'position', 'absolute');
mkr.setDefault(options.border.css, 'pointerEvents', 'none');
mkr.setDefault(options.border.css, 'borderWidth', '1px');
mkr.setDefault(options.border.css, 'borderStyle', 'solid');
mkr.setDefault(options.border.css, 'borderColor', '#666666');
var borderWidth = mkr.unitless(options.border.css.borderWidth);
mkr.setDefault(options.border.css, 'width', width-borderWidth*2);
mkr.setDefault(options.border.css, 'height', height-borderWidth*2);
mkr.setDefault(options.border, 'attr', {});
var classes = 'mkr-border';
'class' in options.border.attr ? options.border.attr.class += ' '+classes : options.border.attr.class = classes;
var border = document.createElement('div');
TweenMax.set(border, options.border);
/*TweenMax.set(dc.border, {attr:{class:'mkr-border'},
css:{position:'absolute', pointerEvents:'none', zIndex:'1000',
left:'0px', top:'0px', border:'1px solid #666666', width:width-2, height:height-2
}});*/
delete options.border;
mkr.setDefault(options, 'attr', {});
mkr.setDefault(options, 'css', {});
mkr.setDefault(options.css, 'width', width);
mkr.setDefault(options.css, 'height', height);
mkr.setDefault(options.css, 'top', '0px');
mkr.setDefault(options.css, 'left', '0px');
mkr.setDefault(options.css, 'zIndex', 1);
mkr.setDefault(options.css, 'overflow', 'hidden');
var dc = new mkr(options);
//dc.width = width;
//dc.height = height;
dc.border = border;
dc.container.appendChild(dc.border);
return dc;
};
/**
* @name mkr#tmln
* @public
* @readonly
* @type TimelineMax
* @description reference to this mkr's built-in TimelineMax instance
**/
Object.defineProperty(mkr.prototype, 'tmln', {
get: function() {
return this._tmln;
}
});
/**
* @name mkr#width
* @public
* @type Number
* @description pixel height of this mkr's container
**/
Object.defineProperty(mkr.prototype, 'width', {
get: function() {
return this.container.offsetWidth;
},
set: function(value) {
this.container.offsetWidth = value;
}
});
/**
* @name mkr#height
* @public
* @type Number
* @description pixel width of this mkr's container
**/
Object.defineProperty(mkr.prototype, 'height', {
get: function() {
return this.container.offsetHeight;
},
set: function(value) {
this.container.offsetHeight = value;
}
});
/**
* @function construct
* @memberof mkr.prototype
* @public
* @description Creates a new instance of a mkr construct
* @param {String} type - The type of construct to create.
* @param {Object=} options - A set of attributes, css, and other properties used to create the construct
* @param {Object=} options.css - CSS properties to apply to the new construct.
* @param {Object=} options.attr - Attributes to apply to the new construct.
* @param {*} [parent=this.container] - The element to append the new construct. Can be an element or a css selector string
* @returns {*} The new construct
**/
mkr.prototype.construct = function(type, options, parent) {
options = options || {};
parent = mkr.default(parent, this.container);
return mkr.construct(type, options, parent, this);
}
/**
* @function clone
* @memberof mkr.prototype
* @public
* @description clone a dom element using cloneNode and apply with optional params
* @param {*} target - A selector string, Array, or element
* @param {Object=} options - A set of attributes and css properties used to create the element
* @param {Object=} options.css - CSS properties to apply to the new object
* @param {Object=} options.attr - Attributes to apply to the new object
* @param {Object=} cloneOpts - Options used to fine-tune the behavior of the clone operation
* @param {Boolean} [cloneOpts.children=true] - Whether children should also be cloned(sets cloneNodes deep parameter)
* @param {*} [parent=this.container] - The element which to append the new element. Can be an element or a css selector string
* @param {int=} index - Insertion index of the clones. Defaults to the end.
* @returns {*} The added element, or array of elements
**/
mkr.prototype.clone = function(target, options, cloneOpts, parent, index) {
parent = parent || this.container;
return mkr.clone(target, options, cloneOpts, parent, index);
};
/**
* @function create
* @memberof mkr.prototype
* @public
* @description Creates a new html element
* @param {String} type - The type of element to create.
* @param {Object=} options - A set of attributes and css properties used to create the element
* @param {Object=} options.css - CSS properties to apply to the new element.
* @param {Object=} options.attr - Attributes to apply to the new element.
* @param {*} [parent=this.container] - The element which to append the new element. Can be an element or a css selector string
* @param {int=} index - Insertion index of the new element. Defaults to the end.
* @returns {Element} The new element
**/
mkr.prototype.create = function(type, options, parent, index) {
options = options || {};
parent = mkr.default(parent, this.container);
var el;
if(type === 'img' && this._preload && options.attr && options.attr.src) {
var img = {src:options.attr.src}
delete options.attr.src;
el = mkr.create(type, options, parent, index);
img.img = el;
this._images.push(img);
return el;
}
return mkr.create(type, options, parent, index);
};
/**
* @function batch
* @memberof mkr.prototype
* @public
* @description Creates a specified number of elements with the same parameters
* @param {String} type - The type of element to create.
* @param {Object=} options - A set of attributes and css properties used to create the elements
* @param {Number} num - The number of elements to produce
* @param {*=} parent - The element to append the new elements. Can be an element or a css selector string
* @returns {Array} An array containing the new elements
**/
mkr.prototype.batch = function(type, options, num, parent) {
var elements = [];
var n = num;
while(n--) {
elements.push(this.create(type, options, parent));
}
return elements;
}
/**
* @function kill
* @memberof mkr.prototype
* @public
* @description Kills this mkr's tweens, child tweens, and timeline. Removes all listeners and removes the container from the DOM
**/
mkr.prototype.kill = function() {
TweenMax.killTweensOf(this);
TweenMax.killTweensOf(this.container);
TweenMax.killChildTweensOf(this.container);
mkr.off('.mkr-'+this.id+' *');
mkr.remove(this.container);
};
/**
* @function each
* @memberof mkr
* @static
* @description Executes the provided function for each target object
* @param {*} target - An single element, array of elements, or a css selector string.
* @param {*} callback - Function to execute for each object
* @param {Object=} context - Context on which the callback is executed (object that should represent the `this` variable inside callback function)
**/
mkr.each = function(target, callback, context) {
if(target == null || typeof target === 'undefined') {
return;
}
var targets;
if(typeof target === 'string') targets = mkr.queryAll(target);
else if(Array.isArray(target)) targets = target;
else targets = [target];
//console.log(targets);
forEach(targets, callback, context);
};
var forEach = function(arrayLike, callback, context) {
for(var i = 0; i < arrayLike.length; i++) {
callback.call(context, arrayLike[i], i)
}
};
/**
* @function on
* @memberof mkr
* @static
* @description Add a listener to one or many objects
* @param {*} target - An single element, array of elements, or a css selector string.
* @param {String} type - A string representing the type of event the listener is associated with
* @param {Function} callback - The function to be executed when the associated event is fired
* @param {Object=} context - Context on which the callback is executed (object that should represent the `this` variable inside callback function)
* @param {Number} [priority=0] - Affects callback execution order. Higher priority listeners are executed before those with lower priority. Listeners of the same priority are executed in order of insertion
**/
mkr.on = function(target, type, callback, context, priority) {
mkr.each(target, function(el) {
//console.log(el, type);
var cxt = mkr.default(context, el);
mkr._triggerMatrix.add(el, type, callback, cxt, priority);
//el.addEventListener(eventType, callback, false);
});
};
/**
* @function once
* @memberof mkr
* @static
* @description Add a listener to one or many objects that only executes once per object
* @param {*} target - An single element, array of elements, or a css selector string.
* @param {String} type - A string representing the type of event the listener is associated with
* @param {Function} callback - The function to be executed when the associated event is fired
* @param {Object=} context - Context on which the callback is executed (object that should represent the `this` variable inside callback function)
* @param {Number} [priority=0] - Affects callback execution order. Higher priority listeners are executed before those with lower priority. Listeners of the same priority are executed in order of insertion
**/
mkr.once = function(target, type, callback, context, priority) {
mkr.each(target, function(el) {
var cxt = mkr.default(context, el);
mkr._triggerMatrix.addOnce(el, type, callback, cxt, priority);
//el.addEventListener(eventType, callback, false);
});
};
/**
* @function off
* @memberof mkr
* @static
* @description Remove listeners from one or many objects. Behaves differently depending on the number of passed arguments.
* - 3+ arguments: Only removes the specified listener from the target
^ - First 2 arguments only: Removes all listeners of provdied event type
* - First argument only: Removes all listeners from the target
* @param {*} target - An single element, array of elements, or a css selector string.
* @param {String=} type - The event type. Excluding this this argument removes all listeners from the target regardless of type.
* @param {Function=} callback - The callback to remove. Excluding this argument removes all listeners of the specified from the target
* @param {Object=} context - Context on which the callback is executed (object that should represent the `this` variable inside callback function)
**/
mkr.off = function(target, type, callback, context) {
var func;
if(type === undefined) func = mkr._triggerMatrix.delete.bind(mkr._triggerMatrix);
else if(callback === undefined) func = mkr._triggerMatrix.removeAll.bind(mkr._triggerMatrix);
else func = mkr._triggerMatrix.remove.bind(mkr._triggerMatrix);
mkr.each(target, function(el) {
var cxt = mkr.default(context, el);
func(el, type, callback, cxt);
//el.removeEventListener(eventType, callback, false);
});
};
/**
* @function clearListeners
* @memberof mkr
* @static
* @description Removes all listeners managed by mkr across all mkr instances. Its a good idea to be sure that you want to do this.
**/
mkr.clearListeners = function() {
mkr._triggerMatrix.clear();
};
/**
* @function reveal
* @memberof mkr
* @static
* @description shortcut for revealing scrollable content.
* @param {*} target - A selector string, Array, or element
* @param {Object=} options - An optional set of attributes and css properties applied to the target
* @param {Object=} parentOpts - An optional set of attributes and css properties applied to every parent of the target
**/
mkr.reveal = function(target, options, parentOpts) {
options = options || {};
mkr.setDefault(options, 'overflowY', 'visible');
mkr.setDefault(options, 'height', 'auto');
TweenMax.set(target, options);
parentOpts = parentOpts || {};
mkr.setDefault(parentOpts, 'overflowY', 'visible');
mkr.each(target, function(el) {
var parent = el.parentNode, lastParent = el.parentNode;
while(parent && parent !== window.document) {
//console.log(parent);
parentOpts.height = el.scrollHeight+(el._gsTransform?el._gsTransform.y:0);
TweenMax.set(parent, parentOpts);
//TweenMax.set(parent, {overflowY:'visible', height:parent.scrollHeight+1});
lastParent = parent;
parent = parent.parentNode;
}
/*if(lastParent && el._gsTransform) {
TweenMax.set(lastParent, {height:'+='+el._gsTransform.y});
}*/
});
};
/**
* @function scroll
* @memberof mkr
* @static
* @description Tweens the scrollable area of targets at the specified speed in px/s. Can target multiple objects
* @param {*} target - A selector string, Array, or element
* @param {Number} [speed=14] - The speed of the tween in px/s
* @param {Object=} options - Additional properties passed to the tween
* @returns {Array} An array of tweens created to facilitate the animation
**/
mkr.scroll = function(target, speed, options) {
options = options || {};
mkr.setDefault(options, 'scrollTo', 'max');
mkr.setDefault(options, 'ease', Power0.easeNone);
speed = mkr.default(speed, 14);
var tweens = [];
mkr.each(target, function(el) {
var scrollTime = ((el.scrollHeight-el.clientHeight)-el.scrollTop)/speed;
tweens.push(TweenMax.to(el, scrollTime, options));
//duration = Math.max(duration, scrollTime);
});
return tweens;
};
/**
* @function split
* @memberof mkr
* @static
* @private
* @description Tweens the scrollable area of targets at the specified speed in px/s. Can target multiple objects
* @param {*} target - A selector string, Array, or element
* @param {Number} [speed=14] - The speed of the tween in px/s
* @param {Object=} options - Additional properties passed to the tween
* @returns {Array} An array of tweens created to facilitate the animation
**/
/*mkr.each('.copy0 > span', function(el) {
var text = el.innerHTML.split('');
var tagged = text.map(function(t) {
t = t === ' ' ? ' ' : t;
return '<span>'+t+'</span>';
});
el.innerHTML = tagged.join('');
});*/
/**
* @function extend
* @memberof mkr
* @static
* @description shortcut for creating class extensions using protoypal inheritance
* @param {Object} baseObj - The object to be extended
* @returns {Object}
**/
mkr.extend = function(baseObj) {
if (typeof Object.create !== 'function') {
Object.create = function (o) {
function F() {}
F.prototype = o;
return new F();
};
}
return Object.create(baseObj);
};
/**
* @function query
* @memberof mkr
* @static
* @description Returns the first element within the baseElement that matches the specified selectors. element.querySelector(selectors) shortcut
* @param {String} selectors - A string containing one or more CSS selectors separated by commas.
* @param {Element} [baseElement=document] - The ancestor element to search. Defaults to the document object
* @returns {Element} The first element within the baseElement that matches the specified selectors.
*/
mkr.query = function(selectors, baseElement) {
baseElement = baseElement || document;
return baseElement["querySelector"](selectors);
};
/**
* @function queryAll
* @memberof mkr
* @static
* @description Returns a NodeList of all elements within the baseElement that match the specified selectors. element.querySelectorAll(selectors) shortcut
* @param {String} selectors - A string containing one or more CSS selectors separated by commas.
* @param {Element} [baseElement=document] - The ancestor element to search. Defaults to the document object
* @returns {NodeList} A NodeList of all elements within the baseElement that match the specified selectors.
**/
mkr.queryAll = function(selectors, baseElement) {
baseElement = baseElement || document;
return baseElement["querySelectorAll"](selectors);
};
/**
* @function construct
* @memberof mkr
* @static
* @description Creates a new instance of a mkr construct
* @param {String} type - The type of construct to create.
* @param {Object=} options - A set of attributes, css, and other properties used to create the construct
* @param {Object=} options.css - CSS properties to apply to the new construct.
* @param {Object=} options.attr - Attributes to apply to the new construct.
* @param {*=} parent - The element to append the new construct. Can be an element or a css selector string
* @returns {*} The new construct
**/
mkr.construct = function(type, options, parent) {
if(type in mkr._constructs) {
mkr.setDefault(options, 'parent', parent);
return new mkr._constructs[type](options);
}
console.warn(type, 'not found!');
return null;
}
mkr._constructs = {};
Object.defineProperty(mkr, 'constructs', {
get: function() {
return mkr._constructs;
}
});
/**
* @function clone
* @memberof mkr
* @static
* @description clone a dom element using cloneNode and apply with optional params
* @param {*} target - A selector string, Array, or element
* @param {Object=} options - A set of attributes and css properties used to create the element
* @param {Object=} options.css - CSS properties to apply to the new object
* @param {Object=} options.attr - Attributes to apply to the new object
* @param {Object=} cloneOpts - Options used to fine-tune the behavior of the clone operation
* @param {Boolean} [cloneOpts.children=true] - Whether children should also be cloned(sets cloneNodes deep parameter)
* @param {*} [parent=null] - The element which to append the new element. Can be an element or a css selector string
* @param {int=} index - Insertion index of the clones. Defaults to the end.
* @returns {*} The added element, or array of elements
**/
mkr.clone = function(target, options, cloneOpts, parent, index) {
options = options || {};
options = mkr.merge(options, mkr.defaults);
cloneOpts = cloneOpts || {};
mkr.setDefault(cloneOpts, 'children', true);
var clone, clones = [];
mkr.each(target, function(el) {
clone = el.cloneNode(cloneOpts.children);
delete clone.id;
TweenLite.set(clone, options);
mkr.add(clone, parent, index);
clones.push(clone);
});
return clones.length > 1 ? clones : clones[0];
};
/**
* @function create
* @memberof mkr
* @static
* @description Creates a new html element
* @param {String} type - The type of element to create.
* @param {Object=} options - A set of attributes and css properties used to create the element
* @param {Object=} options.css - CSS properties to apply to the new object.
* @param {Object=} options.attr - Attributes to apply to the new object.
* @param {*} [parent=null] - The element which to append the new element. Can be an element or a css selector string
* @param {int=} index - Insertion index of the new element. Defaults to the end.
* @returns {Element} The new element
**/
mkr.create = function(type, options, parent, index) {
var t = type.toLowerCase();
options = options || {};
options = mkr.merge(options, mkr.defaults);
parent = mkr.default(parent, null);
if(typeof parent === 'string') parent = mkr.query(parent);
var xlnkns = 'http://www.w3.org/1999/xlink'
var svgns = 'http://www.w3.org/2000/svg';
var svgTags = ['svg', 'defs', 'use', 'image', 'g', 'mask', 'clippath', 'lineargradient', 'radialgradient', 'stop', 'text', 'rect', 'circle', 'ellipse', 'line', 'polyline', 'polygon', 'path'];
var element;
if(svgTags.indexOf(t) >= 0) {
element = document.createElementNS(svgns, type);
//if(type == 'svg') options.css.position = options.css.position || "absolute";
if('xlink:href' in options.attr) {
element.setAttributeNS(xlnkns, 'href', options.attr['xlink:href']);
delete options.attr['xlink:href'];
}
}
else {
element = document.createElement(type);
}
TweenMax.set(element, options);
mkr.add(element, parent, index);
return element;
};
/**
* @function add
* @memberof mkr
* @static
* @description Adds an existing element to the specified parent
* @param {*} target - A single element, array of elements, or a css selector string.
* @param {Element} [parent=document.body] - The ancestor element to search. Defaults to the document object
* @param {int=} index - Insertion index of the target. Defaults to the number of children. When negative, becomes the sum of itself and the number of children
* @returns {*} The added element, or array of elements
**/
mkr.add = function(target, parent, index) {
parent = mkr.default(parent, document.body);
var targets = [];
if(!parent) {
mkr.each(target, function(el) {
targets.push(el);
});
}
else {
if(typeof parent === 'string') parent = mkr.query(parent);
index = mkr.default(index, parent.childNodes.length);
if(index < 0) index = parent.childNodes.length + index;
mkr.each(target, function(el) {
parent.insertBefore(el, parent.childNodes[index]);
targets.push(el);
});
}
return targets.length > 1 ? targets : targets[0];
};
/**
* @function remove
* @memberof mkr
* @static
* @description Removes an existing element from the DOM
* @param {*} target - A single element, array of elements, or a css selector string.
* @returns {Element} The removed element, or array of elements
**/
mkr.remove = function(target) {
var targets = [];
mkr.each(target, function(el) {
targets.push(el.parentNode.removeChild(el));
});
return targets.length > 1 ? targets : targets[0];
};
/**
* @function default
* @memberof mkr
* @static
* @description Examines provided value and returns the fallback if it is undefined
* @param {String} value - The value to examine
* @param {String} value - The fallback value
* @returns {*} returns value or the fallback if it is undefined
**/
mkr.default = function(value, fallback) {
return value === undefined ? fallback : value;
};
/**
* @function getDefault
* @memberof mkr
* @static
* @description Returns target[key] or value if target[key] has not be defined
* @param {Object} target - The target of this operation
* @param {String} key - The name of the property being set
* @param {String} value - The value to set
* @returns {*} target[key] or value if target[key] has not be defined
**/
mkr.getDefault = function(target, key, value) {
/*if(!(key in target)) {
return val;
}
return target[key]*/
return mkr.default(target[key], value);
};
/**
* @function setDefault
* @memberof mkr
* @static
* @description Sets the the value of target[key] to value if key has not already been assigned
* @param {Object} target - The target of this operation
* @param {String} key - The name of the property being set
* @param {String} value - The value to set
* @returns {*} The value of target[key]
**/
mkr.setDefault = function(target, key, value) {
target[key] = mkr.default(target[key], value);
return target[key];
};
/**
* @function createStyleSheet
* @memberof mkr
* @private
* @description Creates the style sheet mkr uses for dynamic styles
* @returns {StyleSheet} The new stylesheet
**/
var createStyleSheet = function() {
var style = document.createElement('style');
style.appendChild(document.createTextNode(''));
document.head.appendChild(style);
return style.sheet;
};
/**
* @function addRule
* @memberof mkr
* @static
* @description Adds a CSS rule to mkr's global stylesheet.
* @param {String} selector - A selector string, that the rule should target
* @param {Object} styles - The styles to add to the new rule
* @param {int=} index - The index at which to insert the rule.
* @returns {int} The index of the newly inserted rule
* @requires {@link https://greensock.com/cssruleplugin CSSRulePlugin}
**/
mkr.addRule = function(selector, styles, index) {
//var ruleString = JSON.stringify(styles).replace(/\"/g, '');
index = index === undefined ? mkr.styles.cssRules.length : index;
//mkr.styles.insertRule(selector + "{" + styles + "}", index);
var rules = selector+"{}";
var n = -1;
try {
n = mkr.styles.insertRule(rules, index);
var rule = mkr.styles.cssRules[n].style;//CSSRulePlugin.getRule(selector);
TweenMax.set(rule, {cssRule:styles});
}
catch(e){console.warn(e)};
return n;
};
/**
* @function removeRule
* @memberof mkr
* @static
* @description Traverses mkr's stylesheet and removes first rule to match the selector.
* @param {String} selector - The selector text of the rule to remove.
**/
mkr.removeRule = function(selector) {
var index = mkr.findRule(selector);
if(index > -1) {
mkr.styles.deleteRule(index);
}
};
/**
* @function hasClass
* @memberof mkr
* @static
* @description Tests whether the target element has the indicated class assigned
* @param {*} target - An single element, or a css selector string.
* @param {String} className - A string representing the class to search for.
**/
mkr.hasClass = function(target, className) {
if(typeof target === 'string') target = mkr.query(target);
if(!target) return false;
return target.className.split(' ').indexOf(className) >= 0;
};
/**
* @function deleteRule
* @memberof mkr
* @static
* @description Delete the CSS rule at the specified index.
* @param {int=} index - The index the rule to remove.
**/
mkr.deleteRule = function(index) {
mkr.styles.deleteRule(index);
};
/**
* @function findRule
* @memberof mkr
* @static
* @description Traverses mkr's stylesheet and returns the index of the first rule to match the selector. Returns -1 if the rule is not found.
* @param {String} selector - Selector used to search the stylesheet
* @returns {int} index - The index of the rule or -1 if not found.
**/
mkr.findRule = function(selector) {
var len = mkr.styles.cssRules.length;
for(var i=0; i < len; i++) {
if(selector === mkr.styles.cssRules[i].selectorText) {
return i;
}
}
return -1;
};
/**
* @function getRule
* @memberof mkr
* @static
* @description Alias for CSSRulePlugin.getRule. Returns the CSSRules that match the provided selector
* @param {String} selector - A selector string by which to select rules
* @returns {CSSStyleDeclaration} - The matched rule(s)
* @requires {@link https://greensock.com/cssruleplugin CSSRulePlugin}
**/
mkr.getRule = function(selector) {
return CSSRulePlugin.getRule(selector);
};
/**
* @function setRule
* @memberof mkr
* @static
* @description Updates the matched CSSRules with the provided styles
* @param {String} selector - A selector string, that the rule should target
* @param {Object} styles - The styles to set of the matched rules
* @requires {@link https://greensock.com/cssruleplugin CSSRulePlugin}
**/
mkr.setRule = function(selector, styles) {
var rule = CSSRulePlugin.getRule(selector);
if(rule) {
TweenMax.set(rule, {cssRule:styles});
}
};
/**
* @function merge
* @memberof mkr
* @static
* @description Merges one object into another, optionally overwriting overlapping keys. This utility method should only be used on objects holding primitive values!
* @param {Object} src - the source object
* @param {Object} merger - The styles to set of the matched rules
* @param {Boolean} [overwrite=false] - Whether to overwrite overlapping values
* @returns {Object} The resulting merge
**/
mkr.merge = function(src, merger, overwrite) {
overwrite = mkr.default(overwrite, false);
var res = mkr.copy(src);
for(var key in merger) {
if(src[key] !== undefined) {//if key is defined
if(typeof src[key] === 'object') {//if both values are objects
if(typeof merger[key] === 'object')
res[key] = mkr.merge(src[key], mkr.copy(merger[key]), overwrite);//set value to their merge
else if(overwrite) res[key] = merger[key]
}
else if(!overwrite) {continue;}
else res[key] = merger[key];
}
else res[key] = merger[key];
}
return res;
};
/**
* @function copy
* @memberof mkr
* @static
* @description Copies an object. This utility method should only be used on objects holding primitive values!
* @param {Object} src - The object to copy
* @returns {Object} The copy
**/
mkr.copy = function(src) {
return JSON.parse(JSON.stringify(src));
};
mkr._units = /(\-?\d+(\.\d+)?)([A-z%]*)/gi;
/**
* @function unit
* @memberof mkr
* @static
* @description Evaluates the provided value and returns the unit suffix it implements
* @param {*} value - The value to evaluate
* @returns {String} The unit suffix
**/
mkr.unit = function(value) {
return String(value).replace(mkr._units, '$3');
};
/**
* @function unitless
* @memberof mkr
* @static
* @description Evaluates the provided value and returns a number without a unit suffix
* @param {*} value - The value to evaluate
* @returns {Number} The numerical value without the unit suffix
**/
mkr.unitless = function(value) {
return Number(String(value).replace(mkr._units, '$1'));
};
/**
* @function unitize
* @memberof mkr
* @static
* @description Evaluates the provided value and returns a string appended with the unit suffix
* @param {*} value - The value to evaluate
* @param {String} [unit='px'] - The unit suffix to append
* @param {Boolean} [override=true] - Whether to replace an existing unit suffix
* @returns {String} The provided value appended with the unit suffix
**/
mkr.unitize = function(value, unit, override) {
unit = mkr.default(unit, 'px');
override = mkr.default(override, true);
if(override) return String(value).replace(mkr._units, '$1'+unit);
var parts = String(value).replace(mkr._units, '$1 $3').split(' ');
unit = (parts[1] && parts[1].length > 0) ? parts[1] : unit;
return parts[0]+unit;
};
/**
* @function setPolyPath
* @memberof mkr
* @static
* @description Shortcut method for dynamically setting the target's clipPath using the polygon func. Useful for animating clipath arrays
* @param {Element} target - Target for clipPath
* @param {Array} points - Array of points to be used as the clipPath
* @param {unit} [unit=%] - The unit of measure to be used in the clipPath
**/
mkr.setPolyClipPath = function(target, points, unit) {
if([null, undefined].indexOf(target) < 0 || [null, undefined].indexOf(points) < 0)
return;
unit = unit === undefined ? '%' : unit;
var clipPath = 'polygon(';
var len = points.length;
for(var i = 0; i < len; i++) {
clipPath += points[i]+unit;
if(i < len-1) {
clipPath += i%2==0?' ':',';
}
}
clipPath += ')';
TweenMax.set(target, {clipPath:clipPath});
};
/**
* @function distance
* @memberof mkr
* @static
* @description Calculates the distance between two points
* @param {Number} x1 - The x-coordinate of the first point
* @param {Number} y1 - The y-coordinate of the first point
* @param {Number} x2 - The x-coordinate of the second point
* @param {Number} y2 - The y-coordinate of the second point
* @returns {Number} The distance between the two points
*
**/
mkr.distance = function(x1, y1, x2, y2) {
return Math.sqrt(Math.pow(x2-x1, 2) + Math.pow(y2-y1, 2));
};
/**
* @function rotation
* @memberof mkr
* @static
* @description Calculates the angle(in radians) between two points
* @param {Number} x1 - The x-coordinate of the first point
* @param {Number} y1 - The y-coordinate of the first point
* @param {Number} x2 - The x-coordinate of the second point
* @param {Number} y2 - The y-coordinate of the second point
* @returns {Number} The angle(in radians) between the two points
*
**/
mkr.rotation = function(x1, y1, x2, y2) {
var dx = x2-x1;
var dy = y2-y1;
return Math.atan2(dy, dx);
};
/**
* @function angle
* @memberof mkr
* @static
* @description Calculates the angle(in degrees) between two points
* @param {Number} x1 - The x-coordinate of the first point
* @param {Number} y1 - The y-coordinate of the first point
* @param {Number} x2 - The x-coordinate of the second point
* @param {Number} y2 - The y-coordinate of the second point
* @returns {Number} The angle(in degrees) between the two points
*
**/
mkr.angle = function(x1, y1, x2, y2) {
var ang = mkr.rotation(x1, y1, x2, y2)*mkr.DEG;
return ang;
};
/**
* @function fix
* @memberof mkr
* @static
* @description Formats a number to the provided number of decimal places
* @param {Number} n - The number to fix
* @param {Number} [decimals=3] - The number of decimal places
* @returns {int} An array of drawing commands.
**/
mkr.fix = function(n, decimals) {
decimals = mkr.default(decimals, 3);
return Number(n.toFixed(decimals));
};
/**
* @function randomRange
* @memberof mkr
* @static
* @description returns a random number between the supplied min and max values
* @param {Number} min - The minimum value
* @param {Number} max - The maximum value
* @returns {Number} A random number between the supplied min and max values
*
**/
mkr.randomRange = function(min, max) {
return Math.floor(Math.random() * (max - min + 0.99)) + min;
};
/**
* @alias mkr.RAD
* @memberof mkr
* @static
* @type Number
* @description Mathematical constant for converting degrees to radians
**/
mkr.RAD = Math.PI/180;
/**
* @alias mkr.DEG
* @memberof mkr
* @static
* @type Number
* @description Mathematical constant for converting radians to degrees
**/
mkr.DEG = 180/Math.PI;
/**
* @name mkr.styles
* @memberof mkr
* @static
* @readonly
* @type StyleSheet
* @description mkr's dynamic stylesheet. Used by mkr.addRule
**/
Object.defineProperty(mkr, 'styles', {
get: function() {
return mkr._styles;
}
});
mkr._styles = createStyleSheet();
/**
* @name mkr.rules
* @memberof mkr
* @static
* @readonly
* @type CSSRuleList
* @description mkr's dynamic stylesheet rules.
**/
Object.defineProperty(mkr, 'rules', {
get: function() {
return mkr.styles.cssRules;
}
});
/**
* @name mkr.defaults
* @memberof mkr
* @static
* @readonly
* @type Object
* @property {Boolean} [force3d=true] - force3d true for all elements by default, Smooths transforms.
* @property {Object} [attr={}] - attributes applied to all created elements
* @property {Object} [css={}] - styles applied to all created elements
* @property {String} [css.position='absolute'] - All elements created my mkr are positioned absolutely by default
* @description Default css and attributes applied to HTMLElements and SVGs. Set keys to change defaults settings for created elements
**/
Object.defineProperty(mkr, 'defaults', {
get: function() {
return mkr._defaults;
}
});
mkr._defaults = {
force3d:true,
css:{position:'absolute'},
attr:{}
};
(function(className, scope) {
var SignalManager = function (options) {
var _signals = {};
this.register = function (signalId) {
if(!(signalId in _signals)) {
_signals[signalId] = new signals.Signal();
}
};
this.add = function (signalId, listener, context, priority, isOnce) {
if(!(signalId in _signals)) {
_signals[signalId] = new signals.Signal();
}
var signal = _signals[signalId];
context = context || null;
priority = priority || 0;
isOnce = isOnce === undefined
return signal.add(listener, context, priority);
};
this.addOnce = function (signalId, listener, context, priority) {
if(!(signalId in _signals)) {
_signals[signalId] = new signals.Signal();
}
var signal = _signals[signalId];
context = context || null;
priority = priority || 0;
return signal.addOnce(listener, context, priority);
};
this.dispatch = function (signalId, params) {
if(!(signalId in _signals)) {
return;
}
_signals[signalId].dispatch.apply(null, Array.prototype.slice.call(arguments, 1));
};
this.dispose = function (signalId) {
if(!(signalId in _signals)) {
return;
}
_signals[signalId].dispose();
delete _signals[signalId];
};
this.forget = function (signalId) {
if(!(signalId in _signals)) {
return;
}
_signals[signalId].forget();
};
this.getNumListeners = function (signalId) {
if(!(signalId in _signals)) {
return 0;
}
return _signals[signalId].getNumListeners();
};
this.halt = function (signalId) {
if(!(signalId in _signals)) {
return;
}
_signals[signalId].halt();
};
this.has = function (signalId, listener, context) {
if(!(signalId in _signals)) {
return false;
}
context = context || null;
return _signals[signalId].has(listener, context);
};
this.remove = function (signalId, listener, context) {
if(!(signalId in _signals)) {
return;
}
context = context || null;
_signals[signalId].remove(listener, context);
};
this.removeAll = function (signalId) {
if(!(signalId in _signals)) {
return;
}
_signals[signalId].removeAll();
};
this.destroy = function () {
for(sigId in _signals) {
_signals[sigId].dispose();
delete _signals[sigId];
}
};
};
scope[className] = SignalManager;
return SignalManager;
})('SignalManager', mkr);
(function(global, className){
var _instances={}, _count=-1, _uid=-1;
var map = function() {
Object.defineProperty(this, '_id', {
enumerable: false,
value: ++_count
});
this._dict = {};
this._keys = {};
_instances[this._id] = this;
};
map.prototype = {
get: function(key) {
if(typeof key === 'string') {
return this._dict[key];
}
if(key._mkrmapid === undefined) {
return undefined;
}
return this._dict[key._mkrmapid];
},
set: function(key, value) {
if(typeof key === 'string') {
this._dict[key] = value;
}
else {
if(key._mkrmapid === undefined) {
Object.defineProperty(key, '_mkrmapid', {
enumerable: false,
value: ++_uid,
configurable: true
});
}
this._keys[key._mkrmapid] = key;
this._dict[key._mkrmapid] = value;
}
return this;
},
has: function(key) {
if(typeof key === 'string') {
return (key in this._dict);
}
if(key._mkrmapid === undefined) {
return false;
}
return (key._mkrmapid in this._dict);
},
delete: function(key) {
var flag = this.has(key);
if(typeof key === 'string') {
delete this._dict[key];
}
if(key._mkrmapid === undefined) {
return false;
}
delete this._keys[key._mkrmapid];
delete this._dict[key._mkrmapid];
delete key._mkrmapid;
return flag;
},
clear: function() {
for(var key in this._dict) {
if(key in this._keys) {
this.delete(this._keys[key]);
continue;
}
this.delete(key);
}
},
forEach: function(callback, context) {
for(var key in this._dict) {
var target = (key in this._keys) ? this._keys[key] : key;
callback.apply(context, [this._dict[key], target, this]);
}
}
}
global[className] = map;
return map;
})(mkr, 'map');
(function(className, scope) {
var matrix = function(options) {
this._mngrs = new mkr.map();
//add a listener
this.add = function(target, type, listener, context, priority, isOnce) {
isOnce = isOnce === undefined ? false : isOnce;
if(!this._mngrs.has(target)){
this._mngrs.set(target, {signals:new scope.SignalManager(), triggers:{}});
}
var mngr = this._mngrs.get(target);
var trigger;
if(mngr.triggers.hasOwnProperty(type)) { //clear existing trigger
trigger = mngr.triggers[type];
target.removeEventListener(type, trigger);
delete mngr.triggers[type];
}
if(isOnce) {
trigger = function(e) {
mngr.signals.dispatch(type, e);
if(mngr.signals.getNumListeners(type) == 0) {
target.removeEventListener(type, trigger);
delete mngr.triggers[type];
}
};
mngr.signals.addOnce(type, listener, context, priority);
}
else {
trigger = function(e){mngr.signals.dispatch(type, e)};
mngr.signals.add(type, listener, context, priority);
}
target.addEventListener(type, trigger);
mngr.triggers[type] = trigger;
};
//add a listener that removes itself after te 1st trigger
this.addOnce = function(target, type, listener, context, priority) {
this.add(target, type, listener, context, priority, true);
};
//remove a single listener
this.remove = function(target, type, listener, context) {
if(!this._mngrs.has(target)) { //no listeners for target exist
return;
}
var mngr = this._mngrs.get(target);
if(!mngr.triggers.hasOwnProperty(type)) { //no listeners of type
return;
}
if(!mngr.signals.has(type, listener, context)) { //not here to remove
return;
}
mngr.signals.remove(type, listener, context);
//remove trigger if no more listeners are attached
if(mngr.signals.getNumListeners(type) == 0) {
var trigger = mngr.triggers[type];
target.removeEventListener(type, trigger);
delete mngr.triggers[type];
}
}
//remove all listeners on a target of the specified type
this.removeAll = function(target, type) {
if(!this._mngrs.has(target)) { //no listeners for target exist
return;
}
var mngr = this._mngrs.get(target);
if(!mngr.triggers.hasOwnProperty(type)) { //no listeners of type
return;
}
mngr.signals.removeAll(type);
var trigger = mngr.triggers[type];
target.removeEventListener(type, trigger);
delete mngr.triggers[type];
}
//remove all listeners for a target
this.delete = function(target) {
if(!this._mngrs.has(target)) { //no listeners for target exist
return;
}
var mngr = this._mngrs.get(target);
for(var type in mngr.triggers) {
var trigger = mngr.triggers[type];
target.removeEventListener(type, trigger);
delete mngr.triggers[type];
}
mngr.signals.destroy();
this._mngrs.delete(target);
}
//clear the entire matrix of listeners
this.clear = function() {
var self = this;
this._mngrs.forEach(function(value, target, map) {
self.delete(target);
});
}
};
scope[className] = matrix;
return matrix;
})('TriggerMatrix', mkr);
/**
* @alias mkr._triggerMatrix
* @memberof mkr
* @private
* @static
* @type TriggerMatrix
* @description mkr's internal event trigger matrix
**/
mkr._triggerMatrix = new mkr.TriggerMatrix();
/**
* @alias mkr.VERSION
* @memberof mkr
* @static
* @readonly
* @type String
* @description returns mkr's version number
**/
Object.defineProperty(mkr, 'VERSION', {
get: function() {
return '0.6.1';
}
});
if(typeof define === 'function' && define.amd){ //AMD
define(function () { return mkr; });
} else if (typeof module !== 'undefined' && module.exports){ //node
module.exports = mkr;
} else { //browser
global[className] = mkr;
}
})(this, 'mkr');
/*!JS Signals <http://millermedeiros.github.com/js-signals/> @license Released under the MIT license Author: Miller MedeirosVersion: 1.0.0 - Build: 268 (2012/11/29 05:48 PM)*/
(function(i){function h(a,b,c,d,e){this._listener=b;this._isOnce=c;this.context=d;this._signal=a;this._priority=e||0}function g(a,b){if(typeof a!=="function")throw Error("listener is a required param of {fn}() and should be a Function.".replace("{fn}",b));}function e(){this._bindings=[];this._prevParams=null;var a=this;this.dispatch=function(){e.prototype.dispatch.apply(a,arguments)}}h.prototype={active:!0,params:null,execute:function(a){var b;this.active&&this._listener&&(a=this.params?this.params.concat(a):
a,b=this._listener.apply(this.context,a),this._isOnce&&this.detach());return b},detach:function(){return this.isBound()?this._signal.remove(this._listener,this.context):null},isBound:function(){return!!this._signal&&!!this._listener},isOnce:function(){return this._isOnce},getListener:function(){return this._listener},getSignal:function(){return this._signal},_destroy:function(){delete this._signal;delete this._listener;delete this.context},toString:function(){return"[SignalBinding isOnce:"+this._isOnce+
", isBound:"+this.isBound()+", active:"+this.active+"]"}};e.prototype={VERSION:"1.0.0",memorize:!1,_shouldPropagate:!0,active:!0,_registerListener:function(a,b,c,d){var e=this._indexOfListener(a,c);if(e!==-1){if(a=this._bindings[e],a.isOnce()!==b)throw Error("You cannot add"+(b?"":"Once")+"() then add"+(!b?"":"Once")+"() the same listener without removing the relationship first.");}else a=new h(this,a,b,c,d),this._addBinding(a);this.memorize&&this._prevParams&&a.execute(this._prevParams);return a},
_addBinding:function(a){var b=this._bindings.length;do--b;while(this._bindings[b]&&a._priority<=this._bindings[b]._priority);this._bindings.splice(b+1,0,a)},_indexOfListener:function(a,b){for(var c=this._bindings.length,d;c--;)if(d=this._bindings[c],d._listener===a&&d.context===b)return c;return-1},has:function(a,b){return this._indexOfListener(a,b)!==-1},add:function(a,b,c){g(a,"add");return this._registerListener(a,!1,b,c)},addOnce:function(a,b,c){g(a,"addOnce");return this._registerListener(a,
!0,b,c)},remove:function(a,b){g(a,"remove");var c=this._indexOfListener(a,b);c!==-1&&(this._bindings[c]._destroy(),this._bindings.splice(c,1));return a},removeAll:function(){for(var a=this._bindings.length;a--;)this._bindings[a]._destroy();this._bindings.length=0},getNumListeners:function(){return this._bindings.length},halt:function(){this._shouldPropagate=!1},dispatch:function(a){if(this.active){var b=Array.prototype.slice.call(arguments),c=this._bindings.length,d;if(this.memorize)this._prevParams=
b;if(c){d=this._bindings.slice();this._shouldPropagate=!0;do c--;while(d[c]&&this._shouldPropagate&&d[c].execute(b)!==!1)}}},forget:function(){this._prevParams=null},dispose:function(){this.removeAll();delete this._bindings;delete this._prevParams},toString:function(){return"[Signal active:"+this.active+" numListeners:"+this.getNumListeners()+"]"}};var f=e;f.Signal=e;typeof define==="function"&&define.amd?define(function(){return f}):typeof module!=="undefined"&&module.exports?module.exports=f:i.signals=
f})(this);