PageRenderTime 1739ms CodeModel.GetById 64ms RepoModel.GetById 2ms app.codeStats 0ms

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