vendor: 20,195 bytes, lines 1-530
1'use strict'; 2 3/** 4 * Copyright Marc J. Schmidt. See the LICENSE file at the top-level 5 * directory of this distribution and at 6 * https://github.com/marcj/css-element-queries/blob/master/LICENSE. 7 */ 8(function (root, factory) { 9 if (typeof define === "function" && define.amd) { 10 define(['./ResizeSensor.js'], factory); 11 } else if (typeof exports === "object") { 12 module.exports = factory(require('./ResizeSensor.js')); 13 } else { 14 root.ElementQueries = factory(root.ResizeSensor); 15 root.ElementQueries.listen(); 16 } 17}(typeof window !== 'undefined' ? window : this, function (ResizeSensor) { 18 19 /** 20 * 21 * @type {Function} 22 * @constructor 23 */ 24 var ElementQueries = function () { 25 //<style> element with our dynamically created styles 26 var cssStyleElement; 27 28 //all rules found for element queries 29 var allQueries = {}; 30 31 //association map to identify which selector belongs to a element from the animationstart event. 32 var idToSelectorMapping = []; 33 34 /** 35 * 36 * @param element 37 * @returns {Number} 38 */ 39 function getEmSize(element) { 40 if (!element) { 41 element = document.documentElement; 42 } 43 var fontSize = window.getComputedStyle(element, null).fontSize; 44 return parseFloat(fontSize) || 16; 45 } 46 47 /** 48 * Get element size 49 * @param {HTMLElement} element 50 * @returns {Object} {width, height} 51 */ 52 function getElementSize(element) { 53 if (!element.getBoundingClientRect) { 54 return { 55 width: element.offsetWidth, 56 height: element.offsetHeight 57 } 58 } 59 60 var rect = element.getBoundingClientRect(); 61 return { 62 width: Math.round(rect.width), 63 height: Math.round(rect.height) 64 } 65 } 66 67 /** 68 * 69 * @copyright https://github.com/Mr0grog/element-query/blob/master/LICENSE 70 * 71 * @param {HTMLElement} element 72 * @param {*} value 73 * @returns {*} 74 */ 75 function convertToPx(element, value) { 76 var numbers = value.split(/\d/); 77 var units = numbers[numbers.length - 1]; 78 value = parseFloat(value); 79 switch (units) { 80 case "px": 81 return value; 82 case "em": 83 return value * getEmSize(element); 84 case "rem": 85 return value * getEmSize(); 86 // Viewport units! 87 // According to http://quirksmode.org/mobile/tableViewport.html 88 // documentElement.clientWidth/Height gets us the most reliable info 89 case "vw": 90 return value * document.documentElement.clientWidth / 100; 91 case "vh": 92 return value * document.documentElement.clientHeight / 100; 93 case "vmin": 94 case "vmax": 95 var vw = document.documentElement.clientWidth / 100; 96 var vh = document.documentElement.clientHeight / 100; 97 var chooser = Math[units === "vmin" ? "min" : "max"]; 98 return value * chooser(vw, vh); 99 default: 100 return value; 101 // for now, not supporting physical units (since they are just a set number of px) 102 // or ex/ch (getting accurate measurements is hard) 103 } 104 } 105 106 /** 107 * 108 * @param {HTMLElement} element 109 * @param {String} id 110 * @constructor 111 */ 112 function SetupInformation(element, id) { 113 this.element = element; 114 var key, option, elementSize, value, actualValue, attrValues, attrValue, attrName; 115 116 var attributes = ['min-width', 'min-height', 'max-width', 'max-height']; 117 118 /** 119 * Extracts the computed width/height and sets to min/max- attribute. 120 */ 121 this.call = function () { 122 // extract current dimensions 123 elementSize = getElementSize(this.element); 124 125 attrValues = {}; 126 127 for (key in allQueries[id]) { 128 if (!allQueries[id].hasOwnProperty(key)) { 129 continue; 130 } 131 option = allQueries[id][key]; 132 133 value = convertToPx(this.element, option.value); 134 135 actualValue = option.property === 'width' ? elementSize.width : elementSize.height; 136 attrName = option.mode + '-' + option.property; 137 attrValue = ''; 138 139 if (option.mode === 'min' && actualValue >= value) { 140 attrValue += option.value; 141 } 142 143 if (option.mode === 'max' && actualValue <= value) { 144 attrValue += option.value; 145 } 146 147 if (!attrValues[attrName]) attrValues[attrName] = ''; 148 if (attrValue && -1 === (' ' + attrValues[attrName] + ' ').indexOf(' ' + attrValue + ' ')) { 149 attrValues[attrName] += ' ' + attrValue; 150 } 151 } 152 153 for (var k in attributes) { 154 if (!attributes.hasOwnProperty(k)) continue; 155 156 if (attrValues[attributes[k]]) { 157 this.element.setAttribute(attributes[k], attrValues[attributes[k]].substr(1)); 158 } else { 159 this.element.removeAttribute(attributes[k]); 160 } 161 } 162 }; 163 } 164 165 /** 166 * @param {HTMLElement} element 167 * @param {Object} id 168 */ 169 function setupElement(element, id) { 170 if (!element.elementQueriesSetupInformation) { 171 element.elementQueriesSetupInformation = new SetupInformation(element, id); 172 } 173 174 if (!element.elementQueriesSensor) { 175 element.elementQueriesSensor = new ResizeSensor(element, function () { 176 element.elementQueriesSetupInformation.call(); 177 }); 178 } 179 } 180 181 /** 182 * Stores rules to the selector that should be applied once resized. 183 * 184 * @param {String} selector 185 * @param {String} mode min|max 186 * @param {String} property width|height 187 * @param {String} value 188 */ 189 function queueQuery(selector, mode, property, value) { 190 if (typeof(allQueries[selector]) === 'undefined') { 191 allQueries[selector] = []; 192 // add animation to trigger animationstart event, so we know exactly when a element appears in the DOM 193 194 var id = idToSelectorMapping.length; 195 cssStyleElement.innerHTML += '\n' + selector + ' {animation: 0.1s element-queries;}'; 196 cssStyleElement.innerHTML += '\n' + selector + ' > .resize-sensor {min-width: '+id+'px;}'; 197 idToSelectorMapping.push(selector); 198 } 199 200 allQueries[selector].push({ 201 mode: mode, 202 property: property, 203 value: value 204 }); 205 } 206 207 function getQuery(container) { 208 var query; 209 if (document.querySelectorAll) query = (container) ? container.querySelectorAll.bind(container) : document.querySelectorAll.bind(document); 210 if (!query && 'undefined' !== typeof $$) query = $$; 211 if (!query && 'undefined' !== typeof jQuery) query = jQuery; 212 213 if (!query) { 214 throw 'No document.querySelectorAll, jQuery or Mootools\'s $$ found.'; 215 } 216 217 return query; 218 } 219 220 /** 221 * If animationStart didn't catch a new element in the DOM, we can manually search for it 222 */ 223 function findElementQueriesElements(container) { 224 var query = getQuery(container); 225 226 for (var selector in allQueries) if (allQueries.hasOwnProperty(selector)) { 227 // find all elements based on the extract query selector from the element query rule 228 var elements = query(selector, container); 229 230 for (var i = 0, j = elements.length; i < j; i++) { 231 setupElement(elements[i], selector); 232 } 233 } 234 } 235 236 /** 237 * 238 * @param {HTMLElement} element 239 */ 240 function attachResponsiveImage(element) { 241 var children = []; 242 var rules = []; 243 var sources = []; 244 var defaultImageId = 0; 245 var lastActiveImage = -1; 246 var loadedImages = []; 247 248 for (var i in element.children) { 249 if (!element.children.hasOwnProperty(i)) continue; 250 251 if (element.children[i].tagName && element.children[i].tagName.toLowerCase() === 'img') { 252 children.push(element.children[i]); 253 254 var minWidth = element.children[i].getAttribute('min-width') || element.children[i].getAttribute('data-min-width'); 255 //var minHeight = element.children[i].getAttribute('min-height') || element.children[i].getAttribute('data-min-height'); 256 var src = element.children[i].getAttribute('data-src') || element.children[i].getAttribute('url'); 257 258 sources.push(src); 259 260 var rule = { 261 minWidth: minWidth 262 }; 263 264 rules.push(rule); 265 266 if (!minWidth) { 267 defaultImageId = children.length - 1; 268 element.children[i].style.display = 'block'; 269 } else { 270 element.children[i].style.display = 'none'; 271 } 272 } 273 } 274 275 lastActiveImage = defaultImageId; 276 277 function check() { 278 var imageToDisplay = false, i; 279 280 for (i in children) { 281 if (!children.hasOwnProperty(i)) continue; 282 283 if (rules[i].minWidth) { 284 if (element.offsetWidth > rules[i].minWidth) { 285 imageToDisplay = i; 286 } 287 } 288 } 289 290 if (!imageToDisplay) { 291 //no rule matched, show default 292 imageToDisplay = defaultImageId; 293 } 294 295 if (lastActiveImage !== imageToDisplay) { 296 //image change 297 298 if (!loadedImages[imageToDisplay]) { 299 //image has not been loaded yet, we need to load the image first in memory to prevent flash of 300 //no content 301 302 var image = new Image(); 303 image.onload = function () { 304 children[imageToDisplay].src = sources[imageToDisplay]; 305 306 children[lastActiveImage].style.display = 'none'; 307 children[imageToDisplay].style.display = 'block'; 308 309 loadedImages[imageToDisplay] = true; 310 311 lastActiveImage = imageToDisplay; 312 }; 313 314 image.src = sources[imageToDisplay]; 315 } else { 316 children[lastActiveImage].style.display = 'none'; 317 children[imageToDisplay].style.display = 'block'; 318 lastActiveImage = imageToDisplay; 319 } 320 } else { 321 //make sure for initial check call the .src is set correctly 322 children[imageToDisplay].src = sources[imageToDisplay]; 323 } 324 } 325 326 element.resizeSensorInstance = new ResizeSensor(element, check); 327 check(); 328 } 329 330 function findResponsiveImages() { 331 var query = getQuery(); 332 333 var elements = query('[data-responsive-image],[responsive-image]'); 334 for (var i = 0, j = elements.length; i < j; i++) { 335 attachResponsiveImage(elements[i]); 336 } 337 } 338 339 var regex = /,?[\s\t]*([^,\n]*?)((?:\[[\s\t]*?(?:min|max)-(?:width|height)[\s\t]*?[~$\^]?=[\s\t]*?"[^"]*?"[\s\t]*?])+)([^,\n\s\{]*)/mgi; 340 var attrRegex = /\[[\s\t]*?(min|max)-(width|height)[\s\t]*?[~$\^]?=[\s\t]*?"([^"]*?)"[\s\t]*?]/mgi; 341 342 /** 343 * @param {String} css 344 */ 345 function extractQuery(css) { 346 var match, smatch, attrs, attrMatch; 347 348 css = css.replace(/'/g, '"'); 349 while (null !== (match = regex.exec(css))) { 350 smatch = match[1] + match[3]; 351 attrs = match[2]; 352 353 while (null !== (attrMatch = attrRegex.exec(attrs))) { 354 queueQuery(smatch, attrMatch[1], attrMatch[2], attrMatch[3]); 355 } 356 } 357 } 358 359 /** 360 * @param {CssRule[]|String} rules 361 */ 362 function readRules(rules) { 363 var selector = ''; 364 365 if (!rules) { 366 return; 367 } 368 369 if ('string' === typeof rules) { 370 rules = rules.toLowerCase(); 371 if (-1 !== rules.indexOf('min-width') || -1 !== rules.indexOf('max-width')) { 372 extractQuery(rules); 373 } 374 } else { 375 for (var i = 0, j = rules.length; i < j; i++) { 376 if (1 === rules[i].type) { 377 selector = rules[i].selectorText || rules[i].cssText; 378 if (-1 !== selector.indexOf('min-height') || -1 !== selector.indexOf('max-height')) { 379 extractQuery(selector); 380 } else if (-1 !== selector.indexOf('min-width') || -1 !== selector.indexOf('max-width')) { 381 extractQuery(selector); 382 } 383 } else if (4 === rules[i].type) { 384 readRules(rules[i].cssRules || rules[i].rules); 385 } else if (3 === rules[i].type) { 386 if(rules[i].styleSheet.hasOwnProperty("cssRules")) { 387 readRules(rules[i].styleSheet.cssRules); 388 } 389 } 390 } 391 } 392 } 393 394 var defaultCssInjected = false; 395 396 /** 397 * Searches all css rules and setups the event listener to all elements with element query rules.. 398 */ 399 this.init = function () { 400 var animationStart = 'animationstart'; 401 if (typeof document.documentElement.style['webkitAnimationName'] !== 'undefined') { 402 animationStart = 'webkitAnimationStart'; 403 } else if (typeof document.documentElement.style['MozAnimationName'] !== 'undefined') { 404 animationStart = 'mozanimationstart'; 405 } else if (typeof document.documentElement.style['OAnimationName'] !== 'undefined') { 406 animationStart = 'oanimationstart'; 407 } 408 409 document.body.addEventListener(animationStart, function (e) { 410 var element = e.target; 411 var styles = element && window.getComputedStyle(element, null); 412 var animationName = styles && styles.getPropertyValue('animation-name'); 413 var requiresSetup = animationName && (-1 !== animationName.indexOf('element-queries')); 414 415 if (requiresSetup) { 416 element.elementQueriesSensor = new ResizeSensor(element, function () { 417 if (element.elementQueriesSetupInformation) { 418 element.elementQueriesSetupInformation.call(); 419 } 420 }); 421 422 var sensorStyles = window.getComputedStyle(element.resizeSensor, null); 423 var id = sensorStyles.getPropertyValue('min-width'); 424 id = parseInt(id.replace('px', '')); 425 setupElement(e.target, idToSelectorMapping[id]); 426 } 427 }); 428 429 if (!defaultCssInjected) { 430 cssStyleElement = document.createElement('style'); 431 cssStyleElement.type = 'text/css'; 432 cssStyleElement.innerHTML = '[responsive-image] > img, [data-responsive-image] {overflow: hidden; padding: 0; } [responsive-image] > img, [data-responsive-image] > img {width: 100%;}'; 433 434 //safari wants at least one rule in keyframes to start working 435 cssStyleElement.innerHTML += '\n@keyframes element-queries { 0% { visibility: inherit; } }'; 436 document.getElementsByTagName('head')[0].appendChild(cssStyleElement); 437 defaultCssInjected = true; 438 } 439 440 for (var i = 0, j = document.styleSheets.length; i < j; i++) { 441 try { 442 if (document.styleSheets[i].href && 0 === document.styleSheets[i].href.indexOf('file://')) { 443 console.warn("CssElementQueries: unable to parse local css files, " + document.styleSheets[i].href); 444 } 445 446 readRules(document.styleSheets[i].cssRules || document.styleSheets[i].rules || document.styleSheets[i].cssText); 447 } catch (e) { 448 } 449 } 450 451 findResponsiveImages(); 452 }; 453 454 /** 455 * Go through all collected rules (readRules()) and attach the resize-listener. 456 * Not necessary to call it manually, since we detect automatically when new elements 457 * are available in the DOM. However, sometimes handy for dirty DOM modifications. 458 * 459 * @param {HTMLElement} container only elements of the container are considered (document.body if not set) 460 */ 461 this.findElementQueriesElements = function (container) { 462 findElementQueriesElements(container); 463 }; 464 465 this.update = function () { 466 this.init(); 467 }; 468 }; 469 470 ElementQueries.update = function () { 471 ElementQueries.instance.update(); 472 }; 473 474 /** 475 * Removes all sensor and elementquery information from the element. 476 * 477 * @param {HTMLElement} element 478 */ 479 ElementQueries.detach = function (element) { 480 if (element.elementQueriesSetupInformation) { 481 //element queries 482 element.elementQueriesSensor.detach(); 483 delete element.elementQueriesSetupInformation; 484 delete element.elementQueriesSensor; 485 486 } else if (element.resizeSensorInstance) { 487 //responsive image 488 489 element.resizeSensorInstance.detach(); 490 delete element.resizeSensorInstance; 491 } 492 }; 493 494 ElementQueries.init = function () { 495 if (!ElementQueries.instance) { 496 ElementQueries.instance = new ElementQueries(); 497 } 498 499 ElementQueries.instance.init(); 500 }; 501 502 var domLoaded = function (callback) { 503 /* Mozilla, Chrome, Opera */ 504 if (document.addEventListener) { 505 document.addEventListener('DOMContentLoaded', callback, false); 506 } 507 /* Safari, iCab, Konqueror */ 508 else if (/KHTML|WebKit|iCab/i.test(navigator.userAgent)) { 509 var DOMLoadTimer = setInterval(function () { 510 if (/loaded|complete/i.test(document.readyState)) { 511 callback(); 512 clearInterval(DOMLoadTimer); 513 } 514 }, 10); 515 } 516 /* Other web browsers */ 517 else window.onload = callback; 518 }; 519 520 ElementQueries.findElementQueriesElements = function (container) { 521 ElementQueries.instance.findElementQueriesElements(container); 522 }; 523 524 ElementQueries.listen = function () { 525 domLoaded(ElementQueries.init); 526 }; 527 528 return ElementQueries; 529 530}));
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.