(function($) {// v5 namespace var MK = { api : {}, ui : {}, component : {}, }; // Global window.MK = MK; // http://alistapart.com/article/container-queries-once-more-unto-the-breach window.elementQuery = (function() { // implementations for testing actual element query properties var queryMatchers = { "min-width": function(element, value, units) { var el = element; var px = convertToPx(el, value, units); return value && el && el.offsetWidth >= px; }, "max-width": function(element, value, units) { var el = element; var px = convertToPx(el, value, units); return value && el && el.offsetWidth < px; } }; // convert an element query into a css class name we can replace it with var classNameForRules = function(rules) { var name = "query"; for (var i = 0, len = rules.length; i < len; i++) { name += "_" + rules[i].property + "_" + rules[i].value + rules[i].units; } return name; }; // determine the px value for a measurement (e.g. "5em") on a given element var convertToPx = function(element, value, units) { 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) } }; // determine the size of an em in a given element var getEmSize = function(element) { if (!element) { element = document.documentElement; } if (window.getComputedStyle) { return parseFloat(getComputedStyle(element).fontSize) || 16; } // TODO: support IE? return 16; }; // test whether an element matches a set of query rules var elementMatchesRules = function(element, rules) { for (var i = rules.length - 1; i > -1; i--) { var rule = rules[i]; var matcher = queryMatchers[rule.property]; if (matcher && !matcher(element, rule.value, rule.units)) { return false; } } return true; }; var loader = { // parse an array of CSSStyleSheet objects for element queries loadStyleSheets: function(sheets, callback) { var completed = 0; for (var i = 0, len = sheets.length; i < len; i++) { // if( (sheets[i].href && sheets[i].href.indexOf('media.min.css') != -1) || (sheets[i].href && sheets[i].href.indexOf('media.css') != -1) ) { this.loadStyleSheet(sheets[i], function() { completed += 1; if (completed === len) { callback && callback(); } }); // } } }, // parse a single CSSStyleSheet object for element queries loadStyleSheet: function(sheet, callback) { // if (sheet.ownerNode.nodeName === "STYLE") { // if(sheet.ownerNode.id !== 'js-media-query') return; // var result = elementQuery.parser.parseStyleText(sheet.ownerNode.innerHTML); // sheet.ownerNode.innerHTML += result.newCss; // elementQuery.queries = elementQuery.queries.concat(result.queries); // callback && callback(); // } // else if (sheet.href) { if (sheet.href && sheet.ownerNode.id === 'js-media-query-css') { var xhr = new XMLHttpRequest(); xhr.open("GET", sheet.href, true); xhr.onreadystatechange = function() { if (xhr.readyState === 4) { if (xhr.status === 200) { var result = elementQuery.parser.parseStyleText(xhr.responseText); elementQuery.queries = elementQuery.queries.concat(result.queries); var style = document.createElement("style"); style.innerHTML = result.newCss; document.body.appendChild(style); } else if (window.console) { console.log("Could not load stylesheet at " + sheet.href); } callback && callback(); } } xhr.send(null); } }, }; // public API var elementQuery = { autoInit: true, init: function() { var evaluated = false; this.loader.loadStyleSheets(document.styleSheets, function() { evaluated = true; elementQuery.evaluateQueries(); }); // if we are still waiting for some asynchronous ones, go ahead and evaluate // any found queries now for minimum latency if (!evaluated) { elementQuery.evaluateQueries(); } }, // update the styling for all the elements that have queries evaluateQueries: function(context) { context = context || document; var queries = this.queries; for (var i = 0, len = queries.length; i < len; i++) { var elements = context.querySelectorAll(queries[i].selector); for (var j = 0; j < elements.length; j++) { var element = elements[j]; if (elementMatchesRules(element, queries[i].rules)) { element.classList.add(queries[i].className); } else { element.classList.remove(queries[i].className); } } } }, queryMatchers: queryMatchers, queries: [], classNameForRules: classNameForRules, loader: loader }; // re-run all queries on resize window.addEventListener("resize", function() { elementQuery.evaluateQueries(); }, false); // automatically look for things on window load window.addEventListener("load", function() { if (elementQuery.autoInit) { elementQuery.init(); setTimeout(function() { elementQuery.evaluateQueries(); }, 1000); } }); // TODO: re-run all queries... on an interval? // override setTimeout, addEventListener, etc to hit every possible JS entry // point? Not really an ideal solution to this. // Repaint events in Mozilla? return elementQuery; }()); (function(elementQuery) { // Identifies comments in CSS var COMMENT_PATTERN = /(\/\*)[\s\S]*?(\*\/)/g; // $1 is the end of a block ("}") // $2 is all of a simple @rule ("@something ...;") // $3 is the start of a rule with a block, excluding the opening { // this could be an @media rule or a simple style rule. var STATEMENT_END_OR_START_PATTERN = /\s*(?:(\})|(@\S+\s+[^;{]+;)|(?:([^{}]+)\{))/g; // element queries look like: // `:media(property: value)` or `:media((property: value) and (property: value))` var QUERY_PATTERN = /:media\s*\(([^)]*)\)/g; var QUERY_RULES_PATTERN = /\(?([^\s:]+):\s*(\d+(?:\.\d+)?)(px|em|rem|vw|vh|vmin|vmax)\)?/g; var WHITESPACE_PATTERN = /^\s*$/; // Parse CSS content for element queries elementQuery.parser = { /** * This the main entry point for parsing some CSS. Pass it a string of * CSS content and you'll get back an object like: * { * queries: [array of query objects] * newCss: [string of new CSS] * } * The `newCss` property contains new CSS rules to use in place of the * rules that have element queries -- they replace the queries in selectors * with classes you can turn on and off on the relevant elements. You'll * want to insert the new CSS content into a '; return parent.insertBefore(p.lastChild, parent.firstChild); } /** * Returns the value of `html5.elements` as an array. * @private * @returns {Array} An array of shived element node names. */ function getElements() { var elements = html5.elements; return typeof elements == 'string' ? elements.split(' ') : elements; } /** * Extends the built-in list of html5 elements * @memberOf html5 * @param {String|Array} newElements whitespace separated list or array of new element names to shiv * @param {Document} ownerDocument The context document. */ function addElements(newElements, ownerDocument) { var elements = html5.elements; if(typeof elements != 'string'){ elements = elements.join(' '); } if(typeof newElements != 'string'){ newElements = newElements.join(' '); } html5.elements = elements +' '+ newElements; shivDocument(ownerDocument); } /** * Returns the data associated to the given document * @private * @param {Document} ownerDocument The document. * @returns {Object} An object of data. */ function getExpandoData(ownerDocument) { var data = expandoData[ownerDocument[expando]]; if (!data) { data = {}; expanID++; ownerDocument[expando] = expanID; expandoData[expanID] = data; } return data; } /** * returns a shived element for the given nodeName and document * @memberOf html5 * @param {String} nodeName name of the element * @param {Document|DocumentFragment} ownerDocument The context document. * @returns {Object} The shived element. */ function createElement(nodeName, ownerDocument, data){ if (!ownerDocument) { ownerDocument = document; } if(supportsUnknownElements){ return ownerDocument.createElement(nodeName); } if (!data) { data = getExpandoData(ownerDocument); } var node; if (data.cache[nodeName]) { node = data.cache[nodeName].cloneNode(); } else if (saveClones.test(nodeName)) { node = (data.cache[nodeName] = data.createElem(nodeName)).cloneNode(); } else { node = data.createElem(nodeName); } // Avoid adding some elements to fragments in IE < 9 because // * Attributes like `name` or `type` cannot be set/changed once an element // is inserted into a document/fragment // * Link elements with `src` attributes that are inaccessible, as with // a 403 response, will cause the tab/window to crash // * Script elements appended to fragments will execute when their `src` // or `text` property is set return node.canHaveChildren && !reSkip.test(nodeName) && !node.tagUrn ? data.frag.appendChild(node) : node; } /** * returns a shived DocumentFragment for the given document * @memberOf html5 * @param {Document} ownerDocument The context document. * @returns {Object} The shived DocumentFragment. */ function createDocumentFragment(ownerDocument, data){ if (!ownerDocument) { ownerDocument = document; } if(supportsUnknownElements){ return ownerDocument.createDocumentFragment(); } data = data || getExpandoData(ownerDocument); var clone = data.frag.cloneNode(), i = 0, elems = getElements(), l = elems.length; for(;i