lightcase.js 51 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893
  1. /*
  2. * Lightcase - jQuery Plugin
  3. * The smart and flexible Lightbox Plugin.
  4. *
  5. * @author Cornel Boppart <cornel@bopp-art.com>
  6. * @copyright Author
  7. *
  8. * @version 2.5.0 (11/03/2018)
  9. */
  10. ;(function ($) {
  11. 'use strict';
  12. var _self = {
  13. cache: {},
  14. support: {},
  15. objects: {},
  16. /**
  17. * Initializes the plugin
  18. *
  19. * @param {object} options
  20. * @return {object}
  21. */
  22. init: function (options) {
  23. return this.each(function () {
  24. $(this).unbind('click.lightcase').bind('click.lightcase', function (event) {
  25. event.preventDefault();
  26. $(this).lightcase('start', options);
  27. });
  28. });
  29. },
  30. /**
  31. * Starts the plugin
  32. *
  33. * @param {object} options
  34. * @return {void}
  35. */
  36. start: function (options) {
  37. _self.origin = lightcase.origin = this;
  38. _self.settings = lightcase.settings = $.extend(true, {
  39. idPrefix: 'lightcase-',
  40. classPrefix: 'lightcase-',
  41. attrPrefix: 'lc-',
  42. transition: 'elastic',
  43. transitionOpen: null,
  44. transitionClose: null,
  45. transitionIn: null,
  46. transitionOut: null,
  47. cssTransitions: true,
  48. speedIn: 250,
  49. speedOut: 250,
  50. width: null,
  51. height: null,
  52. maxWidth: 800,
  53. maxHeight: 500,
  54. forceWidth: false,
  55. forceHeight: false,
  56. liveResize: true,
  57. fullScreenModeForMobile: true,
  58. mobileMatchExpression: /(iphone|ipod|ipad|android|blackberry|symbian)/,
  59. disableShrink: false,
  60. fixedRatio: true,
  61. shrinkFactor: .75,
  62. overlayOpacity: .9,
  63. slideshow: false,
  64. slideshowAutoStart: true,
  65. breakBeforeShow: false,
  66. timeout: 5000,
  67. swipe: true,
  68. useKeys: true,
  69. useCategories: true,
  70. useAsCollection: false,
  71. navigateEndless: true,
  72. closeOnOverlayClick: true,
  73. title: null,
  74. caption: null,
  75. showTitle: true,
  76. showCaption: true,
  77. showSequenceInfo: true,
  78. inline: {
  79. width: 'auto',
  80. height: 'auto'
  81. },
  82. ajax: {
  83. width: 'auto',
  84. height: 'auto',
  85. type: 'get',
  86. dataType: 'html',
  87. data: {}
  88. },
  89. iframe: {
  90. width: 800,
  91. height: 500,
  92. frameborder: 0
  93. },
  94. flash: {
  95. width: 400,
  96. height: 205,
  97. wmode: 'transparent'
  98. },
  99. video: {
  100. width: 400,
  101. height: 225,
  102. poster: '',
  103. preload: 'auto',
  104. controls: true,
  105. autobuffer: true,
  106. autoplay: true,
  107. loop: false
  108. },
  109. attr: 'data-rel',
  110. href: null,
  111. type: null,
  112. typeMapping: {
  113. 'image': 'jpg,jpeg,gif,png,bmp',
  114. 'flash': 'swf',
  115. 'video': 'mp4,mov,ogv,ogg,webm',
  116. 'iframe': 'html,php',
  117. 'ajax': 'json,txt',
  118. 'inline': '#'
  119. },
  120. errorMessage: function () {
  121. return '<p class="' + _self.settings.classPrefix + 'error">' + _self.settings.labels['errorMessage'] + '</p>';
  122. },
  123. labels: {
  124. 'errorMessage': 'Source could not be found...',
  125. 'sequenceInfo.of': ' of ',
  126. 'close': 'Close',
  127. 'navigator.prev': 'Prev',
  128. 'navigator.next': 'Next',
  129. 'navigator.play': 'Play',
  130. 'navigator.pause': 'Pause'
  131. },
  132. markup: function () {
  133. _self.objects.body.append(
  134. _self.objects.overlay = $('<div id="' + _self.settings.idPrefix + 'overlay"></div>'),
  135. _self.objects.loading = $('<div id="' + _self.settings.idPrefix + 'loading" class="' + _self.settings.classPrefix + 'icon-spin"></div>'),
  136. _self.objects.case = $('<div id="' + _self.settings.idPrefix + 'case" aria-hidden="true" role="dialog"></div>')
  137. );
  138. _self.objects.case.after(
  139. _self.objects.close = $('<a href="#" class="' + _self.settings.classPrefix + 'icon-close"><span>' + _self.settings.labels['close'] + '</span></a>'),
  140. _self.objects.nav = $('<div id="' + _self.settings.idPrefix + 'nav"></div>')
  141. );
  142. _self.objects.nav.append(
  143. _self.objects.prev = $('<a href="#" class="' + _self.settings.classPrefix + 'icon-prev"><span>' + _self.settings.labels['navigator.prev'] + '</span></a>').hide(),
  144. _self.objects.next = $('<a href="#" class="' + _self.settings.classPrefix + 'icon-next"><span>' + _self.settings.labels['navigator.next'] + '</span></a>').hide(),
  145. _self.objects.play = $('<a href="#" class="' + _self.settings.classPrefix + 'icon-play"><span>' + _self.settings.labels['navigator.play'] + '</span></a>').hide(),
  146. _self.objects.pause = $('<a href="#" class="' + _self.settings.classPrefix + 'icon-pause"><span>' + _self.settings.labels['navigator.pause'] + '</span></a>').hide()
  147. );
  148. _self.objects.case.append(
  149. _self.objects.content = $('<div id="' + _self.settings.idPrefix + 'content"></div>'),
  150. _self.objects.info = $('<div id="' + _self.settings.idPrefix + 'info"></div>')
  151. );
  152. _self.objects.content.append(
  153. _self.objects.contentInner = $('<div class="' + _self.settings.classPrefix + 'contentInner"></div>')
  154. );
  155. _self.objects.info.append(
  156. _self.objects.sequenceInfo = $('<div id="' + _self.settings.idPrefix + 'sequenceInfo"></div>'),
  157. _self.objects.title = $('<h4 id="' + _self.settings.idPrefix + 'title"></h4>'),
  158. _self.objects.caption = $('<p id="' + _self.settings.idPrefix + 'caption"></p>')
  159. );
  160. },
  161. onInit: {},
  162. onStart: {},
  163. onBeforeCalculateDimensions: {},
  164. onAfterCalculateDimensions: {},
  165. onBeforeShow: {},
  166. onFinish: {},
  167. onResize: {},
  168. onClose: {},
  169. onCleanup: {}
  170. },
  171. options,
  172. // Load options from data-lc-options attribute
  173. _self.origin.data ? _self.origin.data('lc-options') : {});
  174. _self.objects.document = $('html');
  175. _self.objects.body = $('body');
  176. // Call onInit hook functions
  177. _self._callHooks(_self.settings.onInit);
  178. _self.objectData = _self._setObjectData(this);
  179. _self._addElements();
  180. _self._open();
  181. _self.dimensions = _self.getViewportDimensions();
  182. },
  183. /**
  184. * Getter method for objects
  185. *
  186. * @param {string} name
  187. * @return {object}
  188. */
  189. get: function (name) {
  190. return _self.objects[name];
  191. },
  192. /**
  193. * Getter method for objectData
  194. *
  195. * @return {object}
  196. */
  197. getObjectData: function () {
  198. return _self.objectData;
  199. },
  200. /**
  201. * Sets the object data
  202. *
  203. * @param {object} object
  204. * @return {object} objectData
  205. */
  206. _setObjectData: function (object) {
  207. var $object = $(object),
  208. objectData = {
  209. this: $(object),
  210. title: _self.settings.title || $object.attr(_self._prefixAttributeName('title')) || $object.attr('title'),
  211. caption: _self.settings.caption || $object.attr(_self._prefixAttributeName('caption')) || $object.children('img').attr('alt'),
  212. url: _self._determineUrl(),
  213. requestType: _self.settings.ajax.type,
  214. requestData: _self.settings.ajax.data,
  215. requestDataType: _self.settings.ajax.dataType,
  216. rel: $object.attr(_self._determineAttributeSelector()),
  217. type: _self.settings.type || _self._verifyDataType(_self._determineUrl()),
  218. isPartOfSequence: _self.settings.useAsCollection || _self._isPartOfSequence($object.attr(_self.settings.attr), ':'),
  219. isPartOfSequenceWithSlideshow: _self._isPartOfSequence($object.attr(_self.settings.attr), ':slideshow'),
  220. currentIndex: $(_self._determineAttributeSelector()).index($object),
  221. sequenceLength: $(_self._determineAttributeSelector()).length
  222. };
  223. // Add sequence info to objectData
  224. objectData.sequenceInfo = (objectData.currentIndex + 1) + _self.settings.labels['sequenceInfo.of'] + objectData.sequenceLength;
  225. // Add next/prev index
  226. objectData.prevIndex = objectData.currentIndex - 1;
  227. objectData.nextIndex = objectData.currentIndex + 1;
  228. return objectData;
  229. },
  230. /**
  231. * Prefixes a data attribute name with defined name from 'settings.attrPrefix'
  232. * to ensure more uniqueness for all lightcase related/used attributes.
  233. *
  234. * @param {string} name
  235. * @return {string}
  236. */
  237. _prefixAttributeName: function (name) {
  238. return 'data-' + _self.settings.attrPrefix + name;
  239. },
  240. /**
  241. * Determines the link target considering 'settings.href' and data attributes
  242. * but also with a fallback to the default 'href' value.
  243. *
  244. * @return {string}
  245. */
  246. _determineLinkTarget: function () {
  247. return _self.settings.href || $(_self.origin).attr(_self._prefixAttributeName('href')) || $(_self.origin).attr('href');
  248. },
  249. /**
  250. * Determines the attribute selector to use, depending on
  251. * whether categorized collections are beeing used or not.
  252. *
  253. * @return {string} selector
  254. */
  255. _determineAttributeSelector: function () {
  256. var $origin = $(_self.origin),
  257. selector = '';
  258. if (typeof _self.cache.selector !== 'undefined') {
  259. selector = _self.cache.selector;
  260. } else if (_self.settings.useCategories === true && $origin.attr(_self._prefixAttributeName('categories'))) {
  261. var categories = $origin.attr(_self._prefixAttributeName('categories')).split(' ');
  262. $.each(categories, function (index, category) {
  263. if (index > 0) {
  264. selector += ',';
  265. }
  266. selector += '[' + _self._prefixAttributeName('categories') + '~="' + category + '"]';
  267. });
  268. } else {
  269. selector = '[' + _self.settings.attr + '="' + $origin.attr(_self.settings.attr) + '"]';
  270. }
  271. _self.cache.selector = selector;
  272. return selector;
  273. },
  274. /**
  275. * Determines the correct resource according to the
  276. * current viewport and density.
  277. *
  278. * @return {string} url
  279. */
  280. _determineUrl: function () {
  281. var dataUrl = _self._verifyDataUrl(_self._determineLinkTarget()),
  282. width = 0,
  283. density = 0,
  284. supportLevel = '',
  285. url;
  286. $.each(dataUrl, function (index, src) {
  287. switch (_self._verifyDataType(src.url)) {
  288. case 'video':
  289. var video = document.createElement('video'),
  290. videoType = _self._verifyDataType(src.url) + '/' + _self._getFileUrlSuffix(src.url);
  291. // Check if browser can play this type of video format
  292. if (supportLevel !== 'probably' && supportLevel !== video.canPlayType(videoType) && video.canPlayType(videoType) !== '') {
  293. supportLevel = video.canPlayType(videoType);
  294. url = src.url;
  295. }
  296. break;
  297. default:
  298. if (
  299. // Check density
  300. _self._devicePixelRatio() >= src.density &&
  301. src.density >= density &&
  302. // Check viewport width
  303. _self._matchMedia()('screen and (min-width:' + src.width + 'px)').matches &&
  304. src.width >= width
  305. ) {
  306. width = src.width;
  307. density = src.density;
  308. url = src.url;
  309. }
  310. break;
  311. }
  312. });
  313. return url;
  314. },
  315. /**
  316. * Normalizes an url and returns information about the resource path,
  317. * the viewport width as well as density if defined.
  318. *
  319. * @param {string} url Path to resource in format of an url or srcset
  320. * @return {object}
  321. */
  322. _normalizeUrl: function (url) {
  323. var srcExp = /^\d+$/;
  324. return url.split(',').map(function (str) {
  325. var src = {
  326. width: 0,
  327. density: 0
  328. };
  329. str.trim().split(/\s+/).forEach(function (url, i) {
  330. if (i === 0) {
  331. return src.url = url;
  332. }
  333. var value = url.substring(0, url.length - 1),
  334. lastChar = url[url.length - 1],
  335. intVal = parseInt(value, 10),
  336. floatVal = parseFloat(value);
  337. if (lastChar === 'w' && srcExp.test(value)) {
  338. src.width = intVal;
  339. } else if (lastChar === 'h' && srcExp.test(value)) {
  340. src.height = intVal;
  341. } else if (lastChar === 'x' && !isNaN(floatVal)) {
  342. src.density = floatVal;
  343. }
  344. });
  345. return src;
  346. });
  347. },
  348. /**
  349. * Verifies if the link is part of a sequence
  350. *
  351. * @param {string} rel
  352. * @param {string} expression
  353. * @return {boolean}
  354. */
  355. _isPartOfSequence: function (rel, expression) {
  356. var getSimilarLinks = $('[' + _self.settings.attr + '="' + rel + '"]'),
  357. regexp = new RegExp(expression);
  358. return (regexp.test(rel) && getSimilarLinks.length > 1);
  359. },
  360. /**
  361. * Verifies if the slideshow should be enabled
  362. *
  363. * @return {boolean}
  364. */
  365. isSlideshowEnabled: function () {
  366. return (_self.objectData.isPartOfSequence && (_self.settings.slideshow === true || _self.objectData.isPartOfSequenceWithSlideshow === true));
  367. },
  368. /**
  369. * Loads the new content to show
  370. *
  371. * @return {void}
  372. */
  373. _loadContent: function () {
  374. if (_self.cache.originalObject) {
  375. _self._restoreObject();
  376. }
  377. _self._createObject();
  378. },
  379. /**
  380. * Creates a new object
  381. *
  382. * @return {void}
  383. */
  384. _createObject: function () {
  385. var $object;
  386. // Create object
  387. switch (_self.objectData.type) {
  388. case 'image':
  389. $object = $(new Image());
  390. $object.attr({
  391. // The time expression is required to prevent the binding of an image load
  392. 'src': _self.objectData.url,
  393. 'alt': _self.objectData.title
  394. });
  395. break;
  396. case 'inline':
  397. $object = $('<div class="' + _self.settings.classPrefix + 'inlineWrap"></div>');
  398. $object.html(_self._cloneObject($(_self.objectData.url)));
  399. // Add custom attributes from _self.settings
  400. $.each(_self.settings.inline, function (name, value) {
  401. $object.attr(_self._prefixAttributeName(name), value);
  402. });
  403. break;
  404. case 'ajax':
  405. $object = $('<div class="' + _self.settings.classPrefix + 'inlineWrap"></div>');
  406. // Add custom attributes from _self.settings
  407. $.each(_self.settings.ajax, function (name, value) {
  408. if (name !== 'data') {
  409. $object.attr(_self._prefixAttributeName(name), value);
  410. }
  411. });
  412. break;
  413. case 'flash':
  414. $object = $('<embed src="' + _self.objectData.url + '" type="application/x-shockwave-flash"></embed>');
  415. // Add custom attributes from _self.settings
  416. $.each(_self.settings.flash, function (name, value) {
  417. $object.attr(name, value);
  418. });
  419. break;
  420. case 'video':
  421. $object = $('<video></video>');
  422. $object.attr('src', _self.objectData.url);
  423. // Add custom attributes from _self.settings
  424. $.each(_self.settings.video, function (name, value) {
  425. $object.attr(name, value);
  426. });
  427. break;
  428. default:
  429. $object = $('<iframe></iframe>');
  430. $object.attr({
  431. 'src': _self.objectData.url
  432. });
  433. // Add custom attributes from _self.settings
  434. $.each(_self.settings.iframe, function (name, value) {
  435. $object.attr(name, value);
  436. });
  437. break;
  438. }
  439. _self._addObject($object);
  440. _self._loadObject($object);
  441. },
  442. /**
  443. * Adds the new object to the markup
  444. *
  445. * @param {object} $object
  446. * @return {void}
  447. */
  448. _addObject: function ($object) {
  449. // Add object to content holder
  450. _self.objects.contentInner.html($object);
  451. // Start loading
  452. _self._loading('start');
  453. // Call onStart hook functions
  454. _self._callHooks(_self.settings.onStart);
  455. // Add sequenceInfo to the content holder or hide if its empty
  456. if (_self.settings.showSequenceInfo === true && _self.objectData.isPartOfSequence) {
  457. _self.objects.sequenceInfo.html(_self.objectData.sequenceInfo);
  458. _self.objects.sequenceInfo.show();
  459. } else {
  460. _self.objects.sequenceInfo.empty();
  461. _self.objects.sequenceInfo.hide();
  462. }
  463. // Add title to the content holder or hide if its empty
  464. if (_self.settings.showTitle === true && _self.objectData.title !== undefined && _self.objectData.title !== '') {
  465. _self.objects.title.html(_self.objectData.title);
  466. _self.objects.title.show();
  467. } else {
  468. _self.objects.title.empty();
  469. _self.objects.title.hide();
  470. }
  471. // Add caption to the content holder or hide if its empty
  472. if (_self.settings.showCaption === true && _self.objectData.caption !== undefined && _self.objectData.caption !== '') {
  473. _self.objects.caption.html(_self.objectData.caption);
  474. _self.objects.caption.show();
  475. } else {
  476. _self.objects.caption.empty();
  477. _self.objects.caption.hide();
  478. }
  479. },
  480. /**
  481. * Loads the new object
  482. *
  483. * @param {object} $object
  484. * @return {void}
  485. */
  486. _loadObject: function ($object) {
  487. // Load the object
  488. switch (_self.objectData.type) {
  489. case 'inline':
  490. if ($(_self.objectData.url)) {
  491. _self._showContent($object);
  492. } else {
  493. _self.error();
  494. }
  495. break;
  496. case 'ajax':
  497. $.ajax(
  498. $.extend({}, _self.settings.ajax, {
  499. url: _self.objectData.url,
  500. type: _self.objectData.requestType,
  501. dataType: _self.objectData.requestDataType,
  502. data: _self.objectData.requestData,
  503. success: function (data, textStatus, jqXHR) {
  504. // Check for X-Ajax-Location
  505. if (jqXHR.getResponseHeader('X-Ajax-Location')) {
  506. _self.objectData.url = jqXHR.getResponseHeader('X-Ajax-Location');
  507. _self._loadObject($object);
  508. }
  509. else {
  510. // Unserialize if data is transferred as json
  511. if (_self.objectData.requestDataType === 'json') {
  512. _self.objectData.data = data;
  513. } else {
  514. $object.html(data);
  515. }
  516. _self._showContent($object);
  517. }
  518. },
  519. error: function (jqXHR, textStatus, errorThrown) {
  520. _self.error();
  521. }
  522. })
  523. );
  524. break;
  525. case 'flash':
  526. _self._showContent($object);
  527. break;
  528. case 'video':
  529. if (typeof($object.get(0).canPlayType) === 'function' || _self.objects.case.find('video').length === 0) {
  530. _self._showContent($object);
  531. } else {
  532. _self.error();
  533. }
  534. break;
  535. default:
  536. if (_self.objectData.url) {
  537. $object.on('load', function () {
  538. _self._showContent($object);
  539. });
  540. $object.on('error', function () {
  541. _self.error();
  542. });
  543. } else {
  544. _self.error();
  545. }
  546. break;
  547. }
  548. },
  549. /**
  550. * Throws an error message if something went wrong
  551. *
  552. * @return {void}
  553. */
  554. error: function () {
  555. _self.objectData.type = 'error';
  556. var $object = $('<div class="' + _self.settings.classPrefix + 'inlineWrap"></div>');
  557. $object.html(_self.settings.errorMessage);
  558. _self.objects.contentInner.html($object);
  559. _self._showContent(_self.objects.contentInner);
  560. },
  561. /**
  562. * Calculates the dimensions to fit content
  563. *
  564. * @param {object} $object
  565. * @return {void}
  566. */
  567. _calculateDimensions: function ($object) {
  568. _self._cleanupDimensions();
  569. if (!$object) return;
  570. // Set default dimensions
  571. var dimensions = {
  572. ratio: 1,
  573. objectWidth: $object.attr('width') ? $object.attr('width') : $object.attr(_self._prefixAttributeName('width')),
  574. objectHeight: $object.attr('height') ? $object.attr('height') : $object.attr(_self._prefixAttributeName('height'))
  575. };
  576. if (!_self.settings.disableShrink) {
  577. // Add calculated maximum width/height to dimensions
  578. dimensions.maxWidth = parseInt(_self.dimensions.windowWidth * _self.settings.shrinkFactor);
  579. dimensions.maxHeight = parseInt(_self.dimensions.windowHeight * _self.settings.shrinkFactor);
  580. // If the auto calculated maxWidth/maxHeight greather than the user-defined one, use that.
  581. if (dimensions.maxWidth > _self.settings.maxWidth) {
  582. dimensions.maxWidth = _self.settings.maxWidth;
  583. }
  584. if (dimensions.maxHeight > _self.settings.maxHeight) {
  585. dimensions.maxHeight = _self.settings.maxHeight;
  586. }
  587. // Calculate the difference between screen width/height and image width/height
  588. dimensions.differenceWidthAsPercent = parseInt(100 / dimensions.maxWidth * dimensions.objectWidth);
  589. dimensions.differenceHeightAsPercent = parseInt(100 / dimensions.maxHeight * dimensions.objectHeight);
  590. switch (_self.objectData.type) {
  591. case 'image':
  592. case 'flash':
  593. case 'video':
  594. case 'iframe':
  595. case 'ajax':
  596. case 'inline':
  597. if (_self.objectData.type === 'image' || _self.settings.fixedRatio === true) {
  598. if (dimensions.differenceWidthAsPercent > 100 && dimensions.differenceWidthAsPercent > dimensions.differenceHeightAsPercent) {
  599. dimensions.objectWidth = dimensions.maxWidth;
  600. dimensions.objectHeight = parseInt(dimensions.objectHeight / dimensions.differenceWidthAsPercent * 100);
  601. }
  602. if (dimensions.differenceHeightAsPercent > 100 && dimensions.differenceHeightAsPercent > dimensions.differenceWidthAsPercent) {
  603. dimensions.objectWidth = parseInt(dimensions.objectWidth / dimensions.differenceHeightAsPercent * 100);
  604. dimensions.objectHeight = dimensions.maxHeight;
  605. }
  606. if (dimensions.differenceHeightAsPercent > 100 && dimensions.differenceWidthAsPercent < dimensions.differenceHeightAsPercent) {
  607. dimensions.objectWidth = parseInt(dimensions.maxWidth / dimensions.differenceHeightAsPercent * dimensions.differenceWidthAsPercent);
  608. dimensions.objectHeight = dimensions.maxHeight;
  609. }
  610. break;
  611. }
  612. case 'error':
  613. if (!isNaN(dimensions.objectWidth) && dimensions.objectWidth > dimensions.maxWidth) {
  614. dimensions.objectWidth = dimensions.maxWidth;
  615. }
  616. break;
  617. default:
  618. if ((isNaN(dimensions.objectWidth) || dimensions.objectWidth > dimensions.maxWidth) && !_self.settings.forceWidth) {
  619. dimensions.objectWidth = dimensions.maxWidth;
  620. }
  621. if (((isNaN(dimensions.objectHeight) && dimensions.objectHeight !== 'auto') || dimensions.objectHeight > dimensions.maxHeight) && !_self.settings.forceHeight) {
  622. dimensions.objectHeight = dimensions.maxHeight;
  623. }
  624. break;
  625. }
  626. }
  627. if (_self.settings.forceWidth) {
  628. try {
  629. dimensions.objectWidth = _self.settings[_self.objectData.type].width;
  630. } catch (e) {
  631. dimensions.objectWidth = _self.settings.width || dimensions.objectWidth;
  632. }
  633. dimensions.maxWidth = null;
  634. }
  635. if ($object.attr(_self._prefixAttributeName('max-width'))) {
  636. dimensions.maxWidth = $object.attr(_self._prefixAttributeName('max-width'));
  637. }
  638. if (_self.settings.forceHeight) {
  639. try {
  640. dimensions.objectHeight = _self.settings[_self.objectData.type].height;
  641. } catch (e) {
  642. dimensions.objectHeight = _self.settings.height || dimensions.objectHeight;
  643. }
  644. dimensions.maxHeight = null;
  645. }
  646. if ($object.attr(_self._prefixAttributeName('max-height'))) {
  647. dimensions.maxHeight = $object.attr(_self._prefixAttributeName('max-height'));
  648. }
  649. _self._adjustDimensions($object, dimensions);
  650. },
  651. /**
  652. * Adjusts the dimensions
  653. *
  654. * @param {object} $object
  655. * @param {object} dimensions
  656. * @return {void}
  657. */
  658. _adjustDimensions: function ($object, dimensions) {
  659. // Adjust width and height
  660. $object.css({
  661. 'width': dimensions.objectWidth,
  662. 'height': dimensions.objectHeight,
  663. 'max-width': dimensions.maxWidth,
  664. 'max-height': dimensions.maxHeight
  665. });
  666. _self.objects.contentInner.css({
  667. 'width': $object.outerWidth(),
  668. 'height': $object.outerHeight(),
  669. 'max-width': '100%'
  670. });
  671. _self.objects.case.css({
  672. 'width': _self.objects.contentInner.outerWidth(),
  673. 'max-width': '100%'
  674. });
  675. // Adjust margin
  676. _self.objects.case.css({
  677. 'margin-top': parseInt(-(_self.objects.case.outerHeight() / 2)),
  678. 'margin-left': parseInt(-(_self.objects.case.outerWidth() / 2))
  679. });
  680. },
  681. /**
  682. * Handles the _loading
  683. *
  684. * @param {string} process
  685. * @return {void}
  686. */
  687. _loading: function (process) {
  688. if (process === 'start') {
  689. _self.objects.case.addClass(_self.settings.classPrefix + 'loading');
  690. _self.objects.loading.show();
  691. } else if (process === 'end') {
  692. _self.objects.case.removeClass(_self.settings.classPrefix + 'loading');
  693. _self.objects.loading.hide();
  694. }
  695. },
  696. /**
  697. * Gets the client screen dimensions
  698. *
  699. * @return {object} dimensions
  700. */
  701. getViewportDimensions: function () {
  702. return {
  703. windowWidth: $(window).innerWidth(),
  704. windowHeight: $(window).innerHeight()
  705. };
  706. },
  707. /**
  708. * Verifies the url
  709. *
  710. * @param {string} dataUrl
  711. * @return {object} dataUrl Clean url for processing content
  712. */
  713. _verifyDataUrl: function (dataUrl) {
  714. if (!dataUrl || dataUrl === undefined || dataUrl === '') {
  715. return false;
  716. }
  717. if (dataUrl.indexOf('#') > -1) {
  718. dataUrl = dataUrl.split('#');
  719. dataUrl = '#' + dataUrl[dataUrl.length - 1];
  720. }
  721. return _self._normalizeUrl(dataUrl.toString());
  722. },
  723. //
  724. /**
  725. * Tries to get the (file) suffix of an url
  726. *
  727. * @param {string} url
  728. * @return {string}
  729. */
  730. _getFileUrlSuffix: function (url) {
  731. var re = /(?:\.([^.]+))?$/;
  732. return re.exec(url.toLowerCase())[1];
  733. },
  734. /**
  735. * Verifies the data type of the content to load
  736. *
  737. * @param {string} url
  738. * @return {string|boolean} Array key if expression matched, else false
  739. */
  740. _verifyDataType: function (url) {
  741. var typeMapping = _self.settings.typeMapping;
  742. // Early abort if dataUrl couldn't be verified
  743. if (!url) {
  744. return false;
  745. }
  746. // Verify the dataType of url according to typeMapping which
  747. // has been defined in settings.
  748. for (var key in typeMapping) {
  749. if (typeMapping.hasOwnProperty(key)) {
  750. var suffixArr = typeMapping[key].split(',');
  751. for (var i = 0; i < suffixArr.length; i++) {
  752. var suffix = suffixArr[i].toLowerCase(),
  753. regexp = new RegExp('\.(' + suffix + ')$', 'i'),
  754. str = url.toLowerCase().split('?')[0].substr(-5);
  755. if (regexp.test(str) === true || (key === 'inline' && (url.indexOf(suffix) > -1))) {
  756. return key;
  757. }
  758. }
  759. }
  760. }
  761. // If no expression matched, return 'iframe'.
  762. return 'iframe';
  763. },
  764. /**
  765. * Extends html markup with the essential tags
  766. *
  767. * @return {void}
  768. */
  769. _addElements: function () {
  770. if (typeof _self.objects.case !== 'undefined' && $('#' + _self.objects.case.attr('id')).length) {
  771. return;
  772. }
  773. _self.settings.markup();
  774. },
  775. /**
  776. * Shows the loaded content
  777. *
  778. * @param {object} $object
  779. * @return {void}
  780. */
  781. _showContent: function ($object) {
  782. // Add data attribute with the object type
  783. _self.objects.document.attr(_self._prefixAttributeName('type'), _self.objectData.type);
  784. _self.cache.object = $object;
  785. // Call onBeforeShow hook functions
  786. _self._callHooks(_self.settings.onBeforeShow);
  787. if (_self.settings.breakBeforeShow) return;
  788. _self.show();
  789. },
  790. /**
  791. * Starts the 'inTransition'
  792. * @return {void}
  793. */
  794. _startInTransition: function () {
  795. switch (_self.transition.in()) {
  796. case 'scrollTop':
  797. case 'scrollRight':
  798. case 'scrollBottom':
  799. case 'scrollLeft':
  800. case 'scrollHorizontal':
  801. case 'scrollVertical':
  802. _self.transition.scroll(_self.objects.case, 'in', _self.settings.speedIn);
  803. _self.transition.fade(_self.objects.contentInner, 'in', _self.settings.speedIn);
  804. break;
  805. case 'elastic':
  806. if (_self.objects.case.css('opacity') < 1) {
  807. _self.transition.zoom(_self.objects.case, 'in', _self.settings.speedIn);
  808. _self.transition.fade(_self.objects.contentInner, 'in', _self.settings.speedIn);
  809. }
  810. case 'fade':
  811. case 'fadeInline':
  812. _self.transition.fade(_self.objects.case, 'in', _self.settings.speedIn);
  813. _self.transition.fade(_self.objects.contentInner, 'in', _self.settings.speedIn);
  814. break;
  815. default:
  816. _self.transition.fade(_self.objects.case, 'in', 0);
  817. break;
  818. }
  819. // End loading.
  820. _self._loading('end');
  821. _self.isBusy = false;
  822. // Set index of the first item opened
  823. if (!_self.cache.firstOpened) {
  824. _self.cache.firstOpened = _self.objectData.this;
  825. }
  826. // Fade in the info with delay
  827. _self.objects.info.hide();
  828. setTimeout(function () {
  829. _self.transition.fade(_self.objects.info, 'in', _self.settings.speedIn);
  830. }, _self.settings.speedIn);
  831. // Call onFinish hook functions
  832. _self._callHooks(_self.settings.onFinish);
  833. },
  834. /**
  835. * Processes the content to show
  836. *
  837. * @return {void}
  838. */
  839. _processContent: function () {
  840. _self.isBusy = true;
  841. // Fade out the info at first
  842. _self.transition.fade(_self.objects.info, 'out', 0);
  843. switch (_self.settings.transitionOut) {
  844. case 'scrollTop':
  845. case 'scrollRight':
  846. case 'scrollBottom':
  847. case 'scrollLeft':
  848. case 'scrollVertical':
  849. case 'scrollHorizontal':
  850. if (_self.objects.case.is(':hidden')) {
  851. _self.transition.fade(_self.objects.contentInner, 'out', 0);
  852. _self.transition.fade(_self.objects.case, 'out', 0, 0, function () {
  853. _self._loadContent();
  854. });
  855. } else {
  856. _self.transition.scroll(_self.objects.case, 'out', _self.settings.speedOut, function () {
  857. _self._loadContent();
  858. });
  859. }
  860. break;
  861. case 'fade':
  862. if (_self.objects.case.is(':hidden')) {
  863. _self.transition.fade(_self.objects.case, 'out', 0, 0, function () {
  864. _self._loadContent();
  865. });
  866. } else {
  867. _self.transition.fade(_self.objects.case, 'out', _self.settings.speedOut, 0, function () {
  868. _self._loadContent();
  869. });
  870. }
  871. break;
  872. case 'fadeInline':
  873. case 'elastic':
  874. if (_self.objects.case.is(':hidden')) {
  875. _self.transition.fade(_self.objects.case, 'out', 0, 0, function () {
  876. _self._loadContent();
  877. });
  878. } else {
  879. _self.transition.fade(_self.objects.contentInner, 'out', _self.settings.speedOut, 0, function () {
  880. _self._loadContent();
  881. });
  882. }
  883. break;
  884. default:
  885. _self.transition.fade(_self.objects.case, 'out', 0, 0, function () {
  886. _self._loadContent();
  887. });
  888. break;
  889. }
  890. },
  891. /**
  892. * Handles events for gallery buttons
  893. *
  894. * @return {void}
  895. */
  896. _handleEvents: function () {
  897. _self._unbindEvents();
  898. _self.objects.nav.children().not(_self.objects.close).hide();
  899. // If slideshow is enabled, show play/pause and start timeout.
  900. if (_self.isSlideshowEnabled()) {
  901. // Only start the timeout if slideshow autostart is enabled and slideshow is not pausing
  902. if (
  903. (_self.settings.slideshowAutoStart === true || _self.isSlideshowStarted) &&
  904. !_self.objects.nav.hasClass(_self.settings.classPrefix + 'paused')
  905. ) {
  906. _self._startTimeout();
  907. } else {
  908. _self._stopTimeout();
  909. }
  910. }
  911. if (_self.settings.liveResize) {
  912. _self._watchResizeInteraction();
  913. }
  914. _self.objects.close.click(function (event) {
  915. event.preventDefault();
  916. _self.close();
  917. });
  918. if (_self.settings.closeOnOverlayClick === true) {
  919. _self.objects.overlay.css('cursor', 'pointer').click(function (event) {
  920. event.preventDefault();
  921. _self.close();
  922. });
  923. }
  924. if (_self.settings.useKeys === true) {
  925. _self._addKeyEvents();
  926. }
  927. if (_self.objectData.isPartOfSequence) {
  928. _self.objects.nav.attr(_self._prefixAttributeName('ispartofsequence'), true);
  929. _self.objects.nav.data('items', _self._setNavigation());
  930. _self.objects.prev.click(function (event) {
  931. event.preventDefault();
  932. if (_self.settings.navigateEndless === true || !_self.item.isFirst()) {
  933. _self.objects.prev.unbind('click');
  934. _self.cache.action = 'prev';
  935. _self.objects.nav.data('items').prev.click();
  936. if (_self.isSlideshowEnabled()) {
  937. _self._stopTimeout();
  938. }
  939. }
  940. });
  941. _self.objects.next.click(function (event) {
  942. event.preventDefault();
  943. if (_self.settings.navigateEndless === true || !_self.item.isLast()) {
  944. _self.objects.next.unbind('click');
  945. _self.cache.action = 'next';
  946. _self.objects.nav.data('items').next.click();
  947. if (_self.isSlideshowEnabled()) {
  948. _self._stopTimeout();
  949. }
  950. }
  951. });
  952. if (_self.isSlideshowEnabled()) {
  953. _self.objects.play.click(function (event) {
  954. event.preventDefault();
  955. _self._startTimeout();
  956. });
  957. _self.objects.pause.click(function (event) {
  958. event.preventDefault();
  959. _self._stopTimeout();
  960. });
  961. }
  962. // Enable swiping if activated
  963. if (_self.settings.swipe === true) {
  964. if ($.isPlainObject($.event.special.swipeleft)) {
  965. _self.objects.case.on('swipeleft', function (event) {
  966. event.preventDefault();
  967. _self.objects.next.click();
  968. if (_self.isSlideshowEnabled()) {
  969. _self._stopTimeout();
  970. }
  971. });
  972. }
  973. if ($.isPlainObject($.event.special.swiperight)) {
  974. _self.objects.case.on('swiperight', function (event) {
  975. event.preventDefault();
  976. _self.objects.prev.click();
  977. if (_self.isSlideshowEnabled()) {
  978. _self._stopTimeout();
  979. }
  980. });
  981. }
  982. }
  983. }
  984. },
  985. /**
  986. * Adds the key events
  987. *
  988. * @return {void}
  989. */
  990. _addKeyEvents: function () {
  991. $(document).bind('keyup.lightcase', function (event) {
  992. // Do nothing if lightcase is in process
  993. if (_self.isBusy) {
  994. return;
  995. }
  996. switch (event.keyCode) {
  997. // Escape key
  998. case 27:
  999. _self.objects.close.click();
  1000. break;
  1001. // Backward key
  1002. case 37:
  1003. if (_self.objectData.isPartOfSequence) {
  1004. _self.objects.prev.click();
  1005. }
  1006. break;
  1007. // Forward key
  1008. case 39:
  1009. if (_self.objectData.isPartOfSequence) {
  1010. _self.objects.next.click();
  1011. }
  1012. break;
  1013. }
  1014. });
  1015. },
  1016. /**
  1017. * Starts the slideshow timeout
  1018. *
  1019. * @return {void}
  1020. */
  1021. _startTimeout: function () {
  1022. _self.isSlideshowStarted = true;
  1023. _self.objects.play.hide();
  1024. _self.objects.pause.show();
  1025. _self.cache.action = 'next';
  1026. _self.objects.nav.removeClass(_self.settings.classPrefix + 'paused');
  1027. _self.timeout = setTimeout(function () {
  1028. _self.objects.nav.data('items').next.click();
  1029. }, _self.settings.timeout);
  1030. },
  1031. /**
  1032. * Stops the slideshow timeout
  1033. *
  1034. * @return {void}
  1035. */
  1036. _stopTimeout: function () {
  1037. _self.objects.play.show();
  1038. _self.objects.pause.hide();
  1039. _self.objects.nav.addClass(_self.settings.classPrefix + 'paused');
  1040. clearTimeout(_self.timeout);
  1041. },
  1042. /**
  1043. * Sets the navigator buttons (prev/next)
  1044. *
  1045. * @return {object} items
  1046. */
  1047. _setNavigation: function () {
  1048. var $links = $((_self.cache.selector || _self.settings.attr)),
  1049. sequenceLength = _self.objectData.sequenceLength - 1,
  1050. items = {
  1051. prev: $links.eq(_self.objectData.prevIndex),
  1052. next: $links.eq(_self.objectData.nextIndex)
  1053. };
  1054. if (_self.objectData.currentIndex > 0) {
  1055. _self.objects.prev.show();
  1056. } else {
  1057. items.prevItem = $links.eq(sequenceLength);
  1058. }
  1059. if (_self.objectData.nextIndex <= sequenceLength) {
  1060. _self.objects.next.show();
  1061. } else {
  1062. items.next = $links.eq(0);
  1063. }
  1064. if (_self.settings.navigateEndless === true) {
  1065. _self.objects.prev.show();
  1066. _self.objects.next.show();
  1067. }
  1068. return items;
  1069. },
  1070. /**
  1071. * Item information/status
  1072. *
  1073. */
  1074. item: {
  1075. /**
  1076. * Verifies if the current item is first item.
  1077. *
  1078. * @return {boolean}
  1079. */
  1080. isFirst: function () {
  1081. return (_self.objectData.currentIndex === 0);
  1082. },
  1083. /**
  1084. * Verifies if the current item is first item opened.
  1085. *
  1086. * @return {boolean}
  1087. */
  1088. isFirstOpened: function () {
  1089. return _self.objectData.this.is(_self.cache.firstOpened);
  1090. },
  1091. /**
  1092. * Verifies if the current item is last item.
  1093. *
  1094. * @return {boolean}
  1095. */
  1096. isLast: function () {
  1097. return (_self.objectData.currentIndex === (_self.objectData.sequenceLength - 1));
  1098. }
  1099. },
  1100. /**
  1101. * Clones the object for inline elements
  1102. *
  1103. * @param {object} $object
  1104. * @return {object} $clone
  1105. */
  1106. _cloneObject: function ($object) {
  1107. var $clone = $object.clone(),
  1108. objectId = $object.attr('id');
  1109. // If element is hidden, cache the object and remove
  1110. if ($object.is(':hidden')) {
  1111. _self._cacheObjectData($object);
  1112. $object.attr('id', _self.settings.idPrefix + 'temp-' + objectId).empty();
  1113. } else {
  1114. // Prevent duplicated id's
  1115. $clone.removeAttr('id');
  1116. }
  1117. return $clone.show();
  1118. },
  1119. /**
  1120. * Verifies if it is a mobile device
  1121. *
  1122. * @return {boolean}
  1123. */
  1124. isMobileDevice: function () {
  1125. var deviceAgent = navigator.userAgent.toLowerCase(),
  1126. agentId = deviceAgent.match(_self.settings.mobileMatchExpression);
  1127. return agentId ? true : false;
  1128. },
  1129. /**
  1130. * Verifies if css transitions are supported
  1131. *
  1132. * @return {string|boolean} The transition prefix if supported, else false.
  1133. */
  1134. isTransitionSupported: function () {
  1135. var body = _self.objects.body.get(0),
  1136. isTransitionSupported = false,
  1137. transitionMapping = {
  1138. 'transition': '',
  1139. 'WebkitTransition': '-webkit-',
  1140. 'MozTransition': '-moz-',
  1141. 'OTransition': '-o-',
  1142. 'MsTransition': '-ms-'
  1143. };
  1144. for (var key in transitionMapping) {
  1145. if (transitionMapping.hasOwnProperty(key) && key in body.style) {
  1146. _self.support.transition = transitionMapping[key];
  1147. isTransitionSupported = true;
  1148. }
  1149. }
  1150. return isTransitionSupported;
  1151. },
  1152. /**
  1153. * Transition types
  1154. *
  1155. */
  1156. transition: {
  1157. /**
  1158. * Returns the correct transition type according to the status of interaction.
  1159. *
  1160. * @return {string} Transition type
  1161. */
  1162. in: function () {
  1163. if (_self.settings.transitionOpen && !_self.cache.firstOpened) {
  1164. return _self.settings.transitionOpen;
  1165. }
  1166. return _self.settings.transitionIn;
  1167. },
  1168. /**
  1169. * Fades in/out the object
  1170. *
  1171. * @param {object} $object
  1172. * @param {string} type
  1173. * @param {number} speed
  1174. * @param {number} opacity
  1175. * @param {function} callback
  1176. * @return {void} Animates an object
  1177. */
  1178. fade: function ($object, type, speed, opacity, callback) {
  1179. var isInTransition = type === 'in',
  1180. startTransition = {},
  1181. startOpacity = $object.css('opacity'),
  1182. endTransition = {},
  1183. endOpacity = opacity ? opacity: isInTransition ? 1 : 0;
  1184. if (!_self.isOpen && isInTransition) return;
  1185. startTransition['opacity'] = startOpacity;
  1186. endTransition['opacity'] = endOpacity;
  1187. $object.css(_self.support.transition + 'transition', 'none');
  1188. $object.css(startTransition).show();
  1189. // Css transition
  1190. if (_self.support.transitions) {
  1191. endTransition[_self.support.transition + 'transition'] = speed + 'ms ease';
  1192. setTimeout(function () {
  1193. $object.css(endTransition);
  1194. setTimeout(function () {
  1195. $object.css(_self.support.transition + 'transition', '');
  1196. if (callback && (_self.isOpen || !isInTransition)) {
  1197. callback();
  1198. }
  1199. }, speed);
  1200. }, 15);
  1201. } else {
  1202. // Fallback to js transition
  1203. $object.stop();
  1204. $object.animate(endTransition, speed, callback);
  1205. }
  1206. },
  1207. /**
  1208. * Scrolls in/out the object
  1209. *
  1210. * @param {object} $object
  1211. * @param {string} type
  1212. * @param {number} speed
  1213. * @param {function} callback
  1214. * @return {void} Animates an object
  1215. */
  1216. scroll: function ($object, type, speed, callback) {
  1217. var isInTransition = type === 'in',
  1218. transition = isInTransition ? _self.settings.transitionIn : _self.settings.transitionOut,
  1219. direction = 'left',
  1220. startTransition = {},
  1221. startOpacity = isInTransition ? 0 : 1,
  1222. startOffset = isInTransition ? '-50%' : '50%',
  1223. endTransition = {},
  1224. endOpacity = isInTransition ? 1 : 0,
  1225. endOffset = isInTransition ? '50%' : '-50%';
  1226. if (!_self.isOpen && isInTransition) return;
  1227. switch (transition) {
  1228. case 'scrollTop':
  1229. direction = 'top';
  1230. break;
  1231. case 'scrollRight':
  1232. startOffset = isInTransition ? '150%' : '50%';
  1233. endOffset = isInTransition ? '50%' : '150%';
  1234. break;
  1235. case 'scrollBottom':
  1236. direction = 'top';
  1237. startOffset = isInTransition ? '150%' : '50%';
  1238. endOffset = isInTransition ? '50%' : '150%';
  1239. break;
  1240. case 'scrollHorizontal':
  1241. startOffset = isInTransition ? '150%' : '50%';
  1242. endOffset = isInTransition ? '50%' : '-50%';
  1243. break;
  1244. case 'scrollVertical':
  1245. direction = 'top';
  1246. startOffset = isInTransition ? '-50%' : '50%';
  1247. endOffset = isInTransition ? '50%' : '150%';
  1248. break;
  1249. }
  1250. if (_self.cache.action === 'prev') {
  1251. switch (transition) {
  1252. case 'scrollHorizontal':
  1253. startOffset = isInTransition ? '-50%' : '50%';
  1254. endOffset = isInTransition ? '50%' : '150%';
  1255. break;
  1256. case 'scrollVertical':
  1257. startOffset = isInTransition ? '150%' : '50%';
  1258. endOffset = isInTransition ? '50%' : '-50%';
  1259. break;
  1260. }
  1261. }
  1262. startTransition['opacity'] = startOpacity;
  1263. startTransition[direction] = startOffset;
  1264. endTransition['opacity'] = endOpacity;
  1265. endTransition[direction] = endOffset;
  1266. $object.css(_self.support.transition + 'transition', 'none');
  1267. $object.css(startTransition).show();
  1268. // Css transition
  1269. if (_self.support.transitions) {
  1270. endTransition[_self.support.transition + 'transition'] = speed + 'ms ease';
  1271. setTimeout(function () {
  1272. $object.css(endTransition);
  1273. setTimeout(function () {
  1274. $object.css(_self.support.transition + 'transition', '');
  1275. if (callback && (_self.isOpen || !isInTransition)) {
  1276. callback();
  1277. }
  1278. }, speed);
  1279. }, 15);
  1280. } else {
  1281. // Fallback to js transition
  1282. $object.stop();
  1283. $object.animate(endTransition, speed, callback);
  1284. }
  1285. },
  1286. /**
  1287. * Zooms in/out the object
  1288. *
  1289. * @param {object} $object
  1290. * @param {string} type
  1291. * @param {number} speed
  1292. * @param {function} callback
  1293. * @return {void} Animates an object
  1294. */
  1295. zoom: function ($object, type, speed, callback) {
  1296. var isInTransition = type === 'in',
  1297. startTransition = {},
  1298. startOpacity = $object.css('opacity'),
  1299. startScale = isInTransition ? 'scale(0.75)' : 'scale(1)',
  1300. endTransition = {},
  1301. endOpacity = isInTransition ? 1 : 0,
  1302. endScale = isInTransition ? 'scale(1)' : 'scale(0.75)';
  1303. if (!_self.isOpen && isInTransition) return;
  1304. startTransition['opacity'] = startOpacity;
  1305. startTransition[_self.support.transition + 'transform'] = startScale;
  1306. endTransition['opacity'] = endOpacity;
  1307. $object.css(_self.support.transition + 'transition', 'none');
  1308. $object.css(startTransition).show();
  1309. // Css transition
  1310. if (_self.support.transitions) {
  1311. endTransition[_self.support.transition + 'transform'] = endScale;
  1312. endTransition[_self.support.transition + 'transition'] = speed + 'ms ease';
  1313. setTimeout(function () {
  1314. $object.css(endTransition);
  1315. setTimeout(function () {
  1316. $object.css(_self.support.transition + 'transform', '');
  1317. $object.css(_self.support.transition + 'transition', '');
  1318. if (callback && (_self.isOpen || !isInTransition)) {
  1319. callback();
  1320. }
  1321. }, speed);
  1322. }, 15);
  1323. } else {
  1324. // Fallback to js transition
  1325. $object.stop();
  1326. $object.animate(endTransition, speed, callback);
  1327. }
  1328. }
  1329. },
  1330. /**
  1331. * Calls all the registered functions of a specific hook
  1332. *
  1333. * @param {object} hooks
  1334. * @return {void}
  1335. */
  1336. _callHooks: function (hooks) {
  1337. if (typeof(hooks) === 'object') {
  1338. $.each(hooks, function(index, hook) {
  1339. if (typeof(hook) === 'function') {
  1340. hook.call(_self.origin);
  1341. }
  1342. });
  1343. }
  1344. },
  1345. /**
  1346. * Caches the object data
  1347. *
  1348. * @param {object} $object
  1349. * @return {void}
  1350. */
  1351. _cacheObjectData: function ($object) {
  1352. $.data($object, 'cache', {
  1353. id: $object.attr('id'),
  1354. content: $object.html()
  1355. });
  1356. _self.cache.originalObject = $object;
  1357. },
  1358. /**
  1359. * Restores the object from cache
  1360. *
  1361. * @return void
  1362. */
  1363. _restoreObject: function () {
  1364. var $object = $('[id^="' + _self.settings.idPrefix + 'temp-"]');
  1365. $object.attr('id', $.data(_self.cache.originalObject, 'cache').id);
  1366. $object.html($.data(_self.cache.originalObject, 'cache').content);
  1367. },
  1368. /**
  1369. * Executes functions for a window resize.
  1370. * It stops an eventual timeout and recalculates dimensions.
  1371. *
  1372. * @param {object} dimensions
  1373. * @return {void}
  1374. */
  1375. resize: function (event, dimensions) {
  1376. if (!_self.isOpen) return;
  1377. if (_self.isSlideshowEnabled()) {
  1378. _self._stopTimeout();
  1379. }
  1380. if (typeof dimensions === 'object' && dimensions !== null) {
  1381. if (dimensions.width) {
  1382. _self.cache.object.attr(
  1383. _self._prefixAttributeName('width'),
  1384. dimensions.width
  1385. );
  1386. }
  1387. if (dimensions.maxWidth) {
  1388. _self.cache.object.attr(
  1389. _self._prefixAttributeName('max-width'),
  1390. dimensions.maxWidth
  1391. );
  1392. }
  1393. if (dimensions.height) {
  1394. _self.cache.object.attr(
  1395. _self._prefixAttributeName('height'),
  1396. dimensions.height
  1397. );
  1398. }
  1399. if (dimensions.maxHeight) {
  1400. _self.cache.object.attr(
  1401. _self._prefixAttributeName('max-height'),
  1402. dimensions.maxHeight
  1403. );
  1404. }
  1405. }
  1406. _self.dimensions = _self.getViewportDimensions();
  1407. _self._calculateDimensions(_self.cache.object);
  1408. // Call onResize hook functions
  1409. _self._callHooks(_self.settings.onResize);
  1410. },
  1411. /**
  1412. * Watches for any resize interaction and caches the new sizes.
  1413. *
  1414. * @return {void}
  1415. */
  1416. _watchResizeInteraction: function () {
  1417. $(window).resize(_self.resize);
  1418. },
  1419. /**
  1420. * Stop watching any resize interaction related to _self.
  1421. *
  1422. * @return {void}
  1423. */
  1424. _unwatchResizeInteraction: function () {
  1425. $(window).off('resize', _self.resize);
  1426. },
  1427. /**
  1428. * Switches to the fullscreen mode
  1429. *
  1430. * @return {void}
  1431. */
  1432. _switchToFullScreenMode: function () {
  1433. _self.settings.shrinkFactor = 1;
  1434. _self.settings.overlayOpacity = 1;
  1435. $('html').addClass(_self.settings.classPrefix + 'fullScreenMode');
  1436. },
  1437. /**
  1438. * Enters into the lightcase view
  1439. *
  1440. * @return {void}
  1441. */
  1442. _open: function () {
  1443. _self.isOpen = true;
  1444. _self.support.transitions = _self.settings.cssTransitions ? _self.isTransitionSupported() : false;
  1445. _self.support.mobileDevice = _self.isMobileDevice();
  1446. if (_self.support.mobileDevice) {
  1447. $('html').addClass(_self.settings.classPrefix + 'isMobileDevice');
  1448. if (_self.settings.fullScreenModeForMobile) {
  1449. _self._switchToFullScreenMode();
  1450. }
  1451. }
  1452. if (!_self.settings.transitionIn) {
  1453. _self.settings.transitionIn = _self.settings.transition;
  1454. }
  1455. if (!_self.settings.transitionOut) {
  1456. _self.settings.transitionOut = _self.settings.transition;
  1457. }
  1458. switch (_self.transition.in()) {
  1459. case 'fade':
  1460. case 'fadeInline':
  1461. case 'elastic':
  1462. case 'scrollTop':
  1463. case 'scrollRight':
  1464. case 'scrollBottom':
  1465. case 'scrollLeft':
  1466. case 'scrollVertical':
  1467. case 'scrollHorizontal':
  1468. if (_self.objects.case.is(':hidden')) {
  1469. _self.objects.close.css('opacity', 0);
  1470. _self.objects.overlay.css('opacity', 0);
  1471. _self.objects.case.css('opacity', 0);
  1472. _self.objects.contentInner.css('opacity', 0);
  1473. }
  1474. _self.transition.fade(_self.objects.overlay, 'in', _self.settings.speedIn, _self.settings.overlayOpacity, function () {
  1475. _self.transition.fade(_self.objects.close, 'in', _self.settings.speedIn);
  1476. _self._handleEvents();
  1477. _self._processContent();
  1478. });
  1479. break;
  1480. default:
  1481. _self.transition.fade(_self.objects.overlay, 'in', 0, _self.settings.overlayOpacity, function () {
  1482. _self.transition.fade(_self.objects.close, 'in', 0);
  1483. _self._handleEvents();
  1484. _self._processContent();
  1485. });
  1486. break;
  1487. }
  1488. _self.objects.document.addClass(_self.settings.classPrefix + 'open');
  1489. _self.objects.case.attr('aria-hidden', 'false');
  1490. },
  1491. /**
  1492. * Shows the lightcase by starting the transition
  1493. */
  1494. show: function () {
  1495. // Call onCalculateDimensions hook functions
  1496. _self._callHooks(_self.settings.onBeforeCalculateDimensions);
  1497. _self._calculateDimensions(_self.cache.object);
  1498. // Call onAfterCalculateDimensions hook functions
  1499. _self._callHooks(_self.settings.onAfterCalculateDimensions);
  1500. _self._startInTransition();
  1501. },
  1502. /**
  1503. * Escapes from the lightcase view
  1504. *
  1505. * @return {void}
  1506. */
  1507. close: function () {
  1508. _self.isOpen = false;
  1509. if (_self.isSlideshowEnabled()) {
  1510. _self._stopTimeout();
  1511. _self.isSlideshowStarted = false;
  1512. _self.objects.nav.removeClass(_self.settings.classPrefix + 'paused');
  1513. }
  1514. _self.objects.loading.hide();
  1515. _self._unbindEvents();
  1516. _self._unwatchResizeInteraction();
  1517. $('html').removeClass(_self.settings.classPrefix + 'open');
  1518. _self.objects.case.attr('aria-hidden', 'true');
  1519. _self.objects.nav.children().hide();
  1520. _self.objects.close.hide();
  1521. // Call onClose hook functions
  1522. _self._callHooks(_self.settings.onClose);
  1523. // Fade out the info at first
  1524. _self.transition.fade(_self.objects.info, 'out', 0);
  1525. switch (_self.settings.transitionClose || _self.settings.transitionOut) {
  1526. case 'fade':
  1527. case 'fadeInline':
  1528. case 'scrollTop':
  1529. case 'scrollRight':
  1530. case 'scrollBottom':
  1531. case 'scrollLeft':
  1532. case 'scrollHorizontal':
  1533. case 'scrollVertical':
  1534. _self.transition.fade(_self.objects.case, 'out', _self.settings.speedOut, 0, function () {
  1535. _self.transition.fade(_self.objects.overlay, 'out', _self.settings.speedOut, 0, function () {
  1536. _self.cleanup();
  1537. });
  1538. });
  1539. break;
  1540. case 'elastic':
  1541. _self.transition.zoom(_self.objects.case, 'out', _self.settings.speedOut, function () {
  1542. _self.transition.fade(_self.objects.overlay, 'out', _self.settings.speedOut, 0, function () {
  1543. _self.cleanup();
  1544. });
  1545. });
  1546. break;
  1547. default:
  1548. _self.cleanup();
  1549. break;
  1550. }
  1551. },
  1552. /**
  1553. * Unbinds all given events
  1554. *
  1555. * @return {void}
  1556. */
  1557. _unbindEvents: function () {
  1558. // Unbind overlay event
  1559. _self.objects.overlay.unbind('click');
  1560. // Unbind key events
  1561. $(document).unbind('keyup.lightcase');
  1562. // Unbind swipe events
  1563. _self.objects.case.unbind('swipeleft').unbind('swiperight');
  1564. // Unbind navigator events
  1565. _self.objects.prev.unbind('click');
  1566. _self.objects.next.unbind('click');
  1567. _self.objects.play.unbind('click');
  1568. _self.objects.pause.unbind('click');
  1569. // Unbind close event
  1570. _self.objects.close.unbind('click');
  1571. },
  1572. /**
  1573. * Cleans up the dimensions
  1574. *
  1575. * @return {void}
  1576. */
  1577. _cleanupDimensions: function () {
  1578. var opacity = _self.objects.contentInner.css('opacity');
  1579. _self.objects.case.css({
  1580. 'width': '',
  1581. 'height': '',
  1582. 'top': '',
  1583. 'left': '',
  1584. 'margin-top': '',
  1585. 'margin-left': ''
  1586. });
  1587. _self.objects.contentInner.removeAttr('style').css('opacity', opacity);
  1588. _self.objects.contentInner.children().removeAttr('style');
  1589. },
  1590. /**
  1591. * Cleanup after aborting lightcase
  1592. *
  1593. * @return {void}
  1594. */
  1595. cleanup: function () {
  1596. _self._cleanupDimensions();
  1597. _self.objects.loading.hide();
  1598. _self.objects.overlay.hide();
  1599. _self.objects.case.hide();
  1600. _self.objects.prev.hide();
  1601. _self.objects.next.hide();
  1602. _self.objects.play.hide();
  1603. _self.objects.pause.hide();
  1604. _self.objects.document.removeAttr(_self._prefixAttributeName('type'));
  1605. _self.objects.nav.removeAttr(_self._prefixAttributeName('ispartofsequence'));
  1606. _self.objects.contentInner.empty().hide();
  1607. _self.objects.info.children().empty();
  1608. if (_self.cache.originalObject) {
  1609. _self._restoreObject();
  1610. }
  1611. // Call onCleanup hook functions
  1612. _self._callHooks(_self.settings.onCleanup);
  1613. // Restore cache
  1614. _self.cache = {};
  1615. },
  1616. /**
  1617. * Returns the supported match media or undefined if the browser
  1618. * doesn't support match media.
  1619. *
  1620. * @return {mixed}
  1621. */
  1622. _matchMedia: function () {
  1623. return window.matchMedia || window.msMatchMedia;
  1624. },
  1625. /**
  1626. * Returns the devicePixelRatio if supported. Else, it simply returns
  1627. * 1 as the default.
  1628. *
  1629. * @return {number}
  1630. */
  1631. _devicePixelRatio: function () {
  1632. return window.devicePixelRatio || 1;
  1633. },
  1634. /**
  1635. * Checks if method is public
  1636. *
  1637. * @return {boolean}
  1638. */
  1639. _isPublicMethod: function (method) {
  1640. return (typeof _self[method] === 'function' && method.charAt(0) !== '_');
  1641. },
  1642. /**
  1643. * Exports all public methods to be accessible, callable
  1644. * from global scope.
  1645. *
  1646. * @return {void}
  1647. */
  1648. _export: function () {
  1649. window.lightcase = {};
  1650. $.each(_self, function (property) {
  1651. if (_self._isPublicMethod(property)) {
  1652. lightcase[property] = _self[property];
  1653. }
  1654. });
  1655. }
  1656. };
  1657. _self._export();
  1658. $.fn.lightcase = function (method) {
  1659. // Method calling logic (only public methods are applied)
  1660. if (_self._isPublicMethod(method)) {
  1661. return _self[method].apply(this, Array.prototype.slice.call(arguments, 1));
  1662. } else if (typeof method === 'object' || !method) {
  1663. return _self.init.apply(this, arguments);
  1664. } else {
  1665. $.error('Method ' + method + ' does not exist on jQuery.lightcase');
  1666. }
  1667. };
  1668. })(jQuery);