ekko-lightbox.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661
  1. const Lightbox = (($) => {
  2. const NAME = 'ekkoLightbox'
  3. const JQUERY_NO_CONFLICT = $.fn[NAME]
  4. const Default = {
  5. title: '',
  6. footer: '',
  7. showArrows: true, //display the left / right arrows or not
  8. wrapping: true, //if true, gallery loops infinitely
  9. type: null, //force the lightbox into image / youtube mode. if null, or not image|youtube|vimeo; detect it
  10. alwaysShowClose: false, //always show the close button, even if there is no title
  11. loadingMessage: '<div class="ekko-lightbox-loader"><div><div></div><div></div></div></div>', // http://tobiasahlin.com/spinkit/
  12. leftArrow: '<span>&#10094;</span>',
  13. rightArrow: '<span>&#10095;</span>',
  14. strings: {
  15. close: 'Close',
  16. fail: 'Failed to load image:',
  17. type: 'Could not detect remote target type. Force the type using data-type',
  18. },
  19. doc: document, // if in an iframe can specify top.document
  20. onShow() {},
  21. onShown() {},
  22. onHide() {},
  23. onHidden() {},
  24. onNavigate() {},
  25. onContentLoaded() {}
  26. }
  27. class Lightbox {
  28. /**
  29. Class properties:
  30. _$element: null -> the <a> element currently being displayed
  31. _$modal: The bootstrap modal generated
  32. _$modalDialog: The .modal-dialog
  33. _$modalContent: The .modal-content
  34. _$modalBody: The .modal-body
  35. _$modalHeader: The .modal-header
  36. _$modalFooter: The .modal-footer
  37. _$lightboxContainerOne: Container of the first lightbox element
  38. _$lightboxContainerTwo: Container of the second lightbox element
  39. _$lightboxBody: First element in the container
  40. _$modalArrows: The overlayed arrows container
  41. _$galleryItems: Other <a>'s available for this gallery
  42. _galleryName: Name of the current data('gallery') showing
  43. _galleryIndex: The current index of the _$galleryItems being shown
  44. _config: {} the options for the modal
  45. _modalId: unique id for the current lightbox
  46. _padding / _border: CSS properties for the modal container; these are used to calculate the available space for the content
  47. */
  48. static get Default() {
  49. return Default
  50. }
  51. constructor($element, config) {
  52. this._config = $.extend({}, Default, config)
  53. this._$modalArrows = null
  54. this._galleryIndex = 0
  55. this._galleryName = null
  56. this._padding = null
  57. this._border = null
  58. this._titleIsShown = false
  59. this._footerIsShown = false
  60. this._wantedWidth = 0
  61. this._wantedHeight = 0
  62. this._touchstartX = 0
  63. this._touchendX = 0
  64. this._modalId = `ekkoLightbox-${Math.floor((Math.random() * 1000) + 1)}`;
  65. this._$element = $element instanceof jQuery ? $element : $($element)
  66. this._isBootstrap3 = $.fn.modal.Constructor.VERSION[0] == 3;
  67. let h4 = `<h4 class="modal-title">${this._config.title || "&nbsp;"}</h4>`;
  68. let btn = `<button type="button" class="close" data-dismiss="modal" aria-label="${this._config.strings.close}"><span aria-hidden="true">&times;</span></button>`;
  69. let header = `<div class="modal-header${this._config.title || this._config.alwaysShowClose ? '' : ' hide'}">`+(this._isBootstrap3 ? btn+h4 : h4+btn)+`</div>`;
  70. let footer = `<div class="modal-footer${this._config.footer ? '' : ' hide'}">${this._config.footer || "&nbsp;"}</div>`;
  71. let body = '<div class="modal-body"><div class="ekko-lightbox-container"><div class="ekko-lightbox-item fade in show"></div><div class="ekko-lightbox-item fade"></div></div></div>'
  72. let dialog = `<div class="modal-dialog" role="document"><div class="modal-content">${header}${body}${footer}</div></div>`
  73. $(this._config.doc.body).append(`<div id="${this._modalId}" class="ekko-lightbox modal fade" tabindex="-1" tabindex="-1" role="dialog" aria-hidden="true">${dialog}</div>`)
  74. this._$modal = $(`#${this._modalId}`, this._config.doc)
  75. this._$modalDialog = this._$modal.find('.modal-dialog').first()
  76. this._$modalContent = this._$modal.find('.modal-content').first()
  77. this._$modalBody = this._$modal.find('.modal-body').first()
  78. this._$modalHeader = this._$modal.find('.modal-header').first()
  79. this._$modalFooter = this._$modal.find('.modal-footer').first()
  80. this._$lightboxContainer = this._$modalBody.find('.ekko-lightbox-container').first()
  81. this._$lightboxBodyOne = this._$lightboxContainer.find('> div:first-child').first()
  82. this._$lightboxBodyTwo = this._$lightboxContainer.find('> div:last-child').first()
  83. this._border = this._calculateBorders()
  84. this._padding = this._calculatePadding()
  85. this._galleryName = this._$element.data('gallery')
  86. if (this._galleryName) {
  87. this._$galleryItems = $(document.body).find(`*[data-gallery="${this._galleryName}"]`)
  88. this._galleryIndex = this._$galleryItems.index(this._$element)
  89. $(document).on('keydown.ekkoLightbox', this._navigationalBinder.bind(this))
  90. // add the directional arrows to the modal
  91. if (this._config.showArrows && this._$galleryItems.length > 1) {
  92. this._$lightboxContainer.append(`<div class="ekko-lightbox-nav-overlay"><a href="#">${this._config.leftArrow}</a><a href="#">${this._config.rightArrow}</a></div>`)
  93. this._$modalArrows = this._$lightboxContainer.find('div.ekko-lightbox-nav-overlay').first()
  94. this._$lightboxContainer.on('click', 'a:first-child', event => {
  95. event.preventDefault()
  96. return this.navigateLeft()
  97. })
  98. this._$lightboxContainer.on('click', 'a:last-child', event => {
  99. event.preventDefault()
  100. return this.navigateRight()
  101. })
  102. this.updateNavigation()
  103. }
  104. }
  105. this._$modal
  106. .on('show.bs.modal', this._config.onShow.bind(this))
  107. .on('shown.bs.modal', () => {
  108. this._toggleLoading(true)
  109. this._handle()
  110. return this._config.onShown.call(this)
  111. })
  112. .on('hide.bs.modal', this._config.onHide.bind(this))
  113. .on('hidden.bs.modal', () => {
  114. if (this._galleryName) {
  115. $(document).off('keydown.ekkoLightbox')
  116. $(window).off('resize.ekkoLightbox')
  117. }
  118. this._$modal.remove()
  119. return this._config.onHidden.call(this)
  120. })
  121. .modal(this._config)
  122. $(window).on('resize.ekkoLightbox', () => {
  123. this._resize(this._wantedWidth, this._wantedHeight)
  124. })
  125. this._$lightboxContainer
  126. .on('touchstart', () => {
  127. this._touchstartX = event.changedTouches[0].screenX;
  128. })
  129. .on('touchend', () => {
  130. this._touchendX = event.changedTouches[0].screenX;
  131. this._swipeGesure();
  132. })
  133. }
  134. element() {
  135. return this._$element;
  136. }
  137. modal() {
  138. return this._$modal;
  139. }
  140. navigateTo(index) {
  141. if (index < 0 || index > this._$galleryItems.length-1)
  142. return this
  143. this._galleryIndex = index
  144. this.updateNavigation()
  145. this._$element = $(this._$galleryItems.get(this._galleryIndex))
  146. this._handle();
  147. }
  148. navigateLeft() {
  149. if(!this._$galleryItems)
  150. return;
  151. if (this._$galleryItems.length === 1)
  152. return
  153. if (this._galleryIndex === 0) {
  154. if (this._config.wrapping)
  155. this._galleryIndex = this._$galleryItems.length - 1
  156. else
  157. return
  158. }
  159. else //circular
  160. this._galleryIndex--
  161. this._config.onNavigate.call(this, 'left', this._galleryIndex)
  162. return this.navigateTo(this._galleryIndex)
  163. }
  164. navigateRight() {
  165. if(!this._$galleryItems)
  166. return;
  167. if (this._$galleryItems.length === 1)
  168. return
  169. if (this._galleryIndex === this._$galleryItems.length - 1) {
  170. if (this._config.wrapping)
  171. this._galleryIndex = 0
  172. else
  173. return
  174. }
  175. else //circular
  176. this._galleryIndex++
  177. this._config.onNavigate.call(this, 'right', this._galleryIndex)
  178. return this.navigateTo(this._galleryIndex)
  179. }
  180. updateNavigation() {
  181. if (!this._config.wrapping) {
  182. let $nav = this._$lightboxContainer.find('div.ekko-lightbox-nav-overlay')
  183. if (this._galleryIndex === 0)
  184. $nav.find('a:first-child').addClass('disabled')
  185. else
  186. $nav.find('a:first-child').removeClass('disabled')
  187. if (this._galleryIndex === this._$galleryItems.length - 1)
  188. $nav.find('a:last-child').addClass('disabled')
  189. else
  190. $nav.find('a:last-child').removeClass('disabled')
  191. }
  192. }
  193. close() {
  194. return this._$modal.modal('hide');
  195. }
  196. // helper private methods
  197. _navigationalBinder(event) {
  198. event = event || window.event;
  199. if (event.keyCode === 39)
  200. return this.navigateRight()
  201. if (event.keyCode === 37)
  202. return this.navigateLeft()
  203. }
  204. // type detection private methods
  205. _detectRemoteType(src, type) {
  206. type = type || false;
  207. if(!type && this._isImage(src))
  208. type = 'image';
  209. if(!type && this._getYoutubeId(src))
  210. type = 'youtube';
  211. if(!type && this._getVimeoId(src))
  212. type = 'vimeo';
  213. if(!type && this._getInstagramId(src))
  214. type = 'instagram';
  215. if(!type || ['image', 'youtube', 'vimeo', 'instagram', 'video', 'url'].indexOf(type) < 0)
  216. type = 'url';
  217. return type;
  218. }
  219. _isImage(string) {
  220. return string && string.match(/(^data:image\/.*,)|(\.(jp(e|g|eg)|gif|png|bmp|webp|svg)((\?|#).*)?$)/i)
  221. }
  222. _containerToUse() {
  223. // if currently showing an image, fade it out and remove
  224. let $toUse = this._$lightboxBodyTwo
  225. let $current = this._$lightboxBodyOne
  226. if(this._$lightboxBodyTwo.hasClass('in')) {
  227. $toUse = this._$lightboxBodyOne
  228. $current = this._$lightboxBodyTwo
  229. }
  230. $current.removeClass('in show')
  231. setTimeout(() => {
  232. if(!this._$lightboxBodyTwo.hasClass('in'))
  233. this._$lightboxBodyTwo.empty()
  234. if(!this._$lightboxBodyOne.hasClass('in'))
  235. this._$lightboxBodyOne.empty()
  236. }, 500)
  237. $toUse.addClass('in show')
  238. return $toUse
  239. }
  240. _handle() {
  241. let $toUse = this._containerToUse()
  242. this._updateTitleAndFooter()
  243. let currentRemote = this._$element.attr('data-remote') || this._$element.attr('href')
  244. let currentType = this._detectRemoteType(currentRemote, this._$element.attr('data-type') || false)
  245. if(['image', 'youtube', 'vimeo', 'instagram', 'video', 'url'].indexOf(currentType) < 0)
  246. return this._error(this._config.strings.type)
  247. switch(currentType) {
  248. case 'image':
  249. this._preloadImage(currentRemote, $toUse)
  250. this._preloadImageByIndex(this._galleryIndex, 3)
  251. break;
  252. case 'youtube':
  253. this._showYoutubeVideo(currentRemote, $toUse);
  254. break;
  255. case 'vimeo':
  256. this._showVimeoVideo(this._getVimeoId(currentRemote), $toUse);
  257. break;
  258. case 'instagram':
  259. this._showInstagramVideo(this._getInstagramId(currentRemote), $toUse);
  260. break;
  261. case 'video':
  262. this._showHtml5Video(currentRemote, $toUse);
  263. break;
  264. default: // url
  265. this._loadRemoteContent(currentRemote, $toUse);
  266. break;
  267. }
  268. return this;
  269. }
  270. _getYoutubeId(string) {
  271. if(!string)
  272. return false;
  273. let matches = string.match(/^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|\&v=)([^#\&\?]*).*/)
  274. return (matches && matches[2].length === 11) ? matches[2] : false
  275. }
  276. _getVimeoId(string) {
  277. return string && string.indexOf('vimeo') > 0 ? string : false
  278. }
  279. _getInstagramId(string) {
  280. return string && string.indexOf('instagram') > 0 ? string : false
  281. }
  282. // layout private methods
  283. _toggleLoading(show) {
  284. show = show || false
  285. if(show) {
  286. this._$modalDialog.css('display', 'none')
  287. this._$modal.removeClass('in show')
  288. $('.modal-backdrop').append(this._config.loadingMessage)
  289. }
  290. else {
  291. this._$modalDialog.css('display', 'block')
  292. this._$modal.addClass('in show')
  293. $('.modal-backdrop').find('.ekko-lightbox-loader').remove()
  294. }
  295. return this;
  296. }
  297. _calculateBorders() {
  298. return {
  299. top: this._totalCssByAttribute('border-top-width'),
  300. right: this._totalCssByAttribute('border-right-width'),
  301. bottom: this._totalCssByAttribute('border-bottom-width'),
  302. left: this._totalCssByAttribute('border-left-width'),
  303. }
  304. }
  305. _calculatePadding() {
  306. return {
  307. top: this._totalCssByAttribute('padding-top'),
  308. right: this._totalCssByAttribute('padding-right'),
  309. bottom: this._totalCssByAttribute('padding-bottom'),
  310. left: this._totalCssByAttribute('padding-left'),
  311. }
  312. }
  313. _totalCssByAttribute(attribute) {
  314. return parseInt(this._$modalDialog.css(attribute), 10) +
  315. parseInt(this._$modalContent.css(attribute), 10) +
  316. parseInt(this._$modalBody.css(attribute), 10)
  317. }
  318. _updateTitleAndFooter() {
  319. let title = this._$element.data('title') || ""
  320. let caption = this._$element.data('footer') || ""
  321. this._titleIsShown = false
  322. if (title || this._config.alwaysShowClose) {
  323. this._titleIsShown = true
  324. this._$modalHeader.css('display', '').find('.modal-title').html(title || "&nbsp;")
  325. }
  326. else
  327. this._$modalHeader.css('display', 'none')
  328. this._footerIsShown = false
  329. if (caption) {
  330. this._footerIsShown = true
  331. this._$modalFooter.css('display', '').html(caption)
  332. }
  333. else
  334. this._$modalFooter.css('display', 'none')
  335. return this;
  336. }
  337. _showYoutubeVideo(remote, $containerForElement) {
  338. let id = this._getYoutubeId(remote)
  339. let query = remote.indexOf('&') > 0 ? remote.substr(remote.indexOf('&')) : ''
  340. let width = this._$element.data('width') || 560
  341. let height = this._$element.data('height') || width / ( 560/315 )
  342. return this._showVideoIframe(
  343. `//www.youtube.com/embed/${id}?badge=0&autoplay=1&html5=1${query}`,
  344. width,
  345. height,
  346. $containerForElement
  347. );
  348. }
  349. _showVimeoVideo(id, $containerForElement) {
  350. let width = this._$element.data('width') || 500
  351. let height = this._$element.data('height') || width / ( 560/315 )
  352. return this._showVideoIframe(id + '?autoplay=1', width, height, $containerForElement)
  353. }
  354. _showInstagramVideo(id, $containerForElement) {
  355. // instagram load their content into iframe's so this can be put straight into the element
  356. let width = this._$element.data('width') || 612
  357. let height = width + 80;
  358. id = id.substr(-1) !== '/' ? id + '/' : id; // ensure id has trailing slash
  359. $containerForElement.html(`<iframe width="${width}" height="${height}" src="${id}embed/" frameborder="0" allowfullscreen></iframe>`);
  360. this._resize(width, height);
  361. this._config.onContentLoaded.call(this);
  362. if (this._$modalArrows) //hide the arrows when showing video
  363. this._$modalArrows.css('display', 'none');
  364. this._toggleLoading(false);
  365. return this;
  366. }
  367. _showVideoIframe(url, width, height, $containerForElement) { // should be used for videos only. for remote content use loadRemoteContent (data-type=url)
  368. height = height || width; // default to square
  369. $containerForElement.html(`<div class="embed-responsive embed-responsive-16by9"><iframe width="${width}" height="${height}" src="${url}" frameborder="0" allowfullscreen class="embed-responsive-item"></iframe></div>`);
  370. this._resize(width, height);
  371. this._config.onContentLoaded.call(this);
  372. if (this._$modalArrows)
  373. this._$modalArrows.css('display', 'none'); //hide the arrows when showing video
  374. this._toggleLoading(false);
  375. return this;
  376. }
  377. _showHtml5Video(url, $containerForElement) { // should be used for videos only. for remote content use loadRemoteContent (data-type=url)
  378. let width = this._$element.data('width') || 560
  379. let height = this._$element.data('height') || width / ( 560/315 )
  380. $containerForElement.html(`<div class="embed-responsive embed-responsive-16by9"><video width="${width}" height="${height}" src="${url}" preload="auto" autoplay controls class="embed-responsive-item"></video></div>`);
  381. this._resize(width, height);
  382. this._config.onContentLoaded.call(this);
  383. if (this._$modalArrows)
  384. this._$modalArrows.css('display', 'none'); //hide the arrows when showing video
  385. this._toggleLoading(false);
  386. return this;
  387. }
  388. _loadRemoteContent(url, $containerForElement) {
  389. let width = this._$element.data('width') || 560;
  390. let height = this._$element.data('height') || 560;
  391. let disableExternalCheck = this._$element.data('disableExternalCheck') || false;
  392. this._toggleLoading(false);
  393. // external urls are loading into an iframe
  394. // local ajax can be loaded into the container itself
  395. if (!disableExternalCheck && !this._isExternal(url)) {
  396. $containerForElement.load(url, $.proxy(() => {
  397. return this._$element.trigger('loaded.bs.modal');l
  398. }));
  399. } else {
  400. $containerForElement.html(`<iframe src="${url}" frameborder="0" allowfullscreen></iframe>`);
  401. this._config.onContentLoaded.call(this);
  402. }
  403. if (this._$modalArrows) //hide the arrows when remote content
  404. this._$modalArrows.css('display', 'none')
  405. this._resize(width, height);
  406. return this;
  407. }
  408. _isExternal(url) {
  409. let match = url.match(/^([^:\/?#]+:)?(?:\/\/([^\/?#]*))?([^?#]+)?(\?[^#]*)?(#.*)?/);
  410. if (typeof match[1] === "string" && match[1].length > 0 && match[1].toLowerCase() !== location.protocol)
  411. return true;
  412. if (typeof match[2] === "string" && match[2].length > 0 && match[2].replace(new RegExp(`:(${{
  413. "http:": 80,
  414. "https:": 443
  415. }[location.protocol]})?$`), "") !== location.host)
  416. return true;
  417. return false;
  418. }
  419. _error( message ) {
  420. console.error(message);
  421. this._containerToUse().html(message);
  422. this._resize(300, 300);
  423. return this;
  424. }
  425. _preloadImageByIndex(startIndex, numberOfTimes) {
  426. if(!this._$galleryItems)
  427. return;
  428. let next = $(this._$galleryItems.get(startIndex), false)
  429. if(typeof next == 'undefined')
  430. return
  431. let src = next.attr('data-remote') || next.attr('href')
  432. if (next.attr('data-type') === 'image' || this._isImage(src))
  433. this._preloadImage(src, false)
  434. if(numberOfTimes > 0)
  435. return this._preloadImageByIndex(startIndex + 1, numberOfTimes-1);
  436. }
  437. _preloadImage( src, $containerForImage) {
  438. $containerForImage = $containerForImage || false
  439. let img = new Image();
  440. if ($containerForImage) {
  441. // if loading takes > 200ms show a loader
  442. let loadingTimeout = setTimeout(() => {
  443. $containerForImage.append(this._config.loadingMessage)
  444. }, 200)
  445. img.onload = () => {
  446. if(loadingTimeout)
  447. clearTimeout(loadingTimeout)
  448. loadingTimeout = null;
  449. let image = $('<img />');
  450. image.attr('src', img.src);
  451. image.addClass('img-fluid');
  452. // backward compatibility for bootstrap v3
  453. image.css('width', '100%');
  454. $containerForImage.html(image);
  455. if (this._$modalArrows)
  456. this._$modalArrows.css('display', '') // remove display to default to css property
  457. this._resize(img.width, img.height);
  458. this._toggleLoading(false);
  459. return this._config.onContentLoaded.call(this);
  460. };
  461. img.onerror = () => {
  462. this._toggleLoading(false);
  463. return this._error(this._config.strings.fail+` ${src}`);
  464. };
  465. }
  466. img.src = src;
  467. return img;
  468. }
  469. _swipeGesure() {
  470. if (this._touchendX < this._touchstartX) {
  471. return this.navigateRight();
  472. }
  473. if (this._touchendX > this._touchstartX) {
  474. return this.navigateLeft();
  475. }
  476. }
  477. _resize( width, height ) {
  478. height = height || width
  479. this._wantedWidth = width
  480. this._wantedHeight = height
  481. // if width > the available space, scale down the expected width and height
  482. let widthBorderAndPadding = this._padding.left + this._padding.right + this._border.left + this._border.right
  483. let maxWidth = Math.min(width + widthBorderAndPadding, this._config.doc.body.clientWidth)
  484. if((width + widthBorderAndPadding) > maxWidth) {
  485. height = ((maxWidth - widthBorderAndPadding) / width) * height
  486. width = maxWidth
  487. } else
  488. width = (width + widthBorderAndPadding)
  489. let headerHeight = 0,
  490. footerHeight = 0
  491. // as the resize is performed the modal is show, the calculate might fail
  492. // if so, default to the default sizes
  493. if (this._footerIsShown)
  494. footerHeight = this._$modalFooter.outerHeight(true) || 55
  495. if (this._titleIsShown)
  496. headerHeight = this._$modalHeader.outerHeight(true) || 67
  497. let borderPadding = this._padding.top + this._padding.bottom + this._border.bottom + this._border.top
  498. //calculated each time as resizing the window can cause them to change due to Bootstraps fluid margins
  499. let margins = parseFloat(this._$modalDialog.css('margin-top')) + parseFloat(this._$modalDialog.css('margin-bottom'));
  500. let maxHeight = Math.min(height, $(window).height() - borderPadding - margins - headerHeight - footerHeight);
  501. if(height > maxHeight) {
  502. // if height > the available height, scale down the width
  503. let factor = Math.min(maxHeight / height, 1);
  504. width = Math.ceil(factor * width);
  505. }
  506. this._$lightboxContainer.css('height', maxHeight)
  507. this._$modalDialog.css('width', 'auto') .css('maxWidth', width);
  508. let modal = this._$modal.data('bs.modal');
  509. if (modal) {
  510. // v4 method is mistakenly protected
  511. try {
  512. modal._handleUpdate();
  513. } catch(Exception) {
  514. modal.handleUpdate();
  515. }
  516. }
  517. return this;
  518. }
  519. static _jQueryInterface(config) {
  520. config = config || {}
  521. return this.each(() => {
  522. let $this = $(this)
  523. let _config = $.extend(
  524. {},
  525. Lightbox.Default,
  526. $this.data(),
  527. typeof config === 'object' && config
  528. )
  529. new Lightbox(this, _config)
  530. })
  531. }
  532. }
  533. $.fn[NAME] = Lightbox._jQueryInterface
  534. $.fn[NAME].Constructor = Lightbox
  535. $.fn[NAME].noConflict = () => {
  536. $.fn[NAME] = JQUERY_NO_CONFLICT
  537. return Lightbox._jQueryInterface
  538. }
  539. return Lightbox
  540. })(jQuery)
  541. export default Lightbox