1/** 2Script: Slideshow.js 3 Slideshow - A javascript class for Mootools to stream and animate the presentation of images on your website. 4 5License: 6 MIT-style license. 7 8Copyright: 9 Copyright (c) 2008 [Aeron Glemann](http://www.electricprism.com/aeron/). 10 11Dependencies: 12 Mootools 1.2 Core: Fx.Morph, Fx.Tween, Selectors, Element.Dimensions. 13 Mootools 1.2 More: Assets. 14*/ 15 16Slideshow = new Class({ 17 Implements: [Chain, Events, Options], 18 19 options: {/* 20 onComplete: $empty, 21 onEnd: $empty, 22 onStart: $empty,*/ 23 captions: false, 24 center: true, 25 classes: [], 26 controller: false, 27 delay: 2000, 28 duration: 750, 29 fast: false, 30 height: false, 31 href: '', 32 hu: '', 33 linked: false, 34 loader: {'animate': ['css/loader-#.png', 12]}, 35 loop: true, 36 match: /\?slide=(\d+)$/, 37 overlap: true, 38 paused: false, 39 properties: ['href', 'rel', 'rev', 'title'], 40 random: false, 41 replace: [/(\.[^\.]+)$/, 't$1'], 42 resize: 'width', 43 slide: 0, 44 thumbnails: false, 45 titles: true, 46 transition: function(p){return -(Math.cos(Math.PI * p) - 1) / 2;}, 47 width: false 48 }, 49 50/** 51Constructor: initialize 52 Creates an instance of the Slideshow class. 53 54Arguments: 55 element - (element) The wrapper element. 56 data - (array or object) The images and optional thumbnails, captions and links for the show. 57 options - (object) The options below. 58 59Syntax: 60 var myShow = new Slideshow(element, data, options); 61*/ 62 63 initialize: function(el, data, options){ 64 this.setOptions(options); 65 this.slideshow = $(el); 66 if (!this.slideshow) 67 return; 68 this.slideshow.set('styles', {'display': 'block', 'position': 'relative', 'z-index': 0}); 69 var match = window.location.href.match(this.options.match); 70 this.slide = (this.options.match && match) ? match[1].toInt() : this.options.slide; 71 this.counter = this.delay = this.transition = 0; 72 this.direction = 'left'; 73 this.paused = false; 74 if (!this.options.overlap) 75 this.options.duration *= 2; 76 var anchor = this.slideshow.getElement('a') || new Element('a'); 77 if (!this.options.href) 78 this.options.href = anchor.get('href') || ''; 79 if (this.options.hu.length && !this.options.hu.test(/\/$/)) 80 this.options.hu += '/'; 81 if (this.options.fast === true) 82 this.options.fast = 2; 83 84 // styles 85 86 var keys = ['slideshow', 'first', 'prev', 'play', 'pause', 'next', 'last', 'images', 'captions', 'controller', 'thumbnails', 'hidden', 'visible', 'inactive', 'active', 'loader']; 87 var values = keys.map(function(key, i){ 88 return this.options.classes[i] || key; 89 }, this); 90 this.classes = values.associate(keys); 91 this.classes.get = function(){ 92 var str = '.' + this.slideshow; 93 for (var i = 0, l = arguments.length; i < l; i++) 94 str += ('-' + this[arguments[i]]); 95 return str; 96 }.bind(this.classes); 97 98 // data 99 100 if (!data){ 101 this.options.hu = ''; 102 data = {}; 103 var thumbnails = this.slideshow.getElements(this.classes.get('thumbnails') + ' img'); 104 this.slideshow.getElements(this.classes.get('images') + ' img').each(function(img, i){ 105 var src = img.get('src'); 106 var caption = $pick(img.get('alt'), img.get('title'), ''); 107 var parent = img.getParent(); 108 var properties = (parent.get('tag') == 'a') ? parent.getProperties : {}; 109 var href = img.getParent().get('href') || ''; 110 var thumbnail = (thumbnails[i]) ? thumbnails[i].get('src') : ''; 111 data[src] = {'caption': caption, 'href': href, 'thumbnail': thumbnail}; 112 }); 113 } 114 var loaded = this.load(data); 115 if (!loaded) 116 return; 117 118 // events 119 120 this.events = $H({'keydown': [], 'keyup': [], 'mousemove': []}); 121 var keyup = function(e){ 122 switch(e.key){ 123 case 'left': 124 this.prev(e.shift); break; 125 case 'right': 126 this.next(e.shift); break; 127 case 'p': 128 this.pause(); break; 129 } 130 }.bind(this); 131 this.events.keyup.push(keyup); 132 document.addEvent('keyup', keyup); 133 134 // required elements 135 136 var el = this.slideshow.getElement(this.classes.get('images')); 137 var images = (el) ? el.empty() : new Element('div', {'class': this.classes.get('images').substr(1)}).inject(this.slideshow); 138 var div = images.getSize(); 139 this.height = this.options.height || div.y; 140 this.width = this.options.width || div.x;
141 images.set({'styles': {'display': 'block', 'height': this.height, 'overflow': 'hidden', 'position': 'relative', 'width': this.width}}); 142 this.slideshow.store('images', images); 143 this.a = this.image = this.slideshow.getElement('img') || new Element('img'); 144 if (Browser.Engine.trident && Browser.Engine.version > 4) 145 this.a.style.msInterpolationMode = 'bicubic'; 146 this.a.set('styles', {'display': 'none', 'position': 'absolute', 'zIndex': 1}); 147 this.b = this.a.clone(); 148 [this.a, this.b].each(function(img){ 149 anchor.clone().cloneEvents(anchor).grab(img).inject(images); 150 }); 151 152 // optional elements 153 154 if (this.options.captions) 155 this._captions(); 156 if (this.options.controller) 157 this._controller(); 158 if (this.options.loader) 159 this._loader(); 160 if (this.options.thumbnails) 161 this._thumbnails(); 162 163 // begin show 164 165 this._preload(); 166 }, 167 168/** 169Public method: go 170 Jump directly to a slide in the show. 171 172Arguments: 173 n - (integer) The index number of the image to jump to, 0 being the first image in the show. 174 175Syntax: 176 myShow.go(n); 177*/ 178 179 go: function(n, direction){ 180 if ((this.slide - 1 + this.data.images.length) % this.data.images.length == n || $time() < this.transition) 181 return; 182 $clear(this.timer); 183 this.delay = 0; 184 this.direction = (direction) ? direction : ((n < this.slide) ? 'right' : 'left'); 185 this.slide = n; 186 if (this.preloader) 187 this.preloader = this.preloader.destroy(); 188 this._preload(this.options.fast == 2 || (this.options.fast == 1 && this.paused)); 189 }, 190 191/** 192Public method: first 193 Goes to the first image in the show. 194 195Syntax: 196 myShow.first(); 197*/ 198 199 first: function(){ 200 this.prev(true); 201 }, 202 203/** 204Public method: prev 205 Goes to the previous image in the show. 206 207Syntax: 208 myShow.prev(); 209*/ 210 211 prev: function(first){ 212 var n = 0; 213 if (!first){ 214 if (this.options.random){ 215 216 // if it's a random show get the previous slide from the showed array 217 218 if (this.showed.i < 2) 219 return; 220 this.showed.i -= 2; 221 n = this.showed.array[this.showed.i]; 222 } 223 else 224 n = (this.slide - 2 + this.data.images.length) % this.data.images.length; 225 } 226 this.go(n, 'right'); 227 }, 228 229/** 230Public method: pause 231 Toggles play / pause state of the show. 232 233Arguments: 234 p - (undefined, 1 or 0) Call pause with no arguments to toggle the pause state. Call pause(1) to force pause, or pause(0) to force play. 235 236Syntax: 237 myShow.pause(p); 238*/ 239 240 pause: function(p){ 241 if ($chk(p)) 242 this.paused = (p) ? false : true; 243 if (this.paused){ 244 this.paused = false; 245 this.delay = this.transition = 0; 246 this.timer = this._preload.delay(100, this); 247 [this.a, this.b].each(function(img){ 248 ['morph', 'tween'].each(function(p){ 249 if (this.retrieve(p)) this.get(p).resume(); 250 }, img); 251 }); 252 if (this.options.controller) 253 this.slideshow.getElement('.' + this.classes.pause).removeClass(this.classes.play); 254 } 255 else { 256 this.paused = true; 257 this.delay = Number.MAX_VALUE; 258 this.transition = 0; 259 $clear(this.timer); 260 [this.a, this.b].each(function(img){ 261 ['morph', 'tween'].each(function(p){ 262 if (this.retrieve(p)) this.get(p).pause(); 263 }, img); 264 }); 265 if (this.options.controller) 266 this.slideshow.getElement('.' + this.classes.pause).addClass(this.classes.play); 267 } 268 }, 269 270/** 271Public method: next 272 Goes to the next image in the show. 273 274Syntax: 275 myShow.next(); 276*/ 277 278 next: function(last){ 279 var n = (last) ? this.data.images.length - 1 : this.slide; 280 this.go(n, 'left'); 281 }, 282 283/** 284Public method: last 285 Goes to the last image in the show. 286 287Syntax: 288 myShow.last(); 289*/ 290 291 last: function(){ 292 this.next(true); 293 }, 294 295/** 296Public method: load 297 Loads a new data set into the show: will stop the current show, rewind and rebuild thumbnails if applicable. 298 299Arguments: 300 data - (array or object) The images and optional thumbnails, captions and links for the show. 301 302Syntax: 303 myShow.load(data); 304*/ 305 306 load: function(data){ 307 this.firstrun = true; 308 this.showed = {'array': [], 'i': 0}; 309 if ($type(data) == 'array'){ 310 this.options.captions = false; 311 data = new Array(data.length).associate(data.map(function(image, i){ return image
311+ '?' + i })); 312 } 313 this.data = {'images': [], 'captions': [], 'hrefs': [], 'thumbnails': []}; 314 for (var image in data){ 315 var obj = data[image] || {}; 316 var caption = (obj.caption) ? obj.caption.trim() : ''; 317 var href = (obj.href) ? obj.href.trim() : ((this.options.linked) ? this.options.hu + image : this.options.href); 318 var thumbnail = (obj.thumbnail) ? obj.thumbnail.trim() : image.replace(this.options.replace[0], this.options.replace[1]); 319 this.data.images.push(image); 320 this.data.captions.push(caption); 321 this.data.hrefs.push(href); 322 this.data.thumbnails.push(thumbnail); 323 } 324 if (this.options.random) 325 this.slide = $random(0, this.data.images.length - 1); 326 327 // only run when data is loaded dynamically into an existing slideshow instance 328 329 if (this.options.thumbnails && this.slideshow.retrieve('thumbnails')) 330 this._thumbnails(); 331 if (this.slideshow.retrieve('images')){ 332 [this.a, this.b].each(function(img){ 333 ['morph', 'tween'].each(function(p){ 334 if (this.retrieve(p)) this.get(p).cancel(); 335 }, img); 336 }); 337 this.slide = this.transition = 0; 338 this.go(0); 339 } 340 return this.data.images.length; 341 }, 342 343/** 344Public method: destroy 345 Destroys a Slideshow instance. 346 347Arguments: 348 p - (string) The images and optional thumbnails, captions and links for the show. 349 350Syntax: 351 myShow.destroy(p); 352*/ 353 354 destroy: function(p){ 355 this.events.each(function(array, e){ 356 array.each(function(fn){ document.removeEvent(e, fn); }); 357 }); 358 this.pause(1); 359 if (this.options.loader) 360 $clear(this.slideshow.retrieve('loader').retrieve('timer')); 361 if (this.options.thumbnails) 362 $clear(this.slideshow.retrieve('thumbnails').retrieve('timer')); 363 this.slideshow.uid = Native.UID++; 364 if (p) 365 this.slideshow[p](); 366 }, 367 368/** 369Private method: preload 370 Preloads the next slide in the show, once loaded triggers the show, updates captions, thumbnails, etc. 371*/ 372 373 _preload: function(fast){ 374 if (!this.preloader) 375 this.preloader = new Asset.image(this.options.hu + this.data.images[this.slide], {'onload': function(){ 376 this.store('loaded', true); 377 }}); 378 if (this.preloader.retrieve('loaded') && $time() > this.delay && $time() > this.transition){ 379 if (this.stopped){ 380 if (this.options.captions) 381 this.slideshow.retrieve('captions').get('morph').cancel().start(this.classes.get('captions', 'hidden')); 382 this.pause(1); 383 if (this.end) 384 this.fireEvent('end'); 385 this.stopped = this.end = false; 386 return; 387 } 388 this.image = (this.counter % 2) ? this.b : this.a; 389 this.image.set('styles', {'display': 'block', 'height': 'auto', 'visibility': 'hidden', 'width': 'auto', 'zIndex': this.counter}); 390 ['src', 'height', 'width'].each(function(prop){ 391 this.image.set(prop, this.preloader.get(prop)); 392 }, this); 393 this._resize(this.image); 394 this._center(this.image); 395 var anchor = this.image.getParent(); 396 if (this.data.hrefs[this.slide]) 397 anchor.set('href', this.data.hrefs[this.slide]); 398 else 399 anchor.erase('href'); 400 var text = (this.data.captions[this.slide]) 401 ? this.data.captions[this.slide].replace(/<.+?>/gm, '').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, "'") 402 : ''; 403 this.image.set('alt', text); 404 if (this.options.titles) 405 anchor.set('title', text); 406 if (this.options.loader) 407 this.slideshow.retrieve('loader').fireEvent('hide'); 408 if (this.options.captions) 409 this.slideshow.retrieve('captions').fireEvent('update', fast); 410 if (this.options.thumbnails) 411 this.slideshow.retrieve('thumbnails').fireEvent('update', fast); 412 this._show(fast); 413 this._loaded(); 414 } 415 else { 416 if ($time() > this.delay && this.options.loader) 417 this.slideshow.retrieve('loader').fireEvent('show'); 418 this.timer = (this.paused && this.preloader.retrieve('loaded')) ? null : this._preload.delay(100, this, fast); 419 } 420 }, 421 422/** 423Private method: show 424 Does the slideshow effect. 425*/ 426 427 _show: function(fast){ 428 if (!this.image.retrieve('morph')){ 429 var options = (this.options.overlap) ? {'duration': this.options.duration, 'link': 'cancel'} : {'duration': this.options.duration / 2, 'link': 'chain'}; 430 $$(this.a, this.b).set('morph', $merge(options, {'onStart': this._start.bind(this), 'onComplete': this._complete.bind(this), 'transition': this.options.transition})); 431 } 432 var hidden = this.classes.get('images', ((this.direction == 'left') ? 'next' : 'prev')); 433 var visible = this.classes.get('images', 'visible'); 434 var img = (this.counter % 2) ? this.a : this.b; 435 if (fast){ 436 img.get('morph').cancel().set(hidden); 437 this.image.get('morph').cancel().set(visible); 438 } 439 else { 440 if (this.options.overlap){ 441 img.get('morph').set(visible); 442 this.image.get('morph').set(hidden).start(visible); 443 } 444 else { 445 var fn = function(hidden, visible){ 446 this.image.get('morph').set(hidden).start(visible); 447 }.pass([hidden, visible], this); 448 hidden = this.classes.get('images', ((this.direction == 'left') ? 'prev' : 'next')); 449 img.get('morph').set(visible).start(hidden).chain(fn); 450 } 451 } 452 }, 453 454/** 455Private method: loaded 456 Run after the current image has been loaded, sets up the next image to be shown. 457*/ 458 459 _loaded: function(){ 460 this.counter++; 461 this.delay = (this.paused) ? Number.MAX_VALUE : $time() + this.options.duration + this.options.delay; 462 this.direction = 'left'; 463 this.transition = (this.options.fast == 2 || (this.options.fast == 1 && this.paused)) ? 0 : $time() + this.options.duration; 464 if (this.slide + 1 == this.data.images.length && !this.options.loop && !this.options.random) 465 this.stopped = this.end = true; 466 if (this.options.random){ 467 this.showed.i++; 468 if (this.showed.i >= this.showed.array.length){ 469 var n = this.slide; 470 if (this.showed.array.getLast() != n) this.showed.array.push(n); 471 while (this.slide == n) 472 this.slide = $random(0, this.data.images.length - 1); 473 } 474 else 475 this.slide = this.showed.array[this.showed.i]; 476 } 477 else 478 this.slide = (this.slide + 1) % this.data.images.length; 479 if (this.image.getStyle('visibility') != 'visible') 480 (function(){ this.image.setStyle('visibility', 'visible'); }).delay(1, this); 481 if (this.preloader) 482 this.preloader = this.preloader.destroy(); 483 this._preload(); 484 }, 485 486/** 487Private method: center 488 Center an image. 489*/ 490 491 _center: function(img){ 492 if (this.options.center){ 493 var size = img.getSize(); 494 img.set('styles', {'left': (size.x - this.width) / -2, 'top': (size.y - this.height) / -2}); 495 } 496 }, 497 498/** 499Private method: resize 500 Resizes an image. 501*/ 502 503 _resize: function(img){ 504 if (this.options.resize){ 505 var h = this.preloader.get('height'), w = this.preloader.get('width'); 506 var dh = this.height / h, dw = this.width / w, d; 507 if (this.options.resize == 'length') 508 d = (dh > dw) ? dw : dh; 509 else 510 d = (dh > dw) ? dh : dw;
511 img.set('styles', {height: Math.ceil(h * d), width: Math.ceil(w * d)}); 512 } 513 }, 514 515/** 516Private method: start 517 Callback on start of slide change. 518*/ 519 520 _start: function(){ 521 this.fireEvent('start'); 522 }, 523 524/** 525Private method: complete 526 Callback on start of slide change. 527*/ 528 529 _complete: function(){ 530 if (this.firstrun && this.options.paused){ 531 this.firstrun = false; 532 this.pause(1); 533 } 534 this.fireEvent('complete'); 535 }, 536 537/** 538Private method: captions 539 Builds the optional caption element, adds interactivity. 540 This method can safely be removed if the captions option is not enabled. 541*/ 542 543 _captions: function(){ 544 if (this.options.captions === true) 545 this.options.captions = {}; 546 var el = this.slideshow.getElement(this.classes.get('captions')); 547 var captions = (el) ? el.empty() : new Element('div', {'class': this.classes.get('captions').substr(1)}).inject(this.slideshow); 548 captions.set({ 549 'events': { 550 'update': function(fast){ 551 var captions = this.slideshow.retrieve('captions'); 552 var empty = (this.data.captions[this.slide] === ''); 553 if (fast){ 554 var p = (empty) ? 'hidden' : 'visible'; 555 captions.set('html', this.data.captions[this.slide]).get('morph').cancel().set(this.classes.get('captions', p)); 556 } 557 else { 558 var fn = (empty) ? $empty : function(n){ 559 this.slideshow.retrieve('captions').set('html', this.data.captions[n]).morph(this.classes.get('captions', 'visible')) 560 }.pass(this.slide, this); 561 captions.get('morph').cancel().start(this.classes.get('captions', 'hidden')).chain(fn); 562 } 563 }.bind(this) 564 }, 565 'morph': $merge(this.options.captions, {'link': 'chain'}) 566 }); 567 this.slideshow.store('captions', captions); 568 }, 569 570/** 571Private method: controller 572 Builds the optional controller element, adds interactivity. 573 This method can safely be removed if the controller option is not enabled. 574*/ 575 576 _controller: function(){ 577 if (this.options.controller === true) 578 this.options.controller = {}; 579 var el = this.slideshow.getElement(this.classes.get('controller')); 580 var controller = (el) ? el.empty() : new Element('div', {'class': this.classes.get('controller').substr(1)}).inject(this.slideshow); 581 var ul = new Element('ul').inject(controller); 582 $H({'first': 'Shift + Leftwards Arrow', 'prev': 'Leftwards Arrow', 'pause': 'P', 'next': 'Rightwards Arrow', 'last': 'Shift + Rightwards Arrow'}).each(function(accesskey, action){ 583 var li = new Element('li', { 584 'class': (action == 'pause' && this.options.paused) ? this.classes.play + ' ' + this.classes[action] : this.classes[action] 585 }).inject(ul); 586 var a = this.slideshow.retrieve(action, new Element('a', { 587 'title': ((action == 'pause') ? this.classes.play.capitalize() + ' / ' : '') + this.classes[action].capitalize() + ' [' + accesskey + ']' 588 }).inject(li)); 589 a.set('events', { 590 'click': function(action){this[action]();}.pass(action, this), 591 'mouseenter': function(active){this.addClass(active);}.pass(this.classes.active, a), 592 'mouseleave': function(active){this.removeClass(active);}.pass(this.classes.active, a) 593 }); 594 }, this); 595 controller.set({ 596 'events': { 597 'hide': function(hidden){ 598 if (!this.retrieve('hidden')) 599 this.store('hidden', true).morph(hidden); 600 }.pass(this.classes.get('controller', 'hidden'), controller), 601 'show': function(visible){ 602 if (this.retrieve('hidden')) 603 this.store('hidden', false).morph(visible); 604 }.pass(this.classes.get('controller', 'visible'), controller) 605 }, 606 'morph': $merge(this.options.controller, {'link': 'cancel'}) 607 }).store('hidden', false); 608 var keydown = function(e){ 609 if (['left', 'right', 'p'].contains(e.key)){ 610 var controller = this.slideshow.retrieve('controller'); 611 if (controller.retrieve('hidden')) 612 controller.get('morph').set(this.classes.get('controller', 'visible')); 613 switch(e.key){ 614 case 'left': 615 this.slideshow.retrieve((e.shift) ? 'first' : 'prev').fireEvent('mouseenter'); break; 616 case 'right': 617 this.slideshow.retrieve((e.shift) ? 'last' : 'next').fireEvent('mouseenter'); break; 618 default: 619 this.slideshow.retrieve('pause').fireEvent('mouseenter'); break; 620 } 621 } 622 }.bind(this); 623 this.events.keydown.push(keydown); 624 var keyup = function(e){ 625 if (['left', 'right', 'p'].contains(e.key)){
626 var controller = this.slideshow.retrieve('controller'); 627 if (controller.retrieve('hidden')) 628 controller.store('hidden', false).fireEvent('hide'); 629 switch(e.key){ 630 case 'left': 631 this.slideshow.retrieve((e.shift) ? 'first' : 'prev').fireEvent('mouseleave'); break; 632 case 'right': 633 this.slideshow.retrieve((e.shift) ? 'last' : 'next').fireEvent('mouseleave'); break; 634 default: 635 this.slideshow.retrieve('pause').fireEvent('mouseleave'); break; 636 } 637 } 638 }.bind(this); 639 this.events.keyup.push(keyup); 640 var mousemove = function(e){ 641 var images = this.slideshow.retrieve('images').getCoordinates(); 642 if (e.page.x > images.left && e.page.x < images.right && e.page.y > images.top && e.page.y < images.bottom) 643 this.slideshow.retrieve('controller').fireEvent('show'); 644 else 645 this.slideshow.retrieve('controller').fireEvent('hide'); 646 }.bind(this); 647 this.events.mousemove.push(mousemove); 648 document.addEvents({'keydown': keydown, 'keyup': keyup, 'mousemove': mousemove}); 649 this.slideshow.retrieve('controller', controller).fireEvent('hide'); 650 }, 651 652/** 653Private method: loader 654 Builds the optional loader element, adds interactivity. 655 This method can safely be removed if the loader option is not enabled. 656*/ 657 658 _loader: function(){ 659 if (this.options.loader === true) 660 this.options.loader = {}; 661 var loader = new Element('div', { 662 'class': this.classes.get('loader').substr(1), 663 'morph': $merge(this.options.loader, {'link': 'cancel'}) 664 }).store('hidden', false).store('i', 1).inject(this.slideshow.retrieve('images')); 665 if (this.options.loader.animate){ 666 for (var i = 0; i < this.options.loader.animate[1]; i++) 667 img = new Asset.image(this.options.loader.animate[0].replace(/#/, i)); 668 if (Browser.Engine.trident4 && this.options.loader.animate[0].contains('png')) 669 loader.setStyle('backgroundImage', 'none'); 670 } 671 loader.set('events', { 672 'animate': function(){ 673 var loader = this.slideshow.retrieve('loader'); 674 var i = (loader.retrieve('i').toInt() + 1) % this.options.loader.animate[1]; 675 loader.store('i', i); 676 var img = this.options.loader.animate[0].replace(/#/, i); 677 if (Browser.Engine.trident4 && this.options.loader.animate[0].contains('png')) 678 loader.style.filter = 'progid:DXImageTransform.Microsoft.AlphaImageLoader(src="' + img + '", sizingMethod="scale")'; 679 else 680 loader.setStyle('backgroundImage', 'url(' + img + ')'); 681 }.bind(this), 682 'hide': function(){ 683 var loader = this.slideshow.retrieve('loader'); 684 if (!loader.retrieve('hidden')){ 685 loader.store('hidden', true).morph(this.classes.get('loader', 'hidden')); 686 if (this.options.loader.animate) 687 $clear(loader.retrieve('timer')); 688 } 689 }.bind(this), 690 'show': function(){ 691 var loader = this.slideshow.retrieve('loader'); 692 if (loader.retrieve('hidden')){ 693 loader.store('hidden', false).morph(this.classes.get('loader', 'visible')); 694 if (this.options.loader.animate) 695 loader.store('timer', function(){this.fireEvent('animate');}.periodical(50, loader)); 696 } 697 }.bind(this) 698 }); 699 this.slideshow.retrieve('loader', loader).fireEvent('hide'); 700 }, 701 702/** 703Private method: thumbnails 704 Builds the optional thumbnails element, adds interactivity. 705 This method can safely be removed if the thumbnails option is not enabled. 706*/ 707 708 _thumbnails: function(){ 709 if (this.options.thumbnails === true) 710 this.options.thumbnails = {}; 711 var el = this.slideshow.getElement(this.classes.get('thumbnails')); 712 var thumbnails = (el) ? el.empty() : new Element('div', {'class': this.classes.get('thumbnails').substr(1)}).inject(this.slideshow); 713 thumbnails.setStyle('overflow', 'hidden'); 714 var ul = new Element('ul', {'tween': {'link': 'cancel'}}).inject(thumbnails); 715 this.data.thumbnails.each(function(thumbnail, i){ 716 var li = new Element('li').inject(ul); 717 var a = new Element('a', { 718 'events': { 719 'click': function(i){ 720 this.go(i); 721 return false; 722 }.pass(i, this), 723 'loaded': function(){ 724 this.data.thumbnails.pop(); 725 if (!this.data.thumbnails.length){ 726 var div = thumbnails.getCoordinates(); 727 var props = thumbnails.retrieve('props'); 728 var limit = 0, pos = props[1], size = props[2]; 729 thumbnails.getElements('li').each(function(li){ 730 var li = li.getCoordinates(); 731 if (li[pos] > limit) limit = li[pos]; 732 }, this); 733 thumbnails.store('limit', div[size] + div[props[0]] - limit); 734 } 735 }.bind(this) 736 }, 737 'href': this.options.hu + this.data.images[i], 738 'morph': $merge(this.options.thumbnails, {'link': 'cancel'}) 739 }).inject(li); 740 if (this.data.captions[i] && this.options.titles) 741 a.set('title', this.data.captions[i].replace(/<.+?>/gm, '').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, "'")); 742 var img = new Asset.image(this.options.hu + thumbnail, { 743 'onload': function(){this.fireEvent('loaded');}.bind(a) 744 }).inject(a); 745 }, this); 746 thumbnails.set('events', { 747 'scroll': function(n, fast){ 748 var div = this.getCoordinates(); 749 var ul = this.getElement('ul').getPosition(); 750 var props = this.retrieve('props'); 751 var axis = props[3], delta, pos = props[0], size = props[2], value; 752 var tween = this.getElement('ul').get('tween', {'property': pos}); 753 if ($chk(n)){ 754 var li = this.getElements('li')[n].getCoordinates(); 755 delta = div[pos] + (div[size] / 2) - (li[size] / 2) - li[pos] 756 value = (ul[axis] - div[pos] + delta).limit(this.retrieve('limit'), 0); 757 if (fast) 758 tween.set(value); 759 else 760 tween.start(value); 761 } 762 else{ 763 var area = div[props[2]] / 3, page = this.retrieve('page'), velocity = -0.2; 764 if (page[axis] < (div[pos] + area)) 765 delta = (page[axis] - div[pos] - area) * velocity; 766 else if (page[axis] > (div[pos] + div[size] - area)) 767 delta = (page[axis] - div[pos] - div[size] + area) * velocity; 768 if (delta){ 769 value = (ul[axis] - div[pos] + delta).limit(this.retrieve('limit'), 0); 770 tween.set(value); 771 } 772 } 773 }.bind(thumbnails), 774 'update': function(fast){ 775 var thumbnails = this.slideshow.retrieve('thumbnails'); 776 thumbnails.getElements('a').each(function(a, i){ 777 if (i == this.slide){ 778 if (!a.retrieve('active', false)){ 779 a.store('active', true); 780 var active = this.classes.get('thumbnails', 'active'); 781 if (fast) a.get('morph').set(active); 782 else a.morph(active); 783 } 784 } 785 else { 786 if (a.retrieve('active', true)){ 787 a.store('active', false); 788 var inactive = this.classes.get('thumbnails', 'inactive'); 789 if (fast) a.get('morph').set(inactive); 790 else a.morph(inactive); 791 } 792 } 793 }, this); 794 if (!thumbnails.retrieve('mouseover')) 795 thumbnails.fireEvent('scroll', [this.slide, fast]); 796 }.bind(this) 797 }) 798 var div = thumbnails.getCoordinates(); 799 thumbnails.store('props', (div.height > div.width) ? ['top', 'bottom', 'height', 'y'] : ['left', 'right', 'width', 'x']); 800 var mousemove = function(e){ 801 var div = this.getCoordinates(); 802 if (e.page.x > div.left && e.page.x < div.right && e.page.y > div.top && e.page.y < div.bottom){ 803 this.store('page', e.page); 804 if (!this.retrieve('mouseover')){ 805 this.store('mouseover', true); 806 this.store('timer', function(){this.fireEvent('scroll');}
806.periodical(50, this)); 807 } 808 } 809 else { 810 if (this.retrieve('mouseover')){ 811 this.store('mouseover', false); 812 $clear(this.retrieve('timer')); 813 } 814 } 815 }.bind(thumbnails); 816 this.events.mousemove.push(mousemove); 817 document.addEvent('mousemove', mousemove); 818 this.slideshow.store('thumbnails', thumbnails); 819 } 820});
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.