1// ========================================================================== 2// Project: Image Scale 3// Description: Scale images to fit or fill any target size via two simple properties: scale and align. 4// Copyright: ©2012-2016 GestiXi 5// License: Licensed under the MIT license (see LICENCE) 6// Version: 2.2 7// Author: Nicolas BADIA 8// ========================================================================== 9 10// Uses CommonJS, AMD or browser globals to create a jQuery plugin. 11(function(factory) { 12 if (typeof define === 'function' && define.amd) { 13 // AMD. Register as an anonymous module. 14 define(['jquery'], factory); 15 } 16 else if (typeof module === 'object' && module.exports) { 17 // Node/CommonJS 18 module.exports = function( root, jQuery ) { 19 if ( jQuery === undefined ) { 20 // require('jQuery') returns a factory that requires window to 21 // build a jQuery instance, we normalize how we use modules 22 // that require this pattern but the window provided is a noop 23 // if it's defined (how jquery works) 24 if (typeof window !== 'undefined') { 25 jQuery = require('jquery'); 26 } 27 else { 28 jQuery = require('jquery')(root); 29 } 30 } 31 factory(jQuery); 32 return jQuery; 33 }; 34 } 35 else { 36 // Browser globals 37 factory(jQuery); 38 } 39}(function($) { 40 41 "use strict"; 42 43 // .......................................................... 44 // IMAGE SCALE PLUGIN DEFINITION 45 // 46 47 $.fn.imageScale = function( options ) { 48 49 return this.each(function() { 50 var that = this, 51 $this = $(this), 52 data = $this.data('imageScale'), 53 $img = this.tagName === 'IMG' ? $this : $this.find("img"); 54 55 if (!data) { 56 var didLoad = $img[0].complete, 57 formattedOpt = $.extend({}, $.fn.imageScale.defaults, typeof options == 'object' && options), 58 59 loadFunc = function() { 60 $this.data('imageScale', (data = new ImageScale(that, formattedOpt))); 61 62 data.scale(true, formattedOpt); 63 }; 64 65 if (didLoad) { 66 loadFunc.apply($this[0]); 67 } 68 else { 69 $img.on("load", loadFunc).attr("src", $img.attr("src")); 70 } 71 } 72 else { 73 if (typeof options == 'string') data[options](); 74 else if (typeof options == 'object') { 75 var method = options.method || 'scale'; 76 data[method](false, options); 77 } 78 else data.scale(); 79 } 80 }) 81 } 82 83 $.fn.imageScale.defaults = { 84 /** 85 Determines how the image will scale to fit within its containing space. Possible values: 86 87 * **fill** - stretches or compresses the source image to fill the target frame 88 * **best-fill** - fits the shortest side of the source image within the target frame while maintaining the original aspect ratio 89 * **best-fit** - fits the longest edge of the source image within the target frame while maintaining the original aspect ratio 90 * **best-fit-down** - same as *best-fit* but will not stretch the source if it is smaller than the target 91 * **none** - the source image is left unscaled 92 93 @type String 94 @default best-fill 95 @since Version 1.2 96 */ 97 scale: 'best-fill', 98 99 /** 100 Align the image within its frame. Possible values: 101 102 * **left** 103 * **right** 104 * **center** 105 * **top** 106 * **bottom** 107 * **top-left** 108 * **top-right** 109 * **bottom-left** 110 * **bottom-right** 111 112 @type String 113 @default center 114 @since Version 1.2 115 */ 116 align: 'center', 117 118 /** 119 A jQuery Object against which the image size will be calculated. 120 If null, the parent of the image will be used. 121 122 @type jQuery Object 123 @default null 124 @since Version 1.0 125 */ 126 parent: null, 127 128 /** 129 A boolean determining if the parent should hide its overflow. 130 131 @type Boolean 132 @default true 133 @since Version 1.0 134 */ 135 hideParentOverflow: true, 136 137 /** 138 A duration in milliseconds determining how long the fadeIn animation will run when your image is scale for the firstTime. 139 140 Set it to 0 if you don't want any animation. 141 142 @type Number|String 143 @default 0 144 @since Version 1.1 145 */ 146 fadeInDuration: 0, 147 148 /** 149 A boolean indicating if the image size should be rescaled when the window is resized. 150 151 The window size is checked using requestAnimationFrame for good performance. 152 153 Example: 154 155 $images.imageScale({ 156 rescaleOnResize: true 157 }); 158 159 @type Boolean 160 @default false 161 @since Version 1.0 162 */ 163 rescaleOnResize: false, 164 165 /** 166 A function that will be call each time the receiver is scaled. 167 168 Example: 169 170 $images.imageScale({ 171 didScale: function() { 172 console.log('did scale img: ', this.element); 173 } 174 }); 175 176 @type Function 177 @param firstTime {Boolean} true if the image was scale for the first time. 178 @param options {Object} the options passed to the scale method. 179 @since Version 2.0 180 */ 181 didScale: function(firstTime, options) {}, 182 183 /** 184 A number indicating the log level : 185 186 0: silent 187 1: error 188 2: error & warning 189 3: error & warning & notice 190 191 @type Number 192 @default 0 193 @since Version 1.0 194 */ 195 logLevel: 0 196 } 197 198 // .......................................................... 199 // IMAGE SCALE PUBLIC CLASS DEFINITION 200 // 201 202 var ImageScale = function(element, options) { 203 var that = this; 204 that.options = options; 205 that.element = element; 206 207 var $element = that.$element = $(element), 208 $img = that.$img = element.tagName === 'IMG' ? $element : $element.find("img"), 209 img = that.img = $img[0]; 210 211 that.src = $img.attr('src'); 212
213 that.imgWidth = img.naturalWidth || img.width; 214 that.imgHeight = img.naturalHeight || img.height; 215 216 var $parent = that.$parent = options.parent?options.parent:$($element.parent()[0]); 217 that.parent = $parent[0]; 218 219 // Fixes: https://github.com/gestixi/image-scale/issues/1 220 if ($parent.css('position') === 'static') { 221 $parent.css('position', 'relative'); 222 } 223 224 if (options.rescaleOnResize) { 225 $(window).resize(function(e) { that.scheduleScale(); }); 226 } 227 } 228 229 $.fn.imageScale.Constructor = ImageScale; 230 231 ImageScale.prototype = { 232 233 NONE: "none", 234 FILL: "fill", 235 BEST_FILL: "best-fill", 236 BEST_FIT: "best-fit", 237 BEST_FIT_DOWN_ONLY: "best-fit-down", 238 239 ALIGN_LEFT: 'left', 240 ALIGN_RIGHT: 'right', 241 ALIGN_CENTER: 'center', 242 ALIGN_TOP: 'top', 243 ALIGN_BOTTOM: 'bottom', 244 ALIGN_TOP_LEFT: 'top-left', 245 ALIGN_TOP_RIGHT: 'top-right', 246 ALIGN_BOTTOM_LEFT: 'bottom-left', 247 ALIGN_BOTTOM_RIGHT: 'bottom-right', 248 249 constructor: ImageScale, 250 251 /** 252 The initial element. 253 254 @type DOM Element 255 */ 256 element: null, 257 258 /** 259 The passed options. 260 261 @type Object 262 */ 263 options: null, 264 265 /** 266 Main method. Used to scale the images. 267 268 When `rescaleOnResize` is set to true, this method is executed each time the 269 windows size changes. 270 271 If `rescaleOnResize` is set to false, you may want to call it manually. Here is an 272 example on how you should do it: 273 274 $image.imageScale('scale'); 275 276 277 @param {Boolean} firstTime 278 */ 279 scale: function(firstTime, opt) { 280 if (this._isDestroyed || this._canScale === false) return; 281 282 var that = this, 283 options = this.options, 284 $parent = this.$parent, 285 element = this.element, 286 $element = this.$element, 287 img = this.img, 288 $img = this.$img; 289 290 if (firstTime) { 291 if (options.hideParentOverflow) { 292 $parent.css({ overflow: 'hidden' }); 293 } 294 } 295 else { 296 // If the source of the image has changed 297 if (this.src !== $img.attr('src')) { 298 this.destroy(); 299 $element.data('imageScale', null); 300 $element.imageScale(options); 301 return; 302 } 303 } 304 305 this._didScheduleScale = false; 306 307 if (options.rescaleOnResize && !opt) { 308 if (!this._needUpdate(this.parent)) return; 309 } 310 opt = opt ? opt : {}; 311 312 var transition = opt.transition; 313 if (transition) { 314 this._canScale = false; 315 $element.css('transition', 'all '+transition+'ms'); 316 317 setTimeout(function() { 318 that._canScale = null; 319 $element.css('transition', 'null'); 320 }, transition); 321 } 322 323 var destWidth = opt.destWidth ? opt.destWidth : $parent.outerWidth(), 324 destHeight = opt.destHeight ? opt.destHeight : $parent.outerHeight(), 325 326 destInnerWidth = opt.destWidth ? opt.destWidth : $parent.innerWidth(), 327 destInnerHeight = opt.destHeight ? opt.destHeight : $parent.innerHeight(), 328 329 widthOffset = destWidth - destInnerWidth, 330 heightOffset = destHeight - destInnerHeight, 331 332 scaleData = $element.attr('data-scale'), 333 alignData = $element.attr('data-align'), 334 335 scale = scaleData?scaleData:options.scale, 336 align = alignData?alignData:options.align, 337 338 fadeInDuration = options.fadeInDuration; 339 340 if (!scale) { 341 if (options.logLevel > 2) { 342 console.log("imageScale - DEBUG NOTICE: The scale property is null.", element); 343 } 344 return; 345 } 346 347 if (this._cacheDestWidth === destWidth && this._cacheDestHeight === destHeight) { 348 if (options.logLevel > 2) { 349 console.log("imageScale - DEBUG NOTICE: The parent size hasn't changed: dest width: '"+destWidth+"' - dest height: '"+destHeight+"'.", element); 350 } 351 } 352 353 var sourceWidth = this.imgWidth, 354 sourceHeight = this.imgHeight; 355 356 if (!(destWidth && destHeight && sourceWidth && sourceHeight)) { 357 if (options.logLevel > 0) { 358 console.error("imageScale - DEBUG ERROR: The dimensions are incorrect: source width: '"+sourceWidth+"' - source height: '"+sourceHeight+"' - dest width: '"+destWidth+"' - dest height: '"+destHeight+"'.", element); 359 } 360 return; 361 } 362 363 this._cacheDestWidth = destWidth; 364 this._cacheDestHeight = destHeight; 365 366 var layout = this._innerFrameForSize(scale, align, sourceWidth, sourceHeight, destWidth, destHeight); 367 368 if (widthOffset) layout.x -= widthOffset/2; 369 if (heightOffset) layout.y -= heightOffset/2; 370 371 $element.css({ position: 'absolute', top: layout.y+'px', left: layout.x+'px', width: layout.width+'px', height: layout.height+'px', 'max-width': 'none' }); 372 373 if (firstTime && fadeInDuration) { 374 $element.css({ display: 'none' }); 375 $element.fadeIn(fadeInDuration); 376 } 377 378 options.didScale.call(this, firstTime, opt); 379 }, 380 381 /** 382 Removes the data from the element. 383 384 Here is an example on how you can call the destroy method: 385 386 $image.imageScale('destroy'); 387 388 */ 389 destroy: function() { 390 this._isDestroyed = true; 391 this.$element.removeData('imageScale'); 392 }, 393 394 /** 395 @private 396 397 Returns a frame (x, y, width, height) fitting the source size (sourceWidth & sourceHeight) within the 398 destination size (destWidth & destHeight) according to the align and scale properties. 399 400 @param {String} scale 401 @param {String} align 402 @param {Number}
402 sourceWidth 403 @param {Number} sourceHeight 404 @param {Number} destWidth 405 @param {Number} destHeight 406 @returns {Object} the inner frame with properties: { x: value, y: value, width: value, height: value } 407 */ 408 _innerFrameForSize: function(scale, align, sourceWidth, sourceHeight, destWidth, destHeight) { 409 var scaleX, 410 scaleY, 411 result; 412 413 // Fast path 414 result = { x: 0, y: 0, width: destWidth, height: destHeight }; 415 if (scale === this.FILL) return result; 416 417 // Determine the appropriate scale 418 scaleX = destWidth / sourceWidth; 419 scaleY = destHeight / sourceHeight; 420 421 switch (scale) { 422 case this.BEST_FIT_DOWN_ONLY: 423 if (scale !== this.BEST_FIT_DOWN_ONLY && this.options.logLevel > 1) { 424 console.warn("imageScale - DEBUG WARNING: The scale '"+scale+"' was not understood."); 425 } 426 427 if ((sourceWidth > destWidth) || (sourceHeight > destHeight)) { 428 scale = scaleX < scaleY ? scaleX : scaleY; 429 } else { 430 scale = 1.0; 431 } 432 break; 433 case this.BEST_FIT: 434 scale = scaleX < scaleY ? scaleX : scaleY; 435 break; 436 case this.NONE: 437 scale = 1.0; 438 break; 439 //case this.BEST_FILL: 440 default: 441 scale = scaleX > scaleY ? scaleX : scaleY; 442 break; 443 } 444 445 sourceWidth *= scale; 446 sourceHeight *= scale; 447 result.width = Math.round(sourceWidth); 448 result.height = Math.round(sourceHeight); 449 450 // Align the image within its frame 451 switch (align) { 452 case this.ALIGN_LEFT: 453 result.x = 0; 454 result.y = (destHeight / 2) - (sourceHeight / 2); 455 break; 456 case this.ALIGN_RIGHT: 457 result.x = destWidth - sourceWidth; 458 result.y = (destHeight / 2) - (sourceHeight / 2); 459 break; 460 case this.ALIGN_TOP: 461 result.x = (destWidth / 2) - (sourceWidth / 2); 462 result.y = 0; 463 break; 464 case this.ALIGN_BOTTOM: 465 result.x = (destWidth / 2) - (sourceWidth / 2); 466 result.y = destHeight - sourceHeight; 467 break; 468 case this.ALIGN_TOP_LEFT: 469 result.x = 0; 470 result.y = 0; 471 break; 472 case this.ALIGN_TOP_RIGHT: 473 result.x = destWidth - sourceWidth; 474 result.y = 0; 475 break; 476 case this.ALIGN_BOTTOM_LEFT: 477 result.x = 0; 478 result.y = destHeight - sourceHeight; 479 break; 480 case this.ALIGN_BOTTOM_RIGHT: 481 result.x = destWidth - sourceWidth; 482 result.y = destHeight - sourceHeight; 483 break; 484 default: // this.ALIGN_CENTER 485 if (align !== this.ALIGN_CENTER && this.options.logLevel > 1) { 486 console.warn("imageScale - DEBUG WARNING: The align '"+align+"' was not understood."); 487 } 488 result.x = (destWidth / 2) - (sourceWidth / 2); 489 result.y = (destHeight / 2) - (sourceHeight / 2); 490 } 491 492 return result; 493 }, 494 495 /** 496 @private 497 498 Determines if the windows size has changed since the last update. 499 500 @returns {Boolean} 501 */ 502 _needUpdate: function(parent) { 503 var size = parent.clientHeight + ' ' + parent.clientWidth; 504 505 if (this._lastParentSize !== size) { 506 this._lastParentSize = size; 507 return true; 508 } 509 return false; 510 }, 511 512 /** 513 @private 514 515 Schedule a scale update. 516 */ 517 scheduleScale: function() { 518 if (this._didScheduleScale) return; 519 520 if (window.requestAnimationFrame) { 521 var that = this; 522 this._didScheduleScale = true; 523 // setTimeout important when resizing down if the scrollbar were visible 524 requestAnimationFrame(function() { setTimeout(function() { that.scale(); }, 0); }); 525 } 526 else { 527 this.scale(); 528 } 529 } 530 } 531 532}));
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.