From dbd120f00241592bd1128fb226523c524dcc29dd Mon Sep 17 00:00:00 2001 From: Seneral <12800840+Seneral@users.noreply.github.com> Date: Sun, 5 Feb 2017 15:34:13 +0100 Subject: [PATCH 1/6] Removed writing of fixed width and height values on images to prevent unexpected behaviour --- hisrc.js | 7 ------- 1 file changed, 7 deletions(-) diff --git a/hisrc.js b/hisrc.js index 192eb37..af982c9 100644 --- a/hisrc.js +++ b/hisrc.js @@ -229,13 +229,6 @@ $el.data('m1src', src); } - // check for zero which often happens in safari. - if (!$el.attr('width') && $el.width() > 0) { - $el.attr('width', $el.width()); - } - if (!$el.attr('height') && $el.height() > 0) { - $el.attr('height', $el.height()); - } $el.on('speedTestComplete.hisrc', function(){ From e6b0201eeca386a596461d03b0a003d04be6cbae Mon Sep 17 00:00:00 2001 From: Seneral <12800840+Seneral@users.noreply.github.com> Date: Sun, 5 Feb 2017 16:20:01 +0100 Subject: [PATCH 2/6] Improved screen quality detection Replaced zoom level as decision between image quality with actual resolution (SubHD, HD and Retina) --- hisrc.js | 62 +++++++++++++++++++++----------------------------------- 1 file changed, 23 insertions(+), 39 deletions(-) diff --git a/hisrc.js b/hisrc.js index af982c9..e03b084 100644 --- a/hisrc.js +++ b/hisrc.js @@ -12,7 +12,6 @@ connectionTestResult: null, connectionKbps: null, connectionType: null, - devicePixelRatio: null }; $.hisrc.defaults = { @@ -23,7 +22,9 @@ speedTestKB: 50, speedTestExpireMinutes: 30, forcedBandwidth: false, - srcIsLowResolution: true + srcIsLowResolution: true, + minHDSize: 1080, + minRetinaSize: 2560, }; // for performance, run this right away (requires jQuery, but no need to wait for DOM to be ready) @@ -36,29 +37,15 @@ var settings = $.extend({ callback: function() {} }, $.hisrc.defaults, options), + $els = $(this); - $els = $(this), - - // check bandwidth via @Modernizr's network-connection.js - connection = navigator.connection || { type: 0 }, // polyfill - - isSlowConnection = connection.type == 3 - || connection.type == 4 - || /^[23]g$/.test(connection.type); - - - // get pixel ratio - $.hisrc.devicePixelRatio = 1; - if(window.devicePixelRatio !== undefined) { - $.hisrc.devicePixelRatio = window.devicePixelRatio; - } else if (window.matchMedia !== undefined) { - for (var i = 1; i <= 2; i += 0.5) { - if (window.matchMedia('(min-resolution: ' + i + 'dppx)').matches) { - $.hisrc.devicePixelRatio = i; - } - } - } + // check bandwidth via @Modernizr's network-connection.js + var connection = navigator.connection || { type: 0 }, // polyfill + isSlowConnection = connection.type == 3 || connection.type == 4 || /^[23]g$/.test(connection.type); + // Check for screen resolution (local, not global, so each hiSrc replacement call can specify different sizes) + var HDSupport = Math.max(screen.width, screen.height) >= settings.minHDSize, + retinaSupport = Math.max(screen.width, screen.height) >= settings.minRetinaSize; // variables/functions below for speed test are taken from Foresight.js // Copyright (c) 2012 Adam Bradley @@ -228,34 +215,31 @@ if (!$el.data('m1src')) { $el.data('m1src', src); } - - + $el.on('speedTestComplete.hisrc', function(){ if (speedConnectionStatus === STATUS_COMPLETE) { - if (isSlowConnection) { + if (isSlowConnection || !HDSupport) { + // Slow connection or no HD support: keep default $el.attr( 'src', $el.data('m1src') ); - } else { - - // check if client can get high res image - if ($.hisrc.devicePixelRatio > 1 && $.hisrc.bandwidth === 'high') { + } else if (HDSupport){ + // High bandwidth and atleast HD support: load higher quality image + if (retinaSupport && $.hisrc.bandwidth === 'high') { var image2x = $el.data('2x'); if (!image2x) { // use naming convention. image2x = $el.data('m1src').replace(/\.\w+$/, function(match) { return "@2x" + match; }); } setImageSource( $el, image2x ); - } else { - // don't load 1x unless src is a low res version. - if (settings.srcIsLowResolution) { - var image1x = $el.data('1x'); - if (!image1x) { - // use naming convention. - image1x = $el.data('m1src').replace(/\.\w+$/, function(match) { return "@1x" + match; }); - } - setImageSource( $el, image1x ); + } else if (settings.srcIsLowResolution) { + // High bandwidth and HD support and 1x not already loaded: load 1x + var image1x = $el.data('1x'); + if (!image1x) { + // use naming convention. + image1x = $el.data('m1src').replace(/\.\w+$/, function(match) { return "@1x" + match; }); } + setImageSource( $el, image1x ); } } // turn off so hisrc() can be called many times on same element. From b4cf442d1bbb483169fd7a6bae2b0a77ee84e363 Mon Sep 17 00:00:00 2001 From: Seneral <12800840+Seneral@users.noreply.github.com> Date: Sun, 5 Feb 2017 16:28:34 +0100 Subject: [PATCH 3/6] Fixed implementation of the speed test - SpeedTest is now performed globally by setting the complete event globally on $(document) - SpeedTest can now actually explicitly be started before loading the DOM and explicitly ommitted in all other calls by setting the speedTestUri - all calls to hiSrc now use existing SpeedTest results or subscribe to the completed event to wait with their execution --- hisrc.js | 54 +++++++++++++++++++++++++++++++++--------------------- 1 file changed, 33 insertions(+), 21 deletions(-) diff --git a/hisrc.js b/hisrc.js index e03b084..ee96ad1 100644 --- a/hisrc.js +++ b/hisrc.js @@ -71,16 +71,16 @@ $.hisrc.bandwidth = settings.forcedBandwidth; $.hisrc.connectionTestResult = 'forced'; speedConnectionStatus = STATUS_COMPLETE; - $els.trigger('speedTestComplete.hisrc'); + $(document).trigger('speedTestComplete.hisrc'); return; } // if the device pixel ratio is 1, then no need to do a network connection // speed test since it can't show hi-res anyways - if ( $.hisrc.devicePixelRatio === 1 ) { + if ( !HDSupport ) { $.hisrc.connectionTestResult = 'skip'; speedConnectionStatus = STATUS_COMPLETE; - $els.trigger('speedTestComplete.hisrc'); + $(document).trigger('speedTestComplete.hisrc'); return; } @@ -95,7 +95,7 @@ // we know this connection is slow, don't bother even doing a speed test $.hisrc.connectionTestResult = 'connTypeSlow'; speedConnectionStatus = STATUS_COMPLETE; - $els.trigger('speedTestComplete.hisrc'); + $(document).trigger('speedTestComplete.hisrc'); return; } @@ -104,19 +104,24 @@ try { var fsData = JSON.parse( localStorage.getItem( LOCAL_STORAGE_KEY ) ); if ( fsData !== null ) { - if ( ( new Date() ).getTime() < fsData.exp ) { + if ( ( new Date() ).getTime() < fsData.exp && settings.speedTestExpireMinutes !== 0 ) { // already have connection data within our desired timeframe // use this recent data instead of starting another test $.hisrc.bandwidth = fsData.bw; $.hisrc.connectionKbps = fsData.kbps; $.hisrc.connectionTestResult = 'localStorage'; speedConnectionStatus = STATUS_COMPLETE; - $els.trigger('speedTestComplete.hisrc'); + $(document).trigger('speedTestComplete.hisrc'); return; } } } catch( e ) { } + if (speedTestUri) { + // Start speed test if an URI has been specified + // This is useful when having multiple calls to hiSrc but only one should perform a speed test + //alert ("Starting speed test on device with " + (retinaSupport? "Retina" : (HDSupport? "HD" : "LowDef")) + " support!"); + var speedTestImg = document.createElement( 'img' ), endTime, @@ -127,9 +132,7 @@ // speed test image download completed // figure out how long it took and an estimated connection speed endTime = ( new Date() ).getTime(); - - var duration = ( endTime - startTime ) / 1000; - duration = ( duration > 1 ? duration : 1 ); // just to ensure we don't divide by 0 + var duration = Math.max ((endTime - startTime ) / 1000, 1); $.hisrc.connectionKbps = ( ( settings.speedTestKB * 1024 * 8 ) / duration ) / 1024; $.hisrc.bandwidth = ( $.hisrc.connectionKbps >= settings.minKbpsForHighBandwidth ? 'high' : 'low' ); @@ -188,8 +191,10 @@ localStorage.setItem( LOCAL_STORAGE_KEY, JSON.stringify( fsDataToSet ) ); } catch( e ) { } - // trigger swap once speedtest is complete. - $els.trigger('speedTestComplete.hisrc'); + // invoke all hiSrc calls subscribed to the this speed test complete event + $(document).trigger('speedTestComplete.hisrc'); + // clear event list so all calls to hisrc currently subscribed are removed + $(document).off('speedTestComplete.hisrc'); }, setImageSource = function ( $el, src ) { @@ -216,16 +221,16 @@ $el.data('m1src', src); } - $el.on('speedTestComplete.hisrc', function(){ - - if (speedConnectionStatus === STATUS_COMPLETE) { - - if (isSlowConnection || !HDSupport) { + var replace = function(){ + if ($.hisrc.connectionTestResult) { + // connection test has already been completed atleast once globally + if (isSlowConnection || $.hisrc.bandwidth === 'low' || !HDSupport) { // Slow connection or no HD support: keep default $el.attr( 'src', $el.data('m1src') ); - } else if (HDSupport){ + } else if ($.hisrc.bandwidth === 'high' && HDSupport) { // High bandwidth and atleast HD support: load higher quality image - if (retinaSupport && $.hisrc.bandwidth === 'high') { + if (retinaSupport) { + // High bandwidth and retina support: load 2x var image2x = $el.data('2x'); if (!image2x) { // use naming convention. @@ -242,10 +247,17 @@ setImageSource( $el, image1x ); } } - // turn off so hisrc() can be called many times on same element. - $el.off('speedTestComplete.hisrc'); } - }); + }; + + // Make sure replacements are being performed once the speed test has completed + if (speedConnectionStatus !== STATUS_COMPLETE && !$.hisrc.connectionTestResult) { + // speed test not yet completed: subscribe to the complete event + $(document).on('speedTestComplete.hisrc', replace); + } + else { // speed test already completed: replace right now + replace (); + } } }); From 5f19bb6a96746d923927511048bb3095fcdcfe83 Mon Sep 17 00:00:00 2001 From: Seneral <12800840+Seneral@users.noreply.github.com> Date: Sun, 5 Feb 2017 16:32:18 +0100 Subject: [PATCH 4/6] Implemented second chance test - Added optional 'SecondChance' test performs the SpeedTest once more when failed on HD-capable devices - Enable by setting secondChance parameter to true - Goal is to eliminate wrong bandwidth detection on desktop PCs due to bandwidth fluctuation; - This will not affect mobile networks or devices not needing this - It is only happening once at maximum during one cache expiration period --- hisrc.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/hisrc.js b/hisrc.js index ee96ad1..bc0f13e 100644 --- a/hisrc.js +++ b/hisrc.js @@ -12,6 +12,7 @@ connectionTestResult: null, connectionKbps: null, connectionType: null, + secondChanceUsed: null, }; $.hisrc.defaults = { @@ -25,6 +26,7 @@ srcIsLowResolution: true, minHDSize: 1080, minRetinaSize: 2560, + secondChance: false, }; // for performance, run this right away (requires jQuery, but no need to wait for DOM to be ready) @@ -175,6 +177,16 @@ // if we haven't already gotten a speed connection status then save the info if (speedConnectionStatus === STATUS_COMPLETE) { return; } + if (settings.secondChance && !$.hisrc.secondChanceUsed && HDSupport && $.hisrc.bandwidth === 'low' + && (connTestResult === 'networkSuccess' || connTestResult === 'networkError' || connTestResult === 'networkAbort')) { + // Re-run the test once on HD capable devices if second chance is specified and the last try failed + // This should eliminate wrong bandwidth detection on desktop PCs due to bandwidth fluctuations if desired + // This will not affect mobile networks or devices not needing this, and will only occur once during the test expire period + $.hisrc.secondChanceUsed = true; + initSpeedTest (); + return; + } + // first one with an answer wins speedConnectionStatus = STATUS_COMPLETE; $.hisrc.connectionTestResult = connTestResult; From 75ee934b5071fd91597ef896c84e6d1129dc9970 Mon Sep 17 00:00:00 2001 From: Seneral <12800840+Seneral@users.noreply.github.com> Date: Sun, 5 Feb 2017 16:36:36 +0100 Subject: [PATCH 5/6] Added missing bracket caused by a merging error --- hisrc.js | 1 + 1 file changed, 1 insertion(+) diff --git a/hisrc.js b/hisrc.js index bc0f13e..542a6dc 100644 --- a/hisrc.js +++ b/hisrc.js @@ -171,6 +171,7 @@ setTimeout( function () { speedTestComplete( 'networkSlow' ); }, speedTestTimeoutMS ); + } }, speedTestComplete = function ( connTestResult, expireMinutes ) { From 1d9357f9f00a98435621c25b29d7749b98c93e8d Mon Sep 17 00:00:00 2001 From: Seneral <12800840+Seneral@users.noreply.github.com> Date: Thu, 9 Feb 2017 18:32:32 +0100 Subject: [PATCH 6/6] Updated Readme to reflect latest changes --- README.md | 91 ++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 59 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index 1dbc3c5..a02b182 100644 --- a/README.md +++ b/README.md @@ -26,20 +26,20 @@ Installing HiSRC How the HiSRC jQuery Plugin Works ===== -The browser loads a "mobile first" image with an old-fashion `IMG` `SRC` attribute. +The browser loads a "mobile first" image with the old-fashion `IMG` `SRC` attribute. -Then the HiSRC jQuery plugin checks if the device has mobile bandwidth (like 3G) and if so leaves the "mobile first" image in place. +Then the HiSRC jQuery plugin checks if the device has mobile bandwidth (like 3G) or generally bad connection by performing a sample download and if so leaves the "mobile first" image in place. -If there is a high speed connection *and* the browser supports a device pixel ration greater, then a 2x image takes the place of the "mobile first" image. +If the device has a high speed connection *and* a high resolution (default 1080p), then a 1x image takes the place of the "mobile first" image. -If there is a high speed and better resolution, then a 1x image takes the place of the "mobile first" image. +If the device has a high speed connection and an even better resolution (default 2560p), then a 2x image takes the place of the "mobile first" image. Setting up ===== -Associate jQuery to your web document as well as the HiSRC plugin as well. +Associate jQuery to your web document as well as the HiSRC plugin. -In this code example, jQuery is link to Google's CDN and the HiSRC plugin is in the same directory as the web document: +In this code example, jQuery is linked from Google's CDN and the HiSRC plugin is in the same directory as the web document: ```html @@ -48,38 +48,59 @@ In this code example, jQuery is link to Google's CDN and the HiSRC plugin is in Use basic jQuery to pick out which images should be HiSRC'd: -```html -$(document).ready(function(){ - $(".hisrc img").hisrc(); - $(".hisrc img+img").hisrc({ - useTransparentGif: true, - speedTestUri: '50K.jpg' - }); -}) +``` JavaScript +$(document).ready (function () +{ // Replace all images with HiSRC + $("img.hisrc, hisrc img").hisrc({ + speedTestUri: "img/50K.jpg", + useTransparentGif: true, + minHDSize: 800, + minRetinaSize: 1500 + }); +}); ``` The high-resolution image links should be placed as the value of "data-1x" and "data-2x" in the markup of your web page. Alternatively if you ommit data-1x or data-2x, HiSRC will automatically use the convention of appending "@1x" or "@2x" to the filename. For example cat.png will become cat@1x.png and cat@2x.png. -```html +``` html

