imgix-optimizer 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 42dc4880ac65b270786015db528c75ba85b38d9552babe386ef4d93189c49dcb
4
- data.tar.gz: f20dfffa57e6305c77528e295d606abea7798b58e447c4831dc8e2834481bbab
3
+ metadata.gz: 5f8ebafc45462fd9534d73b40fd5d700688a15c94852d57e63f5e47e633d26f2
4
+ data.tar.gz: 95d929ae1beda1f74fc82cedd1f414c90a069ab8ece1d90a67159782393d9ec7
5
5
  SHA512:
6
- metadata.gz: 1abe08ab58629f47d4790474a8453a6c751bf3934a7618ea02d639628f11f29cbd8aa4dd2c074431e41c778552821c4496f58c936ab5af4e6a14b4184fb2f6a4
7
- data.tar.gz: 5fbe66cd48a96c5b583856d8a15d3e92102f01720a4acadcbadbab960f4d911d3c9c231978d3119bb2a5bca84b7966a541ada936dcb755d08c065da1e24369e4
6
+ metadata.gz: 8c42f618d3b5aef875911b05b2e76b942110fcaa06af2a870d2d3443bc8bf131eb7b2d21d520f4cdcdfae5be04e053eae4424951dbcf545e74220b2e9b404edd
7
+ data.tar.gz: d4d88a4384f3030009e6f5e9564cc5788e4fd712be36a305dcc87062ad7816d16765456b939cc052884f635b3d6250985fe28b502d88a410e9dfcf679ad982d4
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- imgix-optimizer (0.0.4)
4
+ imgix-optimizer (0.0.5)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -0,0 +1 @@
1
+ !function(){"use strict";var t=function(e,i){if(!(e instanceof i))throw new TypeError("Cannot call a class as a function")},e=function(){function l(e,i){for(var t=0;t<i.length;t++){var l=i[t];l.enumerable=l.enumerable||!1,l.configurable=!0,"value"in l&&(l.writable=!0),Object.defineProperty(e,l.key,l)}}return function(e,i,t){return i&&l(e.prototype,i),t&&l(e,t),e}}(),l=function(){function i(e){t(this,i),this.timeToFade=500,this.dpr=window.devicePixelRatio||1,this.el=$(e),"none"!=this.el.css("background-image")&&(this.initEl(),this.initOptimization(),this.initEventListeners())}return e(i,[{key:"initOptimization",value:function(){var e=this;$("<img>").on("load",function(){return e.renderTmpPlaceholderEl()}).attr("src",this.placeholderImgUrl)}},{key:"initEl",value:function(){this.setPlaceholderImgUrl(),this.setContainerTmpCss(),this.setElTmpCss()}},{key:"setPlaceholderImgUrl",value:function(){this.placeholderImgUrl=this.el.css("background-image").replace("url(","").replace(")","").replace(/\"/gi,"").replace(/\'/gi,"").split(", ")[0]}},{key:"setContainerTmpCss",value:function(){this.parentStyles={display:this.el.parent().css("display"),position:this.el.parent().css("position")},this.el.parent().css({display:"block",position:"relative"})}},{key:"setElTmpCss",value:function(){"absolute"!=this.el.css("position")&&this.el.css("position","relative")}},{key:"renderTmpPlaceholderEl",value:function(){this.initTmpPlaceholderEl(),this.setTmpPlaceholderElCss(),this.addTmpPlaceholderElToDom(),this.renderFullSizeImg()}},{key:"initTmpPlaceholderEl",value:function(){this.tmpPlaceholderEl=this.el.clone(),this.tmpPlaceholderEl.html("")}},{key:"setTmpPlaceholderElCss",value:function(){this.tmpPlaceholderEl.addClass("imgix-optimizing"),this.tmpPlaceholderEl.css({position:"absolute",top:this.el.position().top,left:this.el.position().left,width:this.el.outerWidth(),height:this.el.outerHeight(),backgroundColor:"transparent"})}},{key:"addTmpPlaceholderElToDom",value:function(){this.tmpPlaceholderEl.insertBefore(this.el)}},{key:"renderFullSizeImg",value:function(){this.removeElBgImg(),this.initTmpFullSizeEl(),this.setTmpFullSizeElImg(),this.addTmpFullSizeElToDom(),this.initTransition()}},{key:"removeElBgImg",value:function(){this.elBgColor=this.el.css("background-color"),this.el.css("background-color","transparent"),this.el.css("background-image","")}},{key:"initTmpFullSizeEl",value:function(){this.tmpFullSizeEl=this.tmpPlaceholderEl.clone()}},{key:"setFullSizeImgUrl",value:function(){var e=this.placeholderImgUrl.split("?"),i=e[e.length-1].split("&"),t={};for(var l in i.map(function(e){return t[e.split("=")[0]]=e.split("=")[1]}),this.el.outerWidth()>=this.el.outerHeight()?(t.w=this.el.outerWidth()*this.dpr,delete t.h):(t.h=this.el.outerHeight()*this.dpr,delete t.w),i=[],t)i.push(l+"="+t[l]);return this.fullSizeImgUrl=e[0]+"?"+i.join("&")}},{key:"setTmpFullSizeElImg",value:function(){this.setFullSizeImgUrl(),this.tmpFullSizeEl.css("background-image",'url("'+this.fullSizeImgUrl+'")')}},{key:"addTmpFullSizeElToDom",value:function(){this.tmpFullSizeEl.insertBefore(this.tmpPlaceholderEl)}},{key:"initTransition",value:function(){$("<img>").on("load",$.proxy(this.transitionImg,this)).attr("src",this.fullSizeImgUrl)}},{key:"transitionImg",value:function(){var e=this;this.fadeOutTmpPlaceholderEl(),setTimeout(function(){e.updateElImg(),e.replaceElTmpCss(),e.replaceContainerTmpCss(),e.removeTmpEls()},this.timeToFade)}},{key:"fadeOutTmpPlaceholderEl",value:function(){this.tmpPlaceholderEl.fadeTo(this.timeToFade,0)}},{key:"updateElImg",value:function(){this.setFullSizeImgUrl(),this.el.css("background-image","url('"+this.fullSizeImgUrl+"')")}},{key:"replaceElTmpCss",value:function(){this.el.css("background-color",this.elBgColor)}},{key:"replaceContainerTmpCss",value:function(){this.el.parent().css({display:this.parentStyles.display,position:this.parentStyles.position})}},{key:"removeTmpEls",value:function(){this.tmpPlaceholderEl.remove(),this.tmpFullSizeEl.remove(),this.tmpPlaceholderEl=void 0,this.tmpFullSizeEl=void 0}},{key:"initEventListeners",value:function(){var i=this;this.initResizeEnd(),$(window).on("resizeEnd",function(e){return i.updateElImg()})}},{key:"initResizeEnd",value:function(){$(window).resize(function(){this.resizeTo&&clearTimeout(this.resizeTo),this.resizeTo=setTimeout(function(){$(this).trigger("resizeEnd")},500)})}}]),i}(),s=function(){function i(e){t(this,i),this.timeToFade=500,this.placeholderImg=$(e),this.initPlaceholder(),this.initOptimization()}return e(i,[{key:"initOptimization",value:function(){$("<img>").on("load",$.proxy(this.renderFullSizeImg,this)).attr("src",this.placeholderImg.attr("src"))}},{key:"initPlaceholder",value:function(){this.setPlaceholderCss()}},{key:"setPlaceholderCss",value:function(){"absolute"!=this.placeholderImg.css("position")&&this.placeholderImg.css("position","relative")}},{key:"renderFullSizeImg",value:function(){this.initFullSizeImg(),this.setFullSizeImgTempCss(),this.setFullSizeImgSrc(),this.addFullSizeImgToDom(),this.initTransition()}},{key:"initFullSizeImg",value:function(){this.fullSizeImg=this.placeholderImg.clone()}},{key:"setFullSizeImgTempCss",value:function(){this.fullSizeImg.css({position:"absolute",top:this.placeholderImg.position().top,left:this.placeholderImg.position().left,width:this.placeholderImg.width(),height:this.placeholderImg.height()})}},{key:"setFullSizeImgSrc",value:function(){var e=this.placeholderImg.attr("src").replace(/(\?|\&)(w=)(\d+)/i,"$1$2"+this.placeholderImg.width()).replace(/(\?|\&)(h=)(\d+)/i,"$1$2"+this.placeholderImg.height());this.fullSizeImg.attr("ix-src",e),this.fullSizeImg.addClass("img-responsive imgix-optimizing"),this.fullSizeImg.removeAttr("data-optimize-img")}},{key:"addFullSizeImgToDom",value:function(){this.fullSizeImg.insertBefore(this.placeholderImg)}},{key:"initTransition",value:function(){var e=this;this.fullSizeImg.on("load",function(){return e.transitionImg()}),imgix.init()}},{key:"transitionImg",value:function(){var e=this;if(!this.placeholderImg)return!0;this.fadeOutPlaceholder(),setTimeout(function(){e.removeFullSizeImgProperties(),e.removeImg()},this.timeToFade)}},{key:"fadeOutPlaceholder",value:function(){this.placeholderImg.fadeTo(this.timeToFade,0)}},{key:"removeFullSizeImgProperties",value:function(){this.fullSizeImg.removeAttr("style"),this.fullSizeImg.removeClass("imgix-optimizing")}},{key:"removeImg",value:function(){this.placeholderImg&&(this.placeholderImg.remove(),this.placeholderImg=void 0)}}]),i}(),i=function(){function i(){var e=0<arguments.length&&void 0!==arguments[0]?arguments[0]:{};t(this,i),this.initOptions(e),this.optimizeImages(),this.optimizeBgImages()}return e(i,[{key:"initOptions",value:function(){var e=0<arguments.length&&void 0!==arguments[0]?arguments[0]:{};this.options=e;var i={parent:"body"};for(var t in i)i.hasOwnProperty(t)&&!this.options[t]&&(this.options[t]=i[t])}},{key:"optimizeImages",value:function(){$(this.options.parent+" img[data-optimize-img]").each(function(e,i){new s(i)})}},{key:"optimizeBgImages",value:function(){return $(this.options.parent+" [data-optimize-bg-img]").each(function(e,i){new l(i)}),!0}}]),i}();window.Imgix=window.Imgix||{},Imgix.ImgixBgImage=l,Imgix.ImgixImage=s,Imgix.Optimizer=i}();
@@ -98,7 +98,14 @@
98
98
  }, {
99
99
  key: 'setContainerTmpCss',
100
100
  value: function setContainerTmpCss() {
101
- this.el.parent().css('position', 'relative');
101
+ this.parentStyles = {
102
+ display: this.el.parent().css('display'),
103
+ position: this.el.parent().css('position')
104
+ };
105
+ this.el.parent().css({
106
+ display: 'block',
107
+ position: 'relative'
108
+ });
102
109
  }
103
110
 
104
111
  /**
@@ -308,6 +315,7 @@
308
315
  setTimeout(function () {
309
316
  _this2.updateElImg();
310
317
  _this2.replaceElTmpCss();
318
+ _this2.replaceContainerTmpCss();
311
319
  _this2.removeTmpEls();
312
320
  }, this.timeToFade);
313
321
  }
@@ -349,6 +357,19 @@
349
357
  this.el.css('background-color', this.elBgColor);
350
358
  }
351
359
 
360
+ /**
361
+ * Reset the container's adjusted CSS properties.
362
+ */
363
+
364
+ }, {
365
+ key: 'replaceContainerTmpCss',
366
+ value: function replaceContainerTmpCss() {
367
+ this.el.parent().css({
368
+ display: this.parentStyles.display,
369
+ position: this.parentStyles.position
370
+ });
371
+ }
372
+
352
373
  /**
353
374
  * Remove both temporary elements from the DOM.
354
375
  */
data/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "imgix-optimizer",
3
- "version": "0.0.4",
3
+ "version": "0.0.5",
4
4
  "devDependencies": {
5
5
  "babel-core": "^6.26.3",
6
6
  "babel-preset-es2015-rollup": "^3.0.0",
@@ -58,7 +58,14 @@ export default class ImgixBgImage {
58
58
  * correct location.
59
59
  */
60
60
  setContainerTmpCss() {
61
- this.el.parent().css('position', 'relative');
61
+ this.parentStyles = {
62
+ display: this.el.parent().css('display'),
63
+ position: this.el.parent().css('position')
64
+ }
65
+ this.el.parent().css({
66
+ display: 'block',
67
+ position: 'relative'
68
+ });
62
69
  }
63
70
 
64
71
  /**
@@ -225,6 +232,7 @@ export default class ImgixBgImage {
225
232
  setTimeout(() => {
226
233
  this.updateElImg();
227
234
  this.replaceElTmpCss();
235
+ this.replaceContainerTmpCss();
228
236
  this.removeTmpEls();
229
237
  }, this.timeToFade);
230
238
  }
@@ -257,6 +265,16 @@ export default class ImgixBgImage {
257
265
  this.el.css('background-color', this.elBgColor);
258
266
  }
259
267
 
268
+ /**
269
+ * Reset the container's adjusted CSS properties.
270
+ */
271
+ replaceContainerTmpCss() {
272
+ this.el.parent().css({
273
+ display: this.parentStyles.display,
274
+ position: this.parentStyles.position
275
+ });
276
+ }
277
+
260
278
  /**
261
279
  * Remove both temporary elements from the DOM.
262
280
  */
@@ -0,0 +1,695 @@
1
+ (function () {
2
+ 'use strict';
3
+
4
+ var classCallCheck = function (instance, Constructor) {
5
+ if (!(instance instanceof Constructor)) {
6
+ throw new TypeError("Cannot call a class as a function");
7
+ }
8
+ };
9
+
10
+ var createClass = function () {
11
+ function defineProperties(target, props) {
12
+ for (var i = 0; i < props.length; i++) {
13
+ var descriptor = props[i];
14
+ descriptor.enumerable = descriptor.enumerable || false;
15
+ descriptor.configurable = true;
16
+ if ("value" in descriptor) descriptor.writable = true;
17
+ Object.defineProperty(target, descriptor.key, descriptor);
18
+ }
19
+ }
20
+
21
+ return function (Constructor, protoProps, staticProps) {
22
+ if (protoProps) defineProperties(Constructor.prototype, protoProps);
23
+ if (staticProps) defineProperties(Constructor, staticProps);
24
+ return Constructor;
25
+ };
26
+ }();
27
+
28
+ var ImgixBgImage = function () {
29
+ function ImgixBgImage(el) {
30
+ classCallCheck(this, ImgixBgImage);
31
+
32
+ // Length of time to complete fade-in transition.
33
+ this.timeToFade = 500;
34
+ // Device pixel ratio assumes 1 if not set.
35
+ this.dpr = window['devicePixelRatio'] || 1;
36
+ // The primary element (i.e. the one with the background image).
37
+ this.el = $(el);
38
+ // Background image CSS property must be present.
39
+ if (this.el.css('background-image') == 'none') {
40
+ return;
41
+ }
42
+ // Prepare the element and its container for optimization.
43
+ this.initEl();
44
+ // Kick off the optimization process.
45
+ this.initOptimization();
46
+ // Listen for window resize events.
47
+ this.initEventListeners();
48
+ }
49
+
50
+ /**
51
+ * Load an image in memory (not within the DOM) with the same source as the
52
+ * placeholder image. Once that has completed, we know we're safe to begin
53
+ * processing.
54
+ */
55
+
56
+
57
+ createClass(ImgixBgImage, [{
58
+ key: 'initOptimization',
59
+ value: function initOptimization() {
60
+ var _this = this;
61
+
62
+ $('<img>').on('load', function () {
63
+ return _this.renderTmpPlaceholderEl();
64
+ }).attr('src', this.placeholderImgUrl);
65
+ }
66
+
67
+ // ---------------------------------------- | Main Element
68
+
69
+ /**
70
+ * Prepare the main element and its container for optimization.
71
+ */
72
+
73
+ }, {
74
+ key: 'initEl',
75
+ value: function initEl() {
76
+ this.setPlaceholderImgUrl();
77
+ this.setContainerTmpCss();
78
+ this.setElTmpCss();
79
+ }
80
+
81
+ /**
82
+ * Set reference to original image URL, which is expected to be a small
83
+ * placeholder.
84
+ */
85
+
86
+ }, {
87
+ key: 'setPlaceholderImgUrl',
88
+ value: function setPlaceholderImgUrl() {
89
+ this.placeholderImgUrl = this.el.css('background-image').replace('url(', '').replace(')', '').replace(/\"/gi, "").replace(/\'/gi, "").split(', ')[0];
90
+ }
91
+
92
+ /**
93
+ * The parent of our jumbotron container should be relatively positioned
94
+ * (temporarily) so that we can absolutely position the temp image in the
95
+ * correct location.
96
+ */
97
+
98
+ }, {
99
+ key: 'setContainerTmpCss',
100
+ value: function setContainerTmpCss() {
101
+ this.parentStyles = {
102
+ display: this.el.parent().css('display'),
103
+ position: this.el.parent().css('position')
104
+ };
105
+ this.el.parent().css({
106
+ display: 'block',
107
+ position: 'relative'
108
+ });
109
+ }
110
+
111
+ /**
112
+ * The main element must have a position set for it to be rendered on top of
113
+ * the temporary full-size image. We assume that if the element is not
114
+ * explicitly positioned absolutely, then it can safely be positioned
115
+ * relatively.
116
+ */
117
+
118
+ }, {
119
+ key: 'setElTmpCss',
120
+ value: function setElTmpCss() {
121
+ if (this.el.css('position') != 'absolute') {
122
+ this.el.css('position', 'relative');
123
+ }
124
+ }
125
+
126
+ // ---------------------------------------- | Placeholder Image (Temp)
127
+
128
+ /**
129
+ * Render a clone of the element with the background image directly behind
130
+ * itself.
131
+ */
132
+
133
+ }, {
134
+ key: 'renderTmpPlaceholderEl',
135
+ value: function renderTmpPlaceholderEl() {
136
+ this.initTmpPlaceholderEl();
137
+ this.setTmpPlaceholderElCss();
138
+ this.addTmpPlaceholderElToDom();
139
+ this.renderFullSizeImg();
140
+ }
141
+
142
+ /**
143
+ * Create a clone of the element with the background image. Remove content
144
+ * from the clone -- often elements with a background image contain content.
145
+ */
146
+
147
+ }, {
148
+ key: 'initTmpPlaceholderEl',
149
+ value: function initTmpPlaceholderEl() {
150
+ this.tmpPlaceholderEl = this.el.clone();
151
+ this.tmpPlaceholderEl.html('');
152
+ }
153
+
154
+ /**
155
+ * Position the clone directly behind the main element
156
+ */
157
+
158
+ }, {
159
+ key: 'setTmpPlaceholderElCss',
160
+ value: function setTmpPlaceholderElCss() {
161
+ this.tmpPlaceholderEl.addClass('imgix-optimizing');
162
+ this.tmpPlaceholderEl.css({
163
+ position: 'absolute',
164
+ top: this.el.position().top,
165
+ left: this.el.position().left,
166
+ width: this.el.outerWidth(),
167
+ height: this.el.outerHeight(),
168
+ backgroundColor: 'transparent'
169
+ });
170
+ }
171
+
172
+ /**
173
+ * Add temporary element to the DOM, directly before the main element
174
+ * containing the background image.
175
+ */
176
+
177
+ }, {
178
+ key: 'addTmpPlaceholderElToDom',
179
+ value: function addTmpPlaceholderElToDom() {
180
+ this.tmpPlaceholderEl.insertBefore(this.el);
181
+ }
182
+
183
+ // ---------------------------------------- | Full-Size Image (Temp)
184
+
185
+ /**
186
+ * Create another clone, this time of the temporary placeholder image. This
187
+ * new element sits behind the other two and is responsible for loading the
188
+ * full-size image.
189
+ */
190
+
191
+ }, {
192
+ key: 'renderFullSizeImg',
193
+ value: function renderFullSizeImg() {
194
+ this.removeElBgImg();
195
+ this.initTmpFullSizeEl();
196
+ this.setTmpFullSizeElImg();
197
+ this.addTmpFullSizeElToDom();
198
+ this.initTransition();
199
+ }
200
+
201
+ /**
202
+ * Remove the background color and image from the main element. The user won't
203
+ * notice this transition because the temp duplicate image is already set and
204
+ * is sitting behind the primary element.
205
+ *
206
+ * This also stores a reference to the original background color so we can put
207
+ * it back when the transition is complete.
208
+ */
209
+
210
+ }, {
211
+ key: 'removeElBgImg',
212
+ value: function removeElBgImg() {
213
+ this.elBgColor = this.el.css('background-color');
214
+ this.el.css('background-color', 'transparent');
215
+ this.el.css('background-image', '');
216
+ }
217
+
218
+ /**
219
+ * The temporary full-size element is a clone of the temporary placeholder
220
+ * image element.
221
+ */
222
+
223
+ }, {
224
+ key: 'initTmpFullSizeEl',
225
+ value: function initTmpFullSizeEl() {
226
+ this.tmpFullSizeEl = this.tmpPlaceholderEl.clone();
227
+ }
228
+
229
+ /**
230
+ * Sets a reference to the full-size image URL based on the current dimensions
231
+ * of the main element.
232
+ */
233
+
234
+ }, {
235
+ key: 'setFullSizeImgUrl',
236
+ value: function setFullSizeImgUrl() {
237
+ // Work with the placeholdler image URL, which has been pulled from the
238
+ // background-image css property of the main elements.
239
+ var url = this.placeholderImgUrl.split('?');
240
+ // q is an array of querystring parameters as ["k=v", "k=v", ...].
241
+ var q = url[url.length - 1].split('&');
242
+ // Mapping q converts the array to an object of querystring parameters as
243
+ // { k: v, k: v, ... }.
244
+ var args = {};
245
+ q.map(function (x) {
246
+ return args[x.split('=')[0]] = x.split('=')[1];
247
+ });
248
+ // If the image's container is wider than it is tall, we only set width and
249
+ // unset height, and vice versa.
250
+ if (this.el.outerWidth() >= this.el.outerHeight()) {
251
+ args['w'] = this.el.outerWidth() * this.dpr;
252
+ delete args['h'];
253
+ } else {
254
+ args['h'] = this.el.outerHeight() * this.dpr;
255
+ delete args['w'];
256
+ }
257
+ // Redefine q and go the other direction -- take the args object and convert
258
+ // it back to an array of querystring parameters, as ["k=v", "k=v", ...].
259
+ q = [];
260
+ for (var k in args) {
261
+ q.push(k + '=' + args[k]);
262
+ }
263
+ // Store the result and return.
264
+ return this.fullSizeImgUrl = url[0] + '?' + q.join('&');
265
+ }
266
+
267
+ /**
268
+ * Change the URL of this temporary element's background image to be the
269
+ * full-size image.
270
+ */
271
+
272
+ }, {
273
+ key: 'setTmpFullSizeElImg',
274
+ value: function setTmpFullSizeElImg() {
275
+ this.setFullSizeImgUrl();
276
+ this.tmpFullSizeEl.css('background-image', 'url("' + this.fullSizeImgUrl + '")');
277
+ }
278
+
279
+ /**
280
+ * Add the temporary full-size element direct before the temporary placeholder
281
+ * element.
282
+ */
283
+
284
+ }, {
285
+ key: 'addTmpFullSizeElToDom',
286
+ value: function addTmpFullSizeElToDom() {
287
+ this.tmpFullSizeEl.insertBefore(this.tmpPlaceholderEl);
288
+ }
289
+
290
+ // ---------------------------------------- | Transition
291
+
292
+ /**
293
+ * Load full-size image in memory. When it has loaded we can confidentally
294
+ * fade out the placeholder, knowing the full-size image will be in its place.
295
+ */
296
+
297
+ }, {
298
+ key: 'initTransition',
299
+ value: function initTransition() {
300
+ $('<img>').on('load', $.proxy(this.transitionImg, this)).attr('src', this.fullSizeImgUrl);
301
+ }
302
+
303
+ /**
304
+ * Fade out the temporary placeholder, set the background-image on the main
305
+ * element to the full-size URL, then remove the temporary elements behind the
306
+ * main element
307
+ */
308
+
309
+ }, {
310
+ key: 'transitionImg',
311
+ value: function transitionImg() {
312
+ var _this2 = this;
313
+
314
+ this.fadeOutTmpPlaceholderEl();
315
+ setTimeout(function () {
316
+ _this2.updateElImg();
317
+ _this2.replaceElTmpCss();
318
+ _this2.replaceContainerTmpCss();
319
+ _this2.removeTmpEls();
320
+ }, this.timeToFade);
321
+ }
322
+
323
+ /**
324
+ * Fade out the placeholder element. This was the temporary clone of the main
325
+ * element that has a placeholder background image.
326
+ *
327
+ * Rememeber the main element's background image was unset and its color set
328
+ * to transparent. That is why fading out this temporary image will work
329
+ * properly.
330
+ */
331
+
332
+ }, {
333
+ key: 'fadeOutTmpPlaceholderEl',
334
+ value: function fadeOutTmpPlaceholderEl() {
335
+ this.tmpPlaceholderEl.fadeTo(this.timeToFade, 0);
336
+ }
337
+
338
+ /**
339
+ * Reset the image URL (this helps if the size of the element has changed),
340
+ * then set the background image to the new source.
341
+ */
342
+
343
+ }, {
344
+ key: 'updateElImg',
345
+ value: function updateElImg() {
346
+ this.setFullSizeImgUrl();
347
+ this.el.css('background-image', 'url(\'' + this.fullSizeImgUrl + '\')');
348
+ }
349
+
350
+ /**
351
+ * Set the background color back to what it was before the transition.
352
+ */
353
+
354
+ }, {
355
+ key: 'replaceElTmpCss',
356
+ value: function replaceElTmpCss() {
357
+ this.el.css('background-color', this.elBgColor);
358
+ }
359
+
360
+ /**
361
+ * Reset the container's adjusted CSS properties.
362
+ */
363
+
364
+ }, {
365
+ key: 'replaceContainerTmpCss',
366
+ value: function replaceContainerTmpCss() {
367
+ this.el.parent().css({
368
+ display: this.parentStyles.display,
369
+ position: this.parentStyles.position
370
+ });
371
+ }
372
+
373
+ /**
374
+ * Remove both temporary elements from the DOM.
375
+ */
376
+
377
+ }, {
378
+ key: 'removeTmpEls',
379
+ value: function removeTmpEls() {
380
+ this.tmpPlaceholderEl.remove();
381
+ this.tmpFullSizeEl.remove();
382
+ this.tmpPlaceholderEl = undefined;
383
+ this.tmpFullSizeEl = undefined;
384
+ }
385
+
386
+ // ---------------------------------------- | Event Listeners
387
+
388
+ /**
389
+ * Listener for window resize events and update the image when the event ends.
390
+ */
391
+
392
+ }, {
393
+ key: 'initEventListeners',
394
+ value: function initEventListeners() {
395
+ var _this3 = this;
396
+
397
+ this.initResizeEnd();
398
+ $(window).on('resizeEnd', function (event) {
399
+ return _this3.updateElImg();
400
+ });
401
+ }
402
+
403
+ /**
404
+ * Trigger "resizeEnd" event on the window object after resizing has ceased
405
+ * for at least 0.5 seconds.
406
+ */
407
+
408
+ }, {
409
+ key: 'initResizeEnd',
410
+ value: function initResizeEnd() {
411
+ $(window).resize(function () {
412
+ if (this.resizeTo) {
413
+ clearTimeout(this.resizeTo);
414
+ }
415
+ this.resizeTo = setTimeout(function () {
416
+ $(this).trigger('resizeEnd');
417
+ }, 500);
418
+ });
419
+ }
420
+ }]);
421
+ return ImgixBgImage;
422
+ }();
423
+
424
+ var ImgixImage = function () {
425
+ function ImgixImage(img) {
426
+ classCallCheck(this, ImgixImage);
427
+
428
+ // Length of crossfade transition.
429
+ this.timeToFade = 500;
430
+ // The main image (pixelated placeholder).
431
+ this.placeholderImg = $(img);
432
+ // Configure the main placeholder image.
433
+ this.initPlaceholder();
434
+ // Kick off the optimization process.
435
+ this.initOptimization();
436
+ }
437
+
438
+ /**
439
+ * Load an image in memory (not within the DOM) with the same source as the
440
+ * placeholder image. Once that has completed, we know we're safe to begin
441
+ * processing.
442
+ */
443
+
444
+
445
+ createClass(ImgixImage, [{
446
+ key: 'initOptimization',
447
+ value: function initOptimization() {
448
+ $('<img>').on('load', $.proxy(this.renderFullSizeImg, this)).attr('src', this.placeholderImg.attr('src'));
449
+ }
450
+
451
+ // ---------------------------------------- | Placeholder Image
452
+
453
+ /**
454
+ * Make necessary CSS adjustments to main placeholder image.
455
+ */
456
+
457
+ }, {
458
+ key: 'initPlaceholder',
459
+ value: function initPlaceholder() {
460
+ this.setPlaceholderCss();
461
+ }
462
+
463
+ /**
464
+ * The main image must have a position set for it to remain in front of the
465
+ * full-size image. We assume that if the element is not explicitly positioned
466
+ * absolutely, then it can safely be positioned relatively.
467
+ */
468
+
469
+ }, {
470
+ key: 'setPlaceholderCss',
471
+ value: function setPlaceholderCss() {
472
+ if (this.placeholderImg.css('position') != 'absolute') {
473
+ this.placeholderImg.css('position', 'relative');
474
+ }
475
+ }
476
+
477
+ // ---------------------------------------- | Full-Size Image
478
+
479
+ /**
480
+ * Render the full-size image behind the placeholder image.
481
+ */
482
+
483
+ }, {
484
+ key: 'renderFullSizeImg',
485
+ value: function renderFullSizeImg() {
486
+ this.initFullSizeImg();
487
+ this.setFullSizeImgTempCss();
488
+ this.setFullSizeImgSrc();
489
+ this.addFullSizeImgToDom();
490
+ this.initTransition();
491
+ }
492
+
493
+ /**
494
+ * The full-size image is a clone of the placeholder image. This enables us to
495
+ * easily replace it without losing any necessary styles or attributes.
496
+ */
497
+
498
+ }, {
499
+ key: 'initFullSizeImg',
500
+ value: function initFullSizeImg() {
501
+ this.fullSizeImg = this.placeholderImg.clone();
502
+ }
503
+
504
+ /**
505
+ * Give the full-size image a temporary set of CSS rules so that it can sit
506
+ * directly behind the placeholder image while loading.
507
+ */
508
+
509
+ }, {
510
+ key: 'setFullSizeImgTempCss',
511
+ value: function setFullSizeImgTempCss() {
512
+ this.fullSizeImg.css({
513
+ position: 'absolute',
514
+ top: this.placeholderImg.position().top,
515
+ left: this.placeholderImg.position().left,
516
+ width: this.placeholderImg.width(),
517
+ height: this.placeholderImg.height()
518
+ });
519
+ }
520
+
521
+ /**
522
+ * Prep the full-size image with the attributes necessary to become its full
523
+ * size. Right now it is still just a replica of the placeholder, sitting
524
+ * right behind the placeholder.
525
+ *
526
+ * We set the src directly even though we're using imgix.js because older
527
+ * browsers don't support the srcset attribute which is what imgix.js relies
528
+ * upon.
529
+ */
530
+
531
+ }, {
532
+ key: 'setFullSizeImgSrc',
533
+ value: function setFullSizeImgSrc() {
534
+ var newSrc = this.placeholderImg.attr('src').replace(/(\?|\&)(w=)(\d+)/i, '$1$2' + this.placeholderImg.width()).replace(/(\?|\&)(h=)(\d+)/i, '$1$2' + this.placeholderImg.height());
535
+ this.fullSizeImg.attr('ix-src', newSrc);
536
+ // TODO: Make this a configurable option or document it as a more semantic temporary class
537
+ this.fullSizeImg.addClass('img-responsive imgix-optimizing');
538
+ // TODO: This should respect the option from the Optimizer class for the select
539
+ this.fullSizeImg.removeAttr('data-optimize-img');
540
+ }
541
+
542
+ /**
543
+ * Render the full-size image in the DOM.
544
+ */
545
+
546
+ }, {
547
+ key: 'addFullSizeImgToDom',
548
+ value: function addFullSizeImgToDom() {
549
+ this.fullSizeImg.insertBefore(this.placeholderImg);
550
+ }
551
+
552
+ // ---------------------------------------- | Image Transition
553
+
554
+ /**
555
+ * Once the full-size image is loaded, begin the transition. This is the
556
+ * critical piece of this process. Imgix.js uses the ix-src attribute to build
557
+ * out the srcset attribute. Then, based on the sizes attribute, the browser
558
+ * determines which source to render. Therefore we can't preload in memory
559
+ * because we need imgix to do its thing directly in the DOM.
560
+ */
561
+
562
+ }, {
563
+ key: 'initTransition',
564
+ value: function initTransition() {
565
+ var _this = this;
566
+
567
+ this.fullSizeImg.on('load', function () {
568
+ return _this.transitionImg();
569
+ });
570
+ imgix.init();
571
+ }
572
+
573
+ /**
574
+ * Fade out the placeholder image, effectively showing the image behind it.
575
+ *
576
+ * Once the fade out transition has completed, remove any temporary properties
577
+ * from the full-size image (so it gets back to being a clone of the
578
+ * placeholder, with the full-size src).
579
+ *
580
+ * Finally, remove the placeholder image from the DOM since we don't need it
581
+ * any more.
582
+ */
583
+
584
+ }, {
585
+ key: 'transitionImg',
586
+ value: function transitionImg() {
587
+ var _this2 = this;
588
+
589
+ if (!this.placeholderImg) return true;
590
+ this.fadeOutPlaceholder();
591
+ setTimeout(function () {
592
+ _this2.removeFullSizeImgProperties();
593
+ _this2.removeImg();
594
+ }, this.timeToFade);
595
+ }
596
+
597
+ /**
598
+ * Fade out the placeholder image.
599
+ */
600
+
601
+ }, {
602
+ key: 'fadeOutPlaceholder',
603
+ value: function fadeOutPlaceholder() {
604
+ this.placeholderImg.fadeTo(this.timeToFade, 0);
605
+ }
606
+
607
+ /**
608
+ * Remove temporary styles and class from the full-size image, which
609
+ * effectively means it has replaced the placeholder image.
610
+ */
611
+
612
+ }, {
613
+ key: 'removeFullSizeImgProperties',
614
+ value: function removeFullSizeImgProperties() {
615
+ this.fullSizeImg.removeAttr('style');
616
+ // TODO: Update this with how the class is handled above.
617
+ this.fullSizeImg.removeClass('imgix-optimizing');
618
+ }
619
+
620
+ /**
621
+ * Remove the placeholder image from the DOM since we no longer need it.
622
+ */
623
+
624
+ }, {
625
+ key: 'removeImg',
626
+ value: function removeImg() {
627
+ if (!this.placeholderImg) {
628
+ return;
629
+ }
630
+ this.placeholderImg.remove();
631
+ this.placeholderImg = undefined;
632
+ }
633
+ }]);
634
+ return ImgixImage;
635
+ }();
636
+
637
+ var Optimizer = function () {
638
+ function Optimizer() {
639
+ var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
640
+ classCallCheck(this, Optimizer);
641
+
642
+ this.initOptions(options);
643
+ this.optimizeImages();
644
+ this.optimizeBgImages();
645
+ }
646
+
647
+ // ---------------------------------------- | Options
648
+
649
+ createClass(Optimizer, [{
650
+ key: 'initOptions',
651
+ value: function initOptions() {
652
+ var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
653
+
654
+ this.options = options;
655
+ var defaultOptions = {
656
+ parent: 'body'
657
+ };
658
+ for (var key in defaultOptions) {
659
+ if (defaultOptions.hasOwnProperty(key) && !this.options[key]) {
660
+ this.options[key] = defaultOptions[key];
661
+ }
662
+ }
663
+ }
664
+
665
+ // ---------------------------------------- | Inline Images
666
+
667
+ }, {
668
+ key: 'optimizeImages',
669
+ value: function optimizeImages() {
670
+ $(this.options.parent + ' img[data-optimize-img]').each(function (idx, img) {
671
+ new ImgixImage(img);
672
+ });
673
+ }
674
+
675
+ // ---------------------------------------- | Background Images
676
+
677
+ }, {
678
+ key: 'optimizeBgImages',
679
+ value: function optimizeBgImages() {
680
+ $(this.options.parent + ' [data-optimize-bg-img]').each(function (idx, img) {
681
+ new ImgixBgImage(img);
682
+ });
683
+ return true;
684
+ }
685
+ }]);
686
+ return Optimizer;
687
+ }();
688
+
689
+ window['Imgix'] = window['Imgix'] || {};
690
+
691
+ Imgix.ImgixBgImage = ImgixBgImage;
692
+ Imgix.ImgixImage = ImgixImage;
693
+ Imgix.Optimizer = Optimizer;
694
+
695
+ }());
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: imgix-optimizer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sean C Davis
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-08-28 00:00:00.000000000 Z
11
+ date: 2018-08-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -37,7 +37,7 @@ files:
37
37
  - LICENSE
38
38
  - README.md
39
39
  - Rakefile
40
- - dist/imgix-optimizer-0.0.3.min.js
40
+ - dist/imgix-optimizer-0.0.5.min.js
41
41
  - dist/imgix-optimizer.js
42
42
  - dist/index.html
43
43
  - dist/main.css
@@ -56,6 +56,7 @@ files:
56
56
  - src/optimizer.js
57
57
  - test/test.js
58
58
  - vendor/assets/javascripts/.keep
59
+ - vendor/assets/javascripts/imgix-optimizer.js
59
60
  homepage: https://www.ample.co/
60
61
  licenses:
61
62
  - MIT
@@ -1 +0,0 @@
1
- !function(){"use strict";var t=function(e,i){if(!(e instanceof i))throw new TypeError("Cannot call a class as a function")},e=function(){function l(e,i){for(var t=0;t<i.length;t++){var l=i[t];l.enumerable=l.enumerable||!1,l.configurable=!0,"value"in l&&(l.writable=!0),Object.defineProperty(e,l.key,l)}}return function(e,i,t){return i&&l(e.prototype,i),t&&l(e,t),e}}(),l=function(){function i(e){t(this,i),this.timeToFade=500,this.dpr=window.devicePixelRatio||1,this.el=$(e),"none"!=this.el.css("background-image")&&(this.initEl(),this.initOptimization(),this.initEventListeners())}return e(i,[{key:"initOptimization",value:function(){var e=this;$("<img>").on("load",function(){return e.renderTmpPlaceholderEl()}).attr("src",this.placeholderImgUrl)}},{key:"initEl",value:function(){this.setPlaceholderImgUrl(),this.setContainerTmpCss(),this.setElTmpCss()}},{key:"setPlaceholderImgUrl",value:function(){this.placeholderImgUrl=this.el.css("background-image").replace("url(","").replace(")","").replace(/\"/gi,"").replace(/\'/gi,"").split(", ")[0]}},{key:"setContainerTmpCss",value:function(){this.el.parent().css("position","relative")}},{key:"setElTmpCss",value:function(){"absolute"!=this.el.css("position")&&this.el.css("position","relative")}},{key:"renderTmpPlaceholderEl",value:function(){this.initTmpPlaceholderEl(),this.setTmpPlaceholderElCss(),this.addTmpPlaceholderElToDom(),this.renderFullSizeImg()}},{key:"initTmpPlaceholderEl",value:function(){this.tmpPlaceholderEl=this.el.clone(),this.tmpPlaceholderEl.html("")}},{key:"setTmpPlaceholderElCss",value:function(){this.tmpPlaceholderEl.addClass("imgix-optimizing"),this.tmpPlaceholderEl.css({position:"absolute",top:this.el.position().top,left:this.el.position().left,width:this.el.outerWidth(),height:this.el.outerHeight(),backgroundColor:"transparent"})}},{key:"addTmpPlaceholderElToDom",value:function(){this.tmpPlaceholderEl.insertBefore(this.el)}},{key:"renderFullSizeImg",value:function(){this.removeElBgImg(),this.initTmpFullSizeEl(),this.setTmpFullSizeElImg(),this.addTmpFullSizeElToDom(),this.initTransition()}},{key:"removeElBgImg",value:function(){this.elBgColor=this.el.css("background-color"),this.el.css("background-color","transparent"),this.el.css("background-image","")}},{key:"initTmpFullSizeEl",value:function(){this.tmpFullSizeEl=this.tmpPlaceholderEl.clone()}},{key:"setFullSizeImgUrl",value:function(){var e=this.placeholderImgUrl.split("?"),i=e[e.length-1].split("&"),t={};for(var l in i.map(function(e){return t[e.split("=")[0]]=e.split("=")[1]}),this.el.outerWidth()>=this.el.outerHeight()?(t.w=this.el.outerWidth()*this.dpr,delete t.h):(t.h=this.el.outerHeight()*this.dpr,delete t.w),i=[],t)i.push(l+"="+t[l]);return this.fullSizeImgUrl=e[0]+"?"+i.join("&")}},{key:"setTmpFullSizeElImg",value:function(){this.setFullSizeImgUrl(),this.tmpFullSizeEl.css("background-image",'url("'+this.fullSizeImgUrl+'")')}},{key:"addTmpFullSizeElToDom",value:function(){this.tmpFullSizeEl.insertBefore(this.tmpPlaceholderEl)}},{key:"initTransition",value:function(){$("<img>").on("load",$.proxy(this.transitionImg,this)).attr("src",this.fullSizeImgUrl)}},{key:"transitionImg",value:function(){var e=this;this.fadeOutTmpPlaceholderEl(),setTimeout(function(){e.updateElImg(),e.replaceElTmpCss(),e.removeTmpEls()},this.timeToFade)}},{key:"fadeOutTmpPlaceholderEl",value:function(){this.tmpPlaceholderEl.fadeTo(this.timeToFade,0)}},{key:"updateElImg",value:function(){this.setFullSizeImgUrl(),this.el.css("background-image","url('"+this.fullSizeImgUrl+"')")}},{key:"replaceElTmpCss",value:function(){this.el.css("background-color",this.elBgColor)}},{key:"removeTmpEls",value:function(){this.tmpPlaceholderEl.remove(),this.tmpFullSizeEl.remove(),this.tmpPlaceholderEl=void 0,this.tmpFullSizeEl=void 0}},{key:"initEventListeners",value:function(){var i=this;this.initResizeEnd(),$(window).on("resizeEnd",function(e){return i.updateElImg()})}},{key:"initResizeEnd",value:function(){$(window).resize(function(){this.resizeTo&&clearTimeout(this.resizeTo),this.resizeTo=setTimeout(function(){$(this).trigger("resizeEnd")},500)})}}]),i}(),s=function(){function i(e){t(this,i),this.timeToFade=500,this.placeholderImg=$(e),this.initPlaceholder(),this.initOptimization()}return e(i,[{key:"initOptimization",value:function(){$("<img>").on("load",$.proxy(this.renderFullSizeImg,this)).attr("src",this.placeholderImg.attr("src"))}},{key:"initPlaceholder",value:function(){this.setPlaceholderCss()}},{key:"setPlaceholderCss",value:function(){"absolute"!=this.placeholderImg.css("position")&&this.placeholderImg.css("position","relative")}},{key:"renderFullSizeImg",value:function(){this.initFullSizeImg(),this.setFullSizeImgTempCss(),this.setFullSizeImgSrc(),this.addFullSizeImgToDom(),this.initTransition()}},{key:"initFullSizeImg",value:function(){this.fullSizeImg=this.placeholderImg.clone()}},{key:"setFullSizeImgTempCss",value:function(){this.fullSizeImg.css({position:"absolute",top:this.placeholderImg.position().top,left:this.placeholderImg.position().left,width:this.placeholderImg.width(),height:this.placeholderImg.height()})}},{key:"setFullSizeImgSrc",value:function(){var e=this.placeholderImg.attr("src").replace(/(\?|\&)(w=)(\d+)/i,"$1$2"+this.placeholderImg.width()).replace(/(\?|\&)(h=)(\d+)/i,"$1$2"+this.placeholderImg.height());this.fullSizeImg.attr("ix-src",e),this.fullSizeImg.addClass("img-responsive imgix-optimizing"),this.fullSizeImg.removeAttr("data-optimize-img")}},{key:"addFullSizeImgToDom",value:function(){this.fullSizeImg.insertBefore(this.placeholderImg)}},{key:"initTransition",value:function(){var e=this;this.fullSizeImg.on("load",function(){return e.transitionImg()}),imgix.init()}},{key:"transitionImg",value:function(){var e=this;if(!this.placeholderImg)return!0;this.fadeOutPlaceholder(),setTimeout(function(){e.removeFullSizeImgProperties(),e.removeImg()},this.timeToFade)}},{key:"fadeOutPlaceholder",value:function(){this.placeholderImg.fadeTo(this.timeToFade,0)}},{key:"removeFullSizeImgProperties",value:function(){this.fullSizeImg.removeAttr("style"),this.fullSizeImg.removeClass("imgix-optimizing")}},{key:"removeImg",value:function(){this.placeholderImg&&(this.placeholderImg.remove(),this.placeholderImg=void 0)}}]),i}(),i=function(){function i(){var e=0<arguments.length&&void 0!==arguments[0]?arguments[0]:{};t(this,i),this.initOptions(e),this.optimizeImages(),this.optimizeBgImages()}return e(i,[{key:"initOptions",value:function(){var e=0<arguments.length&&void 0!==arguments[0]?arguments[0]:{};this.options=e;var i={parent:"body"};for(var t in i)i.hasOwnProperty(t)&&!this.options[t]&&(this.options[t]=i[t])}},{key:"optimizeImages",value:function(){$(this.options.parent+" img[data-optimize-img]").each(function(e,i){new s(i)})}},{key:"optimizeBgImages",value:function(){return $(this.options.parent+" [data-optimize-bg-img]").each(function(e,i){new l(i)}),!0}}]),i}();window.Imgix=window.Imgix||{},Imgix.ImgixBgImage=l,Imgix.ImgixImage=s,Imgix.Optimizer=i}();