sort-media-queries
Version:
Sort media queries.
253 lines (227 loc) • 5.41 kB
JavaScript
var extend = require('xtend');
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 [];
};