1/*! 2 * Justified Gallery - v3.5.4 3 * http://miromannino.github.io/Justified-Gallery/ 4 * Copyright (c) 2015 Miro Mannino 5 * Licensed under the MIT license. 6 */ 7(function($) { 8 9 /* Events 10 jg.complete : called when all the gallery has been created 11 jg.resize : called when the gallery has been resized 12 */ 13 14 $.fn.justifiedGallery = function (arg) { 15 // Default options 16 var defaults = { 17 sizeRangeSuffixes : { 18 'lt100': '', // e.g. Flickr uses '_t' 19 'lt240': '', // e.g. Flickr uses '_m' 20 'lt320': '', // e.g. Flickr uses '_n' 21 'lt500': '', // e.g. Flickr uses '' 22 'lt640': '', // e.g. Flickr uses '_z' 23 'lt1024': '', // e.g. Flickr uses '_b' 24 }, 25 rowHeight : 120, 26 maxRowHeight : 0, // negative value = no limits, 0 = 1.5 * rowHeight 27 margins : 1, 28 border: -1, // negative value = same as margins, 0 = disabled 29 30 lastRow : 'nojustify', // or can be 'justify' or 'hide' 31 justifyThreshold: 0.75, /* if row width / available space > 0.75 it will be always justified 32 (i.e. lastRow setting is not considered) */ 33 fixedHeight : false, 34 waitThumbnailsLoad : true, 35 captions : true, 36 cssAnimation: false, 37 imagesAnimationDuration : 500, // ignored with css animations 38 captionSettings : { // ignored with css animations 39 animationDuration : 500, 40 visibleOpacity : 0.7, 41 nonVisibleOpacity : 0.0 42 },
43 rel : null, // rewrite the rel of each analyzed links 44 target : null, // rewrite the target of all links 45 extension : /\.[^.\\/]+$/, 46 refreshTime : 100, 47 randomize : false 48 }; 49 50 function checkSettings (context) { 51 var settings = context.settings; 52 53 function checkSuffixesRange(range) { 54 if (typeof settings.sizeRangeSuffixes[range] !== 'string') 55 throw 'sizeRangeSuffixes.' + range + ' must be a string'; 56 } 57 58 function checkOrConvertNumber(parent, settingName) { 59 if (typeof parent[settingName] === 'string') { 60 parent[settingName] = parseFloat(parent[settingName], 10); 61 if (isNaN(parent[settingName])) throw 'invalid number for ' + settingName; 62 } else if (typeof parent[settingName] === 'number') { 63 if (isNaN(parent[settingName])) throw 'invalid number for ' + settingName; 64 } else { 65 throw settingName + ' must be a number'; 66 } 67 } 68 69 if (typeof settings.sizeRangeSuffixes !== 'object') 70 throw 'sizeRangeSuffixes must be defined and must be an object'; 71 72 checkSuffixesRange('lt100'); 73 checkSuffixesRange('lt240'); 74 checkSuffixesRange('lt320'); 75 checkSuffixesRange('lt500'); 76 checkSuffixesRange('lt640'); 77 checkSuffixesRange('lt1024'); 78 79 checkOrConvertNumber(settings, 'rowHeight'); 80 checkOrConvertNumber(settings, 'maxRowHeight'); 81 82 if (settings.maxRowHeight > 0 && 83 settings.maxRowHeight < settings.rowHeight) { 84 settings.maxRowHeight = settings.rowHeight; 85 } 86 87 checkOrConvertNumber(settings, 'margins'); 88 checkOrConvertNumber(settings, 'border'); 89 90 if (settings.lastRow !== 'nojustify' && 91 settings.lastRow !== 'justify' && 92 settings.lastRow !== 'hide') { 93 throw 'lastRow must be "nojustify", "justify" or "hide"'; 94 } 95 96 checkOrConvertNumber(settings, 'justifyThreshold'); 97 if (settings.justifyThreshold < 0 || settings.justifyThreshold > 1) 98 throw 'justifyThreshold must be in the interval [0,1]'; 99 if (typeof settings.cssAnimation !== 'boolean') { 100 throw 'cssAnimation must be a boolean'; 101 } 102 103 checkOrConvertNumber(settings.captionSettings, 'animationDuration'); 104 checkOrConvertNumber(settings, 'imagesAnimationDuration'); 105 106 checkOrConvertNumber(settings.captionSettings, 'visibleOpacity'); 107 if (settings.captionSettings.visibleOpacity < 0 || settings.captionSettings.visibleOpacity > 1) 108 throw 'captionSettings.visibleOpacity must be in the interval [0, 1]'; 109 110 checkOrConvertNumber(settings.captionSettings, 'nonVisibleOpacity'); 111 if (settings.captionSettings.visibleOpacity < 0 || settings.captionSettings.visibleOpacity > 1) 112 throw 'captionSettings.nonVisibleOpacity must be in the interval [0, 1]'; 113 114 if (typeof settings.fixedHeight !== 'boolean') { 115 throw 'fixedHeight must be a boolean'; 116 } 117 118 if (typeof settings.captions !== 'boolean') { 119 throw 'captions must be a boolean'; 120 } 121 122 checkOrConvertNumber(settings, 'refreshTime'); 123 124 if (typeof settings.randomize !== 'boolean') { 125 throw 'randomize must be a boolean'; 126 } 127 128 } 129 130 function imgFromEntry($entry) { 131 var $img = $entry.find('img'); 132 if ($img.length === 0) $img = $entry.find('a > img'); 133 return $img; 134 } 135 136 function startLoadingSpinnerAnimation(spinnerContext) { 137 clearInterval(spinnerContext.intervalId); 138 spinnerContext.intervalId = setInterval(function () {
139 if (spinnerContext.phase < spinnerContext.$points.length) 140 spinnerContext.$points.eq(spinnerContext.phase).fadeTo(spinnerContext.timeslot, 1); 141 else 142 spinnerContext.$points.eq(spinnerContext.phase - spinnerContext.$points.length) 143 .fadeTo(spinnerContext.timeslot, 0); 144 spinnerContext.phase = (spinnerContext.phase + 1) % (spinnerContext.$points.length * 2); 145 }, spinnerContext.timeslot); 146 } 147 148 function onImageEvent(imageSrc, onLoad, onError) { 149 if (!onLoad && !onError) { 150 return; 151 } 152 /* Check if the image is loaded or not using another image object. 153 We cannot use the 'complete' image property, because some browsers, 154 with a 404 set complete = true */ 155 var memImage = new Image(); 156 var $memImage = $(memImage); 157 if (onLoad) { 158 $memImage.one('load', function () { 159 $memImage.off('load error'); 160 onLoad(memImage); 161 }); 162 } 163 if (onError) { 164 $memImage.one('error', function() { 165 $memImage.off('load error'); 166 onError(memImage); 167 }); 168 } 169 memImage.src = imageSrc; 170 } 171 172 function startImgAnalyzer(context, isForResize) { 173 stopImgAnalyzerStarter(context); 174 context.imgAnalyzerTimeout = setTimeout(function () { 175 analyzeImages(context, isForResize); 176 }, 0.001); 177 analyzeImages(context, isForResize); 178 } 179 180 function checkWidth(context) { 181 context.checkWidthIntervalId = setInterval(function () { 182 var galleryWidth = parseInt(context.$gallery.width(), 10); 183 if (context.galleryWidth !== galleryWidth) { 184 //DEBUG// console.log("resize. old: " + context.galleryWidth + " new: " + galleryWidth); 185 186 context.galleryWidth = galleryWidth; 187 rewind(context); 188 189 // Restart to analyze 190 startImgAnalyzer(context, true); 191 } 192 }, context.settings.refreshTime); 193 } 194 195 function stopImgAnalyzerStarter(context) { 196 context.yield.flushed = 0; 197 if (context.imgAnalyzerTimeout !== null) clearTimeout(context.imgAnalyzerTimeout); 198 } 199 200 function analyzeImages(context, isForResize) { 201 202 /* //DEBUG// 203 var rnd = parseInt(Math.random() * 10000, 10); 204 console.log('analyzeImages ' + rnd + ' start'); 205 console.log('images status: '); 206 for (var i = 0; i < context.entries.length; i++) { 207 var $entry = $(context.entries[i]); 208 var $image = imgFromEntry($entry); 209 console.log(i + ' (alt: ' + $image.attr('alt') + 'loaded: ' + $image.data('jg.loaded') + ')'); 210 }*/ 211 212 /* The first row */ 213 var settings = context.settings; 214 var isLastRow; 215 216 for (var i = context.lastAnalyzedIndex + 1; i < context.entries.length; i++) { 217 var $entry = $(context.entries[i]); 218 var $image = imgFromEntry($entry); 219 220 if ($image.data('jg.loaded') === true || $image.data('jg.loaded') === 'skipped') { 221 isLastRow = i >= context.entries.length - 1; 222 223 var availableWidth = context.galleryWidth - 2 * context.border - ( 224 (context.buildingRow.entriesBuff.length - 1) * settings.margins); 225 var imgAspectRatio = $image.data('jg.imgw') / $image.data('jg.imgh'); 226 if (availableWidth / (context.buildingRow.aspectRatio + imgAspectRatio) < settings.rowHeight) { 227 flushRow(context, isLastRow); 228 if(++context.yield.flushed >= context.yield.every) { 229 //DEBUG// console.log("yield"); 230 startImgAnalyzer(context, isForResize); 231 return; 232 } 233 } 234 235 context.buildingRow.entriesBuff.push($entry); 236 context.buildingRow.aspectRatio += imgAspectRatio; 237 context.buildingRow.width += imgAspectRatio * settings.rowHeight; 238 context.lastAnalyzedIndex = i; 239 240 } else if ($image.data('jg.loaded') !== 'error') { 241 return; 242 } 243 } 244 245 // Last row flush (the row is not full) 246 if (context.buildingRow.entriesBuff.length > 0) flushRow(context, true); 247 248 if (context.spinner.active) { 249 context.spinner.active = false; 250 context.$gallery.height(context.$gallery.height() - context.spinner.$el.innerHeight()); 251 context.spinner.$el.detach(); 252 stopLoadingSpinnerAnimation(context.spinner); 253 } 254 255 /* Stop, if there is, the timeout to start the analyzeImages. 256 This is because an image can be set loaded, and the timeout can be set, 257 but this image can be analyzed yet. 258 */ 259 stopImgAnalyzerStarter(context); 260 261 //On complete callback 262 if (!isForResize) 263 context.$gallery.trigger('jg.complete'); 264 else 265 context.$gallery.trigger('jg.resize'); 266 267 //DEBUG// console.log('analyzeImages ' + rnd + ' end'); 268 } 269 270 function flushRow(context, isLastRow) { 271 var settings = context.settings; 272 var $entry, $image, minHeight, buildingRowRes, offX = context.border; 273 274 //DEBUG// console.log('flush (isLastRow: ' + isLastRow + ')'); 275 276 buildingRowRes = prepareBuildingRow(context, isLastRow); 277 minHeight = buildingRowRes.minHeight; 278 if (isLastRow && settings.lastRow === 'hide' && minHeight === -1) { 279 context.buildingRow.entriesBuff = []; 280 context.buildingRow.aspectRatio = 0; 281 context.buildingRow.width = 0; 282 return; 283 } 284 285 if (settings.maxRowHeight > 0 && settings.maxRowHeight < minHeight) 286 minHeight = settings.maxRowHeight; 287 else if (settings.maxRowHeight === 0 && (1.5 * settings.rowHeight) < minHeight) 288 minHeight = 1.5 * settings.rowHeight; 289 290 for (var i = 0; i < context.buildingRow.entriesBuff.length; i++) { 291 $entry = context.buildingRow.entriesBuff[i]; 292 $image = imgFromEntry($entry); 293 displayEntry($entry, offX, context.offY, $image.data('jg.jimgw'), 294 $image.data('jg.jimgh'), minHeight, context); 295 offX += $image.data('jg.jimgw') + settings.margins; 296 } 297 298 //Gallery Height 299 context.$gallery.height(context.offY + minHeight + context.border +
300 (context.spinner.active ? context.spinner.$el.innerHeight() : 0) 301 ); 302 303 if (!isLastRow || (minHeight <= context.settings.rowHeight && buildingRowRes.justify)) { 304 //Ready for a new row 305 context.offY += minHeight + context.settings.margins; 306 307 //DEBUG// console.log('minHeight: ' + minHeight + ' offY: ' + context.offY); 308 309 context.buildingRow.entriesBuff = []; //clear the array creating a new one 310 context.buildingRow.aspectRatio = 0; 311 context.buildingRow.width = 0; 312 context.$gallery.trigger('jg.rowflush'); 313 } 314 } 315 316 function prepareBuildingRow(context, isLastRow) { 317 var settings = context.settings; 318 var i, $entry, $image, imgAspectRatio, newImgW, newImgH, justify = true; 319 var minHeight = 0; 320 var availableWidth = context.galleryWidth - 2 * context.border - ( 321 (context.buildingRow.entriesBuff.length - 1) * settings.margins); 322 var rowHeight = availableWidth / context.buildingRow.aspectRatio; 323 var justificable = context.buildingRow.width / availableWidth > settings.justifyThreshold; 324 325 //Skip the last row if we can't justify it and the lastRow == 'hide' 326 if (isLastRow && settings.lastRow === 'hide' && !justificable) { 327 for (i = 0; i < context.buildingRow.entriesBuff.length; i++) { 328 $entry = context.buildingRow.entriesBuff[i]; 329 if (settings.cssAnimation) 330 $entry.removeClass('entry-visible'); 331 else 332 $entry.stop().fadeTo(0, 0); 333 } 334 return -1; 335 } 336 337 // With lastRow = nojustify, justify if is justificable (the images will not become too big) 338 if (isLastRow && !justificable && settings.lastRow === 'nojustify') justify = false; 339 340 for (i = 0; i < context.buildingRow.entriesBuff.length; i++) { 341 $image = imgFromEntry(context.buildingRow.entriesBuff[i]); 342 imgAspectRatio = $image.data('jg.imgw') / $image.data('jg.imgh'); 343 344 if (justify) { 345 newImgW = (i === context.buildingRow.entriesBuff.length - 1) ? availableWidth 346 : rowHeight * imgAspectRatio; 347 newImgH = rowHeight; 348 349 /* With fixedHeight the newImgH must be greater than rowHeight. 350 In some cases here this is not satisfied (due to the justification). 351 But we comment it, because is better to have a shorter but justified row instead 352 to have a cropped image at the end. */ 353 /*if (settings.fixedHeight && newImgH < settings.rowHeight) { 354 newImgW = settings.rowHeight * imgAspectRatio; 355 newImgH = settings.rowHeight; 356 }*/ 357 358 } else { 359 newImgW = settings.rowHeight * imgAspectRatio; 360 newImgH = settings.rowHeight; 361 } 362 363 availableWidth -= Math.round(newImgW); 364 $image.data('jg.jimgw', Math.round(newImgW)); 365 $image.data('jg.jimgh', Math.ceil(newImgH)); 366 if (i === 0 || minHeight > newImgH) minHeight = newImgH; 367 } 368 369 if (settings.fixedHeight && minHeight > settings.rowHeight) 370 minHeight = settings.rowHeight; 371 372 return {minHeight: minHeight, justify: justify}; 373 } 374 375 function displayEntry($entry, x, y, imgWidth, imgHeight, rowHeight, context) { 376 var $image = imgFromEntry($entry); 377 $image.css('width', imgWidth); 378 $image.css('height', imgHeight); 379 //if ($entry.get(0) === $image.parent().get(0)) { // this creates an error in link_around_img test 380 $image.css('margin-left', - imgWidth / 2); 381 $image.css('margin-top', - imgHeight / 2); 382 //} 383 $entry.width(imgWidth); 384 $entry.height(rowHeight); 385 $entry.css('top', y); 386 $entry.css('left', x); 387 388 //DEBUG// console.log('displayEntry (w: ' + $image.width() + ' h: ' + $image.height()); 389 390 // Image reloading for an high quality of thumbnails 391 var imageSrc = $image.attr('data-url'); 392 var img_sizes = $.parseJSON($image.attr('data-sizes')); 393 var newImageSrc = newSrc(imageSrc, imgWidth, imgHeight, context, img_sizes); 394 395 $image.one('error', function () { 396 //DEBUG// console.log('revert the original image'); 397 $image.attr('src', $image.data('jg.originalSrc')); //revert to the original thumbnail, we got it. 398 }); 399 400 function loadNewImage() { 401 if (imageSrc !== newImageSrc) { //load the new image after the fadeIn 402 $image.attr('src', newImageSrc); 403 } 404 } 405 406 if ($image.data('jg.loaded') === 'skipped') { 407 onImageEvent(imageSrc, function() { 408 showImg($entry, loadNewImage, context); 409 $image.data('jg.loaded', true); 410 }); 411 } else { 412 showImg($entry, loadNewImage, context); 413 } 414 415 // Captions ------------------------------ 416 var captionMouseEvents = $entry.data('jg.captionMouseEvents'); 417 if (context.settings.captions === true) { 418 var $imgCaption = $entry.find('.caption'); 419 420 421 // Create events (we check again the $imgCaption because it can be still inexistent) 422 if ($imgCaption.length !== 0) { 423 if (!context.settings.cssAnimation) { 424 $imgCaption.stop().fadeTo(context.settings.imagesAnimationDuration, 425 context.settings.captionSettings.nonVisibleOpacity); 426 } 427 if (typeof captionMouseEvents === 'undefined') { 428 captionMouseEvents = { 429 mouseenter: onEntryMouseEnterForCaption, 430 mouseleave: onEntryMouseLeaveForCaption 431 };
432 $entry.on('mouseenter', undefined, context, captionMouseEvents.mouseenter); 433 $entry.on('mouseleave', undefined, context, captionMouseEvents.mouseleave); 434 $entry.data('jg.captionMouseEvents', captionMouseEvents); 435 } 436 } 437 } else { 438 if (typeof captionMouseEvents !== 'undefined') { 439 $entry.off('mouseenter', undefined, context, captionMouseEvents.mouseenter); 440 $entry.off('mouseleave', undefined, context, captionMouseEvents.mouseleave); 441 $entry.removeData('jg.captionMouseEvents'); 442 } 443 } 444 445 } 446 447 /* Given an image src, with the width and the height, returns the new image src with the 448 best suffix to show the best quality thumbnail. */ 449 function newSrc(imageSrc, imgWidth, imgHeight, context, img_sizes) { 450 451 var matchRes = imageSrc.match(context.settings.extension); 452 var ext = (matchRes != null) ? matchRes[0] : ''; 453 var newImageSrc = imageSrc.replace(context.settings.extension, ''); 454 var imgSuffix = ''; 455 456 $.each(img_sizes, function (key, data) { 457 imgSuffix = "-"+ data['width'] + "x" + data['height']; 458 if ( imgHeight < data['height'] ) { 459 return false; 460 } 461 }); 462 463 if ( imgSuffix != '' ) { 464 newImageSrc += imgSuffix + ext; 465 } else { 466 newImageSrc += ext; 467 } 468 469 return newImageSrc; 470 } 471 472 function showImg($entry, callback, context) { 473 if (context.settings.cssAnimation) { 474 $entry.addClass('entry-visible'); 475 callback(); 476 } else { 477 $entry.stop().fadeTo(context.settings.imagesAnimationDuration, 1.0, callback); 478 } 479 } 480 481 function onEntryMouseEnterForCaption (ev) { 482 var $caption = $(ev.currentTarget).find('.caption'); 483 if (ev.data.settings.cssAnimation) { 484 $caption.addClass('caption-visible').removeClass('caption-hidden'); 485 } else { 486 $caption.stop().fadeTo(ev.data.settings.captionSettings.animationDuration, 487 ev.data.settings.captionSettings.visibleOpacity); 488 } 489 } 490 491 function onEntryMouseLeaveForCaption (ev) { 492 var $caption = $(ev.currentTarget).find('.caption'); 493 if (ev.data.settings.cssAnimation) { 494 $caption.removeClass('caption-visible').removeClass('caption-hidden'); 495 } else { 496 $caption.stop().fadeTo(ev.data.settings.captionSettings.animationDuration, 497 ev.data.settings.captionSettings.nonVisibleOpacity); 498 } 499 } 500 501 function stopLoadingSpinnerAnimation(spinnerContext) { 502 clearInterval(spinnerContext.intervalId); 503 spinnerContext.intervalId = null; 504 } 505 506 function rewind(context) { 507 context.lastAnalyzedIndex = -1; 508 context.buildingRow.entriesBuff = []; 509 context.buildingRow.aspectRatio = 0; 510 context.buildingRow.width = 0; 511 context.offY = context.border; 512 } 513 514 function hideImgImmediately($entry, context) { 515 if (context.settings.cssAnimation) { 516 $entry.removeClass('entry-visible'); 517 } else { 518 $entry.stop().fadeTo(0, 0); 519 } 520 } 521 522 return this.each(function (index, gallery) { 523 524 var $gallery = $(gallery); 525 $gallery.addClass('justified-gallery'); 526 527 var context = $gallery.data('jg.context'); 528 if (typeof context === 'undefined') { 529 530 if (typeof arg !== 'undefined' && arg !== null && typeof arg !== 'object') 531 throw 'The argument must be an object'; 532 533 // Spinner init
534 var $spinner = $('<div class="spinner"><span></span><span></span><span></span></div>'); 535 var extendedSettings = $.extend({}, defaults, arg); 536 537 var border = extendedSettings.border >= 0 ? extendedSettings.border : extendedSettings.margins; 538 539 //Context init 540 context = { 541 settings : extendedSettings, 542 imgAnalyzerTimeout : null, 543 entries : null, 544 buildingRow : { 545 entriesBuff : [], 546 width : 0, 547 aspectRatio : 0 548 }, 549 lastAnalyzedIndex : -1, 550 yield : { 551 every : 2, /* do a flush every context.yield.every flushes ( 552 * must be greater than 1, else the analyzeImages will loop */ 553 flushed : 0 //flushed rows without a yield 554 }, 555 border : border, 556 offY : border, 557 spinner : { 558 active : false, 559 phase : 0, 560 timeslot : 150, 561 $el : $spinner, 562 $points : $spinner.find('span'), 563 intervalId : null 564 }, 565 checkWidthIntervalId : null, 566 galleryWidth : $gallery.width(), 567 $gallery : $gallery 568 }; 569 570 $gallery.data('jg.context', context); 571 572 } else if (arg === 'norewind') { 573 /* Hide the image of the buildingRow to prevent strange effects when the row will be 574 re-justified again */ 575 for (var i = 0; i < context.buildingRow.entriesBuff.length; i++) { 576 hideImgImmediately(context.buildingRow.entriesBuff[i], context); 577 } 578 // In this case we don't rewind, and analyze all the images 579 } else { 580 context.settings = $.extend({}, context.settings, arg); 581 context.border = context.settings.border >= 0 ? context.settings.border : context.settings.margins; 582 rewind(context); 583 } 584 checkSettings(context); 585 586 context.entries = $gallery.find('> figure, > div:not(.spinner)').toArray(); 587 if (context.entries.length === 0) return; 588 589 // Randomize 590 if (context.settings.randomize) { 591 context.entries.sort(function () { return Math.random() * 2 - 1; }); 592 $.each(context.entries, function () { 593 $(this).appendTo($gallery); 594 }); 595 } 596 597 var imagesToLoad = false; 598 var skippedImages = false; 599 $.each(context.entries, function (index, entry) { 600 var $entry = $(entry); 601 var $image = imgFromEntry($entry); 602 603 $entry.addClass('jg-entry'); 604 605 if ($image.data('jg.loaded') !== true && $image.data('jg.loaded') !== 'skipped') { 606 607 // Link Rel global overwrite 608 if (context.settings.rel !== null) $entry.attr('rel', context.settings.rel); 609 610 // Link Target global overwrite 611 if (context.settings.target !== null) $entry.attr('target', context.settings.target); 612 613 // Image src 614 var imageSrc = (typeof $image.data('safe-src') !== 'undefined') ? 615 $image.data('safe-src') : $image.attr('src'); 616 $image.data('jg.originalSrc', imageSrc); 617 $image.attr('src', imageSrc); 618 619 var width = parseInt($image.attr('width'), 10); 620 var height = parseInt($image.attr('height'), 10); 621 if(context.settings.waitThumbnailsLoad !== true && !isNaN(width) && !isNaN(height)) { 622 $image.data('jg.imgw', width); 623 $image.data('jg.imgh', height); 624 $image.data('jg.loaded', 'skipped'); 625 skippedImages = true; 626 startImgAnalyzer(context, false); 627 return true; 628 } 629 630 $image.data('jg.loaded', false); 631 imagesToLoad = true; 632 633 // Spinner start 634 if (context.spinner.active === false) { 635 context.spinner.active = true; 636 $gallery.append(context.spinner.$el); 637 $gallery.height(context.offY + context.spinner.$el.innerHeight()); 638 startLoadingSpinnerAnimation(context.spinner); 639 } 640 641 onImageEvent(imageSrc, function imgLoaded (loadImg) { 642 //DEBUG// console.log('img load (alt: ' + $image.attr('alt') + ')'); 643 $image.data('jg.imgw', loadImg.width);
644 $image.data('jg.imgh', loadImg.height); 645 $image.data('jg.loaded', true); 646 startImgAnalyzer(context, false); 647 }, function imgLoadError () { 648 //DEBUG// console.log('img error (alt: ' + $image.attr('alt') + ')'); 649 $image.data('jg.loaded', 'error'); 650 startImgAnalyzer(context, false); 651 }); 652 653 } 654 655 }); 656 657 if (!imagesToLoad && !skippedImages) startImgAnalyzer(context, false); 658 checkWidth(context); 659 }); 660 661 }; 662 663}(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.