viewer.js 42 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699
  1. /*!
  2. * Viewer v0.1.0
  3. * https://github.com/fengyuanchen/viewer
  4. *
  5. * Copyright (c) 2015 Fengyuan Chen
  6. * Released under the MIT license
  7. *
  8. * Date: 2015-09-02T09:08:17.666Z
  9. */
  10. (function (factory) {
  11. if (typeof define === 'function' && define.amd) {
  12. // AMD. Register as anonymous module.
  13. define('viewer', ['jquery'], factory);
  14. } else if (typeof exports === 'object') {
  15. // Node / CommonJS
  16. factory(require('jquery'));
  17. } else {
  18. // Browser globals.
  19. factory(jQuery);
  20. }
  21. })(function ($) {
  22. 'use strict';
  23. var $window = $(window);
  24. var $document = $(document);
  25. // Constants
  26. var NAMESPACE = 'viewer';
  27. var ELEMENT_VIEWER = document.createElement(NAMESPACE);
  28. // Classes
  29. var CLASS_TOGGLE = 'viewer-toggle';
  30. var CLASS_FIXED = 'viewer-fixed';
  31. var CLASS_OPEN = 'viewer-open';
  32. var CLASS_SHOW = 'viewer-show';
  33. var CLASS_HIDE = 'viewer-hide';
  34. var CLASS_FADE = 'viewer-fade';
  35. var CLASS_IN = 'viewer-in';
  36. var CLASS_MOVE = 'viewer-move';
  37. var CLASS_ACTIVE = 'viewer-active';
  38. var CLASS_INVISIBLE = 'viewer-invisible';
  39. var CLASS_TRANSITION = 'viewer-transition';
  40. var CLASS_FULLSCREEN = 'viewer-fullscreen';
  41. var CLASS_FULLSCREEN_EXIT = 'viewer-fullscreen-exit';
  42. var CLASS_CLOSE = 'viewer-close';
  43. // Selectors
  44. var SELECTOR_IMG = 'img';
  45. // Events
  46. var EVENT_MOUSEDOWN = 'mousedown touchstart pointerdown MSPointerDown';
  47. var EVENT_MOUSEMOVE = 'mousemove touchmove pointermove MSPointerMove';
  48. var EVENT_MOUSEUP = 'mouseup touchend touchcancel pointerup pointercancel MSPointerUp MSPointerCancel';
  49. var EVENT_WHEEL = 'wheel mousewheel DOMMouseScroll';
  50. var EVENT_TRANSITIONEND = 'transitionend';
  51. var EVENT_LOAD = 'load.' + NAMESPACE;
  52. var EVENT_KEYDOWN = 'keydown.' + NAMESPACE;
  53. var EVENT_CLICK = 'click.' + NAMESPACE;
  54. var EVENT_RESIZE = 'resize.' + NAMESPACE;
  55. var EVENT_BUILD = 'build.' + NAMESPACE;
  56. var EVENT_BUILT = 'built.' + NAMESPACE;
  57. var EVENT_SHOW = 'show.' + NAMESPACE;
  58. var EVENT_SHOWN = 'shown.' + NAMESPACE;
  59. var EVENT_HIDE = 'hide.' + NAMESPACE;
  60. var EVENT_HIDDEN = 'hidden.' + NAMESPACE;
  61. var EVENT_VIEW = 'view.' + NAMESPACE;
  62. var EVENT_VIEWED = 'viewed.' + NAMESPACE;
  63. // Supports
  64. var SUPPORT_TRANSITION = typeof ELEMENT_VIEWER.style.transition !== 'undefined';
  65. // Others
  66. var round = Math.round;
  67. var sqrt = Math.sqrt;
  68. var abs = Math.abs;
  69. var min = Math.min;
  70. var max = Math.max;
  71. var num = parseFloat;
  72. // Prototype
  73. var prototype = {};
  74. function isString(s) {
  75. return typeof s === 'string';
  76. }
  77. function isNumber(n) {
  78. return typeof n === 'number' && !isNaN(n);
  79. }
  80. function isUndefined(u) {
  81. return typeof u === 'undefined';
  82. }
  83. function toArray(obj, offset) {
  84. var args = [];
  85. if (isNumber(offset)) { // It's necessary for IE8
  86. args.push(offset);
  87. }
  88. return args.slice.apply(obj, args);
  89. }
  90. // Custom proxy to avoid jQuery's guid
  91. function proxy(fn, context) {
  92. var args = toArray(arguments, 2);
  93. return function () {
  94. return fn.apply(context, args.concat(toArray(arguments)));
  95. };
  96. }
  97. function getTransform(options) {
  98. var transforms = [];
  99. var rotate = options.rotate;
  100. var scaleX = options.scaleX;
  101. var scaleY = options.scaleY;
  102. if (isNumber(rotate)) {
  103. transforms.push('rotate(' + rotate + 'deg)');
  104. }
  105. if (isNumber(scaleX) && isNumber(scaleY)) {
  106. transforms.push('scale(' + scaleX + ',' + scaleY + ')');
  107. }
  108. return transforms.length ? transforms.join(' ') : 'none';
  109. }
  110. // e.g.: http://domain.com/path/to/picture.jpg?size=1280×960 -> picture.jpg
  111. function getImageName(url) {
  112. return isString(url) ? url.replace(/^.*\//, '').replace(/[\?&#].*$/, '') : '';
  113. }
  114. function getImageSize(image, callback) {
  115. var newImage;
  116. // Modern browsers
  117. if (image.naturalWidth) {
  118. return callback(image.naturalWidth, image.naturalHeight);
  119. }
  120. // IE8: Don't use `new Image()` here
  121. newImage = document.createElement('img');
  122. newImage.onload = function () {
  123. callback(this.width, this.height);
  124. };
  125. newImage.src = image.src;
  126. }
  127. function Viewer(element, options) {
  128. this.$element = $(element);
  129. this.options = $.extend({}, Viewer.DEFAULTS, $.isPlainObject(options) && options);
  130. this.isImg = false;
  131. this.isBuilt = false;
  132. this.isShown = false;
  133. this.isViewed = false;
  134. this.isFulled = false;
  135. this.isPlayed = false;
  136. this.playing = false;
  137. this.fading = false;
  138. this.transitioning = false;
  139. this.action = false;
  140. this.target = false;
  141. this.index = 0;
  142. this.length = 0;
  143. this.init();
  144. }
  145. $.extend(prototype, {
  146. init: function () {
  147. var options = this.options;
  148. var $this = this.$element;
  149. var isImg = $this.is(SELECTOR_IMG);
  150. var $images = isImg ? $this : $this.find(SELECTOR_IMG);
  151. var length = $images.length;
  152. var ready = $.proxy(this.ready, this);
  153. if (!length) {
  154. return;
  155. }
  156. if ($.isFunction(options.build)) {
  157. $this.one(EVENT_BUILD, options.build);
  158. }
  159. if (this.trigger(EVENT_BUILD).isDefaultPrevented()) {
  160. return;
  161. }
  162. // Override `transiton` option if it is not supported
  163. if (!SUPPORT_TRANSITION) {
  164. options.transition = false;
  165. }
  166. this.isImg = isImg;
  167. this.length = length;
  168. this.count = 0;
  169. this.$images = $images;
  170. this.$body = $('body');
  171. if (options.inline) {
  172. $this.one(EVENT_BUILT, $.proxy(function () {
  173. this.view();
  174. }, this));
  175. $images.each(function () {
  176. if (this.complete) {
  177. ready();
  178. } else {
  179. $(this).one(EVENT_LOAD, ready);
  180. }
  181. });
  182. } else {
  183. $images.addClass(CLASS_TOGGLE);
  184. $this.on(EVENT_CLICK, $.proxy(this.start, this));
  185. }
  186. },
  187. ready: function () {
  188. this.count++;
  189. if (this.count === this.length) {
  190. this.build();
  191. }
  192. }
  193. });
  194. $.extend(prototype, {
  195. build: function () {
  196. var options = this.options;
  197. var $this = this.$element;
  198. var $parent;
  199. var $viewer;
  200. var $button;
  201. var $toolbar;
  202. if (this.isBuilt) {
  203. return;
  204. }
  205. if (!$parent || !$parent.length) {
  206. $parent = $this.parent();
  207. }
  208. this.$parent = $parent;
  209. this.$viewer = $viewer = $(Viewer.TEMPLATE);
  210. this.$canvas = $viewer.find('.viewer-canvas');
  211. this.$footer = $viewer.find('.viewer-footer');
  212. this.$title = $viewer.find('.viewer-title').toggleClass(CLASS_HIDE, !options.title);
  213. this.$toolbar = $toolbar = $viewer.find('.viewer-toolbar').toggleClass(CLASS_HIDE, !options.toolbar);
  214. this.$navbar = $viewer.find('.viewer-navbar').toggleClass(CLASS_HIDE, !options.navbar);
  215. this.$button = $button = $viewer.find('.viewer-button').toggleClass(CLASS_HIDE, !options.button);
  216. this.$tooltip = $viewer.find('.viewer-tooltip');
  217. this.$player = $viewer.find('.viewer-player');
  218. this.$list = $viewer.find('.viewer-list');
  219. $toolbar.find('li[class*=zoom]').toggleClass(CLASS_INVISIBLE, !options.zoomable);
  220. $toolbar.find('li[class*=flip]').toggleClass(CLASS_INVISIBLE, !options.scalable);
  221. if (!options.rotatable) {
  222. $toolbar.find('li[class*=rotate]').addClass(CLASS_INVISIBLE).appendTo($toolbar);
  223. }
  224. if (options.inline) {
  225. $button.addClass(CLASS_FULLSCREEN);
  226. $viewer.css('z-index', options.zIndexInline);
  227. if ($parent.css('position') === 'static') {
  228. $parent.css('position', 'relative');
  229. }
  230. } else {
  231. $button.addClass(CLASS_CLOSE);
  232. $viewer.
  233. css('z-index', options.zIndex).
  234. addClass([CLASS_FIXED, CLASS_FADE, CLASS_HIDE].join(' '));
  235. }
  236. $this.after($viewer);
  237. if (options.inline) {
  238. this.render();
  239. this.bind();
  240. this.isShown = true;
  241. }
  242. this.isBuilt = true;
  243. if ($.isFunction(options.built)) {
  244. $this.one(EVENT_BUILT, options.built);
  245. }
  246. this.trigger(EVENT_BUILT);
  247. },
  248. unbuild: function () {
  249. var options = this.options;
  250. var $this = this.$element;
  251. if (!this.isBuilt) {
  252. return;
  253. }
  254. if (options.inline && !options.container) {
  255. $this.removeClass(CLASS_HIDE);
  256. }
  257. this.$viewer.remove();
  258. }
  259. });
  260. $.extend(prototype, {
  261. bind: function () {
  262. this.$viewer.
  263. on(EVENT_CLICK, $.proxy(this.click, this)).
  264. on(EVENT_WHEEL, $.proxy(this.wheel, this));
  265. this.$canvas.on(EVENT_MOUSEDOWN, $.proxy(this.mousedown, this));
  266. $document.
  267. on(EVENT_MOUSEMOVE, (this._mousemove = proxy(this.mousemove, this))).
  268. on(EVENT_MOUSEUP, (this._mouseup = proxy(this.mouseup, this))).
  269. on(EVENT_KEYDOWN, (this._keydown = proxy(this.keydown, this)));
  270. $window.on(EVENT_RESIZE, (this._resize = proxy(this.resize, this)));
  271. },
  272. unbind: function () {
  273. this.$viewer.
  274. off(EVENT_CLICK, this.click).
  275. off(EVENT_WHEEL, this.wheel);
  276. this.$canvas.off(EVENT_MOUSEDOWN, this.mousedown);
  277. $document.
  278. off(EVENT_MOUSEMOVE, this._mousemove).
  279. off(EVENT_MOUSEUP, this._mouseup).
  280. off(EVENT_KEYDOWN, this._keydown);
  281. $window.off(EVENT_RESIZE, this._resize);
  282. }
  283. });
  284. $.extend(prototype, {
  285. render: function () {
  286. this.initContainer();
  287. this.initViewer();
  288. this.initList();
  289. this.renderViewer();
  290. },
  291. initContainer: function () {
  292. this.container = {
  293. width: $window.innerWidth(),
  294. height: $window.innerHeight()
  295. };
  296. },
  297. initViewer: function () {
  298. var options = this.options;
  299. var $parent = this.$parent;
  300. var viewer;
  301. if (options.inline) {
  302. this.parent = viewer = {
  303. width: max($parent.width(), options.minWidth),
  304. height: max($parent.height(), options.minHeight)
  305. };
  306. }
  307. if (this.isFulled || !viewer) {
  308. viewer = this.container;
  309. }
  310. this.viewer = $.extend({}, viewer);
  311. },
  312. renderViewer: function () {
  313. if (this.options.inline && !this.isFulled) {
  314. this.$viewer.css(this.viewer);
  315. }
  316. },
  317. initList: function () {
  318. var options = this.options;
  319. var $this = this.$element;
  320. var $list = this.$list;
  321. var list = [];
  322. this.$images.each(function (i) {
  323. var src = this.src;
  324. var alt = this.alt || getImageName(src);
  325. var url = options.url;
  326. if (!src) {
  327. return;
  328. }
  329. if (isString(url)) {
  330. url = this.getAttribute(url);
  331. } else if ($.isFunction(url)) {
  332. url = url.call(this, this);
  333. }
  334. list.push(
  335. '<li>' +
  336. '<img' +
  337. ' src="' + src + '"' +
  338. ' data-action="view"' +
  339. ' data-index="' + i + '"' +
  340. ' data-original-url="' + (url || src) + '"' +
  341. ' alt="' + alt + '"' +
  342. '>' +
  343. '</li>'
  344. );
  345. });
  346. $list.html(list.join('')).find(SELECTOR_IMG).one(EVENT_LOAD, {
  347. filled: true
  348. }, $.proxy(this.loadImage, this));
  349. this.$items = $list.children();
  350. if (options.transition) {
  351. $this.one(EVENT_VIEWED, function () {
  352. $list.addClass(CLASS_TRANSITION);
  353. });
  354. }
  355. },
  356. renderList: function (index) {
  357. var i = index || this.index;
  358. var width = this.$items.eq(i).width();
  359. var outerWidth = width + 1; // 1 pixel of `margin-left` width
  360. // Place the active item in the center of the screen
  361. this.$list.css({
  362. width: outerWidth * this.length,
  363. marginLeft: (this.viewer.width - width) / 2 - outerWidth * i
  364. });
  365. },
  366. resetList: function () {
  367. this.$list.empty().removeClass(CLASS_TRANSITION).css('margin-left', 0);
  368. },
  369. initImage: function (callback) {
  370. var options = this.options;
  371. var $image = this.$image;
  372. var viewer = this.viewer;
  373. var footerHeight = this.$footer.height();
  374. var viewerWidth = viewer.width;
  375. var viewerHeight = max(viewer.height - footerHeight, footerHeight);
  376. var oldImage = this.image || {};
  377. getImageSize($image[0], $.proxy(function (naturalWidth, naturalHeight) {
  378. var aspectRatio = naturalWidth / naturalHeight;
  379. var width = viewerWidth;
  380. var height = viewerHeight;
  381. var initialImage;
  382. var image;
  383. if (viewerHeight * aspectRatio > viewerWidth) {
  384. height = viewerWidth / aspectRatio;
  385. } else {
  386. width = viewerHeight * aspectRatio;
  387. }
  388. width = min(width * 0.9, naturalWidth);
  389. height = min(height * 0.9, naturalHeight);
  390. image = {
  391. naturalWidth: naturalWidth,
  392. naturalHeight: naturalHeight,
  393. aspectRatio: aspectRatio,
  394. ratio: width / naturalWidth,
  395. width: width,
  396. height: height,
  397. left: (viewerWidth - width) / 2,
  398. top: (viewerHeight - height) / 2
  399. };
  400. initialImage = $.extend({}, image);
  401. if (options.rotatable) {
  402. image.rotate = oldImage.rotate || 0;
  403. initialImage.rotate = 0;
  404. }
  405. if (options.scalable) {
  406. image.scaleX = oldImage.scaleX || 1;
  407. image.scaleY = oldImage.scaleY || 1;
  408. initialImage.scaleX = 1;
  409. initialImage.scaleY = 1;
  410. }
  411. this.image = image;
  412. this.initialImage = initialImage;
  413. if ($.isFunction(callback)) {
  414. callback();
  415. }
  416. }, this));
  417. },
  418. renderImage: function (callback) {
  419. var image = this.image;
  420. var $image = this.$image;
  421. $image.css({
  422. width: image.width,
  423. height: image.height,
  424. marginLeft: image.left,
  425. marginTop: image.top,
  426. transform: getTransform(image)
  427. });
  428. if ($.isFunction(callback)) {
  429. if (this.options.transition) {
  430. $image.one(EVENT_TRANSITIONEND, callback);
  431. } else {
  432. callback();
  433. }
  434. }
  435. },
  436. resetImage: function () {
  437. this.$image.remove();
  438. this.$image = null;
  439. }
  440. });
  441. $.extend(prototype, {
  442. start: function (e) {
  443. var target = e.target;
  444. if ($(target).hasClass(CLASS_TOGGLE)) {
  445. this.target = target;
  446. this.show();
  447. }
  448. },
  449. click: function (e) {
  450. var $target = $(e.target);
  451. var action = $target.data('action');
  452. var image = this.image;
  453. switch (action) {
  454. case 'mix':
  455. if (this.isPlayed) {
  456. this.stop();
  457. } else {
  458. if (this.options.inline) {
  459. if (this.isFulled) {
  460. this.exit();
  461. } else {
  462. this.full();
  463. }
  464. } else {
  465. this.hide();
  466. }
  467. }
  468. break;
  469. case 'view':
  470. this.view($target.data('index'));
  471. break;
  472. case 'zoom-in':
  473. this.zoom(0.1, true);
  474. break;
  475. case 'zoom-out':
  476. this.zoom(-0.1, true);
  477. break;
  478. case 'one-to-one':
  479. if (this.image.ratio === 1) {
  480. this.zoomTo(this.initialImage.ratio);
  481. } else {
  482. this.zoomTo(1);
  483. }
  484. break;
  485. case 'reset':
  486. this.reset();
  487. break;
  488. case 'prev':
  489. this.prev();
  490. break;
  491. case 'play':
  492. this.play();
  493. break;
  494. case 'next':
  495. this.next();
  496. break;
  497. case 'rotate-left':
  498. this.rotate(-90);
  499. break;
  500. case 'rotate-right':
  501. this.rotate(90);
  502. break;
  503. case 'flip-horizontal':
  504. this.scale(-image.scaleX || -1, image.scaleY || 1);
  505. break;
  506. case 'flip-vertical':
  507. this.scale(image.scaleX || 1, -image.scaleY || -1);
  508. break;
  509. default:
  510. if (this.isPlayed) {
  511. this.stop();
  512. }
  513. }
  514. },
  515. load: function () {
  516. this.initImage($.proxy(function () {
  517. this.renderImage($.proxy(function () {
  518. this.isViewed = true;
  519. this.trigger(EVENT_VIEWED);
  520. }, this));
  521. }, this));
  522. },
  523. loadImage: function (e) {
  524. var image = e.target;
  525. var $image = $(image);
  526. var $parent = $image.parent();
  527. var parentWidth = $parent.width();
  528. var parentHeight = $parent.height();
  529. var filled = e.data && e.data.filled;
  530. getImageSize(image, $.proxy(function (naturalWidth, naturalHeight) {
  531. var aspectRatio = naturalWidth / naturalHeight;
  532. var width = parentWidth;
  533. var height = parentHeight;
  534. if (parentHeight * aspectRatio > parentWidth) {
  535. if (filled) {
  536. width = parentHeight * aspectRatio;
  537. } else {
  538. height = parentWidth / aspectRatio;
  539. }
  540. } else {
  541. if (filled) {
  542. height = parentWidth / aspectRatio;
  543. } else {
  544. width = parentHeight * aspectRatio;
  545. }
  546. }
  547. $image.css({
  548. width: width,
  549. height: height,
  550. marginLeft: (parentWidth - width) / 2,
  551. marginTop: (parentHeight - height) / 2
  552. });
  553. }, this));
  554. },
  555. resize: function () {
  556. this.initContainer();
  557. this.initViewer();
  558. this.renderViewer();
  559. this.renderList();
  560. this.initImage($.proxy(function () {
  561. this.renderImage();
  562. }, this));
  563. if (this.isPlayed) {
  564. this.$player.
  565. find(SELECTOR_IMG).
  566. one(EVENT_LOAD, $.proxy(this.loadImage, this)).
  567. trigger(EVENT_LOAD);
  568. }
  569. },
  570. wheel: function (event) {
  571. var e = event.originalEvent;
  572. var ratio = num(this.options.zoomRatio) || 0.1;
  573. var delta = 1;
  574. if (!this.isViewed) {
  575. return;
  576. }
  577. event.preventDefault();
  578. if (e.deltaY) {
  579. delta = e.deltaY > 0 ? 1 : -1;
  580. } else if (e.wheelDelta) {
  581. delta = -e.wheelDelta / 120;
  582. } else if (e.detail) {
  583. delta = e.detail > 0 ? 1 : -1;
  584. }
  585. this.zoom(-delta * ratio, true);
  586. },
  587. keydown: function (e) {
  588. var options = this.options;
  589. var which = e.which;
  590. if (!this.isFulled || !options.keyboard) {
  591. return;
  592. }
  593. switch (which) {
  594. // (Key: Esc)
  595. case 27:
  596. if (this.isPlayed) {
  597. this.stop();
  598. } else {
  599. if (options.inline) {
  600. if (this.isFulled) {
  601. this.exit();
  602. }
  603. } else {
  604. this.hide();
  605. }
  606. }
  607. break;
  608. // View previous (Key: ←)
  609. case 37:
  610. this.prev();
  611. break;
  612. // Zoom in (Key: ↑)
  613. case 38:
  614. this.zoom(options.zoomRatio, true);
  615. break;
  616. // View next (Key: →)
  617. case 39:
  618. this.next();
  619. break;
  620. // Zoom out (Key: ↓)
  621. case 40:
  622. this.zoom(-options.zoomRatio, true);
  623. break;
  624. // Zoom out to initial size (Key: Ctrl + 0)
  625. case 48:
  626. // Go to next
  627. // Zoom in to natural size (Key: Ctrl + 1)
  628. case 49:
  629. if (e.ctrlKey || e.shiftKey) {
  630. e.preventDefault();
  631. if (this.image.ratio === 1) {
  632. this.zoomTo(this.initialImage.ratio);
  633. } else {
  634. this.zoomTo(1);
  635. }
  636. }
  637. break;
  638. // No default
  639. }
  640. },
  641. mousedown: function (event) {
  642. var options = this.options;
  643. var originalEvent = event.originalEvent;
  644. var touches = originalEvent && originalEvent.touches;
  645. var e = event;
  646. var action = options.movable ? 'move' : false;
  647. var touchesLength;
  648. if (!this.isViewed) {
  649. return;
  650. }
  651. if (touches) {
  652. touchesLength = touches.length;
  653. if (touchesLength > 1) {
  654. if (options.zoomable && touchesLength === 2) {
  655. e = touches[1];
  656. this.startX2 = e.pageX;
  657. this.startY2 = e.pageY;
  658. action = 'zoom';
  659. } else {
  660. return;
  661. }
  662. } else {
  663. if (this.isSwitchable()) {
  664. action = 'switch';
  665. }
  666. }
  667. e = touches[0];
  668. }
  669. if (action) {
  670. event.preventDefault();
  671. if (action === 'move' && options.transition) {
  672. this.$image.removeClass(CLASS_TRANSITION);
  673. }
  674. this.action = action;
  675. // IE8 has `event.pageX/Y`, but not `event.originalEvent.pageX/Y`
  676. // IE10 has `event.originalEvent.pageX/Y`, but not `event.pageX/Y`
  677. this.startX = e.pageX || originalEvent && originalEvent.pageX;
  678. this.startY = e.pageY || originalEvent && originalEvent.pageY;
  679. }
  680. },
  681. mousemove: function (event) {
  682. var options = this.options;
  683. var originalEvent = event.originalEvent;
  684. var touches = originalEvent && originalEvent.touches;
  685. var e = event;
  686. var touchesLength;
  687. if (!this.isViewed) {
  688. return;
  689. }
  690. if (touches) {
  691. touchesLength = touches.length;
  692. if (touchesLength > 1) {
  693. if (options.zoomable && touchesLength === 2) {
  694. e = touches[1];
  695. this.endX2 = e.pageX;
  696. this.endY2 = e.pageY;
  697. } else {
  698. return;
  699. }
  700. }
  701. e = touches[0];
  702. }
  703. if (this.action) {
  704. event.preventDefault();
  705. this.endX = e.pageX || originalEvent && originalEvent.pageX;
  706. this.endY = e.pageY || originalEvent && originalEvent.pageY;
  707. this.change();
  708. }
  709. },
  710. mouseup: function (event) {
  711. var action = this.action;
  712. if (action) {
  713. event.preventDefault();
  714. if (action === 'move' && this.options.transition) {
  715. this.$image.addClass(CLASS_TRANSITION);
  716. }
  717. this.action = false;
  718. }
  719. }
  720. });
  721. $.extend(prototype, {
  722. // Show the viewer (only available in modal mode)
  723. show: function () {
  724. var options = this.options;
  725. var $viewer;
  726. if (options.inline || this.transitioning) {
  727. return;
  728. }
  729. if (!this.isBuilt) {
  730. this.build();
  731. }
  732. if ($.isFunction(options.show)) {
  733. this.$element.one(EVENT_SHOW, options.show);
  734. }
  735. if (this.trigger(EVENT_SHOW).isDefaultPrevented()) {
  736. return;
  737. }
  738. this.$body.addClass(CLASS_OPEN);
  739. $viewer = this.$viewer.removeClass(CLASS_HIDE);
  740. this.$element.one(EVENT_SHOWN, $.proxy(function () {
  741. this.view((this.target ? this.$images.index(this.target) : 0) || this.index);
  742. this.target = false;
  743. }, this));
  744. if (options.transition) {
  745. this.transitioning = true;
  746. /* jshint expr:true */
  747. $viewer.addClass(CLASS_TRANSITION).get(0).offsetWidth;
  748. $viewer.one(EVENT_TRANSITIONEND, $.proxy(this.shown, this)).addClass(CLASS_IN);
  749. } else {
  750. $viewer.addClass(CLASS_IN);
  751. this.shown();
  752. }
  753. },
  754. // Hide the viewer (only available in modal mode)
  755. hide: function () {
  756. var options = this.options;
  757. var $viewer = this.$viewer;
  758. if (options.inline || this.transitioning || !this.isShown) {
  759. return;
  760. }
  761. if ($.isFunction(options.hide)) {
  762. this.$element.one(EVENT_HIDE, options.hide);
  763. }
  764. if (this.trigger(EVENT_HIDE).isDefaultPrevented()) {
  765. return;
  766. }
  767. if (this.isViewed && options.transition) {
  768. this.transitioning = true;
  769. this.$image.one(EVENT_TRANSITIONEND, $.proxy(function () {
  770. $viewer.one(EVENT_TRANSITIONEND, $.proxy(this.hidden, this)).removeClass(CLASS_IN);
  771. }, this));
  772. this.zoomTo(0, false, true);
  773. } else {
  774. $viewer.removeClass(CLASS_IN);
  775. this.hidden();
  776. }
  777. },
  778. /**
  779. * View one of the images with image's index
  780. *
  781. * @param {Number} index
  782. */
  783. view: function (index) {
  784. var options = this.options;
  785. var viewer = this.viewer;
  786. var $title = this.$title;
  787. var $image;
  788. var $item;
  789. var $img;
  790. var url;
  791. var alt;
  792. index = Number(index) || 0;
  793. if (!this.isShown || this.isPlayed || index < 0 || index >= this.length ||
  794. this.isViewed && index === this.index) {
  795. return;
  796. }
  797. if (this.trigger(EVENT_VIEW).isDefaultPrevented()) {
  798. return;
  799. }
  800. $item = this.$items.eq(index);
  801. $img = $item.find(SELECTOR_IMG);
  802. url = $img.data('originalUrl');
  803. alt = $img.attr('alt');
  804. this.$image = $image = $('<img src="' + url + '" alt="' + alt + '">');
  805. $image.
  806. toggleClass(CLASS_TRANSITION, options.transition).
  807. toggleClass(CLASS_MOVE, options.movable).
  808. css({
  809. width: 0,
  810. height: 0,
  811. marginLeft: viewer.width / 2,
  812. marginTop: viewer.height / 2
  813. });
  814. this.$items.eq(this.index).removeClass(CLASS_ACTIVE);
  815. $item.addClass(CLASS_ACTIVE);
  816. this.isViewed = false;
  817. this.index = index;
  818. this.image = null;
  819. $image.one(EVENT_LOAD, $.proxy(this.load, this));
  820. this.$canvas.html($image);
  821. $title.empty();
  822. // Center current item
  823. this.renderList();
  824. // Show title when viewed
  825. this.$element.one(EVENT_VIEWED, $.proxy(function () {
  826. var image = this.image;
  827. var width = image.naturalWidth;
  828. var height = image.naturalHeight;
  829. $title.html(alt + ' (' + width + ' &times; ' + height + ')');
  830. }, this));
  831. },
  832. // View the previous image
  833. prev: function () {
  834. this.view(max(this.index - 1, 0));
  835. },
  836. // View the next image
  837. next: function () {
  838. this.view(min(this.index + 1, this.length - 1));
  839. },
  840. /**
  841. * Move the image
  842. *
  843. * @param {Number} offsetX
  844. * @param {Number} offsetY (optional)
  845. */
  846. move: function (offsetX, offsetY) {
  847. var image = this.image;
  848. // If "offsetY" is not present, its default value is "offsetX"
  849. if (isUndefined(offsetY)) {
  850. offsetY = offsetX;
  851. }
  852. offsetX = num(offsetX);
  853. offsetY = num(offsetY);
  854. if (this.isShown && !this.isPlayed && this.options.movable) {
  855. image.left += isNumber(offsetX) ? offsetX : 0;
  856. image.top += isNumber(offsetY) ? offsetY : 0;
  857. this.renderImage();
  858. }
  859. },
  860. /**
  861. * Zoom the image
  862. *
  863. * @param {Number} ratio
  864. * @param {Boolean} hasTooltip (optional)
  865. */
  866. zoom: function (ratio, hasTooltip) {
  867. var options = this.options;
  868. var minZoomRatio = max(0.01, options.minZoomRatio);
  869. var maxZoomRatio = min(100, options.maxZoomRatio);
  870. var image = this.image;
  871. var width;
  872. var height;
  873. ratio = num(ratio);
  874. if (isNumber(ratio) && this.isShown && !this.isPlayed && options.zoomable) {
  875. if (ratio < 0) {
  876. ratio = 1 / (1 - ratio);
  877. } else {
  878. ratio = 1 + ratio;
  879. }
  880. width = image.width * ratio;
  881. height = image.height * ratio;
  882. ratio = width / image.naturalWidth;
  883. ratio = min(max(ratio, minZoomRatio), maxZoomRatio);
  884. if (ratio > 0.95 && ratio < 1.05) {
  885. ratio = 1;
  886. width = image.naturalWidth;
  887. height = image.naturalHeight;
  888. }
  889. image.left -= (width - image.width) / 2;
  890. image.top -= (height - image.height) / 2;
  891. image.width = width;
  892. image.height = height;
  893. image.ratio = ratio;
  894. this.renderImage();
  895. if (hasTooltip) {
  896. this.tooltip();
  897. }
  898. }
  899. },
  900. /**
  901. * Zoom the image to a special ratio
  902. *
  903. * @param {Number} ratio
  904. * @param {Boolean} hasTooltip (optional)
  905. * @param {Boolean} _zoomable (private)
  906. */
  907. zoomTo: function (ratio, hasTooltip, _zoomable) {
  908. var image = this.image;
  909. var width;
  910. var height;
  911. ratio = max(ratio, 0);
  912. if (isNumber(ratio) && this.isShown && !this.isPlayed && (_zoomable || this.options.zoomable)) {
  913. width = image.naturalWidth * ratio;
  914. height = image.naturalHeight * ratio;
  915. image.left -= (width - image.width) / 2;
  916. image.top -= (height - image.height) / 2;
  917. image.width = width;
  918. image.height = height;
  919. image.ratio = ratio;
  920. this.renderImage();
  921. if (hasTooltip) {
  922. this.tooltip();
  923. }
  924. }
  925. },
  926. /**
  927. * Rotate the image
  928. * https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function#rotate()
  929. *
  930. * @param {Number} degrees
  931. */
  932. rotate: function (degrees) {
  933. var image = this.image;
  934. degrees = num(degrees);
  935. if (isNumber(degrees) && this.isShown && !this.isPlayed && this.options.rotatable) {
  936. image.rotate = ((image.rotate || 0) + degrees);
  937. this.renderImage();
  938. }
  939. },
  940. /**
  941. * Rotate the image to a special angle
  942. *
  943. * @param {Number} degrees
  944. */
  945. rotateTo: function (degrees) {
  946. var image = this.image;
  947. degrees = num(degrees);
  948. if (isNumber(degrees) && this.isShown && !this.isPlayed && this.options.rotatable) {
  949. image.rotate = degrees;
  950. this.renderImage();
  951. }
  952. },
  953. /**
  954. * Scale the image
  955. * https://developer.mozilla.org/en-US/docs/Web/CSS/transform-function#scale()
  956. *
  957. * @param {Number} scaleX
  958. * @param {Number} scaleY (optional)
  959. */
  960. scale: function (scaleX, scaleY) {
  961. var image = this.image;
  962. // If "scaleY" is not present, its default value is "scaleX"
  963. if (isUndefined(scaleY)) {
  964. scaleY = scaleX;
  965. }
  966. scaleX = num(scaleX);
  967. scaleY = num(scaleY);
  968. if (this.isShown && !this.isPlayed && this.options.scalable) {
  969. image.scaleX = isNumber(scaleX) ? scaleX : 1;
  970. image.scaleY = isNumber(scaleY) ? scaleY : 1;
  971. this.renderImage();
  972. }
  973. },
  974. /**
  975. * Scale the abscissa of the image
  976. *
  977. * @param {Number} scaleX
  978. */
  979. scaleX: function (scaleX) {
  980. this.scale(scaleX, this.image.scaleY);
  981. },
  982. /**
  983. * Scale the ordinate of the image
  984. *
  985. * @param {Number} scaleY
  986. */
  987. scaleY: function (scaleY) {
  988. this.scale(this.image.scaleX, scaleY);
  989. },
  990. // Play the images
  991. play: function () {
  992. var options = this.options;
  993. var $player = this.$player;
  994. var load = $.proxy(this.loadImage, this);
  995. var list = [];
  996. var total = 0;
  997. var index = 0;
  998. var playing;
  999. if (!this.isShown || this.isPlayed) {
  1000. return;
  1001. }
  1002. if (options.fullscreen) {
  1003. this.fullscreen();
  1004. }
  1005. this.isPlayed = true;
  1006. $player.addClass(CLASS_SHOW);
  1007. this.$items.each(function (i) {
  1008. var $this = $(this);
  1009. var $img = $this.find(SELECTOR_IMG);
  1010. var $image = $('<img src="' + $img.data('originalUrl') + '" alt="' + $img.attr('alt') + '">');
  1011. total++;
  1012. $image.addClass(CLASS_FADE).toggleClass(CLASS_TRANSITION, options.transition);
  1013. if ($this.hasClass(CLASS_ACTIVE)) {
  1014. $image.addClass(CLASS_IN);
  1015. index = i;
  1016. }
  1017. list.push($image);
  1018. $image.one(EVENT_LOAD, {
  1019. filled: false
  1020. }, load);
  1021. $player.append($image);
  1022. });
  1023. if (isNumber(options.interval) && options.interval > 0) {
  1024. playing = $.proxy(function () {
  1025. this.playing = setTimeout(function () {
  1026. list[index].removeClass(CLASS_IN);
  1027. index++;
  1028. index = index < total ? index : 0;
  1029. list[index].addClass(CLASS_IN);
  1030. playing();
  1031. }, options.interval);
  1032. }, this);
  1033. if (total > 1) {
  1034. playing();
  1035. }
  1036. }
  1037. },
  1038. // Stop play
  1039. stop: function () {
  1040. if (!this.isPlayed) {
  1041. return;
  1042. }
  1043. this.isPlayed = false;
  1044. clearTimeout(this.playing);
  1045. this.$player.removeClass(CLASS_SHOW).empty();
  1046. },
  1047. // Enter modal mode (only available in inline mode)
  1048. full: function () {
  1049. var options = this.options;
  1050. var $image = this.$image;
  1051. var $list = this.$list;
  1052. if (!this.isShown || this.isPlayed || this.isFulled || !options.inline) {
  1053. return;
  1054. }
  1055. this.isFulled = true;
  1056. this.$body.addClass(CLASS_OPEN);
  1057. this.$button.addClass(CLASS_FULLSCREEN_EXIT);
  1058. if (options.transition) {
  1059. $image.removeClass(CLASS_TRANSITION);
  1060. $list.removeClass(CLASS_TRANSITION);
  1061. }
  1062. this.$viewer.addClass(CLASS_FIXED).removeAttr('style').css('z-index', options.zIndex);
  1063. this.initContainer();
  1064. this.viewer = $.extend({}, this.container);
  1065. this.renderList();
  1066. this.initImage($.proxy(function () {
  1067. this.renderImage(function () {
  1068. if (options.transition) {
  1069. setTimeout(function () {
  1070. $image.addClass(CLASS_TRANSITION);
  1071. $list.addClass(CLASS_TRANSITION);
  1072. }, 0);
  1073. }
  1074. });
  1075. }, this));
  1076. },
  1077. // Exit modal mode (only available in inline mode)
  1078. exit: function () {
  1079. var options = this.options;
  1080. var $image = this.$image;
  1081. var $list = this.$list;
  1082. if (!this.isFulled) {
  1083. return;
  1084. }
  1085. this.isFulled = false;
  1086. this.$body.removeClass(CLASS_OPEN);
  1087. this.$button.removeClass(CLASS_FULLSCREEN_EXIT);
  1088. if (options.transition) {
  1089. $image.removeClass(CLASS_TRANSITION);
  1090. $list.removeClass(CLASS_TRANSITION);
  1091. }
  1092. this.$viewer.removeClass(CLASS_FIXED).css('z-index', options.zIndexInline);
  1093. this.viewer = $.extend({}, this.parent);
  1094. this.renderViewer();
  1095. this.renderList();
  1096. this.initImage($.proxy(function () {
  1097. this.renderImage(function () {
  1098. if (options.transition) {
  1099. setTimeout(function () {
  1100. $image.addClass(CLASS_TRANSITION);
  1101. $list.addClass(CLASS_TRANSITION);
  1102. }, 0);
  1103. }
  1104. });
  1105. }, this));
  1106. },
  1107. // Show the current ratio of the image with percentage
  1108. tooltip: function () {
  1109. var options = this.options;
  1110. var $tooltip = this.$tooltip;
  1111. var image = this.image;
  1112. var classes = [
  1113. CLASS_SHOW,
  1114. CLASS_FADE,
  1115. CLASS_TRANSITION
  1116. ].join(' ');
  1117. if (!this.isShown || this.isPlayed || !options.tooltip) {
  1118. return;
  1119. }
  1120. $tooltip.text(round(image.ratio * 100) + '%');
  1121. if (!this.fading) {
  1122. if (options.transition) {
  1123. /* jshint expr:true */
  1124. $tooltip.addClass(classes).get(0).offsetWidth;
  1125. $tooltip.addClass(CLASS_IN);
  1126. } else {
  1127. $tooltip.addClass(CLASS_SHOW);
  1128. }
  1129. } else {
  1130. clearTimeout(this.fading);
  1131. }
  1132. this.fading = setTimeout($.proxy(function () {
  1133. if (options.transition) {
  1134. $tooltip.one(EVENT_TRANSITIONEND, function () {
  1135. $tooltip.removeClass(classes);
  1136. }).removeClass(CLASS_IN);
  1137. } else {
  1138. $tooltip.removeClass(CLASS_SHOW);
  1139. }
  1140. this.fading = false;
  1141. }, this), 1000);
  1142. },
  1143. // Toggle the image size between its natural size and initial size.
  1144. toggle: function () {
  1145. if (this.image.ratio === 1) {
  1146. this.zoomTo(this.initialImage.ratio);
  1147. } else {
  1148. this.zoomTo(1);
  1149. }
  1150. },
  1151. // Reset the image to its initial state.
  1152. reset: function () {
  1153. if (this.isShown && !this.isPlayed) {
  1154. this.image = $.extend({}, this.initialImage);
  1155. this.renderImage();
  1156. }
  1157. },
  1158. // Destroy the viewer
  1159. destroy: function () {
  1160. var $this = this.$element;
  1161. if (this.options.inline) {
  1162. this.unbind();
  1163. } else {
  1164. if (this.isShown) {
  1165. this.unbind();
  1166. }
  1167. this.$images.removeClass(CLASS_TOGGLE);
  1168. $this.off(EVENT_CLICK, this.start);
  1169. }
  1170. this.unbuild();
  1171. $this.removeData(NAMESPACE);
  1172. }
  1173. });
  1174. $.extend(prototype, {
  1175. // A shortcut for triggering custom events
  1176. trigger: function (type, data) {
  1177. var e = $.Event(type, data);
  1178. this.$element.trigger(e);
  1179. return e;
  1180. },
  1181. shown: function () {
  1182. var options = this.options;
  1183. this.transitioning = false;
  1184. this.isFulled = true;
  1185. this.isShown = true;
  1186. this.isVisible = true;
  1187. this.render();
  1188. this.bind();
  1189. if ($.isFunction(options.shown)) {
  1190. this.$element.one(EVENT_SHOWN, options.shown);
  1191. }
  1192. this.trigger(EVENT_SHOWN);
  1193. },
  1194. hidden: function () {
  1195. var options = this.options;
  1196. this.transitioning = false;
  1197. this.isViewed = false;
  1198. this.isFulled = false;
  1199. this.isShown = false;
  1200. this.isVisible = false;
  1201. this.unbind();
  1202. this.$body.removeClass(CLASS_OPEN);
  1203. this.$viewer.addClass(CLASS_HIDE);
  1204. this.resetList();
  1205. this.resetImage();
  1206. if ($.isFunction(options.hidden)) {
  1207. this.$element.one(EVENT_HIDDEN, options.hidden);
  1208. }
  1209. this.trigger(EVENT_HIDDEN);
  1210. },
  1211. fullscreen: function () {
  1212. var documentElement = document.documentElement;
  1213. if (this.isFulled && !document.fullscreenElement && !document.mozFullScreenElement &&
  1214. !document.webkitFullscreenElement && !document.msFullscreenElement) {
  1215. if (documentElement.requestFullscreen) {
  1216. documentElement.requestFullscreen();
  1217. } else if (documentElement.msRequestFullscreen) {
  1218. documentElement.msRequestFullscreen();
  1219. } else if (documentElement.mozRequestFullScreen) {
  1220. documentElement.mozRequestFullScreen();
  1221. } else if (documentElement.webkitRequestFullscreen) {
  1222. documentElement.webkitRequestFullscreen(Element.ALLOW_KEYBOARD_INPUT);
  1223. }
  1224. }
  1225. },
  1226. change: function () {
  1227. var offsetX = this.endX - this.startX;
  1228. var offsetY = this.endY - this.startY;
  1229. switch (this.action) {
  1230. // Move the current image
  1231. case 'move':
  1232. this.move(offsetX, offsetY);
  1233. break;
  1234. // Zoom the current image
  1235. case 'zoom':
  1236. this.zoom(function (x1, y1, x2, y2) {
  1237. var z1 = sqrt(x1 * x1 + y1 * y1);
  1238. var z2 = sqrt(x2 * x2 + y2 * y2);
  1239. return (z2 - z1) / z1;
  1240. }(
  1241. abs(this.startX - this.startX2),
  1242. abs(this.startY - this.startY2),
  1243. abs(this.endX - this.endX2),
  1244. abs(this.endY - this.endY2)
  1245. ));
  1246. this.startX2 = this.endX2;
  1247. this.startY2 = this.endY2;
  1248. break;
  1249. case 'switch':
  1250. this.action = 'switched';
  1251. if (offsetX > 1) {
  1252. this.prev();
  1253. } else if (offsetX < -1) {
  1254. this.next();
  1255. }
  1256. break;
  1257. // No default
  1258. }
  1259. // Override
  1260. this.startX = this.endX;
  1261. this.startY = this.endY;
  1262. },
  1263. isSwitchable: function () {
  1264. var image = this.image;
  1265. var viewer = this.viewer;
  1266. return (image.left >= 0 && image.top >= 0 && image.width <= viewer.width &&
  1267. image.height <= viewer.height);
  1268. }
  1269. });
  1270. $.extend(Viewer.prototype, prototype);
  1271. Viewer.DEFAULTS = {
  1272. // Enable inline mode
  1273. inline: false,
  1274. // Show the button on the top-right of the viewer
  1275. button: true,
  1276. // Show the navbar
  1277. navbar: true,
  1278. // Show the title
  1279. title: true,
  1280. // Show the toolbar
  1281. toolbar: true,
  1282. // Show the tooltip with image ratio (percentage) when zoom in or zoom out
  1283. tooltip: true,
  1284. // Enable to move the image
  1285. movable: true,
  1286. // Enable to zoom the image
  1287. zoomable: true,
  1288. // Enable to rotate the image
  1289. rotatable: true,
  1290. // Enable to scale the image
  1291. scalable: true,
  1292. // Enable CSS3 Transition for some special elements
  1293. transition: true,
  1294. // Enable to request fullscreen when play
  1295. fullscreen: true,
  1296. // Enable keyboard support
  1297. keyboard: true,
  1298. // Define interval of each image when playing
  1299. interval: 5000,
  1300. // Min width of the viewer in inline mode
  1301. minWidth: 200,
  1302. // Min height of the viewer in inline mode
  1303. minHeight: 100,
  1304. // Define the ratio when zoom the image by wheeling mouse
  1305. zoomRatio: 0.1,
  1306. // Define the min ratio of the image when zoom out
  1307. minZoomRatio: 0.01,
  1308. // Define the max ratio of the image when zoom in
  1309. maxZoomRatio: 100,
  1310. // Define the CSS `z-index` value of viewer in modal mode.
  1311. zIndex: 2015,
  1312. // Define the CSS `z-index` value of viewer in inline mode.
  1313. zIndexInline: 0,
  1314. // Define where to get the original image URL for viewing
  1315. // Type: String (an image attribute) or Function (should return an image URL)
  1316. url: 'src',
  1317. // Event shortcuts
  1318. build: null,
  1319. built: null,
  1320. show: null,
  1321. shown: null,
  1322. hide: null,
  1323. hidden: null
  1324. };
  1325. Viewer.TEMPLATE = (
  1326. '<div class="viewer-container">' +
  1327. '<div class="viewer-canvas"></div>' +
  1328. '<div class="viewer-footer">' +
  1329. '<div class="viewer-title"></div>' +
  1330. '<ul class="viewer-toolbar">' +
  1331. '<li class="viewer-zoom-in" data-action="zoom-in"></li>' +
  1332. '<li class="viewer-zoom-out" data-action="zoom-out"></li>' +
  1333. '<li class="viewer-one-to-one" data-action="one-to-one"></li>' +
  1334. '<li class="viewer-reset" data-action="reset"></li>' +
  1335. '<li class="viewer-prev" data-action="prev"></li>' +
  1336. '<li class="viewer-play" data-action="play"></li>' +
  1337. '<li class="viewer-next" data-action="next"></li>' +
  1338. '<li class="viewer-rotate-left" data-action="rotate-left"></li>' +
  1339. '<li class="viewer-rotate-right" data-action="rotate-right"></li>' +
  1340. '<li class="viewer-flip-horizontal" data-action="flip-horizontal"></li>' +
  1341. '<li class="viewer-flip-vertical" data-action="flip-vertical"></li>' +
  1342. '</ul>' +
  1343. '<div class="viewer-navbar">' +
  1344. '<ul class="viewer-list"></ul>' +
  1345. '</div>' +
  1346. '</div>' +
  1347. '<div class="viewer-tooltip"></div>' +
  1348. '<div class="viewer-button" data-action="mix"></div>' +
  1349. '<div class="viewer-player"></div>' +
  1350. '</div>'
  1351. );
  1352. // Save the other viewer
  1353. Viewer.other = $.fn.viewer;
  1354. // Register as jQuery plugin
  1355. $.fn.viewer = function (options) {
  1356. var args = toArray(arguments, 1);
  1357. var result;
  1358. this.each(function () {
  1359. var $this = $(this);
  1360. var data = $this.data(NAMESPACE);
  1361. var fn;
  1362. if (!data) {
  1363. if (/destroy|hide|exit|stop|reset/.test(options)) {
  1364. return;
  1365. }
  1366. $this.data(NAMESPACE, (data = new Viewer(this, options)));
  1367. }
  1368. if (isString(options) && $.isFunction(fn = data[options])) {
  1369. result = fn.apply(data, args);
  1370. }
  1371. });
  1372. return isUndefined(result) ? this : result;
  1373. };
  1374. $.fn.viewer.Constructor = Viewer;
  1375. $.fn.viewer.setDefaults = Viewer.setDefaults;
  1376. // No conflict
  1377. $.fn.viewer.noConflict = function () {
  1378. $.fn.viewer = Viewer.other;
  1379. return this;
  1380. };
  1381. });