summaryrefslogtreecommitdiffstats
path: root/web/lib/ElementQueries.js
diff options
context:
space:
mode:
Diffstat (limited to 'web/lib/ElementQueries.js')
-rwxr-xr-xweb/lib/ElementQueries.js360
1 files changed, 360 insertions, 0 deletions
diff --git a/web/lib/ElementQueries.js b/web/lib/ElementQueries.js
new file mode 100755
index 00000000..0c0906e3
--- /dev/null
+++ b/web/lib/ElementQueries.js
@@ -0,0 +1,360 @@
+/**
+ * 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('<script id="ieScriptLoad" defer src="//:"><\/script>');
+ document.getElementById('ieScriptLoad').onreadystatechange = function() {
+ if (this.readyState == 'complete') {
+ callback();
+ }
+ };
+ @end @*/
+ /* Mozilla, Chrome, Opera */
+ if (document.addEventListener) {
+ document.addEventListener('DOMContentLoaded', callback, false);
+ }
+ /* Safari, iCab, Konqueror */
+ else if (/KHTML|WebKit|iCab/i.test(navigator.userAgent)) {
+ var DOMLoadTimer = setInterval(function () {
+ if (/loaded|complete/i.test(document.readyState)) {
+ callback();
+ clearInterval(DOMLoadTimer);
+ }
+ }, 10);
+ }
+ /* Other web browsers */
+ else window.onload = callback;
+ };
+
+// if (window.addEventListener) {
+// window.addEventListener('load', ElementQueries.init, false);
+// } else {
+// window.attachEvent('onload', ElementQueries.init);
+// }
+// domLoaded(ElementQueries.init);
+
+//})();