entry_page.coffee 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. class app.views.EntryPage extends app.View
  2. @className: '_page'
  3. @errorClass: '_page-error'
  4. @events:
  5. click: 'onClick'
  6. @shortcuts:
  7. altO: 'onAltO'
  8. @routes:
  9. before: 'beforeRoute'
  10. init: ->
  11. @cacheMap = {}
  12. @cacheStack = []
  13. return
  14. deactivate: ->
  15. if super
  16. @empty()
  17. @entry = null
  18. return
  19. loading: ->
  20. @empty()
  21. @trigger 'loading'
  22. return
  23. render: (content = '', fromCache = false) ->
  24. return unless @activated
  25. @empty()
  26. @subview = new (@subViewClass()) @el, @entry
  27. $.batchUpdate @el, =>
  28. @subview.render(content, fromCache)
  29. @addCopyButtons() unless fromCache
  30. return
  31. if app.disabledDocs.findBy 'slug', @entry.doc.slug
  32. @hiddenView = new app.views.HiddenPage @el, @entry
  33. setFaviconForDoc(@entry.doc)
  34. @delay @polyfillMathML
  35. @trigger 'loaded'
  36. return
  37. addCopyButtons: ->
  38. unless @copyButton
  39. @copyButton = document.createElement('button')
  40. @copyButton.innerHTML = '<svg><use xlink:href="#icon-copy"/></svg>'
  41. @copyButton.type = 'button'
  42. @copyButton.className = '_pre-clip'
  43. @copyButton.title = 'Copy to clipboard'
  44. @copyButton.setAttribute 'aria-label', 'Copy to clipboard'
  45. el.appendChild @copyButton.cloneNode(true) for el in @findAllByTag('pre')
  46. return
  47. polyfillMathML: ->
  48. return unless window.supportsMathML is false and !@polyfilledMathML and @findByTag('math')
  49. @polyfilledMathML = true
  50. $.append document.head, """<link rel="stylesheet" href="#{app.config.mathml_stylesheet}">"""
  51. return
  52. LINKS =
  53. home: 'Homepage'
  54. code: 'Source code'
  55. prepareContent: (content) ->
  56. return content unless @entry.isIndex() and @entry.doc.links
  57. links = for link, url of @entry.doc.links
  58. """<a href="#{url}" class="_links-link">#{LINKS[link]}</a>"""
  59. """<p class="_links">#{links.join('')}</p>#{content}"""
  60. empty: ->
  61. @subview?.deactivate()
  62. @subview = null
  63. @hiddenView?.deactivate()
  64. @hiddenView = null
  65. @resetClass()
  66. super
  67. return
  68. subViewClass: ->
  69. app.views["#{$.classify(@entry.doc.type)}Page"] or app.views.BasePage
  70. getTitle: ->
  71. @entry.doc.fullName + if @entry.isIndex() then ' documentation' else " / #{@entry.name}"
  72. beforeRoute: =>
  73. @cache()
  74. @abort()
  75. return
  76. onRoute: (context) ->
  77. isSameFile = context.entry.filePath() is @entry?.filePath()
  78. @entry = context.entry
  79. @restore() or @load() unless isSameFile
  80. return
  81. load: ->
  82. @loading()
  83. @xhr = @entry.loadFile @onSuccess, @onError
  84. return
  85. abort: ->
  86. if @xhr
  87. @xhr.abort()
  88. @xhr = @entry = null
  89. return
  90. onSuccess: (response) =>
  91. return unless @activated
  92. @xhr = null
  93. @render @prepareContent(response)
  94. return
  95. onError: =>
  96. @xhr = null
  97. @render @tmpl('pageLoadError')
  98. @resetClass()
  99. @addClass @constructor.errorClass
  100. app.serviceWorker?.update()
  101. return
  102. cache: ->
  103. return if @xhr or not @entry or @cacheMap[path = @entry.filePath()]
  104. @cacheMap[path] = @el.innerHTML
  105. @cacheStack.push(path)
  106. while @cacheStack.length > app.config.history_cache_size
  107. delete @cacheMap[@cacheStack.shift()]
  108. return
  109. restore: ->
  110. if @cacheMap[path = @entry.filePath()]
  111. @render @cacheMap[path], true
  112. true
  113. onClick: (event) =>
  114. target = $.eventTarget(event)
  115. if target.hasAttribute 'data-retry'
  116. $.stopEvent(event)
  117. @load()
  118. else if target.classList.contains '_pre-clip'
  119. $.stopEvent(event)
  120. target.classList.add if $.copyToClipboard(target.parentNode.textContent) then '_pre-clip-success' else '_pre-clip-error'
  121. setTimeout (-> target.className = '_pre-clip'), 2000
  122. return
  123. onAltO: =>
  124. return unless link = @find('._attribution:last-child ._attribution-link')
  125. @delay -> $.popup(link.href + location.hash)
  126. return