1/** global: Hammer */ 2;(function ($, window) { 3 "use strict"; 4 5 function stringRepeat(s, precision) { 6 // String repeat polyfill 7 if (!String.prototype.repeat) { 8 precision = precision || 1; 9 return new Array(precision + 1).join(s); 10 } 11 return s.repeat(precision); 12 } 13 14 var pluginName = 'imagesCompare', 15 defaults = { 16 initVisibleRatio: 0.5, 17 interactionMode: "drag", // "drag", "mousemove", "click" 18 animationDuration: 400, // default animation duration in ms 19 animationEasing: "swing", 20 addSeparator: true, // add a html element on the separation 21 addDragHandle: true, // add a html drag handle element on the separation 22 precision: 4 23 }; 24 25 // Our object, using revealing module pattern 26 function ImagesCompare(element, options) { 27 element = $(element); 28 options = $.extend({}, defaults, options); 29 options.roundFactor = parseInt('1' + stringRepeat('0', options.precision)); 30 31 this._name = pluginName; 32 33 var frontElement, backElement, separator, dragHandle, lastRatio = 1, size = { 34 width: 0, 35 height: 0, 36 maxWidth: 0, 37 maxHeight: 0 38 }, events = { 39 initialised: "imagesCompare:initialised", 40 changed: "imagesCompare:changed", 41 resized: "imagesCompare:resized" 42 }; 43 44 function onImagesLoaded() { 45 var images = element.find('img'), 46 totalImagesCount= images.length, 47 elementsLoaded = 0; 48 49 function onImageLoaded(){ 50 if (elementsLoaded >= totalImagesCount) { 51 init(); 52 } 53 } 54 55 images.each(function() { 56 // Image already loaded (cached) 57 if ($(this)[0].complete) { 58 totalImagesCount--; 59 onImageLoaded(); 60 } else { 61 // Image loading / error 62 $(this).on('load', function() { 63 elementsLoaded++; 64 onImageLoaded(); 65 }); 66 $(this).on('error', function() { 67 elementsLoaded++; 68 onImageLoaded(); 69 }); 70 } 71 }); 72 } 73 74 onImagesLoaded(); 75 76 function init() { 77 updateDom(); 78 patchSize(); 79 initInteractions(); 80 81 $(frontElement).attr('ratio', options.initVisibleRatio); 82 setVisibleRatio(options.initVisibleRatio); 83 84 // Let the world know we have done the init 85 element.trigger({ 86 type: events.initialised 87 }); 88 } 89 90 function addResize() { 91 $(window).on('resize', function (event) { 92 frontElement.css('clip', ''); 93 patchSize(); 94 setVisibleRatio(lastRatio); 95 96 // Let the world know we have done some resize updates 97 element.trigger({ 98 type: events.resized, 99 originalEvent: event 100 }); 101 }); 102 } 103 104 function initInteractions() { 105 options.interactionMode = options.interactionMode.toLowerCase(); 106 107 if (options.interactionMode != "drag" && options.interactionMode != "mousemove" && options.interactionMode != "click") { 108 console.warn('No valid interactionMode found, valid values are "drag", "mousemove", "click"'); 109 } 110 111 switch (options.interactionMode) { 112 case "drag": 113 initDrag(); 114 break; 115 case "mousemove": 116 initMouseMove(); 117 break; 118 case "click": 119 initClick(); 120 break; 121 default: 122 initDrag(); 123 } 124 } 125 126 function initDrag() { 127 if (typeof Hammer == 'undefined') { 128 console.error('Please include the hammerjs library for drag support'); 129 }
130 addDrag(); 131 addResize(); 132 } 133 134 function initMouseMove() { 135 addMouseMove(); 136 addResize(); 137 } 138 139 function initClick() { 140 addClick(); 141 addResize(); 142 } 143 144 function addClick() { 145 element.on('click', function (event) { 146 var ratio = getElementRatio(event.pageX); 147 setVisibleRatio(ratio); 148 }); 149 } 150 151 function addMouseMove() { 152 var lastMove = 0; 153 var eventThrottle = 1; 154 element.on('mousemove', function (event) { 155 event.preventDefault(); 156 var now = Date.now(); 157 if (now > lastMove + eventThrottle) { 158 lastMove = now; 159 var ratio = getElementRatio(event.pageX); 160 setVisibleRatio(ratio); 161 } 162 }); 163 164 element.on('mouseout', function (event) { 165 var ratio = getElementRatio(event.pageX); 166 setVisibleRatio(ratio); 167 }); 168 } 169 170 function addDrag() { 171 var hammertime = new Hammer(element[0]); 172 hammertime.get('pan').set({direction: Hammer.DIRECTION_HORIZONTAL}); 173 hammertime.on('pan', function (event) { 174 var ratio = getElementRatio(event.srcEvent.pageX); 175 setVisibleRatio(ratio); 176 }); 177 } 178 179 function updateDom() { 180 element.addClass('images-compare-container'); 181 element.css('display', 'inline-block'); 182 183 frontElement = element.find('> *:nth-child(1)'); 184 backElement = element.find('> *:nth-child(2)'); 185 186 frontElement.addClass("images-compare-before"); 187 frontElement.css('display', 'block'); 188 backElement.addClass("images-compare-after"); 189 backElement.css('display', 'block'); 190 191 if (options.addDragHandle) { 192 buildDragHandle(); 193 } 194 195 if (options.addSeparator) { 196 buildSeparator(); 197 } 198 } 199 200 function buildSeparator() { 201 element.prepend("<div class='images-compare-separator'></div>"); 202 separator = element.find(".images-compare-separator"); 203 204 } 205 206 function buildDragHandle() { 207 element.prepend("<div class='images-compare-handle'></div>"); 208 dragHandle = element.find(".images-compare-handle"); 209 dragHandle.append("<span class='images-compare-left-arrow'></span>"); 210 dragHandle.append("<span class='images-compare-right-arrow'></span>"); 211 } 212 213 function patchSize() { 214 var imgRef = backElement.find('img').first(); 215 setSize(imgRef.width(), imgRef.height(), imgRef.naturalWidth(), imgRef.naturalHeight()); 216 element.css('max-width', size.maxWidth + 'px'); 217 element.css('max-height', size.maxHeight + 'px'); 218 frontElement.width(size.width); 219 frontElement.height(size.height); 220 } 221 222 /** 223 * 224 * @param x 225 * @return float 226 */ 227 function getElementRatio(x) { 228 return roundRatio((x - element.offset().left) / frontElement.width()); 229 } 230 231 /** 232 * 233 * @param ratio 234 * @return float 235 */ 236 function roundRatio(ratio) { 237 ratio = Math.round((ratio * options.roundFactor)) / options.roundFactor; 238 if (ratio > 1) { 239 ratio = 1; 240 } 241 242 if (ratio < 0) { 243 ratio = 0; 244 } 245 246 return ratio; 247 248 } 249 250 /** 251 * Animation request 252 * 253 * @param startValue float 254 * @param endValue float 255 * @param duration value in ms 256 * @param easing linear or swing 257 */ 258 function launchAnimation(startValue, endValue, duration, easing) { 259 $(frontElement).attr('ratio', startValue).animate({ratio: startValue}, { 260 duration: 0 261 }); 262 263 $(frontElement).stop().attr('ratio', startValue).animate({ratio: endValue}, { 264 duration: duration, 265 easing: easing, 266 step: function (now) { 267 var width = getRatioValue(now); 268 lastRatio = now; 269 frontElement.attr('ratio', now).css('clip', 'rect(0, ' + width + 'px, ' + size.height + 'px, 0)'); 270 271 if (options.addSeparator) { 272 separator.css('left', width + 'px'); 273 } 274 275 if (options.addDragHandle) { 276 dragHandle.css('left', width + 'px'); 277 } 278 }, 279 done: function (animation, jumpedToEnd) { 280 var ratio = $(frontElement).attr('ratio'); 281 // Let the world know something has changed 282 element.trigger({ 283 type: events.changed, 284 ratio: ratio, 285 value: getRatioValue(ratio), 286 animate: true, 287 animation : animation, 288 jumpedToEnd: jumpedToEnd 289 }); 290 } 291 }); 292 } 293 294 /** 295 * Get value to reach, based on a ratio 296 * 297 * @param ratio float 298 * @return {number} 299 */ 300 function getRatioValue(ratio) { 301 ratio = Math.round((ratio * options.roundFactor)) / options.roundFactor; 302 return Math.round(frontElement.width() * ratio); 303 } 304 305 /** 306 * Change visible ratio 307 * 308 * @param ratio float 309 * @param animate boolean Do we want an animation ? 310 * @param duration in ms 311 * @param easing 'swing', 'linear' 312 */ 313 function setVisibleRatio(ratio, animate, duration, easing) { 314 if (typeof animate == 'undefined') { 315 animate = false; 316 } 317 318 var width = getRatioValue(ratio); 319 320 if (animate) { 321 var finalDuration = duration ? duration : options.animationDuration; 322 var finalEasing = easing ? easing : options.animationEasing; 323 324 launchAnimation(lastRatio, ratio, finalDuration, finalEasing); 325 326 // Let the world know something has changed 327 if (lastRatio != ratio) { 328 element.trigger({ 329 type: events.changed, 330 ratio: lastRatio, 331 value: width, 332 animate: animate 333 }); 334 } 335 336 return; 337 338 } else { 339 frontElement.stop().css('clip', 'rect(0, ' + width + 'px, ' + size.height + 'px, 0)'); 340 341 if (options.addSeparator) { 342 $(separator).stop().css('left', width + 'px'); 343 } 344 345 if (options.addDragHandle) { 346 dragHandle.css('left', width + 'px'); 347 } 348 } 349 350 // Let the world know something has changed 351 if (lastRatio != ratio) { 352 element.trigger({ 353 type: events.changed, 354 ratio: ratio, 355 value: width, 356 animate: animate 357 }); 358 } 359 360 lastRatio = ratio; 361 } 362 363 function setSize(width, height, maxWidth, maxHeight) { 364 if (typeof width != 'undefined') { 365 setWidth(width); 366 } 367 if (typeof height != 'undefined') { 368 setHeight(height); 369 } 370 if (typeof maxWidth != 'undefined') {
371 setMaxWidth(maxWidth); 372 } 373 if (typeof maxHeight != 'undefined') { 374 setMaxHeight(maxHeight); 375 } 376 return size; 377 } 378 379 function setWidth(width) { 380 size.width = width; 381 return size; 382 } 383 384 function setMaxWidth(maxWidth) { 385 size.maxWidth = maxWidth; 386 return size; 387 } 388 389 function setHeight(height) { 390 size.height = height; 391 return size; 392 } 393 394 function setMaxHeight(maxHeight) { 395 size.maxHeight = maxHeight; 396 return size; 397 } 398 399 // public function declaration 400 // returning element to preserve chaining 401 return { 402 "setValue": function (ratio, animate, duration, easing) { 403 setVisibleRatio(ratio, animate, duration, easing); 404 return element; 405 }, 406 "getValue": function () { 407 return lastRatio; 408 }, 409 "on": function (eventName, callback) { 410 element.on(eventName, callback); 411 return element; 412 }, 413 "off": function (eventName, callback) { 414 element.off(eventName, callback); 415 return element; 416 }, 417 "events": function () { 418 return events; 419 } 420 }; 421 } 422 423 424 /** 425 * Plugin declaration 426 * 427 * @param userOptions 428 * @return {*} 429 */ 430 $.fn.imagesCompare = function (userOptions) { 431 var options = $.extend(defaults, userOptions); 432 return this.each(function () { 433 if (!$.data(this, pluginName)) { 434 $.data(this, pluginName, new ImagesCompare(this, options)); 435 } 436 }); 437 }; 438 439})(jQuery, window, document); 440 441// http://www.jacklmoore.com/notes/naturalwidth-and-naturalheight-in-ie/ 442(function ($) { 443 var props = ['Width', 'Height'], prop, propsLength; 444 445 propsLength = props.length; 446 447 for (var index = 0; index < propsLength; index++) { 448 prop = props[index]; 449 /*jslint loopfunc: true */ 450 (function (natural, prop) { 451 $.fn[natural] = (natural in document.createElement('img')) ? 452 function () { 453 return this[0][natural]; 454 } : 455 function () { 456 var 457 node = this[0], 458 img, 459 value = 0; 460 461 if (node.tagName.toLowerCase() === 'img') { 462 img = document.createElement('img'); 463 img.src = node.src; 464 value = img[prop]; 465 } 466 return value; 467 }; 468 }('natural' + prop, prop.toLowerCase())); 469 /*jslint loopfunc: false */ 470 } 471}(jQuery));
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.