kist-dochopper
Version:
Move elements on page depending on media query.
682 lines (568 loc) • 16.9 kB
JavaScript
/*! kist-dochopper 0.3.2 - Move elements on page depending on media query. | Author: Ivan Nikolić <niksy5@gmail.com> (http://ivannikolic.com/), 2015 | License: MIT */
!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self);var n=f;n=n.jQuery||(n.jQuery={}),n=n.fn||(n.fn={}),n.dochopper=e()}}(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
(function (global){
var $ = (typeof window !== "undefined" ? window.$ : typeof global !== "undefined" ? global.$ : null);
var smq = require(10);
var toarray = require(12);
var dom = require(2);
var events = require(3);
var instance = require(6);
var meta = require(7);
var hopOnCondition = require(4);
var emit = require(8)(meta.name);
var jQuery = $;
/**
* @this {Dochopper}
*
* @param {String|Function|jQuery} into
*
* @return {jQuery}
*/
function getIntoElement ( into ) {
var el;
if ( typeof(into) === 'function' ) {
into = into.call(this.element, this.$el);
}
if ( typeof(into) === 'string' ) {
el = '[data-' + this.options.hopFromDataProp + '="' + into + '"]';
}
if ( into instanceof jQuery ) {
el = into;
}
return $(el);
}
/**
* Get conditions from DOM attribute
*
* @return {Array}
*/
function getDomConditions () {
var data = this.$el.data(this.options.hopConditionsDataProp);
return data ? toarray(data) : [];
}
function setConditions () {
this.conditions = this.conditions || [];
this.conditions = this.conditions.concat(getDomConditions.call(this), toarray(this.options.conditions));
this.conditions = smq(this.conditions, 'media');
$.each(this.conditions, $.proxy(function ( index, condition ) {
condition.get = {};
condition.get.media = window.matchMedia(condition.media);
condition.get.into = getIntoElement.call(this, condition.into);
condition.get.listener = $.proxy(hopOnCondition, this, condition.get.into, false);
}, this));
}
/**
* @class
*
* @param {Element} element
* @param {Object} options
*/
function Dochopper ( element, options ) {
this.element = element;
this.options = $.extend(true, {}, this.defaults, options);
this.queueActive = [];
this.queue = [];
instance.setup.call(this);
dom.setup.call(this);
setConditions.call(this);
events.setupInitial.call(this);
events.setupListeners.call(this);
}
$.extend(Dochopper.prototype, {
/**
* @param {jQuery} into
* @param {Object} media
*
* @return {}
*/
hop: function ( into, media ) {
this.$content.detach().appendTo(into);
if ( !this.queue.length ) {
emit(this, 'hop', [into, media]);
}
},
destroy: function () {
dom.destroy.call(this);
events.destroy.call(this);
instance.destroy.call(this);
},
rehop: function () {
var set = [];
$.each(this.conditions, $.proxy(function ( index, condition ) {
if ( condition.get.media.matches ) {
var el = getIntoElement.call(this, condition.into);
// Do this only if new element is not the same as the first cached element
if ( !condition.get.into.is(el) ) {
condition.get.into = el;
condition.get.media.removeListener(condition.get.listener);
condition.get.listener = $.proxy(hopOnCondition, this, condition.get.into, false);
condition.get.media.addListener(condition.get.listener);
set.push([condition.get.into, condition.get.media]);
}
}
}, this));
if ( set.length ) {
this.hop.apply(this, set[set.length - 1]);
}
},
defaults: {
conditions: [],
hop: $.noop,
hopFromDataProp: 'hop-from',
hopConditionsDataProp: 'hop-conditions',
classes: {
item: meta.ns.htmlClass + '-item',
isReady: 'is-ready'
}
}
});
module.exports = Dochopper;
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{}],2:[function(require,module,exports){
(function (global){
var $ = (typeof window !== "undefined" ? window.$ : typeof global !== "undefined" ? global.$ : null);
module.exports = {
setup: function () {
this.$el = $(this.element);
this.$content = this.$el.contents();
},
destroy: function () {
var set = $();
set = set.add(this.$el);
$.each(this.conditions, function ( index, condition ) {
set = set.add(condition.get.into);
});
set.removeClass([this.options.classes.item, this.options.classes.isReady].join(' '));
}
};
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{}],3:[function(require,module,exports){
(function (global){
var $ = (typeof window !== "undefined" ? window.$ : typeof global !== "undefined" ? global.$ : null);
var meta = require(7);
var hopOnCondition = require(4);
var emit = require(8)(meta.name);
module.exports = {
setupInitial: function () {
this.$el.one('init' + this.ens, $.proxy(function ( e, queue ) {
var element = $();
var media = [];
var count = 0;
$.each(queue, function ( index, item ) {
if ( $.isEmptyObject(item) ) {
count++;
return;
}
element = element.add(item.into);
media.push(item.media);
});
// Trigger hop only if we have at least one condition active
if ( count !== queue.length ) {
emit(this, 'hop', [element, media]);
}
while ( queue.length > 0 ) {
queue.pop();
}
}, this));
},
setupListeners: function () {
$.each(this.conditions, $.proxy(function ( index, condition ) {
condition.get.media.addListener(condition.get.listener);
hopOnCondition.call(this, condition.get.into, true, condition.get.media);
}, this));
this.$el.trigger('init' + this.ens, [this.queue]);
},
destroy: function () {
$.each(this.conditions, function ( index, condition ) {
condition.get.media.removeListener(condition.get.listener);
});
}
};
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{}],4:[function(require,module,exports){
(function (global){
var $ = (typeof window !== "undefined" ? window.$ : typeof global !== "undefined" ? global.$ : null);
/**
* @param {jQuery} into
* @param {Boolean} initial
* @param {Object} condition
*/
var hopOnCondition = module.exports = function ( into, initial, condition ) {
var data = {};
if ( condition.matches ) {
data = {
media: condition,
into: into
};
this.queueActive.push(data);
} else {
if ( !initial ) {
this.queueActive.pop();
if ( this.queueActive.length === 0 ) {
data = {
media: {
matches: true,
media: 'root'
},
into: this.$el
};
} else {
data = this.queueActive[this.queueActive.length - 1];
}
}
}
/**
* Set flag when flowing elements are ready.
* This is so we can avoid FOUC on contexts where content has to flow.
*/
if ( initial ) {
this.queue.push(data);
this.$el.add(into)
.addClass([this.options.classes.item, this.options.classes.isReady].join(' '));
}
if ( !$.isEmptyObject(data) && data.media.matches ) {
this.hop(data.into, data.media);
}
};
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{}],5:[function(require,module,exports){
(function (global){
var $ = (typeof window !== "undefined" ? window.$ : typeof global !== "undefined" ? global.$ : null);
var Ctor = require(1);
var meta = require(7);
var isPublicMethod = require(9)(meta.publicMethods);
/**
* @param {Object|String} options
*
* @return {jQuery}
*/
var plugin = module.exports = function ( options ) {
options = options || {};
return this.each(function () {
var instance = $.data(this, meta.name);
if ( isPublicMethod(options) && instance ) {
instance[options]();
} else if ( $.type(options) === 'object' && !instance ) {
$.data(this, meta.name, new Ctor(this, options));
}
});
};
plugin.defaults = Ctor.prototype.defaults;
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{}],6:[function(require,module,exports){
(function (global){
var $ = (typeof window !== "undefined" ? window.$ : typeof global !== "undefined" ? global.$ : null);
var meta = require(7);
var instance = 0;
module.exports = {
setup: function () {
this.uid = instance++;
this.ens = meta.ns.event + '.' + this.uid;
},
destroy: function () {
$.removeData(this.element, meta.name);
}
};
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{}],7:[function(require,module,exports){
module.exports = {
name: 'dochopper',
ns: {
htmlClass: 'kist-Dochopper',
event: '.kist.dochopper'
},
publicMethods: ['destroy','rehop']
};
},{}],8:[function(require,module,exports){
(function (global){
/* jshint maxparams:false */
var $ = (typeof window !== "undefined" ? window.$ : typeof global !== "undefined" ? global.$ : null);
/**
* @param {String} name
*
* @return {Function}
*/
module.exports = function ( name ) {
/**
* @param {Object} ctx
* @param {String} eventName
* @param {Array} data
* @param {jQuery} triggerEl
*/
return function ( ctx, eventName, data, triggerEl ) {
var el = (ctx.dom && ctx.dom.el) || ctx.$el || $({});
if ( ctx.options[eventName] ) {
ctx.options[eventName].apply((el.length === 1 ? el[0] : el.toArray()), data);
}
(triggerEl || el).trigger(((name || '') + eventName).toLowerCase(), data);
};
};
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{}],9:[function(require,module,exports){
(function (global){
var $ = (typeof window !== "undefined" ? window.$ : typeof global !== "undefined" ? global.$ : null);
/**
* @param {Array} methods
*
* @return {Function}
*/
module.exports = function ( methods ) {
/**
* @param {String} name
*
* @return {Boolean}
*/
return function ( name ) {
return typeof(name) === 'string' && $.inArray(name, methods || []) !== -1;
};
};
}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{}],10:[function(require,module,exports){
var extend = require(11);
var mqTypes = ['blank','all','minWidth','minHeight','maxWidth','maxHeight','print'];
/**
* @param {Array} rules
* @param {String} type
* @param {String} prop
*
* @return {Boolean}
*/
function itemsValid ( rules, type, prop ) {
var flag = true;
for ( var i = 0, rulesLength = rules.length; i < rulesLength; i++ ) {
if ( typeof(rules[i]) !== type || ( prop && !rules[i].hasOwnProperty(prop) ) ) {
flag = false;
break;
}
}
return flag;
}
/**
* @param {Array} rules
* @param {String} type
* @param {String} prop
*
* @return {Boolean}
*/
function allValid ( rules, type, prop ) {
if (
!rules || !rules.length || typeof(rules) === 'string'
) {
return 'none';
}
if (
(type === 'object' && (!prop || typeof(prop) !== 'string')) ||
!itemsValid(rules, type, prop)
) {
return 'some';
}
return 'all';
}
/**
* Normalize between array with strings and array with objects
*
* @param {Array} rules
* @param {String} type
* @param {String} prop
*
* @return {Object}
*/
function prepareRules ( rules, type, prop ) {
var collection = [];
var o = {};
for ( var i = 0, rulesLength = rules.length; i < rulesLength; i++ ) {
if ( type === 'string' ) {
o = extend({}, {
__media: rules[i]
});
} else {
o = extend({}, rules[i]);
o.__media = rules[i][prop];
}
collection.push(o);
}
return collection;
}
/**
* @param {Boolean} isMax
*
* @return {Function}
*/
function determineSortOrder ( isMax ) {
/**
* Determine sort order based on provided arguments
*
* @param {Object} a
* @param {Object} b
*
* @return {Integer}
*/
return function ( a, b ) {
var sortValA = a.sortVal;
var sortValB = b.sortVal;
var ruleA = a.item.__media;
var ruleB = b.item.__media;
isMax = typeof(isMax) !== 'undefined' ? isMax : false;
// Consider print for sorting if sortVals are equal
if ( sortValA === sortValB ) {
if ( ruleA.match(/print/) ) {
// a contains print and should be sorted after b
return 1;
}
if ( ruleB.match(/print/) ) {
// b contains print and should be sorted after a
return -1;
}
}
// Return descending sort order for max-(width|height) media queries
if ( isMax ) {
return sortValB - sortValA;
}
// Return ascending sort order
return sortValA - sortValB;
};
}
/**
* @return {Object}
*/
function createCollection () {
var mqCollection = {};
for ( var i = 0, mqTypesLength = mqTypes.length; i < mqTypesLength; i++ ) {
mqCollection[mqTypes[i]] = [];
}
return mqCollection;
}
/**
* @param {Object} collection
* @param {Array} rules
*
* @return {Object}
*/
function addRulesToCollection ( collection, rules ) {
// Sort media queries by kind, this is needed to output them in the right order
for ( var i = 0, rulesLength = rules.length; i < rulesLength; i++ ) {
var item = rules[i];
var rule = item.__media;
var collectionType = 'blank';
var valMatch = rule.match(/\d+/g);
if ( rule.match(/min-width/) ) {
collectionType = 'minWidth';
} else if ( rule.match(/min-height/) ) {
collectionType = 'minHeight';
} else if ( rule.match(/max-width/) ) {
collectionType = 'maxWidth';
} else if ( rule.match(/max-height/) ) {
collectionType = 'maxHeight';
} else if ( rule.match(/print/) ) {
collectionType = 'print';
} else if ( rule.match(/all/) ) {
collectionType = 'all';
}
collection[collectionType].push({
item: item,
sortVal: valMatch ? valMatch[0] : 0
});
}
return collection;
}
/**
* @param {Object} collection
*
* @return {Object}
*/
function sortCollection ( collection ) {
var sorter;
for ( var collectionType in collection ) {
if ( collection.hasOwnProperty(collectionType) ) {
sorter = determineSortOrder(false);
if ( collectionType === 'maxWidth' || collectionType === 'maxHeight' ) {
sorter = determineSortOrder(true);
}
collection[collectionType].sort(sorter);
}
}
return collection;
}
/**
* @param {Object} collection
* @param {String} type
* @param {String} prop
*
* @return {Array}
*/
function transformCollection ( collection, type, prop ) {
var transformed = [];
var collectionItem;
function iterateCollectionItem ( collectionItem ) {
var resultVal;
for ( var i = 0, typeLength = collectionItem.length; i < typeLength; i++ ) {
if ( type === 'string' ) {
resultVal = collectionItem[i].item.__media;
} else {
resultVal = collectionItem[i].item;
delete resultVal.__media;
}
transformed.push(resultVal);
}
}
for ( var i = 0, mqTypesLength = mqTypes.length; i < mqTypesLength; i++ ) {
iterateCollectionItem(collection[mqTypes[i]]);
}
return transformed;
}
/**
* @param {Array} rules
* @param {String} type
* @param {String} prop
*
* @return {Array}
*/
function sortInit ( rules, type, prop ) {
switch ( allValid(rules, type, prop) ) {
case 'none':
return [];
case 'some':
return rules;
}
var collection = createCollection();
rules = prepareRules(rules, type, prop);
addRulesToCollection(collection, rules);
sortCollection(collection);
return transformCollection(collection, type, prop);
}
/**
* @param {Array} rules
* @param {String} prop
*
* @return {Array}
*/
module.exports = function ( rules, prop ) {
if ( rules ) {
if ( prop ) {
return sortInit(rules, 'object', prop);
}
return sortInit(rules, 'string');
}
return [];
};
},{}],11:[function(require,module,exports){
module.exports = extend
function extend() {
var target = {}
for (var i = 0; i < arguments.length; i++) {
var source = arguments[i]
for (var key in source) {
if (source.hasOwnProperty(key)) {
target[key] = source[key]
}
}
}
return target
}
},{}],12:[function(require,module,exports){
module.exports = function(item) {
if(item === undefined) return [];
return Object.prototype.toString.call(item) === "[object Array]" ? item : [item];
}
},{}]},{},[5])(5)
});