PageRenderTime 44ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/docs/assets/plugins/lightbox/ekko-lightbox.coffee

https://gitlab.com/lkotoula/syndromidocs
CoffeeScript | 353 lines | 264 code | 76 blank | 13 comment | 125 complexity | 4c7b90d26ca96268756a521531acf132 MD5 | raw file
  1. ###
  2. Lightbox for Bootstrap 3 by @ashleydw
  3. https://github.com/ashleydw/lightbox
  4. License: https://github.com/ashleydw/lightbox/blob/master/LICENSE
  5. ###
  6. "use strict";
  7. $ = jQuery
  8. EkkoLightbox = ( element, options ) ->
  9. @options = $.extend({
  10. title : null
  11. footer : null
  12. remote : null
  13. }, $.fn.ekkoLightbox.defaults, options || {})
  14. @$element = $(element)
  15. content = ''
  16. @modal_id = if @options.modal_id then @options.modal_id else 'ekkoLightbox-' + Math.floor((Math.random() * 1000) + 1)
  17. header = '<div class="modal-header"'+(if @options.title or @options.always_show_close then '' else ' style="display:none"')+'><button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button><h4 class="modal-title">' + (@options.title || "&nbsp;") + '</h4></div>'
  18. footer = '<div class="modal-footer"'+(if @options.footer then '' else ' style="display:none"')+'>' + @options.footer + '</div>'
  19. $(document.body).append '<div id="' + @modal_id + '" class="ekko-lightbox modal fade" tabindex="-1"><div class="modal-dialog"><div class="modal-content">' + header + '<div class="modal-body"><div class="ekko-lightbox-container"><div></div></div></div>' + footer + '</div></div></div>'
  20. @modal = $ '#' + @modal_id
  21. @modal_dialog = @modal.find('.modal-dialog').first()
  22. @modal_content = @modal.find('.modal-content').first()
  23. @modal_body = @modal.find('.modal-body').first()
  24. @lightbox_container = @modal_body.find('.ekko-lightbox-container').first()
  25. @lightbox_body = @lightbox_container.find('> div:first-child').first()
  26. @showLoading()
  27. @modal_arrows = null
  28. @border = {
  29. top: parseFloat(@modal_dialog.css('border-top-width')) + parseFloat(@modal_content.css('border-top-width')) + parseFloat(@modal_body.css('border-top-width'))
  30. right: parseFloat(@modal_dialog.css('border-right-width')) + parseFloat(@modal_content.css('border-right-width')) + parseFloat(@modal_body.css('border-right-width'))
  31. bottom: parseFloat(@modal_dialog.css('border-bottom-width')) + parseFloat(@modal_content.css('border-bottom-width')) + parseFloat(@modal_body.css('border-bottom-width'))
  32. left: parseFloat(@modal_dialog.css('border-left-width')) + parseFloat(@modal_content.css('border-left-width')) + parseFloat(@modal_body.css('border-left-width'))
  33. }
  34. @padding = {
  35. top: parseFloat(@modal_dialog.css('padding-top')) + parseFloat(@modal_content.css('padding-top')) + parseFloat(@modal_body.css('padding-top'))
  36. right: parseFloat(@modal_dialog.css('padding-right')) + parseFloat(@modal_content.css('padding-right')) + parseFloat(@modal_body.css('padding-right'))
  37. bottom: parseFloat(@modal_dialog.css('padding-bottom')) + parseFloat(@modal_content.css('padding-bottom')) + parseFloat(@modal_body.css('padding-bottom'))
  38. left: parseFloat(@modal_dialog.css('padding-left')) + parseFloat(@modal_content.css('padding-left')) + parseFloat(@modal_body.css('padding-left'))
  39. }
  40. @modal
  41. .on('show.bs.modal', @options.onShow.bind(@))
  42. .on 'shown.bs.modal', =>
  43. @modal_shown()
  44. @options.onShown.call(@)
  45. .on('hide.bs.modal', @options.onHide.bind(@))
  46. .on 'hidden.bs.modal', =>
  47. if @gallery
  48. $(document).off 'keydown.ekkoLightbox'
  49. @modal.remove()
  50. @options.onHidden.call(@)
  51. .modal 'show', options
  52. @modal
  53. EkkoLightbox.prototype = {
  54. modal_shown: ->
  55. # when the modal first loads
  56. if !@options.remote
  57. @error 'No remote target given'
  58. else
  59. @gallery = @$element.data('gallery')
  60. if @gallery
  61. # parents('document.body') fails for some reason, so do this manually
  62. if this.options.gallery_parent_selector == 'document.body' || this.options.gallery_parent_selector == ''
  63. @gallery_items = $(document.body).find('*[data-toggle="lightbox"][data-gallery="' + @gallery + '"]')
  64. else
  65. @gallery_items = @$element.parents(this.options.gallery_parent_selector).first().find('*[data-toggle="lightbox"][data-gallery="' + @gallery + '"]')
  66. @gallery_index = @gallery_items.index(@$element)
  67. $(document).on 'keydown.ekkoLightbox', @navigate.bind(@)
  68. # add the directional arrows to the modal
  69. if @options.directional_arrows && @gallery_items.length > 1
  70. @lightbox_container.append('<div class="ekko-lightbox-nav-overlay"><a href="#" class="'+@strip_stops(@options.left_arrow_class)+'"></a><a href="#" class="'+@strip_stops(@options.right_arrow_class)+'"></a></div>')
  71. @modal_arrows = @lightbox_container.find('div.ekko-lightbox-nav-overlay').first()
  72. @lightbox_container.find('a'+@strip_spaces(@options.left_arrow_class)).on 'click', (event) =>
  73. event.preventDefault()
  74. do @navigate_left
  75. @lightbox_container.find('a'+@strip_spaces(@options.right_arrow_class)).on 'click', (event) =>
  76. event.preventDefault()
  77. do @navigate_right
  78. if @options.type
  79. if @options.type == 'image'
  80. @preloadImage(@options.remote, true)
  81. else if @options.type == 'youtube' && video_id = @getYoutubeId(@options.remote)
  82. @showYoutubeVideo(video_id)
  83. else if @options.type == 'vimeo'
  84. @showVimeoVideo(@options.remote)
  85. else if @options.type == 'instagram'
  86. @showInstagramVideo(@options.remote);
  87. else if @options.type == 'url'
  88. @loadRemoteContent(@options.remote);
  89. else if @options.type == 'video'
  90. @showVideoIframe(@options.remote)
  91. else
  92. @error "Could not detect remote target type. Force the type using data-type=\"image|youtube|vimeo|instagram|url|video\""
  93. else
  94. @detectRemoteType(@options.remote)
  95. strip_stops: (str) ->
  96. str.replace(/\./g, '')
  97. strip_spaces: (str) ->
  98. str.replace(/\s/g, '')
  99. isImage: (str) ->
  100. str.match(/(^data:image\/.*,)|(\.(jp(e|g|eg)|gif|png|bmp|webp|svg)((\?|#).*)?$)/i)
  101. isSwf: (str) ->
  102. str.match(/\.(swf)((\?|#).*)?$/i)
  103. getYoutubeId: (str) ->
  104. match = str.match(/^.*(youtu.be\/|v\/|u\/\w\/|embed\/|watch\?v=|\&v=)([^#\&\?]*).*/);
  105. if match && match[2].length == 11 then match[2] else false
  106. getVimeoId: (str) ->
  107. if str.indexOf('vimeo') > 0 then str else false
  108. getInstagramId: (str) ->
  109. if str.indexOf('instagram') > 0 then str else false
  110. navigate : ( event ) ->
  111. event = event || window.event;
  112. if event.keyCode == 39 || event.keyCode == 37
  113. if event.keyCode == 39
  114. do @navigate_right
  115. else if event.keyCode == 37
  116. do @navigate_left
  117. navigateTo: (index) ->
  118. return @ if index < 0 or index > @gallery_items.length-1
  119. @showLoading()
  120. @gallery_index = index
  121. @$element = $(@gallery_items.get(@gallery_index))
  122. @updateTitleAndFooter()
  123. src = @$element.attr('data-remote') || @$element.attr('href')
  124. @detectRemoteType(src, @$element.attr('data-type') || false)
  125. if @gallery_index + 1 < @gallery_items.length
  126. next = $(@gallery_items.get(@gallery_index + 1), false)
  127. src = next.attr('data-remote') || next.attr('href')
  128. if next.attr('data-type') == 'image' || @isImage(src)
  129. @preloadImage(src, false)
  130. navigate_left: ->
  131. if @gallery_items.length == 1 then return
  132. if @gallery_index == 0 then @gallery_index = @gallery_items.length-1 else @gallery_index-- #circular
  133. @options.onNavigate.call(@, 'left', @gallery_index)
  134. @navigateTo(@gallery_index)
  135. navigate_right: ->
  136. if @gallery_items.length == 1 then return
  137. if @gallery_index == @gallery_items.length-1 then @gallery_index = 0 else @gallery_index++ #circular
  138. @options.onNavigate.call(@, 'right', @gallery_index)
  139. @navigateTo(@gallery_index)
  140. detectRemoteType: (src, type) ->
  141. type = type || false
  142. if type == 'image' || @isImage(src)
  143. @options.type = 'image'
  144. @preloadImage(src, true)
  145. else if type == 'youtube' || video_id = @getYoutubeId(src)
  146. @options.type = 'youtube'
  147. @showYoutubeVideo(video_id)
  148. else if type == 'vimeo' || video_id = @getVimeoId(src)
  149. @options.type = 'vimeo'
  150. @showVimeoVideo(video_id)
  151. else if type == 'instagram' || video_id = @getInstagramId(src)
  152. @options.type = 'instagram'
  153. @showInstagramVideo(video_id)
  154. else if type == 'video'
  155. @options.type = 'video'
  156. @showVideoIframe(video_id)
  157. else
  158. @options.type = 'url'
  159. @loadRemoteContent(src)
  160. updateTitleAndFooter: ->
  161. header = @modal_content.find('.modal-header')
  162. footer = @modal_content.find('.modal-footer')
  163. title = @$element.data('title') || ""
  164. caption = @$element.data('footer') || ""
  165. if title or @options.always_show_close then header.css('display', '').find('.modal-title').html(title || "&nbsp;") else header.css('display', 'none')
  166. if caption then footer.css('display', '').html(caption) else footer.css('display', 'none')
  167. @
  168. showLoading : ->
  169. @lightbox_body.html '<div class="modal-loading">'+@options.loadingMessage+'</div>'
  170. @
  171. showYoutubeVideo : (id) ->
  172. width = @checkDimensions( @$element.data('width') || 560 )
  173. height = width / ( 560/315 ) # aspect ratio
  174. @showVideoIframe('//www.youtube.com/embed/' + id + '?badge=0&autoplay=1&html5=1', width, height)
  175. showVimeoVideo : (id) ->
  176. width = @checkDimensions( @$element.data('width') || 560 )
  177. height = width / ( 500/281 ) # aspect ratio
  178. @showVideoIframe(id + '?autoplay=1', width, height)
  179. showInstagramVideo : (id) ->
  180. # instagram load their content into iframe's so this can be put straight into the element
  181. width = @checkDimensions @$element.data('width') || 612
  182. @resize width
  183. height = width + 80
  184. @lightbox_body.html '<iframe width="'+width+'" height="'+height+'" src="' + @addTrailingSlash(id) + 'embed/" frameborder="0" allowfullscreen></iframe>'
  185. @options.onContentLoaded.call(@)
  186. @modal_arrows.css 'display', 'none' if @modal_arrows #hide the arrows when showing video
  187. showVideoIframe: (url, width, height) -> # should be used for videos only. for remote content use loadRemoteContent (data-type=url)
  188. height = height || width # default to square
  189. @resize width
  190. @lightbox_body.html '<div class="embed-responsive embed-responsive-16by9"><iframe width="' + width + '" height="' + height + '" src="' + url + '" frameborder="0" allowfullscreen class="embed-responsive-item"></iframe></div>'
  191. @options.onContentLoaded.call(@)
  192. @modal_arrows.css 'display', 'none' if @modal_arrows #hide the arrows when showing video
  193. @
  194. loadRemoteContent : (url) ->
  195. width = @$element.data('width') || 560
  196. @resize width
  197. disableExternalCheck = @$element.data('disableExternalCheck') || false
  198. # external urls are loading into an iframe
  199. if !disableExternalCheck && !@isExternal(url)
  200. @lightbox_body.load url, $.proxy =>
  201. @$element.trigger('loaded.bs.modal')
  202. else
  203. @lightbox_body.html '<iframe width="'+width+'" height="'+width+'" src="' + url + '" frameborder="0" allowfullscreen></iframe>'
  204. @options.onContentLoaded.call(@)
  205. @modal_arrows.css 'display', 'none' if @modal_arrows #hide the arrows when remote content
  206. @
  207. isExternal : (url) ->
  208. match = url.match(/^([^:\/?#]+:)?(?:\/\/([^\/?#]*))?([^?#]+)?(\?[^#]*)?(#.*)?/);
  209. return true if typeof match[1] == "string" && match[1].length > 0 && match[1].toLowerCase() != location.protocol
  210. return true if typeof match[2] == "string" && match[2].length > 0 && match[2].replace(new RegExp(":("+{"http:":80,"https:":443}[location.protocol]+")?$"), "") != location.host
  211. false
  212. error : ( message ) ->
  213. @lightbox_body.html message
  214. @
  215. preloadImage : ( src, onLoadShowImage) ->
  216. img = new Image()
  217. if !onLoadShowImage? || onLoadShowImage == true
  218. img.onload = =>
  219. image = $('<img />')
  220. image.attr('src', img.src)
  221. image.addClass('img-responsive')
  222. @lightbox_body.html image
  223. @modal_arrows.css 'display', 'block' if @modal_arrows
  224. image.load =>
  225. @resize img.width
  226. @options.onContentLoaded.call(@)
  227. img.onerror = =>
  228. @error 'Failed to load image: ' + src
  229. img.src = src
  230. img
  231. resize : ( width ) ->
  232. #resize the dialog based on the width given, and adjust the directional arrow padding
  233. width_total = width + @border.left + @padding.left + @padding.right + @border.right
  234. @modal_dialog.css('width', 'auto') .css('max-width', width_total);
  235. @lightbox_container.find('a').css 'line-height', ->
  236. $(@).parent().height() + 'px'
  237. @
  238. checkDimensions: (width) ->
  239. #check that the width given can be displayed, if not return the maximum size that can be
  240. width_total = width + @border.left + @padding.left + @padding.right + @border.right
  241. body_width = document.body.clientWidth
  242. if width_total > body_width
  243. width = @modal_body.width()
  244. width
  245. close : ->
  246. @modal.modal('hide');
  247. addTrailingSlash: (url) ->
  248. if url.substr(-1) != '/'
  249. url += '/'
  250. url
  251. }
  252. $.fn.ekkoLightbox = ( options ) ->
  253. @each ->
  254. $this = $(this)
  255. options = $.extend({
  256. remote : $this.attr('data-remote') || $this.attr('href')
  257. gallery_parent_selector : $this.attr('data-parent')
  258. type : $this.attr('data-type')
  259. }, options, $this.data())
  260. new EkkoLightbox(@, options)
  261. @
  262. $.fn.ekkoLightbox.defaults = {
  263. gallery_parent_selector: 'document.body'
  264. left_arrow_class: '.glyphicon .glyphicon-chevron-left' #include class . here - they are stripped out later
  265. right_arrow_class: '.glyphicon .glyphicon-chevron-right' #include class . here - they are stripped out later
  266. directional_arrows: true #display the left / right arrows or not
  267. type: null #force the lightbox into image / youtube mode. if null, or not image|youtube|vimeo; detect it
  268. always_show_close: true #always show the close button, even if there is no title
  269. loadingMessage: 'Loading...',
  270. onShow : ->
  271. onShown : ->
  272. onHide : ->
  273. onHidden : ->
  274. onNavigate : ->
  275. onContentLoaded : ->
  276. }