app.coffee 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. # App's main client script
  2. $ = require('jquery')
  3. moment = require('lib/moment')
  4. require('lib/fastclick')
  5. handlebars = require('handlebars')
  6. settings = require('settings/root')
  7. exports.initialize = (config) ->
  8. # Use the fastclick module for touch devices.
  9. # Add a class of `needsclick` of the original click
  10. # is needed.
  11. new FastClick(document.body)
  12. setupNavMenus()
  13. setupSmoothScrolling()
  14. setupTimestampFormatting()
  15. setupClickTracking()
  16. setupNavMenus = ->
  17. $mainNav = $('.main-nav')
  18. $mainNavIcon = $mainNav.find('> .icon')
  19. $mainNavList = $mainNav.find('> ul')
  20. $mainNavSearchInput = $mainNav.find('.search-box input')
  21. $searchResultsView = $('.search-results-view')
  22. $searchResultsInput = $searchResultsView.find('input')
  23. $searchResultsList = $searchResultsView.find('.list')
  24. $searchResultsCloseButton = $searchResultsView.find('.close-button')
  25. searchResults = {}
  26. $tocNav = $('.toc-nav')
  27. $tocNavIcon = $tocNav.find('> .icon')
  28. $tocNavList = $tocNav.find('> ul')
  29. $collectionNav = $('.collection-nav')
  30. $collectionNavIcon = $collectionNav.find('> .icon')
  31. $collectionNavList = $collectionNav.find('> ul')
  32. collectionDocs = []
  33. $articleView = $('.container > article.view')
  34. hidePopups = (exceptMe) ->
  35. unless $mainNavList is exceptMe
  36. $mainNavList.hide()
  37. $mainNavIcon.removeClass('open')
  38. unless $tocNavList is exceptMe
  39. $tocNavList.hide()
  40. $tocNavIcon.removeClass('open')
  41. unless $collectionNavList is exceptMe
  42. $collectionNavList.hide()
  43. $collectionNavIcon.removeClass('open')
  44. $('html').on 'click', (e) ->
  45. hidePopups()
  46. # Setup the Main menu
  47. $mainNavIcon.on 'click', (e) ->
  48. e.stopPropagation()
  49. hidePopups($mainNavList)
  50. $mainNavList.toggle()
  51. $mainNavIcon.toggleClass('open')
  52. _gaq?.push(['_trackEvent', 'Site Navigation', 'Click', 'Main Nav Icon'])
  53. $mainNavSearchInput.on 'click', (e) ->
  54. e.stopPropagation()
  55. _gaq?.push(['_trackEvent', 'Site Navigation', 'Click', 'Search Box'])
  56. $searchResultsCloseButton.on 'click', (e) ->
  57. # Close and reset search input fields
  58. $searchResultsView.hide()
  59. $searchResultsInput.val('')
  60. $mainNavSearchInput.val('')
  61. $searchResultsList.html('')
  62. performSearch = (e) ->
  63. e.stopPropagation()
  64. # Show search results on Enter key
  65. if e.which is 13
  66. $inputField = $(e.currentTarget)
  67. term = $.trim $inputField.val()
  68. if term
  69. hidePopups()
  70. $mainNavSearchInput.val(term)
  71. $searchResultsInput.val(term)
  72. document.activeElement.blur() # to hide mobile keyboard
  73. $searchResultsView.show()
  74. $searchResultsList.html('<li>Looking, please wait...</li>')
  75. site = $inputField.attr('data-site')
  76. query = encodeURIComponent("site:#{site} AND (#{term})")
  77. $.ajax
  78. type: 'GET'
  79. url: "/json/search?q=#{query}"
  80. contentType: 'json'
  81. success: (data) ->
  82. if data
  83. data = JSON.parse(data)
  84. $searchResultsList.html("<li><strong>#{data.total_rows}</strong> matches found.</li>")
  85. for row in data.rows
  86. if row.fields.slug
  87. $item = $("<li><h3><a href=\"/#{row.fields.type}/#{row.fields.slug}\"><i class=\"icon icon-#{row.fields.type}\"></i>#{row.fields.title}</a></h3></li>")
  88. else
  89. $item = $("<li><h3><a href=\"/\"><i class=\"icon icon-essay\"></i>Home</a></h3></li>")
  90. $searchResultsList.append($item)
  91. else
  92. $searchResultsList.html("<li>Server didn\'t respond with any data.</li>")
  93. $mainNavSearchInput.on 'keydown', performSearch
  94. $searchResultsInput.on 'keydown', performSearch
  95. # Setup the TOC menu
  96. if $tocNav and $articleView
  97. $articleView.find('h3').each ->
  98. heading = $(@)
  99. text = heading.text()
  100. headingId = 'TOC-' + text.replace(/[\ \_]/g, '-').replace(/[\'\"\.\?\#\:\,\;\@\=]/g, '')
  101. heading.attr('id', headingId)
  102. $tocNavList.append "<li><a href=\"##{headingId}\">#{text}</a></li>"
  103. # Decide if we should show the TOC
  104. if $tocNavList.children().length > 2
  105. $tocNav.show()
  106. $collectionNav.addClass('third')
  107. $tocNavIcon.on 'click', (e) ->
  108. e.stopPropagation()
  109. hidePopups($tocNavList)
  110. $tocNavList.toggle()
  111. $tocNavIcon.toggleClass('open')
  112. _gaq?.push(['_trackEvent', 'Site Navigation', 'Click', 'Document Nav Icon'])
  113. # Setup the Collection menu
  114. if $collectionNav
  115. collectionId = $collectionNav.attr('data-id')
  116. collectionSlug = $collectionNav.attr('data-slug')
  117. if collectionSlug
  118. # Grab the docs for the collection from DB
  119. $collectionNav.show()
  120. $.ajax
  121. type: 'GET'
  122. url: "/json/collection/#{collectionSlug}"
  123. contentType: 'json'
  124. success: (data) ->
  125. if data
  126. data = JSON.parse(data)
  127. collectionObject = null
  128. collectionSponsor = null
  129. for row in data.rows
  130. doc = row.doc
  131. collectionDocs.push(doc) if doc.type in settings.app.content_types
  132. collectionObject ?= doc if doc.type is 'collection'
  133. collectionSponsor ?= doc if doc.type is 'sponsor'
  134. # Loop through docs within collection
  135. for doc in collectionDocs
  136. url = "/#{doc.type}/#{doc.slug}"
  137. selectedClass = if window.location.pathname is url then 'active' else ''
  138. $collectionNavList.append "<li><a href=\"#{url}\" class=\"#{selectedClass}\" data-id=\"#{doc._id}\">#{doc.title}</a></li>"
  139. setupCollectionDocsNav(collectionDocs, $collectionNavList)
  140. # Setup collcetion sponsor inheritance if needed
  141. $loadSponsorSection = $('div[data-action="load-sponsor"]')
  142. if collectionSponsor and $loadSponsorSection
  143. collectionSponsor.text_format = collectionSponsor.format is 'text'
  144. collectionSponsor.image_format = collectionSponsor.format is 'image'
  145. collectionSponsor.video_format = collectionSponsor.format is 'video'
  146. collectionSponsor.embed_format = collectionSponsor.format is 'embed'
  147. collectionSponsor.for_type = collectionObject?.type
  148. collectionSponsor.for_type_tc = collectionObject?.type_tc
  149. $html = $(handlebars.templates['partials/sponsor.html']({sponsor: collectionSponsor}, {}))
  150. $html.insertAfter($loadSponsorSection)
  151. if collectionSponsor.include_default_ad_unit
  152. $loadSponsorSection.prependTo($html).removeClass('hide')
  153. else
  154. $loadSponsorSection.remove()
  155. else
  156. # Remove the doc nav prev and next arrows
  157. $('.doc-nav').remove()
  158. $collectionNavIcon.on 'click', (e) ->
  159. e.stopPropagation()
  160. hidePopups($collectionNavList)
  161. $collectionNavList.toggle()
  162. $collectionNavIcon.toggleClass('open')
  163. _gaq?.push(['_trackEvent', 'Site Navigation', 'Click', 'Collection Nav Icon'])
  164. setupCollectionDocsNav = (docs, $collectionNavList) ->
  165. $docNav = $('.doc-nav')
  166. if $docNav
  167. $docNavPrev = $docNav.find('> .prev')
  168. $docNavNext = $docNav.find('> .next')
  169. $activeLink = $collectionNavList.find('.active')
  170. if $activeLink
  171. prev = -> $activeLink.parent().prev(':not(.heading)')?.find('a')[0]?.click()
  172. next = -> $activeLink.parent().next(':not(.heading)')?.find('a')[0]?.click()
  173. $docNavPrev.on 'click', ->
  174. if prev()
  175. $docNav.children().removeClass('disabled')
  176. else
  177. $docNavPrev.addClass('disabled')
  178. _gaq?.push(['_trackEvent', 'Site Navigation', 'Click', 'Doc Nav Prev'])
  179. $docNavNext.on 'click', ->
  180. if next()
  181. $docNav.children().removeClass('disabled')
  182. else
  183. $docNavNext.addClass('disabled')
  184. _gaq?.push(['_trackEvent', 'Site Navigation', 'Click', 'Doc Nav Next'])
  185. $(document).on 'keydown', (e) ->
  186. if e.which is 37
  187. $docNavPrev.click()
  188. else if e.which is 39
  189. $docNavNext.click()
  190. setupSmoothScrolling = ->
  191. smoothScroll = (hash) ->
  192. $target = $(hash)
  193. $target = $target.length and $target or $('[name=' + hash.slice(1) +']')
  194. if $target.length
  195. adjustment = 15
  196. targetOffset = $target.offset().top - adjustment
  197. $('body').animate({scrollTop: targetOffset}, 400)
  198. return true
  199. # Add some smooth scrolling to anchor links
  200. $('body').on 'click', 'a[href*="#"]', (e) ->
  201. if location.pathname.replace(/^\//,'') is @pathname.replace(/^\//,'') and location.hostname is @hostname
  202. e.preventDefault()
  203. smoothScroll(@hash)
  204. # In case this is a distination to a specific page anchor, let's smooth scroll
  205. smoothScroll(location.hash) if location.hash.length
  206. setupTimestampFormatting = ->
  207. # Convert UTC dates to local time
  208. $('.timestamp').each ->
  209. timestamp = $(@).text()
  210. $(@).html(moment(timestamp).local().format('MMM D, YYYY h:mm A'))
  211. setupClickTracking = ->
  212. # Track some analytic events
  213. $('body').on 'click', '[data-track-click]', (e) ->
  214. label = $(@).attr('data-track-click');
  215. _gaq?.push(['_trackEvent', 'Tracked Items', 'Click', label])
  216. return true