/** * Copyright Marc J. Schmidt. See the LICENSE file at the top-level * directory of this distribution and at * https://github.com/marcj/css-element-queries/blob/master/LICENSE. */ //; //(function() { /** * * @type {Function} * @constructor */ var ElementQueries = window.ElementQueries = function() { this.withTracking = false; var elements = []; /** * * @param element * @returns {Number} */ function getEmSize(element) { if (!element) { element = document.documentElement; } var fontSize = getComputedStyle(element, 'fontSize'); return parseFloat(fontSize) || 16; } /** * * @copyright https://github.com/Mr0grog/element-query/blob/master/LICENSE * * @param {HTMLElement} element * @param {*} value * @returns {*} */ function convertToPx(element, value) { var units = value.replace(/[0-9]*/, ''); value = parseFloat(value); switch (units) { case "px": return value; case "em": return value * getEmSize(element); case "rem": return value * getEmSize(); // Viewport units! // According to http://quirksmode.org/mobile/tableViewport.html // documentElement.clientWidth/Height gets us the most reliable info case "vw": return value * document.documentElement.clientWidth / 100; case "vh": return value * document.documentElement.clientHeight / 100; case "vmin": case "vmax": var vw = document.documentElement.clientWidth / 100; var vh = document.documentElement.clientHeight / 100; var chooser = Math[units === "vmin" ? "min" : "max"]; return value * chooser(vw, vh); default: return value; // for now, not supporting physical units (since they are just a set number of px) // or ex/ch (getting accurate measurements is hard) } } /** * * @param {HTMLElement} element * @constructor */ function SetupInformation(element) { this.element = element; this.options = {}; var key, option, width = 0, height = 0, value, actualValue, attrValues, attrValue, attrName; /** * @param {Object} option {mode: 'min|max', property: 'width|height', value: '123px'} */ this.addOption = function(option) { var idx = [option.mode, option.property, option.value].join(','); this.options[idx] = option; }; var attributes = ['min-width', 'min-height', 'max-width', 'max-height']; /** * Extracts the computed width/height and sets to min/max- attribute. */ this.call = function() { // extract current dimensions width = this.element.offsetWidth; height = this.element.offsetHeight; attrValues = {}; for (key in this.options) { if (!this.options.hasOwnProperty(key)){ continue; } option = this.options[key]; value = convertToPx(this.element, option.value); actualValue = option.property == 'width' ? width : height; attrName = option.mode + '-' + option.property; attrValue = ''; if (option.mode == 'min' && actualValue >= value) { attrValue += option.value; } if (option.mode == 'max' && actualValue <= value) { attrValue += option.value; } if (!attrValues[attrName]) attrValues[attrName] = ''; if (attrValue && -1 === (' '+attrValues[attrName]+' ').indexOf(' ' + attrValue + ' ')) { attrValues[attrName] += ' ' + attrValue; } } for (var k in attributes) { if (attrValues[attributes[k]]) { this.element.setAttribute(attributes[k], attrValues[attributes[k]].substr(1)); } else { this.element.removeAttribute(attributes[k]); } } }; } /** * @param {HTMLElement} element * @param {Object} options */ function setupElement(element, options) { if (element.elementQueriesSetupInformation) { element.elementQueriesSetupInformation.addOption(options); } else { element.elementQueriesSetupInformation = new SetupInformation(element); element.elementQueriesSetupInformation.addOption(options); element.elementQueriesSensor = new ResizeSensor(element, function() { element.elementQueriesSetupInformation.call(); }); } element.elementQueriesSetupInformation.call(); if (ElementQueries.instance.withTracking && elements.indexOf(element) < 0) { elements.push(element); } } /** * @param {String} selector * @param {String} mode min|max * @param {String} property width|height * @param {String} value */ var allQueries = {}; function queueQuery(selector, mode, property, value) { if (typeof(allQueries[mode]) == 'undefined') allQueries[mode] = {}; if (typeof(allQueries[mode][property]) == 'undefined') allQueries[mode][property] = {}; if (typeof(allQueries[mode][property][value]) == 'undefined') allQueries[mode][property][value] = selector; else allQueries[mode][property][value] += ','+selector; } function executeQueries() { var query; if (document.querySelectorAll) query = document.querySelectorAll.bind(document); if (!query && 'undefined' !== typeof $$) query = $$; if (!query && 'undefined' !== typeof jQuery) query = jQuery; if (!query) { throw 'No document.querySelectorAll, jQuery or Mootools\'s $$ found.'; } for (var mode in allQueries) if (allQueries.hasOwnProperty(mode)) { for (var property in allQueries[mode]) if (allQueries[mode].hasOwnProperty(property)) { for (var value in allQueries[mode][property]) if (allQueries[mode][property].hasOwnProperty(value)) { var elements = query(allQueries[mode][property][value]); for (var i = 0, j = elements.length; i < j; i++) { setupElement(elements[i], { mode: mode, property: property, value: value }); } } } } } var regex = /,?[\s\t]*([^,\n]*?)((?:\[[\s\t]*?(?:min|max)-(?:width|height)[\s\t]*?[~$\^]?=[\s\t]*?"[^"]*?"[\s\t]*?])+)([^,\n\s\{]*)/mgi; var attrRegex = /\[[\s\t]*?(min|max)-(width|height)[\s\t]*?[~$\^]?=[\s\t]*?"([^"]*?)"[\s\t]*?]/mgi; /** * @param {String} css */ function extractQuery(css) { var match; var smatch; css = css.replace(/'/g, '"'); while (null !== (match = regex.exec(css))) { smatch = match[1] + match[3]; attrs = match[2]; while (null !== (attrMatch = attrRegex.exec(attrs))) { queueQuery(smatch, attrMatch[1], attrMatch[2], attrMatch[3]); } } } /** * @param {CssRule[]|String} rules */ function readRules(rules) { var selector = ''; if (!rules) { return; } if ('string' === typeof rules) { rules = rules.toLowerCase(); if (-1 !== rules.indexOf('min-width') || -1 !== rules.indexOf('max-width')) { extractQuery(rules); } } else { for (var i = 0, j = rules.length; i < j; i++) { if (1 === rules[i].type) { selector = rules[i].selectorText || rules[i].cssText; if (-1 !== selector.indexOf('min-height') || -1 !== selector.indexOf('max-height')) { extractQuery(selector); }else if(-1 !== selector.indexOf('min-width') || -1 !== selector.indexOf('max-width')) { extractQuery(selector); } } else if (4 === rules[i].type) { readRules(rules[i].cssRules || rules[i].rules); } } } } /** * Searches all css rules and setups the event listener to all elements with element query rules.. * * @param {Boolean} withTracking allows and requires you to use detach, since we store internally all used elements * (no garbage collection possible if you don not call .detach() first) */ this.init = function(withTracking) { this.withTracking = withTracking; for (var i = 0, j = document.styleSheets.length; i < j; i++) { try { readRules(document.styleSheets[i].cssRules || document.styleSheets[i].rules || document.styleSheets[i].cssText); } catch(e) { if (e.name !== 'SecurityError') { throw e; } } } executeQueries(); }; /** * * @param {Boolean} withTracking allows and requires you to use detach, since we store internally all used elements * (no garbage collection possible if you don not call .detach() first) */ this.update = function(withTracking) { this.withTracking = withTracking; this.init(); }; this.detach = function() { if (!this.withTracking) { throw 'withTracking is not enabled. We can not detach elements since we don not store it.' + 'Use ElementQueries.withTracking = true; before domready.'; } var element; while (element = elements.pop()) { ElementQueries.detach(element); } elements = []; }; }; /** * * @param {Boolean} withTracking allows and requires you to use detach, since we store internally all used elements * (no garbage collection possible if you don not call .detach() first) */ ElementQueries.update = function(withTracking) { ElementQueries.instance.update(withTracking); }; /** * Removes all sensor and elementquery information from the element. * * @param {HTMLElement} element */ ElementQueries.detach = function(element) { if (element.elementQueriesSetupInformation) { element.elementQueriesSensor.detach(); delete element.elementQueriesSetupInformation; delete element.elementQueriesSensor; console.log('detached'); } else { console.log('detached already', element); } }; ElementQueries.withTracking = false; ElementQueries.init = function() { if (!ElementQueries.instance) { ElementQueries.instance = new ElementQueries(); } ElementQueries.instance.init(ElementQueries.withTracking); }; var domLoaded = function (callback) { /* Internet Explorer */ /*@cc_on @if (@_win32 || @_win64) document.write('