(function (element) { element.matches = element.matches || element.mozmatchesselector || element.msmatchesselector || element.omatchesselector || element.webkitmatchesselector || function matches(selector) { var element = this, elements = (element.document || element.ownerdocument).queryselectorall(selector), index = 0; while (elements[index] && elements[index] !== element) { ++index; } return elements[index] ? true : false; }; element.closest = element.closest || function closest(selector) { var element = this; while (element) { if (element.matches(selector)) { break; } element = element.parentelement; } return element; }; }(element.prototype)); /*! * a polyfill for webkit's window.getmatchedcssrules, based on * https://gist.github.com/ydaniv/3033012 * * @author: yehonatan daniv * @author: ssafejava * @author: christian "schepp" schaefer * */ 'use strict'; (function () { // polyfill window.getmatchedcssrules() in firefox 6+ if (typeof window.getmatchedcssrules === 'function') { return; } var element_re = /[\w-]+/g, id_re = /#[\w-]+/g, class_re = /\.[\w-]+/g, attr_re = /\[[^\]]+\]/g, // :not() pseudo-class does not add to specificity, but its content does as if it was outside it pseudo_classes_re = /\:(?!not)[\w-]+(\(.*\))?/g, pseudo_elements_re = /\:\:?(after|before|first-letter|first-line|selection)/g; // convert an array-like object to array var toarray = function (list) { var items = []; var i = 0; var listlength = list.length; for (; i < listlength; i++) { items.push(list[i]); } return items; }; // get host of stylesheet var getcsshost = function (href) { var fakelinkofsheet = document.createelement('a'); fakelinkofsheet.href = href; return fakelinkofsheet.host; }; // handles extraction of `cssrules` as an `array` from a stylesheet or something that behaves the same var getsheetrules = function (stylesheet) { var sheetmedia = stylesheet.media && stylesheet.media.mediatext; var sheethost; // if this sheet is cross-origin and option is set skip it if (objectfit.disablecrossdomain == 'true') { sheethost = getcsshost(stylesheet.href); if ((sheethost !== window.location.host)) { return []; } } // if this sheet is disabled skip it if (stylesheet.disabled) { return []; } if (!window.matchmedia) { if (sheetmedia && sheetmedia.length) { return []; } } // if this sheet's media is specified and doesn't match the viewport then skip it else if (sheetmedia && sheetmedia.length && ! window.matchmedia(sheetmedia).matches) { return []; } // get the style rules of this sheet return toarray(stylesheet.cssrules); }; var _find = function (string, re) { var matches = string.match(re); return re ? re.length : 0; }; // calculates the specificity of a given `selector` var calculatescore = function (selector) { var score = [0, 0, 0]; var parts = selector.split(' '); var part; var match; //todo: clean the ':not' part since the last element_re will pick it up while (part = parts.shift(), typeof part === 'string') { // find all pseudo-elements match = _find(part, pseudo_elements_re); score[2] = match; // and remove them match && (part = part.replace(pseudo_elements_re, '')); // find all pseudo-classes match = _find(part, pseudo_classes_re); score[1] = match; // and remove them match && (part = part.replace(pseudo_classes_re, '')); // find all attributes match = _find(part, attr_re); score[1] += match; // and remove them match && (part = part.replace(attr_re, '')); // find all ids match = _find(part, id_re); score[0] = match; // and remove them match && (part = part.replace(id_re, '')); // find all classes match = _find(part, class_re); score[1] += match; // and remove them match && (part = part.replace(class_re, '')); // find all elements score[2] += _find(part, element_re); } return parseint(score.join(''), 10); }; // returns the heights possible specificity score an element can get from a give rule's selectortext var getspecificityscore = function (element, selectortext) { var selectors = selectortext.split(','), selector, score, result = 0; while (selector = selectors.shift()) { if (element.closest(selector)) { score = calculatescore(selector); result = score > result ? score : result; } } return result; }; var sortbyspecificity = function (element, rules) { // comparing function that sorts cssstylerules according to specificity of their `selectortext` var comparespecificity = function (a, b) { return getspecificityscore(element, b.selectortext) - getspecificityscore(element, a.selectortext); }; return rules.sort(comparespecificity); }; //todo: not supporting 2nd argument for selecting pseudo elements //todo: not supporting 3rd argument for checking author style sheets only window.getmatchedcssrules = function (element) { /*, pseudo, author_only*/ var stylesheets; var result = []; var sheet; var rules; var rule; // get stylesheets and convert to a regular array stylesheets = toarray(window.document.stylesheets); // assuming the browser hands us stylesheets in order of appearance // we iterate them from the beginning to follow proper cascade order while (sheet = stylesheets.shift()) { // get the style rules of this sheet rules = getsheetrules(sheet); // loop the rules in order of appearance while (rule = rules.shift()) { // if this is an @import rule if (rule.stylesheet) { // insert the imported stylesheet's rules at the beginning of this stylesheet's rules rules = getsheetrules(rule.stylesheet).concat(rules); // and skip this rule continue; } // if there's no stylesheet attribute but there is a media attribute it's a media rule else if (rule.media) { // insert the contained rules of this media rule to the beginning of this stylesheet's rules rules = getsheetrules(rule).concat(rules); // and skip it continue; } // check if this element matches this rule's selector if (element.closest(rule.selectortext)) { // push the rule to the results set result.push(rule); } } } // sort according to specificity return sortbyspecificity(element, result); }; }()); /* * raf.js * https://github.com/ngryman/raf.js * * original requestanimationframe polyfill by erik möller * inspired from paul_irish gist and post * * copyright (c) 2013 ngryman * licensed under the mit license. */ (function(window) { var lasttime = 0, vendors = ['webkit', 'moz'], requestanimationframe = window.requestanimationframe, cancelanimationframe = window.cancelanimationframe, i = vendors.length; // try to un-prefix existing raf while (--i >= 0 && !requestanimationframe) { requestanimationframe = window[vendors[i] + 'requestanimationframe']; cancelanimationframe = window[vendors[i] + 'cancelanimationframe']; } // polyfill with settimeout fallback // heavily inspired from @darius gist mod: https://gist.github.com/paulirish/1579671#comment-837945 if (!requestanimationframe || !cancelanimationframe) { requestanimationframe = function(callback) { var now = +new date(), nexttime = math.max(lasttime + 16, now); return settimeout(function() { callback(lasttime = nexttime); }, nexttime - now); }; cancelanimationframe = cleartimeout; } // export to window window.requestanimationframe = requestanimationframe; window.cancelanimationframe = cancelanimationframe; }(window)); /*! * polyfill css object-fit * http://helloanselm.com/object-fit * * @author: anselm hannemann * @author: christian "schepp" schaefer * @version: 0.3.4 * */ (function (global) { 'use strict'; // storage variable var objectfit = {}; objectfit._debug = false; objectfit.observer = null; objectfit.disablecrossdomain = 'false'; objectfit.getcomputedstyle = function(element, context) { context = context || window; if (context.getcomputedstyle) { return context.getcomputedstyle(element, null); } else { return element.currentstyle; } }; objectfit.getdefaultcomputedstyle = function(element){ var newelement = element.clonenode(true); var styles = {}; var iframe = document.createelement('iframe'); document.body.appendchild(iframe); iframe.contentwindow.document.open(); iframe.contentwindow.document.write(''); iframe.contentwindow.document.body.appendchild(newelement); iframe.contentwindow.document.close(); var defaultelement = iframe.contentwindow.document.queryselectorall(element.nodename.tolowercase())[0]; var defaultcomputedstyle = this.getcomputedstyle(defaultelement, iframe.contentwindow); var value; var property; for (property in defaultcomputedstyle) { if (defaultcomputedstyle.getpropertyvalue === true) { value = defaultcomputedstyle.getpropertyvalue(property); } else { value = defaultcomputedstyle[property]; } if (value !== null) { switch (property) { default: styles[property] = value; break; case 'width': case 'height': case 'minwidth': case 'minheight': case 'maxwidth': case 'maxheight': break; } } } document.body.removechild(iframe); return styles; }; objectfit.getmatchedstyle = function(element, property){ // element property has highest priority var val = null; var inlineval = null; if (element.style.getpropertyvalue) { inlineval = element.style.getpropertyvalue(property); } else if (element.currentstyle) { inlineval = element.currentstyle[property]; } // get matched rules var rules = window.getmatchedcssrules(element); var i = rules.length; var r; var important; if (i) { // iterate the rules backwards // rules are ordered by priority, highest last for (; i --> 0;) { r = rules[i]; important = r.style.getpropertypriority(property); // if set, only reset if important if (val === null || important) { val = r.style.getpropertyvalue(property); // done if important if (important) { break; } } } } // if it's important, we are done if (!val && inlineval !== null) { val = inlineval; } return val; }; // detects orientation objectfit.orientation = function(replacedelement) { if (replacedelement.parentnode && replacedelement.parentnode.nodename.tolowercase() === 'x-object-fit') { var width = replacedelement.naturalwidth || replacedelement.clientwidth; var height = replacedelement.naturalheight || replacedelement.clientheight; var parentwidth = replacedelement.parentnode.clientwidth; var parentheight = replacedelement.parentnode.clientheight; if (!height || width / height > parentwidth / parentheight) { if (replacedelement.getattribute('data-x-object-relation') !== 'wider') { replacedelement.setattribute('data-x-object-relation','wider'); replacedelement.classname += ' x-object-fit-wider'; if (this._debug && window.console) { console.log('x-object-fit-wider'); } } } else { if (replacedelement.getattribute('data-x-object-relation') !== 'taller') { replacedelement.setattribute('data-x-object-relation','taller'); replacedelement.classname += ' x-object-fit-taller'; if (this._debug && window.console) { console.log('x-object-fit-taller'); } } } } }; objectfit.process = function(args) { if (!args.selector || !args.replacedelements) { return; } // set option objectfit.disablecrossdomain objectfit.disablecrossdomain = args.disablecrossdomain || 'false'; // set option fit-type args.fittype = args.fittype || 'none'; switch (args.fittype) { default: return; case 'none': case 'fill': case 'contain': case 'cover': break; } // set option replacedelements var replacedelements = args.replacedelements; if(!replacedelements.length) { return; } for (var i = 0, replacedelementslength = replacedelements.length; i < replacedelementslength; i++) { this.processelement(replacedelements[i], args); } }; objectfit.processelement = function(replacedelement, args) { var property; var value; var replacedelementstyles = objectfit.getcomputedstyle(replacedelement); var replacedelementdefaultstyles = objectfit.getdefaultcomputedstyle(replacedelement); var wrapperelement = document.createelement('x-object-fit'); if (objectfit._debug && window.console) { console.log('applying to wrapper-------------------------------------------------------'); } for (property in replacedelementstyles) { switch (property) { default: value = objectfit.getmatchedstyle(replacedelement, property); if (value !== null && value !== '') { if (objectfit._debug && window.console) { console.log(property + ': ' + value); } wrapperelement.style[property] = value; } break; case 'length': case 'parentrule': break; } } if (objectfit._debug && window.console) { console.log('applying to replaced element-------------------------------------------------------'); } for (property in replacedelementdefaultstyles) { switch (property) { default: value = replacedelementdefaultstyles[property]; if (objectfit._debug && window.console && value !== '') { console.log(property + ': ' + value); if (replacedelement.style[property] === undefined) { console.log('indexed style properties (`' + property + '`) not supported in: ' + window.navigator.useragent); } } if (replacedelement.style[property]) { replacedelement.style[property] = value; // should work in firefox 35+ and all other browsers } else { replacedelement.style.property = value; } break; case 'length': case 'parentrule': break; } } wrapperelement.setattribute('class','x-object-fit-' + args.fittype); replacedelement.parentnode.insertbefore(wrapperelement, replacedelement); wrapperelement.appendchild(replacedelement); objectfit.orientation(replacedelement); var resizetimer = null; var resizeaction = function () { if (resizetimer !== null) { window.cancelanimationframe(resizetimer); } resizetimer = window.requestanimationframe(function(){ objectfit.orientation(replacedelement); }); }; switch (args.fittype) { default: break; case 'contain': case 'cover': if (window.addeventlistener) { replacedelement.addeventlistener('load', resizeaction, false); window.addeventlistener('resize', resizeaction, false); window.addeventlistener('orientationchange', resizeaction, false); } else { replacedelement.attachevent('onload', resizeaction); window.attachevent('onresize', resizeaction); } break; } }; objectfit.listen = function (args) { var dominsertedaction = function (element){ var i = 0; var argslength = args.length; for (; i < argslength; i++) { if ((element.mozmatchesselector && element.mozmatchesselector(args[i].selector)) || (element.msmatchesselector && element.msmatchesselector(args[i].selector)) || (element.omatchesselector && element.omatchesselector(args[i].selector)) || (element.webkitmatchesselector && element.webkitmatchesselector(args[i].selector)) ) { args[i].replacedelements = [element]; objectfit.process(args[i]); if (objectfit._debug && window.console) { console.log('matching node inserted: ' + element.nodename); } } } }; var dominsertedobserverfunction = function (element) { objectfit.observer.disconnect(); dominsertedaction(element); objectfit.observer.observe(document.documentelement, { childlist: true, subtree: true }); }; var dominsertedeventfunction = function (event) { window.removeeventlistener('domnodeinserted', dominsertedeventfunction, false); dominsertedaction(event.target); window.addeventlistener('domnodeinserted', dominsertedeventfunction, false); }; var domremovedaction = function (element) { if (element.nodename.tolowercase() === 'x-object-fit') { element.parentnode.removechild(element); if (objectfit._debug && window.console) { console.log('matching node removed: ' + element.nodename); } } }; var domremovedobserverfunction = function (element) { objectfit.observer.disconnect(); domremovedaction(element); objectfit.observer.observe(document.documentelement, { childlist: true, subtree: true }); }; var domremovedeventfunction = function (event) { window.removeeventlistener('domnoderemoved', domremovedeventfunction, false); domremovedaction(event.target.parentnode); window.addeventlistener('domnoderemoved', domremovedeventfunction, false); }; if (window.mutationobserver) { if (objectfit._debug && window.console) { console.log('dom mutationobserver'); } this.observer = new mutationobserver(function(mutations) { mutations.foreach(function(mutation) { if (mutation.addednodes && mutation.addednodes.length) { var nodes = mutation.addednodes; for (var i = 0, nodeslength = nodes.length; i < nodeslength; i++) { dominsertedobserverfunction(nodes[i]); } } if (mutation.removednodes && mutation.removednodes.length) { domremovedobserverfunction(mutation.target); } }); }); this.observer.observe(document.documentelement, { childlist: true, subtree: true }); } else if (window.addeventlistener) { if (objectfit._debug && window.console) { console.log('dom mutation events'); } window.addeventlistener('domnodeinserted', dominsertedeventfunction, false); window.addeventlistener('domnoderemoved', domremovedeventfunction, false); } }; objectfit.init = function (args) { if (!args) { return; } if (!(args instanceof array)) { args = [args]; } var i = 0; var argslength = args.length; for (; i < argslength; i++) { args[i].replacedelements = document.queryselectorall(args[i].selector); this.process(args[i]); } this.listen(args); }; objectfit.polyfill = function (args) { if('objectfit' in document.documentelement.style === false) { if (objectfit._debug && window.console) { console.log('object-fit not natively supported'); } // if the library is loaded after document onload event if (document.readystate === 'complete') { objectfit.init(args); } else { // otherwise attach event listeners if (window.addeventlistener) { window.addeventlistener('load', function(){ objectfit.init(args); }, false); } else { window.attachevent('onload', function(){ objectfit.init(args); }); } } } else { if (objectfit._debug && window.console) { console.log('object-fit natively supported'); } } }; /* * amd, module loader, global registration */ // expose modal for loaders that implement the node module pattern. if (typeof module === 'object' && module && typeof module.exports === 'object') { module.exports = objectfit; // register as an amd module } else if (typeof define === 'function' && define.amd) { define([], function () { return objectfit; }); // export into global space } else if (typeof global === 'object' && typeof global.document === 'object') { global.objectfit = objectfit; } }(window));