PageRenderTime 120ms CodeModel.GetById 17ms app.highlight 91ms RepoModel.GetById 1ms app.codeStats 1ms

/app/scripts/vendor/foundation/jquery.foundation.orbit.js

https://bitbucket.org/ceoaliongroo/torredelprior
JavaScript | 897 lines | 723 code | 116 blank | 58 comment | 130 complexity | 7bfee2cb6a002dc84e5301f0a386a4a0 MD5 | raw file
  1/*
  2 * jQuery Orbit Plugin 1.4.0
  3 * www.ZURB.com/playground
  4 * Copyright 2010, ZURB
  5 * Free to use under the MIT license.
  6 * http://www.opensource.org/licenses/mit-license.php
  7*/
  8
  9
 10(function ($) {
 11  'use strict';
 12
 13  $.fn.findFirstImage = function () {
 14    return this.first()
 15            .find('img')
 16            .andSelf().filter('img')
 17            .first();
 18  };
 19
 20  var ORBIT = {
 21
 22    defaults: {
 23      animation: 'horizontal-push',     // fade, horizontal-slide, vertical-slide, horizontal-push, vertical-push
 24      animationSpeed: 600,              // how fast animations are
 25      timer: true,                      // display timer?
 26      advanceSpeed: 4000,               // if timer is enabled, time between transitions
 27      pauseOnHover: false,              // if you hover pauses the slider
 28      startClockOnMouseOut: false,      // if clock should start on MouseOut
 29      startClockOnMouseOutAfter: 1000,  // how long after MouseOut should the timer start again
 30      directionalNav: true,             // manual advancing directional navs
 31      directionalNavRightText: 'Right', // text of right directional element for accessibility
 32      directionalNavLeftText: 'Left',   // text of left directional element for accessibility
 33      captions: true,                   // do you want captions?
 34      captionAnimation: 'fade',         // fade, slideOpen, none
 35      captionAnimationSpeed: 600,       // if so how quickly should they animate in
 36      resetTimerOnClick: false,         // true resets the timer instead of pausing slideshow progress on manual navigation
 37      bullets: false,                   // true or false to activate the bullet navigation
 38      bulletThumbs: false,              // thumbnails for the bullets
 39      bulletThumbLocation: '',          // relative path to thumbnails from this file
 40      afterSlideChange: $.noop,         // callback to execute after slide changes
 41      afterLoadComplete: $.noop,        // callback to execute after everything has been loaded
 42      fluid: true,
 43      centerBullets: true,              // center bullet nav with js, turn this off if you want to position the bullet nav manually
 44      singleCycle: false,               // cycles through orbit slides only once
 45      slideNumber: false,               // display slide numbers?
 46      stackOnSmall: false               // stack slides on small devices (i.e. phones)
 47    },
 48
 49    activeSlide: 0,
 50    numberSlides: 0,
 51    orbitWidth: null,
 52    orbitHeight: null,
 53    locked: null,
 54    timerRunning: null,
 55    degrees: 0,
 56    wrapperHTML: '<div class="orbit-wrapper" />',
 57    timerHTML: '<div class="timer"><span class="mask"><span class="rotator"></span></span><span class="pause"></span></div>',
 58    captionHTML: '<div class="orbit-caption"></div>',
 59    directionalNavHTML: '<div class="slider-nav hide-for-small"><span class="right"></span><span class="left"></span></div>',
 60    bulletHTML: '<ul class="orbit-bullets"></ul>',
 61    slideNumberHTML: '<span class="orbit-slide-counter"></span>',
 62
 63    init: function (element, options) {
 64      var $imageSlides,
 65          imagesLoadedCount = 0,
 66          self = this;
 67
 68      // Bind functions to correct context
 69      this.clickTimer = $.proxy(this.clickTimer, this);
 70      this.addBullet = $.proxy(this.addBullet, this);
 71      this.resetAndUnlock = $.proxy(this.resetAndUnlock, this);
 72      this.stopClock = $.proxy(this.stopClock, this);
 73      this.startTimerAfterMouseLeave = $.proxy(this.startTimerAfterMouseLeave, this);
 74      this.clearClockMouseLeaveTimer = $.proxy(this.clearClockMouseLeaveTimer, this);
 75      this.rotateTimer = $.proxy(this.rotateTimer, this);
 76
 77      this.options = $.extend({}, this.defaults, options);
 78      if (this.options.timer === 'false') this.options.timer = false;
 79      if (this.options.captions === 'false') this.options.captions = false;
 80      if (this.options.directionalNav === 'false') this.options.directionalNav = false;
 81
 82      this.$element = $(element);
 83      this.$wrapper = this.$element.wrap(this.wrapperHTML).parent();
 84      this.$slides = this.$element.children('img, a, div, figure');
 85
 86      this.$element.on('movestart', function(e) {
 87        // If the movestart is heading off in an upwards or downwards
 88        // direction, prevent it so that the browser scrolls normally.
 89        if ((e.distX > e.distY && e.distX < -e.distY) ||
 90            (e.distX < e.distY && e.distX > -e.distY)) {
 91          e.preventDefault();
 92        }
 93      });
 94
 95      this.$element.bind('orbit.next swipeleft', function () {
 96        self.shift('next');
 97      });
 98
 99      this.$element.bind('orbit.prev swiperight', function () {
100        self.shift('prev');
101      });
102
103      this.$element.bind('orbit.goto', function (event, index) {
104        self.shift(index);
105      });
106
107      this.$element.bind('orbit.start', function (event, index) {
108        self.startClock();
109      });
110
111      this.$element.bind('orbit.stop', function (event, index) {
112        self.stopClock();
113      });
114
115      $imageSlides = this.$slides.filter('img');
116
117      if ($imageSlides.length === 0) {
118        this.loaded();
119      } else {
120        $imageSlides.bind('imageready', function () {
121          imagesLoadedCount += 1;
122          if (imagesLoadedCount === $imageSlides.length) {
123            self.loaded();
124          }
125        });
126      }
127    },
128
129    loaded: function () {
130      this.$element
131        .addClass('orbit')
132        .css({width: '1px', height: '1px'});
133
134      if (this.options.stackOnSmall) {
135        this.$element.addClass('orbit-stack-on-small');
136      }
137
138      this.$slides.addClass('orbit-slide');
139
140      this.setDimentionsFromLargestSlide();
141      this.updateOptionsIfOnlyOneSlide();
142      this.setupFirstSlide();
143      this.notifySlideChange();
144
145      if (this.options.timer) {
146        this.setupTimer();
147        this.startClock();
148      }
149
150      if (this.options.captions) {
151        this.setupCaptions();
152      }
153
154      if (this.options.directionalNav) {
155        this.setupDirectionalNav();
156      }
157
158      if (this.options.bullets) {
159        this.setupBulletNav();
160        this.setActiveBullet();
161      }
162
163      this.options.afterLoadComplete.call(this);
164      Holder.run();
165    },
166
167    currentSlide: function () {
168      return this.$slides.eq(this.activeSlide);
169    },
170
171    notifySlideChange: function() {
172      if (this.options.slideNumber) {
173        var txt = (this.activeSlide+1) + ' of ' + this.$slides.length;
174        this.$element.trigger("orbit.change", {slideIndex: this.activeSlide, slideCount: this.$slides.length});
175        if (this.$counter === undefined) {
176          var $counter = $(this.slideNumberHTML).html(txt);
177          this.$counter = $counter;
178          this.$wrapper.append(this.$counter);
179        } else {
180          this.$counter.html(txt);
181        }
182      }
183    },
184
185    setDimentionsFromLargestSlide: function () {
186      //Collect all slides and set slider size of largest image
187      var self = this,
188          $fluidPlaceholder;
189
190      self.$element.add(self.$wrapper).width(this.$slides.first().outerWidth());
191      self.$element.add(self.$wrapper).height(this.$slides.first().height());
192      self.orbitWidth = this.$slides.first().outerWidth();
193      self.orbitHeight = this.$slides.first().height();
194      $fluidPlaceholder = this.$slides.first().findFirstImage().clone();
195
196
197      this.$slides.each(function () {
198        var slide = $(this),
199            slideWidth = slide.outerWidth(),
200            slideHeight = slide.height();
201
202        if (slideWidth > self.$element.outerWidth()) {
203          self.$element.add(self.$wrapper).width(slideWidth);
204          self.orbitWidth = self.$element.outerWidth();
205        }
206        if (slideHeight > self.$element.height()) {
207          self.$element.add(self.$wrapper).height(slideHeight);
208          self.orbitHeight = self.$element.height();
209          $fluidPlaceholder = $(this).findFirstImage().clone();
210        }
211        self.numberSlides += 1;
212      });
213
214      if (this.options.fluid) {
215        if (typeof this.options.fluid === "string") {
216          // $fluidPlaceholder = $("<img>").attr("src", "http://placehold.it/" + this.options.fluid);
217          $fluidPlaceholder = $("<img>").attr("data-src", "holder.js/" + this.options.fluid);
218          //var inner = $("<div/>").css({"display":"inline-block", "width":"2px", "height":"2px"});
219          //$fluidPlaceholder = $("<div/>").css({"float":"left"});
220          //$fluidPlaceholder.wrapInner(inner);
221
222          //$fluidPlaceholder = $("<div/>").css({"height":"1px", "width":"2px"});
223          //$fluidPlaceholder = $("<div style='display:inline-block;width:2px;height:1px;'></div>");
224        }
225
226        self.$element.prepend($fluidPlaceholder);
227        $fluidPlaceholder.addClass('fluid-placeholder');
228        self.$element.add(self.$wrapper).css({width: 'inherit'});
229        self.$element.add(self.$wrapper).css({height: 'inherit'});
230
231        $(window).bind('resize', function () {
232          self.orbitWidth = self.$element.outerWidth();
233          self.orbitHeight = self.$element.height();
234        });
235      }
236    },
237
238    //Animation locking functions
239    lock: function () {
240      this.locked = true;
241    },
242
243    unlock: function () {
244      this.locked = false;
245    },
246
247    updateOptionsIfOnlyOneSlide: function () {
248      if(this.$slides.length === 1) {
249        this.options.directionalNav = false;
250        this.options.timer = false;
251        this.options.bullets = false;
252      }
253    },
254
255    setupFirstSlide: function () {
256      //Set initial front photo z-index and fades it in
257      var self = this;
258      this.$slides.first()
259        .css({"z-index" : 3, "opacity" : 1})
260        .fadeIn(function() {
261          //brings in all other slides IF css declares a display: none
262          self.$slides.css({"display":"block"})
263      });
264    },
265
266    startClock: function () {
267      var self = this;
268
269      if(!this.options.timer) {
270        return false;
271      }
272
273      if (this.$timer.is(':hidden')) {
274        this.clock = setInterval(function () {
275          self.$element.trigger('orbit.next');
276        }, this.options.advanceSpeed);
277      } else {
278        this.timerRunning = true;
279        this.$pause.removeClass('active');
280        this.clock = setInterval(this.rotateTimer, this.options.advanceSpeed / 180, false);
281      }
282    },
283
284    rotateTimer: function (reset) {
285      var degreeCSS = "rotate(" + this.degrees + "deg)";
286      this.degrees += 2;
287      this.$rotator.css({
288        "-webkit-transform": degreeCSS,
289        "-moz-transform": degreeCSS,
290        "-o-transform": degreeCSS,
291        "-ms-transform": degreeCSS
292      });
293      if(this.degrees > 180) {
294        this.$rotator.addClass('move');
295        this.$mask.addClass('move');
296      }
297      if(this.degrees > 360 || reset) {
298        this.$rotator.removeClass('move');
299        this.$mask.removeClass('move');
300        this.degrees = 0;
301        this.$element.trigger('orbit.next');
302      }
303    },
304
305    stopClock: function () {
306      if (!this.options.timer) {
307        return false;
308      } else {
309        this.timerRunning = false;
310        clearInterval(this.clock);
311        this.$pause.addClass('active');
312      }
313    },
314
315    setupTimer: function () {
316      this.$timer = $(this.timerHTML);
317      this.$wrapper.append(this.$timer);
318
319      this.$rotator = this.$timer.find('.rotator');
320      this.$mask = this.$timer.find('.mask');
321      this.$pause = this.$timer.find('.pause');
322
323      this.$timer.click(this.clickTimer);
324
325      if (this.options.startClockOnMouseOut) {
326        this.$wrapper.mouseleave(this.startTimerAfterMouseLeave);
327        this.$wrapper.mouseenter(this.clearClockMouseLeaveTimer);
328      }
329
330      if (this.options.pauseOnHover) {
331        this.$wrapper.mouseenter(this.stopClock);
332      }
333    },
334
335    startTimerAfterMouseLeave: function () {
336      var self = this;
337
338      this.outTimer = setTimeout(function() {
339        if(!self.timerRunning){
340          self.startClock();
341        }
342      }, this.options.startClockOnMouseOutAfter)
343    },
344
345    clearClockMouseLeaveTimer: function () {
346      clearTimeout(this.outTimer);
347    },
348
349    clickTimer: function () {
350      if(!this.timerRunning) {
351          this.startClock();
352      } else {
353          this.stopClock();
354      }
355    },
356
357    setupCaptions: function () {
358      this.$caption = $(this.captionHTML);
359      this.$wrapper.append(this.$caption);
360      this.setCaption();
361    },
362
363    setCaption: function () {
364      var captionLocation = this.currentSlide().attr('data-caption'),
365          captionHTML;
366
367      if (!this.options.captions) {
368        return false;
369      }
370
371      //Set HTML for the caption if it exists
372      if (captionLocation) {
373        //if caption text is blank, don't show captions
374        if ($.trim($(captionLocation).text()).length < 1){
375          return false;
376        }
377        captionHTML = $(captionLocation).html(); //get HTML from the matching HTML entity
378        this.$caption
379          .attr('id', captionLocation) // Add ID caption TODO why is the id being set?
380          .html(captionHTML); // Change HTML in Caption
381          //Animations for Caption entrances
382        switch (this.options.captionAnimation) {
383          case 'none':
384            this.$caption.show();
385            break;
386          case 'fade':
387            this.$caption.fadeIn(this.options.captionAnimationSpeed);
388            break;
389          case 'slideOpen':
390            this.$caption.slideDown(this.options.captionAnimationSpeed);
391            break;
392        }
393      } else {
394        //Animations for Caption exits
395        switch (this.options.captionAnimation) {
396          case 'none':
397            this.$caption.hide();
398            break;
399          case 'fade':
400            this.$caption.fadeOut(this.options.captionAnimationSpeed);
401            break;
402          case 'slideOpen':
403            this.$caption.slideUp(this.options.captionAnimationSpeed);
404            break;
405        }
406      }
407    },
408
409    setupDirectionalNav: function () {
410      var self = this,
411          $directionalNav = $(this.directionalNavHTML);
412
413      $directionalNav.find('.right').html(this.options.directionalNavRightText);
414      $directionalNav.find('.left').html(this.options.directionalNavLeftText);
415
416      this.$wrapper.append($directionalNav);
417
418      this.$wrapper.find('.left').click(function () {
419        self.stopClock();
420        if (self.options.resetTimerOnClick) {
421          self.rotateTimer(true);
422          self.startClock();
423        }
424        self.$element.trigger('orbit.prev');
425      });
426
427      this.$wrapper.find('.right').click(function () {
428        self.stopClock();
429        if (self.options.resetTimerOnClick) {
430          self.rotateTimer(true);
431          self.startClock();
432        }
433        self.$element.trigger('orbit.next');
434      });
435    },
436
437    setupBulletNav: function () {
438      this.$bullets = $(this.bulletHTML);
439      this.$wrapper.append(this.$bullets);
440      this.$slides.each(this.addBullet);
441      this.$element.addClass('with-bullets');
442      if (this.options.centerBullets) this.$bullets.css('margin-left', -this.$bullets.outerWidth() / 2);
443    },
444
445    addBullet: function (index, slide) {
446      var position = index + 1,
447          $li = $('<li>' + (position) + '</li>'),
448          thumbName,
449          self = this;
450
451      if (this.options.bulletThumbs) {
452        thumbName = $(slide).attr('data-thumb');
453        if (thumbName) {
454          $li
455            .addClass('has-thumb')
456            .css({background: "url(" + this.options.bulletThumbLocation + thumbName + ") no-repeat"});;
457        }
458      }
459      this.$bullets.append($li);
460      $li.data('index', index);
461      $li.click(function () {
462        self.stopClock();
463        if (self.options.resetTimerOnClick) {
464          self.rotateTimer(true);
465          self.startClock();
466        }
467        self.$element.trigger('orbit.goto', [$li.data('index')])
468      });
469    },
470
471    setActiveBullet: function () {
472      if(!this.options.bullets) { return false; } else {
473        this.$bullets.find('li')
474          .removeClass('active')
475          .eq(this.activeSlide)
476          .addClass('active');
477      }
478    },
479
480    resetAndUnlock: function () {
481      this.$slides
482        .eq(this.prevActiveSlide)
483        .css({"z-index" : 1});
484      this.unlock();
485      this.options.afterSlideChange.call(this, this.$slides.eq(this.prevActiveSlide), this.$slides.eq(this.activeSlide));
486    },
487
488    shift: function (direction) {
489      var slideDirection = direction;
490
491      //remember previous activeSlide
492      this.prevActiveSlide = this.activeSlide;
493
494      //exit function if bullet clicked is same as the current image
495      if (this.prevActiveSlide == slideDirection) { return false; }
496
497      if (this.$slides.length == "1") { return false; }
498      if (!this.locked) {
499        this.lock();
500        //deduce the proper activeImage
501        if (direction == "next") {
502          this.activeSlide++;
503          if (this.activeSlide == this.numberSlides) {
504              this.activeSlide = 0;
505          }
506        } else if (direction == "prev") {
507          this.activeSlide--
508          if (this.activeSlide < 0) {
509            this.activeSlide = this.numberSlides - 1;
510          }
511        } else {
512          this.activeSlide = direction;
513          if (this.prevActiveSlide < this.activeSlide) {
514            slideDirection = "next";
515          } else if (this.prevActiveSlide > this.activeSlide) {
516            slideDirection = "prev"
517          }
518        }
519
520        //set to correct bullet
521        this.setActiveBullet();
522        this.notifySlideChange();
523
524        //set previous slide z-index to one below what new activeSlide will be
525        this.$slides
526          .eq(this.prevActiveSlide)
527          .css({"z-index" : 2});
528
529        //fade
530        if (this.options.animation == "fade") {
531          this.$slides
532            .eq(this.activeSlide)
533            .css({"opacity" : 0, "z-index" : 3})
534            .animate({"opacity" : 1}, this.options.animationSpeed, this.resetAndUnlock);
535          this.$slides
536              .eq(this.prevActiveSlide)
537              .animate({"opacity":0}, this.options.animationSpeed);
538        }
539
540        //horizontal-slide
541        if (this.options.animation == "horizontal-slide") {
542          if (slideDirection == "next") {
543            this.$slides
544              .eq(this.activeSlide)
545              .css({"left": this.orbitWidth, "z-index" : 3})
546              .css("opacity", 1)
547              .animate({"left" : 0}, this.options.animationSpeed, this.resetAndUnlock);
548          }
549          if (slideDirection == "prev") {
550            this.$slides
551              .eq(this.activeSlide)
552              .css({"left": -this.orbitWidth, "z-index" : 3})
553              .css("opacity", 1)
554              .animate({"left" : 0}, this.options.animationSpeed, this.resetAndUnlock);
555          }
556          this.$slides
557              .eq(this.prevActiveSlide)
558              .css("opacity", 0);
559        }
560
561        //vertical-slide
562        if (this.options.animation == "vertical-slide") {
563          if (slideDirection == "prev") {
564            this.$slides
565              .eq(this.activeSlide)
566              .css({"top": this.orbitHeight, "z-index" : 3})
567              .css("opacity", 1)
568              .animate({"top" : 0}, this.options.animationSpeed, this.resetAndUnlock);
569            this.$slides
570              .eq(this.prevActiveSlide)
571              .css("opacity", 0);
572          }
573          if (slideDirection == "next") {
574            this.$slides
575              .eq(this.activeSlide)
576              .css({"top": -this.orbitHeight, "z-index" : 3})
577              .css("opacity", 1)
578              .animate({"top" : 0}, this.options.animationSpeed, this.resetAndUnlock);
579          }
580          this.$slides
581              .eq(this.prevActiveSlide)
582              .css("opacity", 0);
583        }
584
585        //horizontal-push
586        if (this.options.animation == "horizontal-push") {
587          if (slideDirection == "next") {
588            this.$slides
589              .eq(this.activeSlide)
590              .css({"left": this.orbitWidth, "z-index" : 3})
591              .animate({"left" : 0, "opacity" : 1}, this.options.animationSpeed, this.resetAndUnlock);
592            this.$slides
593              .eq(this.prevActiveSlide)
594              .animate({"left" : -this.orbitWidth}, this.options.animationSpeed, "", function(){
595                $(this).css({"opacity" : 0});
596              });
597          }
598          if (slideDirection == "prev") {
599            this.$slides
600              .eq(this.activeSlide)
601              .css({"left": -this.orbitWidth, "z-index" : 3})
602              .animate({"left" : 0, "opacity" : 1}, this.options.animationSpeed, this.resetAndUnlock);
603            this.$slides
604              .eq(this.prevActiveSlide)
605              .animate({"left" : this.orbitWidth}, this.options.animationSpeed, "", function(){
606                $(this).css({"opacity" : 0});
607              });
608          }
609        }
610
611        //vertical-push
612        if (this.options.animation == "vertical-push") {
613          if (slideDirection == "next") {
614            this.$slides
615              .eq(this.activeSlide)
616              .css({top: -this.orbitHeight, "z-index" : 3})
617              .css("opacity", 1)
618              .animate({top : 0, "opacity":1}, this.options.animationSpeed, this.resetAndUnlock);
619            this.$slides
620              .eq(this.prevActiveSlide)
621              .css("opacity", 0)
622              .animate({top : this.orbitHeight}, this.options.animationSpeed, "");
623          }
624          if (slideDirection == "prev") {
625            this.$slides
626              .eq(this.activeSlide)
627              .css({top: this.orbitHeight, "z-index" : 3})
628              .css("opacity", 1)
629              .animate({top : 0}, this.options.animationSpeed, this.resetAndUnlock);
630            this.$slides
631              .eq(this.prevActiveSlide)
632              .css("opacity", 0)
633              .animate({top : -this.orbitHeight}, this.options.animationSpeed);
634          }
635        }
636
637        this.setCaption();
638      }
639
640      if (this.$slides.last() && this.options.singleCycle) {
641        this.stopClock();
642      }
643    }
644  };
645
646  $.fn.orbit = function (options) {
647    return this.each(function () {
648      var orbit = $.extend({}, ORBIT);
649      orbit.init(this, options);
650    });
651  };
652
653})(jQuery);
654
655/*!
656 * jQuery imageready Plugin
657 * http://www.zurb.com/playground/
658 *
659 * Copyright 2011, ZURB
660 * Released under the MIT License
661 */
662(function ($) {
663
664  var options = {};
665
666  $.event.special.imageready = {
667
668    setup: function (data, namespaces, eventHandle) {
669      options = data || options;
670    },
671
672    add: function (handleObj) {
673      var $this = $(this),
674          src;
675
676      if ( this.nodeType === 1 && this.tagName.toLowerCase() === 'img' && this.src !== '' ) {
677        if (options.forceLoad) {
678          src = $this.attr('src');
679          $this.attr('src', '');
680          bindToLoad(this, handleObj.handler);
681          $this.attr('src', src);
682        } else if ( this.complete || this.readyState === 4 ) {
683          handleObj.handler.apply(this, arguments);
684        } else {
685          bindToLoad(this, handleObj.handler);
686        }
687      }
688    },
689
690    teardown: function (namespaces) {
691      $(this).unbind('.imageready');
692    }
693  };
694
695  function bindToLoad(element, callback) {
696    var $this = $(element);
697
698    $this.bind('load.imageready', function () {
699       callback.apply(element, arguments);
700       $this.unbind('load.imageready');
701     });
702  }
703
704}(jQuery));
705
706/*
707
708Holder - 1.3 - client side image placeholders
709(c) 2012 Ivan Malopinsky / http://imsky.co
710
711Provided under the Apache 2.0 License: http://www.apache.org/licenses/LICENSE-2.0
712Commercial use requires attribution.
713
714*/
715
716var Holder = Holder || {};
717(function (app, win) {
718
719var preempted = false,
720fallback = false,
721canvas = document.createElement('canvas');
722
723//http://javascript.nwbox.com/ContentLoaded by Diego Perini with modifications
724function contentLoaded(n,t){var l="complete",s="readystatechange",u=!1,h=u,c=!0,i=n.document,a=i.documentElement,e=i.addEventListener?"addEventListener":"attachEvent",v=i.addEventListener?"removeEventListener":"detachEvent",f=i.addEventListener?"":"on",r=function(e){(e.type!=s||i.readyState==l)&&((e.type=="load"?n:i)[v](f+e.type,r,u),!h&&(h=!0)&&t.call(n,null))},o=function(){try{a.doScroll("left")}catch(n){setTimeout(o,50);return}r("poll")};if(i.readyState==l)t.call(n,"lazy");else{if(i.createEventObject&&a.doScroll){try{c=!n.frameElement}catch(y){}c&&o()}i[e](f+"DOMContentLoaded",r,u),i[e](f+s,r,u),n[e](f+"load",r,u)}};
725
726//https://gist.github.com/991057 by Jed Schmidt with modifications
727function selector(a){
728	a=a.match(/^(\W)?(.*)/);var b=document["getElement"+(a[1]?a[1]=="#"?"ById":"sByClassName":"sByTagName")](a[2]);
729	var ret=[];	b!=null&&(b.length?ret=b:b.length==0?ret=b:ret=[b]);	return ret;
730}
731
732//shallow object property extend
733function extend(a,b){var c={};for(var d in a)c[d]=a[d];for(var e in b)c[e]=b[e];return c}
734
735function draw(ctx, dimensions, template) {
736	var dimension_arr = [dimensions.height, dimensions.width].sort();
737	var maxFactor = Math.round(dimension_arr[1] / 16),
738		minFactor = Math.round(dimension_arr[0] / 16);
739	var text_height = Math.max(template.size, maxFactor);
740	canvas.width = dimensions.width;
741	canvas.height = dimensions.height;
742	ctx.textAlign = "center";
743	ctx.textBaseline = "middle";
744	ctx.fillStyle = template.background;
745	ctx.fillRect(0, 0, dimensions.width, dimensions.height);
746	ctx.fillStyle = template.foreground;
747	ctx.font = "bold " + text_height + "px sans-serif";
748	var text = template.text ? template.text : (dimensions.width + "x" + dimensions.height);
749	if (Math.round(ctx.measureText(text).width) / dimensions.width > 1) {
750		text_height = Math.max(minFactor, template.size);
751	}
752	ctx.font = "bold " + text_height + "px sans-serif";
753	ctx.fillText(text, (dimensions.width / 2), (dimensions.height / 2), dimensions.width);
754	return canvas.toDataURL("image/png");
755}
756
757if (!canvas.getContext) {
758	fallback = true;
759} else {
760	if (canvas.toDataURL("image/png").indexOf("data:image/png") < 0) {
761		//Android doesn't support data URI
762		fallback = true;
763	} else {
764		var ctx = canvas.getContext("2d");
765	}
766}
767
768var settings = {
769	domain: "holder.js",
770	images: "img",
771	themes: {
772		"gray": {
773			background: "#eee",
774			foreground: "#aaa",
775			size: 12
776		},
777		"social": {
778			background: "#3a5a97",
779			foreground: "#fff",
780			size: 12
781		},
782		"industrial": {
783			background: "#434A52",
784			foreground: "#C2F200",
785			size: 12
786		}
787	}
788};
789
790
791
792app.flags = {
793	dimensions: {
794		regex: /([0-9]+)x([0-9]+)/,
795		output: function(val){
796			var exec = this.regex.exec(val);
797			return {
798				width: +exec[1],
799				height: +exec[2]
800			}
801		}
802	},
803	colors: {
804		regex: /#([0-9a-f]{3,})\:#([0-9a-f]{3,})/i,
805		output: function(val){
806			var exec = this.regex.exec(val);
807			return {
808					size: settings.themes.gray.size,
809					foreground: "#" + exec[2],
810					background: "#" + exec[1]
811					}
812		}
813	},
814	text: {
815		regex: /text\:(.*)/,
816		output: function(val){
817			return this.regex.exec(val)[1];
818		}
819	}
820}
821
822for(var flag in app.flags){
823	app.flags[flag].match = function (val){
824		return val.match(this.regex)
825	}
826}
827
828app.add_theme = function (name, theme) {
829	name != null && theme != null && (settings.themes[name] = theme);
830	return app;
831};
832
833app.add_image = function (src, el) {
834	var node = selector(el);
835	if (node.length) {
836		for (var i = 0, l = node.length; i < l; i++) {
837			var img = document.createElement("img")
838			img.setAttribute("data-src", src);
839			node[i].appendChild(img);
840		}
841	}
842	return app;
843};
844
845app.run = function (o) {
846	var options = extend(settings, o),
847		images = selector(options.images),
848		preempted = true;
849
850	for (var l = images.length, i = 0; i < l; i++) {
851		var theme = settings.themes.gray;
852		var src = images[i].getAttribute("data-src") || images[i].getAttribute("src");
853		if ( !! ~src.indexOf(options.domain)) {
854			var render = false,
855				dimensions = null,
856				text = null;
857			var flags = src.substr(src.indexOf(options.domain) + options.domain.length + 1).split("/");
858			for (sl = flags.length, j = 0; j < sl; j++) {
859				if (app.flags.dimensions.match(flags[j])) {
860					render = true;
861					dimensions = app.flags.dimensions.output(flags[j]);
862				} else if (app.flags.colors.match(flags[j])) {
863					theme = app.flags.colors.output(flags[j]);
864				} else if (options.themes[flags[j]]) {
865					//If a theme is specified, it will override custom colors
866					theme = options.themes[flags[j]];
867				} else if (app.flags.text.match(flags[j])) {
868					text = app.flags.text.output(flags[j]);
869				}
870			}
871			if (render) {
872				images[i].setAttribute("data-src", src);
873				var dimensions_caption = dimensions.width + "x" + dimensions.height;
874				images[i].setAttribute("alt", text ? text : theme.text ? theme.text + " [" + dimensions_caption + "]" : dimensions_caption);
875
876				// Fallback
877        // images[i].style.width = dimensions.width + "px";
878        // images[i].style.height = dimensions.height + "px";
879				images[i].style.backgroundColor = theme.background;
880
881				var theme = (text ? extend(theme, {
882						text: text
883					}) : theme);
884
885				if (!fallback) {
886					images[i].setAttribute("src", draw(ctx, dimensions, theme));
887				}
888			}
889		}
890	}
891	return app;
892};
893contentLoaded(win, function () {
894	preempted || app.run()
895})
896
897})(Holder, window);