HiSRC Images

-
- - -
+
+ + +

Regular images

- - + + ``` -If you know you'll be using HiSRC on a page, you can improve performance by running the network test before the DOM is ready by calling: - -```html -$.hisrc.speedTest(); +If you know you'll be using HiSRC on a page, you can improve performance by starting the network test once before the DOM is ready and omit the test later when HiSRC'ing the images: + + +``` JavaScript +// Perform the first and only speed test of HiSRC +$.hisrc.speedTest ({ + minKbpsForHighBandwidth: 200, + speedTestUri: "img/50K.jpg", + speedTestKB: 50, + secondChance: true, // Enable second chance for desktop +}); +$(document).ready (function () +{ // Replace all images with HiSRC + $("img.hisrc, hisrc img").hisrc({ + speedTestUri: '', // Don't do another speed test + minHDSize: 800, + minRetinaSize: 1500, + }); +}); ``` +In the above example, a speed test using a 50k image is started once the script is first loaded, using the second chance option to help eliminate fails due to bandwidth fluctuations. +When the DOM has finished loading, all images are queued to be replaced by HiSRC once the speed test has finished, ommitting any further speed test. +All devices with a high bandwidth over 200Kbps and a minimum resolution of 800px will load the data-1x image, all with atleast a resolution of 1500px will load the data-2x image. + HiSRC supports the following options: ===== @@ -87,19 +108,25 @@ Option (default) - description __useTransparentGif__ (false) - if true the img src will be set to a transparent gif and the actual image will be displayed using CSS to set the background style. This is useful if you want to use media queries in your CSS to further enhance the responsiveness of your site. -__transparentGifSrc__ (data:image/gif;base64,R0lGODlhAQABAIAAAMz/AAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==) - if useTransparentGif is true this value will replace the img src. If you need to support IE6 and IE7 it is recommend you override this will a url to a 1x1 transparent gif. +__transparentGifSrc__ (data:image/gif;base64,R0lGODlhAQABAIAAAMz/AAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==) - if useTransparentGif is true this value will replace the img src. If you need to support IE6 and IE7 it is recommend you override this with an url to a 1x1 transparent gif. + +__minKbpsForHighBandwidth__ (300) - When doing a speed test, this is the minimum bandwidth considered to be "high speed". Shared across all calls to HiSRC. + +__speedTestUri__ (50K.jpg) - sample data url used for the speed test. It's recommended to change this. Shared across all calls to HiSRC. + +__speedTestKB__ (50) - this should equal the size of the file specified by speedTestUri to determine the download rate properly. Shared across all calls to HiSRC. -__minKbpsForHighBandwidth__ (300) - when doing a speed test this is the minimum bandwidth considered to be "high speed" +__speedTestExpireMinutes__ (30) - for this amount of time the speed test results are cached. Shared across all calls to HiSRC. -__speedTestUri__ (50K.jpg) - url used for the speed test. It's recommended to change this. +__forcedBandwidth__ (false) - set to 'low' or 'high' to override the speed test. Mostly used for debugging. Shared across all calls to HiSRC. -__speedTestKB__ (50) - this should equal the size of the file specified by speedTestUri so we know how long the download should take. +__secondChance__ (false) - try the speed test once again on desktop devices if it failed to eliminate fails solely due to bandwidth fluctuations. Shared across all calls to HiSRC. -__speedTestExpireMinutes__ (30) - we cache the speed test results for this ammount of time. +__srcIsLowResolution__ (true) - if true then src is a low-res image and will be replaced with 1x or 2x. If false, src is a 1x file already and will be replaced with 2x. Use false, if you only have two versions of the image: 1x and 2x. Can differ in each call to match selected images more apropriately. -__forcedBandwidth__ (false) - set to 'low' or 'high' to override the speed test. Mostly used for debugging. +__minHDSize__ (1080) - minimum device resolution needed to consider downloading the x1 image version. Compared with max(width, height). Can differ in each call to match selected images more apropriately. -__srcIsLowResolution__ (true) - if true then src is a low-res image will be replaced with 1x or 2x. If false, src is a 1x file already and will be replaced with 2x. Use false, if you only have two versions of the image: 1x and 2x. +__minRetinaSize__ (2560) - minimum device resolution needed to consider downloading the x2 image version. Compared with max(width, height). Can differ in each call to match selected images more apropriately. Attribution =====