Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Bumping version

  • Loading branch information...
commit a3bb0f9d04adb1aa97acca56413927c928aef54a 1 parent 3b2b49b
@jansepar jansepar authored
View
3  CHANGELOG
@@ -1,3 +1,6 @@
+2.0.7 - Allow specifying quality parameter without it defaulting the format
+ to JPG.
+ Added a flag that allows you to turn resizing to auto/on/off.
2.0.6 - Fixed issue where we were not passing in the prefix when
using `render` with an HTML string, causing assets to not get enabled.
2.0.5 - Add API for fixing Anchor Tags on Firefox when using Capturing.
View
2  bower.json
@@ -1,6 +1,6 @@
{
"name": "mobifyjs",
- "version": "2.0.6",
+ "version": "2.0.7",
"main": "build/mobify.min.js",
"ignore": [
"node_modules",
View
1,368 build/mobify.js
@@ -422,301 +422,301 @@ var requirejs, require, define;
define("almond", function(){});
-define('mobifyjs/utils',[], function() {
-
-// ##
-// # Utility methods
-// ##
-
-var Utils = {};
-
-Utils.extend = function(target){
- [].slice.call(arguments, 1).forEach(function(source) {
- for (var key in source)
- if (source[key] !== undefined)
- target[key] = source[key];
- });
- return target;
-};
-
-Utils.keys = function(obj) {
- var result = [];
- for (var key in obj) {
- if (obj.hasOwnProperty(key))
- result.push(key);
- }
- return result;
-};
-
-Utils.values = function(obj) {
- var result = [];
- for (var key in obj) {
- if (obj.hasOwnProperty(key))
- result.push(obj[key]);
- }
- return result;
-};
-
-Utils.clone = function(obj) {
- var target = {};
- for (var i in obj) {
- if (obj.hasOwnProperty(i)) {
- target[i] = obj[i];
- }
- }
- return target;
-};
-
-// Some url helpers
-/**
- * Takes a url, relative or absolute, and absolutizes it relative to the current
- * document's location/base, with the assistance of an a element.
- */
-var _absolutifyAnchor = document.createElement("a");
-Utils.absolutify = function(url) {
- _absolutifyAnchor.href = url;
- return _absolutifyAnchor.href;
-};
-
-/**
- * Takes an absolute url, returns true if it is an http/s url, false otherwise
- * (e.g. mailto:, gopher://, data:, etc.)
- */
-var _httpUrlRE = /^https?/;
-Utils.httpUrl = function(url) {
- return _httpUrlRE.test(url);
-};
-
-/**
- * outerHTML polyfill - https://gist.github.com/889005
- */
-Utils.outerHTML = function(el){
- if (el.outerHTML) {
- return el.outerHTML;
- }
- else {
- var div = document.createElement('div');
- div.appendChild(el.cloneNode(true));
- var contents = div.innerHTML;
- div = null;
- return contents;
- }
-};
-
-/**
- * Return a string for the doctype of the current document.
- */
-Utils.getDoctype = function(doc) {
- doc = doc || document;
- var doctypeEl = doc.doctype || [].filter.call(doc.childNodes, function(el) {
- return el.nodeType == Node.DOCUMENT_TYPE_NODE
- })[0];
-
- if (!doctypeEl) return '';
-
- return '<!DOCTYPE HTML'
- + (doctypeEl.publicId ? ' PUBLIC "' + doctypeEl.publicId + '"' : '')
- + (doctypeEl.systemId ? ' "' + doctypeEl.systemId + '"' : '')
- + '>';
-};
-
-/**
- * Returns an object that represents the parsed content attribute of the
- * viewport meta tag. Returns false if no viewport meta tag is present.
- */
-Utils.getMetaViewportProperties = function(doc) {
- // Regex to split comma-delimited viewport meta tag properties
- var SPLIT_PROPERTIES_REGEX = /,\s?/;
-
- doc = doc || document;
- var parsedProperties = {}
-
- // Get the viewport meta tag
- var viewport = doc.querySelectorAll('meta[name="viewport"]');
- if (viewport.length == 0) {
- return false;
- }
-
- // Split its properties
- var content = viewport[0].getAttribute('content');
- if (content == null) {
- return false;
- }
- var properties = content.split(SPLIT_PROPERTIES_REGEX);
-
- // Parse the properties into an object
- for (var i = 0; i < properties.length; i++) {
- var property = properties[i].split('=')
-
- if (property.length >= 2) {
- var key = property[0];
- var value = property[1];
- parsedProperties[key] = value;
- }
- }
-
- return parsedProperties;
-}
-
-Utils.removeBySelector = function(selector, doc) {
- doc = doc || document;
-
- var els = doc.querySelectorAll(selector);
- return Utils.removeElements(els, doc);
-};
-
-Utils.removeElements = function(elements, doc) {
- doc = doc || document;
-
- for (var i=0,ii=elements.length; i<ii; i++) {
- var el = elements[i];
- el.parentNode.removeChild(el);
- }
- return elements;
-};
-
-// localStorage detection as seen in such great libraries as Modernizr
-// https://github.com/Modernizr/Modernizr/blob/master/feature-detects/storage/localstorage.js
-// Exposing on Jazzcat for use in qunit tests
-var cachedLocalStorageSupport;
-Utils.supportsLocalStorage = function() {
- if (cachedLocalStorageSupport !== undefined) {
- return cachedLocalStorageSupport;
- }
- var mod = 'modernizr';
- try {
- localStorage.setItem(mod, mod);
- localStorage.removeItem(mod);
- cachedLocalStorageSupport = true;
- } catch(e) {
- cachedLocalStorageSupport = false
- }
- return cachedLocalStorageSupport;
-};
-
-// matchMedia polyfill generator
-// (allows you to specify which document to run polyfill on)
-Utils.matchMedia = function(doc) {
-
-
- var bool,
- docElem = doc.documentElement,
- refNode = docElem.firstElementChild || docElem.firstChild,
- // fakeBody required for <FF4 when executed in <head>
- fakeBody = doc.createElement("body"),
- div = doc.createElement("div");
-
- div.id = "mq-test-1";
- div.style.cssText = "position:absolute;top:-100em";
- fakeBody.style.background = "none";
- fakeBody.appendChild(div);
-
- return function(q){
- div.innerHTML = "&shy;<style media=\"" + q + "\"> #mq-test-1 { width: 42px; }</style>";
-
- docElem.insertBefore(fakeBody, refNode);
- bool = div.offsetWidth === 42;
- docElem.removeChild(fakeBody);
-
- return {
- matches: bool,
- media: q
- };
- };
-};
-
-// readyState: loading --> interactive --> complete
-// | |
-// | |
-// v v
-// Event: DOMContentLoaded onload
-//
-// iOS 4.3 and some Android 2.X.X have a non-typical "loaded" readyState,
-// which is an acceptable readyState to start capturing on, because
-// the data is fully loaded from the server at that state.
-// For some IE (IE10 on Lumia 920 for example), interactive is not
-// indicative of the DOM being ready, therefore "complete" is the only acceptable
-// readyState for IE10
-// Credit to https://github.com/jquery/jquery/commit/0f553ed0ca0c50c5f66377e9f2c6314f822e8f25
-// for the IE10 fix
-Utils.domIsReady = function(doc) {
- var doc = doc || document;
- return doc.attachEvent ? doc.readyState === "complete" : doc.readyState !== "loading";
-};
-
-Utils.getPhysicalScreenSize = function(devicePixelRatio) {
-
- function multiplyByPixelRatio(sizes) {
- var dpr = devicePixelRatio || window.devicePixelRatio || 1;
-
- sizes.width = Math.round(sizes.width * dpr);
- sizes.height = Math.round(sizes.height * dpr);
-
- return sizes;
- }
-
- var iOS = navigator.userAgent.match(/ip(hone|od|ad)/i);
- var androidVersion = (navigator.userAgent.match(/android (\d)/i) || {})[1];
-
- var sizes = {
- width: window.outerWidth
- , height: window.outerHeight
- };
-
- // Old Android and BB10 use physical pixels in outerWidth/Height, which is what we need
- // New Android (4.0 and above) use CSS pixels, requiring devicePixelRatio multiplication
- // iOS lies about outerWidth/Height when zooming, but does expose CSS pixels in screen.width/height
-
- if (!iOS) {
- if (androidVersion > 3) return multiplyByPixelRatio(sizes);
- return sizes;
- }
-
- var isLandscape = window.orientation % 180;
- if (isLandscape) {
- sizes.height = screen.width;
- sizes.width = screen.height;
- } else {
- sizes.width = screen.width;
- sizes.height = screen.height;
- }
-
- return multiplyByPixelRatio(sizes);
-};
-
-Utils.waitForReady = function(doc, callback) {
- // Waits for `doc` to be ready, and then fires callback, passing
- // `doc`.
-
- // We may be in "loading" state by the time we get here, meaning we are
- // not ready to capture. Next step after "loading" is "interactive",
- // which is a valid state to start capturing on (except IE), and thus when ready
- // state changes once, we know we are good to start capturing.
- // Cannot rely on using DOMContentLoaded because this event prematurely fires
- // for some IE10s.
- var ready = false;
-
- var onReady = function() {
- if (!ready) {
- ready = true;
- iid && clearInterval(iid);
- callback(doc);
- }
- }
-
- // Backup with polling incase readystatechange doesn't fire
- // (happens with some Android 2.3 browsers)
- var iid = setInterval(function(){
- if (Utils.domIsReady(doc)) {
- onReady();
- }
- }, 100);
-
- doc.addEventListener("readystatechange", onReady, false);
-};
-
-return Utils;
-
+define('mobifyjs/utils',[], function() {
+
+// ##
+// # Utility methods
+// ##
+
+var Utils = {};
+
+Utils.extend = function(target){
+ [].slice.call(arguments, 1).forEach(function(source) {
+ for (var key in source)
+ if (source[key] !== undefined)
+ target[key] = source[key];
+ });
+ return target;
+};
+
+Utils.keys = function(obj) {
+ var result = [];
+ for (var key in obj) {
+ if (obj.hasOwnProperty(key))
+ result.push(key);
+ }
+ return result;
+};
+
+Utils.values = function(obj) {
+ var result = [];
+ for (var key in obj) {
+ if (obj.hasOwnProperty(key))
+ result.push(obj[key]);
+ }
+ return result;
+};
+
+Utils.clone = function(obj) {
+ var target = {};
+ for (var i in obj) {
+ if (obj.hasOwnProperty(i)) {
+ target[i] = obj[i];
+ }
+ }
+ return target;
+};
+
+// Some url helpers
+/**
+ * Takes a url, relative or absolute, and absolutizes it relative to the current
+ * document's location/base, with the assistance of an a element.
+ */
+var _absolutifyAnchor = document.createElement("a");
+Utils.absolutify = function(url) {
+ _absolutifyAnchor.href = url;
+ return _absolutifyAnchor.href;
+};
+
+/**
+ * Takes an absolute url, returns true if it is an http/s url, false otherwise
+ * (e.g. mailto:, gopher://, data:, etc.)
+ */
+var _httpUrlRE = /^https?/;
+Utils.httpUrl = function(url) {
+ return _httpUrlRE.test(url);
+};
+
+/**
+ * outerHTML polyfill - https://gist.github.com/889005
+ */
+Utils.outerHTML = function(el){
+ if (el.outerHTML) {
+ return el.outerHTML;
+ }
+ else {
+ var div = document.createElement('div');
+ div.appendChild(el.cloneNode(true));
+ var contents = div.innerHTML;
+ div = null;
+ return contents;
+ }
+};
+
+/**
+ * Return a string for the doctype of the current document.
+ */
+Utils.getDoctype = function(doc) {
+ doc = doc || document;
+ var doctypeEl = doc.doctype || [].filter.call(doc.childNodes, function(el) {
+ return el.nodeType == Node.DOCUMENT_TYPE_NODE
+ })[0];
+
+ if (!doctypeEl) return '';
+
+ return '<!DOCTYPE HTML'
+ + (doctypeEl.publicId ? ' PUBLIC "' + doctypeEl.publicId + '"' : '')
+ + (doctypeEl.systemId ? ' "' + doctypeEl.systemId + '"' : '')
+ + '>';
+};
+
+/**
+ * Returns an object that represents the parsed content attribute of the
+ * viewport meta tag. Returns false if no viewport meta tag is present.
+ */
+Utils.getMetaViewportProperties = function(doc) {
+ // Regex to split comma-delimited viewport meta tag properties
+ var SPLIT_PROPERTIES_REGEX = /,\s?/;
+
+ doc = doc || document;
+ var parsedProperties = {}
+
+ // Get the viewport meta tag
+ var viewport = doc.querySelectorAll('meta[name="viewport"]');
+ if (viewport.length == 0) {
+ return false;
+ }
+
+ // Split its properties
+ var content = viewport[0].getAttribute('content');
+ if (content == null) {
+ return false;
+ }
+ var properties = content.split(SPLIT_PROPERTIES_REGEX);
+
+ // Parse the properties into an object
+ for (var i = 0; i < properties.length; i++) {
+ var property = properties[i].split('=')
+
+ if (property.length >= 2) {
+ var key = property[0];
+ var value = property[1];
+ parsedProperties[key] = value;
+ }
+ }
+
+ return parsedProperties;
+}
+
+Utils.removeBySelector = function(selector, doc) {
+ doc = doc || document;
+
+ var els = doc.querySelectorAll(selector);
+ return Utils.removeElements(els, doc);
+};
+
+Utils.removeElements = function(elements, doc) {
+ doc = doc || document;
+
+ for (var i=0,ii=elements.length; i<ii; i++) {
+ var el = elements[i];
+ el.parentNode.removeChild(el);
+ }
+ return elements;
+};
+
+// localStorage detection as seen in such great libraries as Modernizr
+// https://github.com/Modernizr/Modernizr/blob/master/feature-detects/storage/localstorage.js
+// Exposing on Jazzcat for use in qunit tests
+var cachedLocalStorageSupport;
+Utils.supportsLocalStorage = function() {
+ if (cachedLocalStorageSupport !== undefined) {
+ return cachedLocalStorageSupport;
+ }
+ var mod = 'modernizr';
+ try {
+ localStorage.setItem(mod, mod);
+ localStorage.removeItem(mod);
+ cachedLocalStorageSupport = true;
+ } catch(e) {
+ cachedLocalStorageSupport = false
+ }
+ return cachedLocalStorageSupport;
+};
+
+// matchMedia polyfill generator
+// (allows you to specify which document to run polyfill on)
+Utils.matchMedia = function(doc) {
+
+
+ var bool,
+ docElem = doc.documentElement,
+ refNode = docElem.firstElementChild || docElem.firstChild,
+ // fakeBody required for <FF4 when executed in <head>
+ fakeBody = doc.createElement("body"),
+ div = doc.createElement("div");
+
+ div.id = "mq-test-1";
+ div.style.cssText = "position:absolute;top:-100em";
+ fakeBody.style.background = "none";
+ fakeBody.appendChild(div);
+
+ return function(q){
+ div.innerHTML = "&shy;<style media=\"" + q + "\"> #mq-test-1 { width: 42px; }</style>";
+
+ docElem.insertBefore(fakeBody, refNode);
+ bool = div.offsetWidth === 42;
+ docElem.removeChild(fakeBody);
+
+ return {
+ matches: bool,
+ media: q
+ };
+ };
+};
+
+// readyState: loading --> interactive --> complete
+// | |
+// | |
+// v v
+// Event: DOMContentLoaded onload
+//
+// iOS 4.3 and some Android 2.X.X have a non-typical "loaded" readyState,
+// which is an acceptable readyState to start capturing on, because
+// the data is fully loaded from the server at that state.
+// For some IE (IE10 on Lumia 920 for example), interactive is not
+// indicative of the DOM being ready, therefore "complete" is the only acceptable
+// readyState for IE10
+// Credit to https://github.com/jquery/jquery/commit/0f553ed0ca0c50c5f66377e9f2c6314f822e8f25
+// for the IE10 fix
+Utils.domIsReady = function(doc) {
+ var doc = doc || document;
+ return doc.attachEvent ? doc.readyState === "complete" : doc.readyState !== "loading";
+};
+
+Utils.getPhysicalScreenSize = function(devicePixelRatio) {
+
+ function multiplyByPixelRatio(sizes) {
+ var dpr = devicePixelRatio || window.devicePixelRatio || 1;
+
+ sizes.width = Math.round(sizes.width * dpr);
+ sizes.height = Math.round(sizes.height * dpr);
+
+ return sizes;
+ }
+
+ var iOS = navigator.userAgent.match(/ip(hone|od|ad)/i);
+ var androidVersion = (navigator.userAgent.match(/android (\d)/i) || {})[1];
+
+ var sizes = {
+ width: window.outerWidth
+ , height: window.outerHeight
+ };
+
+ // Old Android and BB10 use physical pixels in outerWidth/Height, which is what we need
+ // New Android (4.0 and above) use CSS pixels, requiring devicePixelRatio multiplication
+ // iOS lies about outerWidth/Height when zooming, but does expose CSS pixels in screen.width/height
+
+ if (!iOS) {
+ if (androidVersion > 3) return multiplyByPixelRatio(sizes);
+ return sizes;
+ }
+
+ var isLandscape = window.orientation % 180;
+ if (isLandscape) {
+ sizes.height = screen.width;
+ sizes.width = screen.height;
+ } else {
+ sizes.width = screen.width;
+ sizes.height = screen.height;
+ }
+
+ return multiplyByPixelRatio(sizes);
+};
+
+Utils.waitForReady = function(doc, callback) {
+ // Waits for `doc` to be ready, and then fires callback, passing
+ // `doc`.
+
+ // We may be in "loading" state by the time we get here, meaning we are
+ // not ready to capture. Next step after "loading" is "interactive",
+ // which is a valid state to start capturing on (except IE), and thus when ready
+ // state changes once, we know we are good to start capturing.
+ // Cannot rely on using DOMContentLoaded because this event prematurely fires
+ // for some IE10s.
+ var ready = false;
+
+ var onReady = function() {
+ if (!ready) {
+ ready = true;
+ iid && clearInterval(iid);
+ callback(doc);
+ }
+ }
+
+ // Backup with polling incase readystatechange doesn't fire
+ // (happens with some Android 2.3 browsers)
+ var iid = setInterval(function(){
+ if (Utils.domIsReady(doc)) {
+ onReady();
+ }
+ }, 100);
+
+ doc.addEventListener("readystatechange", onReady, false);
+};
+
+return Utils;
+
});
// Fixes anchor links (on FF)
@@ -1374,395 +1374,395 @@ return Capture;
});
-define('mobifyjs/resizeImages',["mobifyjs/utils"], function(Utils) {
-
-var ResizeImages = window.ResizeImages = {};
-
-var localStorageWebpKey = 'Mobify-Webp-Support-v2';
-
-function persistWebpSupport(supported) {
- if (Utils.supportsLocalStorage()) {
- var webpSupport = {
- supported: supported,
- date: Date.now()
- };
- localStorage.setItem(localStorageWebpKey, JSON.stringify(webpSupport));
- }
-}
-
-/**
- * Synchronous WEBP detection using regular expressions
- * Credit to Ilya Grigorik for WEBP regex matching
- * https://github.com/igrigorik/webp-detect/blob/master/pagespeed.cc
- * Modified to exclude Android native browser on Android 4
- */
-ResizeImages.userAgentWebpDetect = function(userAgent){
- var supportedRe = /(Android\s|Chrome\/|Opera9.8*Version\/..\.|Opera..\.)/i;
- var unsupportedVersionsRe = new RegExp('(Android\\s(0|1|2|3|(4(?!.*Chrome)))\\.)|(Chrome\\/[0-8]\\.)' +
- '|(Chrome\\/9\\.0\\.)|(Chrome\\/1[4-6]\\.)|(Android\\sChrome\\/1.\\.)' +
- '|(Android\\sChrome\\/20\\.)|(Chrome\\/(1.|20|21|22)\\.)' +
- '|(Opera.*(Version/|Opera\\s)(10|11)\\.)', 'i');
-
- // Return false if browser is not supported
- if (!supportedRe.test(userAgent)) {
- return false;
- }
-
- // Return false if a specific browser version is not supported
- if (unsupportedVersionsRe.test(userAgent)) {
- return false;
- }
- return true;
-};
-
-/**
- * Asychronous WEB detection using a data uri.
- * Credit to Modernizer:
- * https://github.com/Modernizr/Modernizr/blob/fb76d75fbf97f715e666b55b8aa04e43ef809f5e/feature-detects/img-webp.js
- */
-ResizeImages.dataUriWebpDetect = function(callback) {
- var image = new Image();
- image.onload = function() {
- var support = (image.width === 1) ? true : false;
- persistWebpSupport(support);
- if (callback) callback(support);
- };
- // this webp generated with Mobify image resizer from
- // http://upload.wikimedia.org/wikipedia/commons/c/ca/1x1.png passed
- // through the Mobify Image resizer:
- // http://ir0.mobify.com/webp/http://upload.wikimedia.org/wikipedia/commons/c/ca/1x1.png
- image.src = 'data:image/webp;base64,UklGRkoAAABXRUJQVlA4WAoAAAAQAAAAAAAAAAAAQUxQSAwAAAABBxAR/Q9ERP8DAABWUDggGAAAADABAJ0BKgEAAQABgBwlpAADcAD+/gbQAA==';
-}
-
-/**
- * Detect WEBP support sync and async. Do our best to determine support
- * with regex, and use data-uri method for future proofing.
- * (note: async test will not complete before first run of `resize`,
- * since onload of detector image won't fire until document is complete)
- * Also caches results for WEBP support in localStorage.
- */
-ResizeImages.supportsWebp = function(callback) {
-
- // Return early if we have persisted WEBP support
- if (Utils.supportsLocalStorage()) {
-
- // Check if WEBP support has already been detected
- var webpSupport;
- var storedSupport = localStorage.getItem(localStorageWebpKey);
-
- // Only JSON.parse if storedSupport is not null, or else things
- // will break on Android 2.3
- storedSupport && (webpSupport = JSON.parse(storedSupport));
-
- // Grab previously cached support value in localStorage.
- if (webpSupport && (Date.now() - webpSupport.date < 604800000)) {
- return webpSupport.supported;
- }
- }
-
- // Run async WEBP detection for future proofing
- // This test may not finish running before the first call of `resize`
- ResizeImages.dataUriWebpDetect(callback);
-
- // Run regex based synchronous WEBP detection
- var support = ResizeImages.userAgentWebpDetect(navigator.userAgent);
-
- persistWebpSupport(support);
-
- return support;
-
-};
-
-/**
- * Returns a URL suitable for use with the 'ir' service.
- */
-ResizeImages.getImageURL = function(url, options) {
- var opts = options;
- if (!opts) {
- opts = ResizeImages.processOptions();
- }
- var bits = [opts.proto + opts.host];
-
- if (opts.projectName) {
- var projectId = "project-" + opts.projectName;
- bits.push(projectId);
- }
-
- if (opts.cacheHours) {
- bits.push('c' + opts.cacheHours);
- }
-
- if (opts.format) {
- bits.push(opts.format + (opts.quality || ''));
- } else if (opts.quality) {
- bits.push('q' + opts.quality);
- }
-
- if (opts.maxWidth) {
- bits.push(opts.maxWidth);
-
- if (opts.maxHeight) {
- bits.push(opts.maxHeight);
- }
- }
-
- bits.push(url);
- return bits.join('/');
-};
-
-/**
- * Replaces src attr of passed element with value of running `getImageUrl` on it
- * Allows overriding of img.getAttribute(x-src) with srcVal
- */
-
-ResizeImages._rewriteSrcAttribute = function(element, opts, srcVal){
- srcVal = element.getAttribute(opts.sourceAttribute) || srcVal;
- if (srcVal) {
- var url = Utils.absolutify(srcVal);
- if (Utils.httpUrl(url)) {
- if (opts.onerror) {
- element.setAttribute('onerror', opts.onerror);
- }
- element.setAttribute(opts.targetAttribute, ResizeImages.getImageURL(url, opts));
- element.setAttribute('data-orig-src', srcVal);
- // if using resize when not capturing, remove the sourceAttribute
- // as long as it's not "src", which is the target attribute used
- // when not capturing.
- if (!capturing && opts.sourceAttribute != opts.targetAttribute) {
- element.removeAttribute(opts.sourceAttribute);
- }
- }
- }
-};
-
-/**
- * Modifies src of `<source />` children of a `<picture>` element to use image
- * resizer
- */
-ResizeImages._resizeSourceElement = function(element, opts, rootSrc) {
- // Grab optional width override
- var width = element.getAttribute('data-width');
- var localOpts = opts;
- if (width) {
- localOpts = Utils.clone(opts);
- localOpts.maxWidth = width;
- }
- // pass along rootSrc if defined on `picture` element
- ResizeImages._rewriteSrcAttribute(element, localOpts, rootSrc);
-};
-
-/**
- * Takes a picture element and calls _resizeSourceElement on its `<source />`
- * children
- */
-ResizeImages._crawlPictureElement = function(el, opts) {
- var sources = el.getElementsByTagName('source');
- // If source elements are erased from the dom, leave the
- // picture element alone.
- if (sources.length === 0 || el.hasAttribute('mobify-optimized')) {
- return;
- }
- el.setAttribute('mobify-optimized', '');
-
- // Grab optional `data-src` attribute on `picture`.
- // Used for preventing writing the same src multiple times for
- // different `source` elements.
- var rootSrc = el.getAttribute('data-src');
-
- // resize the sources
- for(var i = 0, len = sources.length; i < len; i++) {
- ResizeImages._resizeSourceElement(sources[i], opts, rootSrc);
- }
-};
-
-/**
- * Searches a list of target dimensions for the smallest one that is greater than
- * the passed value and return it, or return the greatst value if none are
- * greater.
- *
- * Popular device resolutions:
- * iPhone 3Gs - 320x480
- * iPhone 4 - 640x960
- * iPhone 5 - 650x1156
- *
- * Galaxy SIII/Nexus 4/Nexus 7 - 720x1280
- * Galaxy SIV/Nexus 5 - 1080x1920
- *
- * iPad (non-retina) - 1024x768
- * iPad (retina) - 2048x1536
- *
- * A larger list of target dimensions would include 720px, 800px, 1024px, 1280px
- * and 1920px but they have been omitted due tot heir proximity to other, larger
- * values
- */
-var targetDims = [320, 640, 768, 1080, 1536, 2048, 4000];
-ResizeImages._getBinnedDimension = function(dim) {
- var resultDim = 0;
-
- for (var i = 0, len = targetDims.length; i < len; i++) {
- resultDim = targetDims[i];
- if (resultDim >= dim) {
- break;
- }
- }
- return resultDim;
-};
-
-/**
- * Returns a boolean that indicates whether images should be resized.
- * Looks for the viewport meta tag and parses it to determine whether the
- * website is responsive (the viewport is set to the device's width). This
- * ensures that images that are part of a larger viewport are not scaled.
- */
-ResizeImages._shouldResize = function(document) {
- var metaViewport = Utils.getMetaViewportProperties(document);
- if (!metaViewport) {
- return false;
- }
-
- // It's complicated, but what we want to know is whether the viewport
- // matches the 'ideal viewport'. If either `initial-scale` is 1 or `width`
- // is device-width or both, then the viewport will match the 'ideal
- // viewport'. There are a few other special circumstances under which the
- // viewport could be ideal, but we can't test for them.
- //
- // See: http://www.quirksmode.org/mobile/metaviewport/
-
- // Ideal viewport when width=device-width
- if (!metaViewport['initial-scale'] && metaViewport['width']) {
- return metaViewport['width'] == 'device-width';
- }
-
- // Ideal viewport when initial-scale=1
- if (!metaViewport['width'] && metaViewport['initial-scale']) {
- return metaViewport['initial-scale'] == '1';
- }
-
- // Ideal viewport when width=device-width and the intial-scale is 1 or more
- // (in that case it's just zoomed)
- if (metaViewport['width'] && metaViewport['initial-scale']) {
- initialScale = parseInt(metaViewport['initial-scale']);
- return initialScale >= 1 && metaViewport['width'] == 'device-width';
- }
-
- return false
-};
-
-/**
- * Processes options passed to `resize()`. Takes an options object that
- * potentially has height and width set in css pixels, returns an object where
- * they are expressed in device pixels, and other default options are set.
- */
-ResizeImages.processOptions = function(options) {
- var opts = Utils.clone(ResizeImages.defaults);
- if (options) {
- Utils.extend(opts, options);
- }
-
- // A null value for `resize` triggers the auto detect functionality. This
- // uses the document to determine whether images should be resized and sets
- // it as the new default.
- if (opts.resize == null && options.document) {
- var resize = ResizeImages._shouldResize(options.document);
- ResizeImages.defaults.resize = opts.resize = resize;
- }
-
- if (!opts.format && opts.webp) {
- opts.format = "webp";
- }
-
- // Without `resize` images are served through IR without changing their dimensions
- if (!opts.resize) {
- opts.maxWidth = opts.maxHeight = opts.devicePixelRatio = null;
- }
- else {
- var dpr = opts.devicePixelRatio || window.devicePixelRatio;
-
- var screenSize = Utils.getPhysicalScreenSize(dpr);
-
- // If maxHeight/maxWidth are not specified, use screen dimensions
- // in device pixels
- var width = opts.maxWidth || ResizeImages._getBinnedDimension(screenSize.width);
- var height = opts.maxHeight || undefined;
-
- // Otherwise, compute device pixels
- if (dpr && opts.maxWidth) {
- width = width * dpr;
- if (opts.maxHeight) {
- height = height * dpr;
- }
- }
-
- // round up in case of non-integer device pixel ratios
- opts.maxWidth = Math.ceil(width);
- if (opts.maxHeight && height) {
- opts.maxHeight = Math.ceil(height);
- }
- }
-
- return opts;
-};
-
-/**
- * Searches the collection for image elements and modifies them to use
- * the Image Resize service. Pass `options` to modify how the images are
- * resized.
- */
-ResizeImages.resize = function(elements, options) {
- // Return early if elements is empty
- if (!elements.length) {
- return;
- }
-
- // Supplement `options` with the document from the first element
- if (options && !options.document) {
- options.document = elements[0].ownerDocument;
- }
- var opts = ResizeImages.processOptions(options);
-
- for(var i=0; i < elements.length; i++) {
- var element = elements[i];
-
- // For an `img`, simply modify the src attribute
- if (element.nodeName === 'IMG' && !element.hasAttribute('mobify-optimized')) {
- element.setAttribute('mobify-optimized', '');
- ResizeImages._rewriteSrcAttribute(element, opts);
- }
- // For a `picture`, (potentially) nuke src on `img`, and
- // pass all `source` elements into modifyImages recursively
- else if (element.nodeName === 'PICTURE') {
- ResizeImages._crawlPictureElement(element, opts);
- }
- }
-
- return elements;
-};
-
-ResizeImages.restoreOriginalSrc = function(event) {
- var origSrc;
- event.target.removeAttribute('onerror'); // remove ourselves
- origSrc = event.target.getAttribute('data-orig-src')
- if (origSrc) {
- event.target.setAttribute('src', origSrc);
- }
-};
-
-var capturing = window.Mobify && window.Mobify.capturing || false;
-
-ResizeImages.defaults = {
- proto: '//',
- host: 'ir0.mobify.com',
- projectName: "oss-" + location.hostname.replace(/[^\w]/g, '-'),
- sourceAttribute: "x-src",
- targetAttribute: (capturing ? "x-src" : "src"),
- webp: ResizeImages.supportsWebp(),
- resize: true,
- onerror: 'ResizeImages.restoreOriginalSrc(event);'
-};
-
-return ResizeImages;
-
-});
+define('mobifyjs/resizeImages',["mobifyjs/utils"], function(Utils) {
+
+var ResizeImages = window.ResizeImages = {};
+
+var localStorageWebpKey = 'Mobify-Webp-Support-v2';
+
+function persistWebpSupport(supported) {
+ if (Utils.supportsLocalStorage()) {
+ var webpSupport = {
+ supported: supported,
+ date: Date.now()
+ };
+ localStorage.setItem(localStorageWebpKey, JSON.stringify(webpSupport));
+ }
+}
+
+/**
+ * Synchronous WEBP detection using regular expressions
+ * Credit to Ilya Grigorik for WEBP regex matching
+ * https://github.com/igrigorik/webp-detect/blob/master/pagespeed.cc
+ * Modified to exclude Android native browser on Android 4
+ */
+ResizeImages.userAgentWebpDetect = function(userAgent){
+ var supportedRe = /(Android\s|Chrome\/|Opera9.8*Version\/..\.|Opera..\.)/i;
+ var unsupportedVersionsRe = new RegExp('(Android\\s(0|1|2|3|(4(?!.*Chrome)))\\.)|(Chrome\\/[0-8]\\.)' +
+ '|(Chrome\\/9\\.0\\.)|(Chrome\\/1[4-6]\\.)|(Android\\sChrome\\/1.\\.)' +
+ '|(Android\\sChrome\\/20\\.)|(Chrome\\/(1.|20|21|22)\\.)' +
+ '|(Opera.*(Version/|Opera\\s)(10|11)\\.)', 'i');
+
+ // Return false if browser is not supported
+ if (!supportedRe.test(userAgent)) {
+ return false;
+ }
+
+ // Return false if a specific browser version is not supported
+ if (unsupportedVersionsRe.test(userAgent)) {
+ return false;
+ }
+ return true;
+};
+
+/**
+ * Asychronous WEB detection using a data uri.
+ * Credit to Modernizer:
+ * https://github.com/Modernizr/Modernizr/blob/fb76d75fbf97f715e666b55b8aa04e43ef809f5e/feature-detects/img-webp.js
+ */
+ResizeImages.dataUriWebpDetect = function(callback) {
+ var image = new Image();
+ image.onload = function() {
+ var support = (image.width === 1) ? true : false;
+ persistWebpSupport(support);
+ if (callback) callback(support);
+ };
+ // this webp generated with Mobify image resizer from
+ // http://upload.wikimedia.org/wikipedia/commons/c/ca/1x1.png passed
+ // through the Mobify Image resizer:
+ // http://ir0.mobify.com/webp/http://upload.wikimedia.org/wikipedia/commons/c/ca/1x1.png
+ image.src = 'data:image/webp;base64,UklGRkoAAABXRUJQVlA4WAoAAAAQAAAAAAAAAAAAQUxQSAwAAAABBxAR/Q9ERP8DAABWUDggGAAAADABAJ0BKgEAAQABgBwlpAADcAD+/gbQAA==';
+}
+
+/**
+ * Detect WEBP support sync and async. Do our best to determine support
+ * with regex, and use data-uri method for future proofing.
+ * (note: async test will not complete before first run of `resize`,
+ * since onload of detector image won't fire until document is complete)
+ * Also caches results for WEBP support in localStorage.
+ */
+ResizeImages.supportsWebp = function(callback) {
+
+ // Return early if we have persisted WEBP support
+ if (Utils.supportsLocalStorage()) {
+
+ // Check if WEBP support has already been detected
+ var webpSupport;
+ var storedSupport = localStorage.getItem(localStorageWebpKey);
+
+ // Only JSON.parse if storedSupport is not null, or else things
+ // will break on Android 2.3
+ storedSupport && (webpSupport = JSON.parse(storedSupport));
+
+ // Grab previously cached support value in localStorage.
+ if (webpSupport && (Date.now() - webpSupport.date < 604800000)) {
+ return webpSupport.supported;
+ }
+ }
+
+ // Run async WEBP detection for future proofing
+ // This test may not finish running before the first call of `resize`
+ ResizeImages.dataUriWebpDetect(callback);
+
+ // Run regex based synchronous WEBP detection
+ var support = ResizeImages.userAgentWebpDetect(navigator.userAgent);
+
+ persistWebpSupport(support);
+
+ return support;
+
+};
+
+/**
+ * Returns a URL suitable for use with the 'ir' service.
+ */
+ResizeImages.getImageURL = function(url, options) {
+ var opts = options;
+ if (!opts) {
+ opts = ResizeImages.processOptions();
+ }
+ var bits = [opts.proto + opts.host];
+
+ if (opts.projectName) {
+ var projectId = "project-" + opts.projectName;
+ bits.push(projectId);
+ }
+
+ if (opts.cacheHours) {
+ bits.push('c' + opts.cacheHours);
+ }
+
+ if (opts.format) {
+ bits.push(opts.format + (opts.quality || ''));
+ } else if (opts.quality) {
+ bits.push('q' + opts.quality);
+ }
+
+ if (opts.maxWidth) {
+ bits.push(opts.maxWidth);
+
+ if (opts.maxHeight) {
+ bits.push(opts.maxHeight);
+ }
+ }
+
+ bits.push(url);
+ return bits.join('/');
+};
+
+/**
+ * Replaces src attr of passed element with value of running `getImageUrl` on it
+ * Allows overriding of img.getAttribute(x-src) with srcVal
+ */
+
+ResizeImages._rewriteSrcAttribute = function(element, opts, srcVal){
+ srcVal = element.getAttribute(opts.sourceAttribute) || srcVal;
+ if (srcVal) {
+ var url = Utils.absolutify(srcVal);
+ if (Utils.httpUrl(url)) {
+ if (opts.onerror) {
+ element.setAttribute('onerror', opts.onerror);
+ }
+ element.setAttribute(opts.targetAttribute, ResizeImages.getImageURL(url, opts));
+ element.setAttribute('data-orig-src', srcVal);
+ // if using resize when not capturing, remove the sourceAttribute
+ // as long as it's not "src", which is the target attribute used
+ // when not capturing.
+ if (!capturing && opts.sourceAttribute != opts.targetAttribute) {
+ element.removeAttribute(opts.sourceAttribute);
+ }
+ }
+ }
+};
+
+/**
+ * Modifies src of `<source />` children of a `<picture>` element to use image
+ * resizer
+ */
+ResizeImages._resizeSourceElement = function(element, opts, rootSrc) {
+ // Grab optional width override
+ var width = element.getAttribute('data-width');
+ var localOpts = opts;
+ if (width) {
+ localOpts = Utils.clone(opts);
+ localOpts.maxWidth = width;
+ }
+ // pass along rootSrc if defined on `picture` element
+ ResizeImages._rewriteSrcAttribute(element, localOpts, rootSrc);
+};
+
+/**
+ * Takes a picture element and calls _resizeSourceElement on its `<source />`
+ * children
+ */
+ResizeImages._crawlPictureElement = function(el, opts) {
+ var sources = el.getElementsByTagName('source');
+ // If source elements are erased from the dom, leave the
+ // picture element alone.
+ if (sources.length === 0 || el.hasAttribute('mobify-optimized')) {
+ return;
+ }
+ el.setAttribute('mobify-optimized', '');
+
+ // Grab optional `data-src` attribute on `picture`.
+ // Used for preventing writing the same src multiple times for
+ // different `source` elements.
+ var rootSrc = el.getAttribute('data-src');
+
+ // resize the sources
+ for(var i = 0, len = sources.length; i < len; i++) {
+ ResizeImages._resizeSourceElement(sources[i], opts, rootSrc);
+ }
+};
+
+/**
+ * Searches a list of target dimensions for the smallest one that is greater than
+ * the passed value and return it, or return the greatst value if none are
+ * greater.
+ *
+ * Popular device resolutions:
+ * iPhone 3Gs - 320x480
+ * iPhone 4 - 640x960
+ * iPhone 5 - 650x1156
+ *
+ * Galaxy SIII/Nexus 4/Nexus 7 - 720x1280
+ * Galaxy SIV/Nexus 5 - 1080x1920
+ *
+ * iPad (non-retina) - 1024x768
+ * iPad (retina) - 2048x1536
+ *
+ * A larger list of target dimensions would include 720px, 800px, 1024px, 1280px
+ * and 1920px but they have been omitted due tot heir proximity to other, larger
+ * values
+ */
+var targetDims = [320, 640, 768, 1080, 1536, 2048, 4000];
+ResizeImages._getBinnedDimension = function(dim) {
+ var resultDim = 0;
+
+ for (var i = 0, len = targetDims.length; i < len; i++) {
+ resultDim = targetDims[i];
+ if (resultDim >= dim) {
+ break;
+ }
+ }
+ return resultDim;
+};
+
+/**
+ * Returns a boolean that indicates whether images should be resized.
+ * Looks for the viewport meta tag and parses it to determine whether the
+ * website is responsive (the viewport is set to the device's width). This
+ * ensures that images that are part of a larger viewport are not scaled.
+ */
+ResizeImages._shouldResize = function(document) {
+ var metaViewport = Utils.getMetaViewportProperties(document);
+ if (!metaViewport) {
+ return false;
+ }
+
+ // It's complicated, but what we want to know is whether the viewport
+ // matches the 'ideal viewport'. If either `initial-scale` is 1 or `width`
+ // is device-width or both, then the viewport will match the 'ideal
+ // viewport'. There are a few other special circumstances under which the
+ // viewport could be ideal, but we can't test for them.
+ //
+ // See: http://www.quirksmode.org/mobile/metaviewport/
+
+ // Ideal viewport when width=device-width
+ if (!metaViewport['initial-scale'] && metaViewport['width']) {
+ return metaViewport['width'] == 'device-width';
+ }
+
+ // Ideal viewport when initial-scale=1
+ if (!metaViewport['width'] && metaViewport['initial-scale']) {
+ return metaViewport['initial-scale'] == '1';
+ }
+
+ // Ideal viewport when width=device-width and the intial-scale is 1 or more
+ // (in that case it's just zoomed)
+ if (metaViewport['width'] && metaViewport['initial-scale']) {
+ initialScale = parseInt(metaViewport['initial-scale']);
+ return initialScale >= 1 && metaViewport['width'] == 'device-width';
+ }
+
+ return false
+};
+
+/**
+ * Processes options passed to `resize()`. Takes an options object that
+ * potentially has height and width set in css pixels, returns an object where
+ * they are expressed in device pixels, and other default options are set.
+ */
+ResizeImages.processOptions = function(options) {
+ var opts = Utils.clone(ResizeImages.defaults);
+ if (options) {
+ Utils.extend(opts, options);
+ }
+
+ // A null value for `resize` triggers the auto detect functionality. This
+ // uses the document to determine whether images should be resized and sets
+ // it as the new default.
+ if (opts.resize == null && options.document) {
+ var resize = ResizeImages._shouldResize(options.document);
+ ResizeImages.defaults.resize = opts.resize = resize;
+ }
+
+ if (!opts.format && opts.webp) {
+ opts.format = "webp";
+ }
+
+ // Without `resize` images are served through IR without changing their dimensions
+ if (!opts.resize) {
+ opts.maxWidth = opts.maxHeight = opts.devicePixelRatio = null;
+ }
+ else {
+ var dpr = opts.devicePixelRatio || window.devicePixelRatio;
+
+ var screenSize = Utils.getPhysicalScreenSize(dpr);
+
+ // If maxHeight/maxWidth are not specified, use screen dimensions
+ // in device pixels
+ var width = opts.maxWidth || ResizeImages._getBinnedDimension(screenSize.width);
+ var height = opts.maxHeight || undefined;
+
+ // Otherwise, compute device pixels
+ if (dpr && opts.maxWidth) {
+ width = width * dpr;
+ if (opts.maxHeight) {
+ height = height * dpr;
+ }
+ }
+
+ // round up in case of non-integer device pixel ratios
+ opts.maxWidth = Math.ceil(width);
+ if (opts.maxHeight && height) {
+ opts.maxHeight = Math.ceil(height);
+ }
+ }
+
+ return opts;
+};
+
+/**
+ * Searches the collection for image elements and modifies them to use
+ * the Image Resize service. Pass `options` to modify how the images are
+ * resized.
+ */
+ResizeImages.resize = function(elements, options) {
+ // Return early if elements is empty
+ if (!elements.length) {
+ return;
+ }
+
+ // Supplement `options` with the document from the first element
+ if (options && !options.document) {
+ options.document = elements[0].ownerDocument;
+ }
+ var opts = ResizeImages.processOptions(options);
+
+ for(var i=0; i < elements.length; i++) {
+ var element = elements[i];
+
+ // For an `img`, simply modify the src attribute
+ if (element.nodeName === 'IMG' && !element.hasAttribute('mobify-optimized')) {
+ element.setAttribute('mobify-optimized', '');
+ ResizeImages._rewriteSrcAttribute(element, opts);
+ }
+ // For a `picture`, (potentially) nuke src on `img`, and
+ // pass all `source` elements into modifyImages recursively
+ else if (element.nodeName === 'PICTURE') {
+ ResizeImages._crawlPictureElement(element, opts);
+ }
+ }
+
+ return elements;
+};
+
+ResizeImages.restoreOriginalSrc = function(event) {
+ var origSrc;
+ event.target.removeAttribute('onerror'); // remove ourselves
+ origSrc = event.target.getAttribute('data-orig-src')
+ if (origSrc) {
+ event.target.setAttribute('src', origSrc);
+ }
+};
+
+var capturing = window.Mobify && window.Mobify.capturing || false;
+
+ResizeImages.defaults = {
+ proto: '//',
+ host: 'ir0.mobify.com',
+ projectName: "oss-" + location.hostname.replace(/[^\w]/g, '-'),
+ sourceAttribute: "x-src",
+ targetAttribute: (capturing ? "x-src" : "src"),
+ webp: ResizeImages.supportsWebp(),
+ resize: true,
+ onerror: 'ResizeImages.restoreOriginalSrc(event);'
+};
+
+return ResizeImages;
+
+});
/**
* The Jazzcat client is a library for loading JavaScript from the Jazzcat
View
2  package.json
@@ -1,6 +1,6 @@
{
"name": "mobifyjs",
- "version": "2.0.6",
+ "version": "2.0.7",
"description": "The Mobify.js client side adaptation library.",
"author": "Mobify <dev@mobify.com>",
"dependencies": {
View
4 www/v2/docs/index.md
@@ -36,7 +36,7 @@ any element that loads external resources!**):
<script>!function(a,b,c,d,e){function g(a,c,d,e){var f=b.getElementsByTagName("script")[0];a.src=e,a.id=c,a.setAttribute("class",d),f.parentNode.insertBefore(a,f)}a.Mobify={points:[+new Date]};var f=/((; )|#|&|^)mobify=(\d)/.exec(location.hash+"; "+b.cookie);if(f&&f[3]){if(!+f[3])return}else if(!c())return;b.write('<plaintext style="display:none">'),setTimeout(function(){var c=a.Mobify=a.Mobify||{};c.capturing=!0;var f=b.createElement("script"),h="mobify",i=function(){var c=new Date;c.setTime(c.getTime()+3e5),b.cookie="mobify=0; expires="+c.toGMTString()+"; path=/",a.location=a.location.href};f.onload=function(){if(e)if("string"==typeof e){var c=b.createElement("script");c.onerror=i,g(c,"main-executable",h,mainUrl)}else a.Mobify.mainExecutable=e.toString(),e()},f.onerror=i,g(f,"mobify-js",h,d)})}(window,document,function(){a=/webkit|(firefox)[\/\s](\d+)|(opera)[\s\S]*version[\/\s](\d+)|(trident)[\/\s](\d+)|3ds/i.exec(navigator.userAgent);return!a||a[1]&&4>+a[2]||a[3]&&11>+a[4]||a[5]&&6>+a[6]?!1:!0},
// path to mobify.js
- "//cdn.mobify.com/mobifyjs/build/mobify-2.0.6.min.js",
+ "//cdn.mobify.com/mobifyjs/build/mobify-2.0.7.min.js",
// calls to APIs go here (or path to a main.js)
function() {
@@ -70,7 +70,7 @@ the DOM is ready.
Then, paste the following tag before <code>&lt;/head&gt;</code>, or top of
<code>&lt;body&gt;</code>:
- <script async src="//cdn.mobify.com/mobifyjs/build/mobify-2.0.6.min.js"></script>
+ <script async src="//cdn.mobify.com/mobifyjs/build/mobify-2.0.7.min.js"></script>
<script>
var intervalId = setInterval(function(){
if (window.Mobify) {
Please sign in to comment.
Something went wrong with that request. Please try again.