search.coffee 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. class app.views.Search extends app.View
  2. SEARCH_PARAM = app.config.search_param
  3. @el: '._search'
  4. @activeClass: '_search-active'
  5. @elements:
  6. input: '._search-input'
  7. resetLink: '._search-clear'
  8. @events:
  9. input: 'onInput'
  10. click: 'onClick'
  11. submit: 'onSubmit'
  12. @shortcuts:
  13. typing: 'autoFocus'
  14. altG: 'google'
  15. altS: 'stackoverflow'
  16. @routes:
  17. root: 'onRoot'
  18. after: 'afterRoute'
  19. init: ->
  20. @addSubview @scope = new app.views.SearchScope @el
  21. @searcher = new app.Searcher
  22. @searcher
  23. .on 'results', @onResults
  24. .on 'end', @onEnd
  25. app.on 'ready', @onReady
  26. $.on window, 'hashchange', @searchUrl
  27. $.on window, 'focus', @autoFocus
  28. return
  29. focus: ->
  30. @delay =>
  31. @input.focus() unless document.activeElement is @input
  32. return
  33. autoFocus: =>
  34. unless app.isMobile()
  35. @input.focus() unless document.activeElement is @input
  36. return
  37. reset: ->
  38. @el.reset()
  39. @onInput()
  40. @autoFocus()
  41. return
  42. onReady: =>
  43. @value = ''
  44. @delay @onInput
  45. return
  46. onInput: =>
  47. return if not @value? or # ignore events pre-"ready"
  48. @value is @input.value
  49. @value = @input.value
  50. if @value.length
  51. @search()
  52. else
  53. @clear()
  54. return
  55. search: (url = false) ->
  56. @addClass @constructor.activeClass
  57. @trigger 'searching'
  58. @hasResults = null
  59. @flags = urlSearch: url, initialResults: true
  60. @searcher.find @scope.getScope().entries.all(), 'text', @value
  61. return
  62. searchUrl: =>
  63. if app.router.isRoot()
  64. @scope.searchUrl()
  65. else if not app.router.isDocIndex()
  66. return
  67. return unless value = @extractHashValue()
  68. @input.value = @value = value
  69. @input.setSelectionRange(value.length, value.length)
  70. @search true
  71. true
  72. clear: ->
  73. @removeClass @constructor.activeClass
  74. @trigger 'clear'
  75. return
  76. externalSearch: (url) ->
  77. if value = @value
  78. value = "#{@scope.name()} #{value}" if @scope.name()
  79. $.popup "#{url}#{encodeURIComponent value}"
  80. @reset()
  81. return
  82. google: =>
  83. @externalSearch "https://www.google.com/search?q="
  84. return
  85. stackoverflow: =>
  86. @externalSearch "https://stackoverflow.com/search?q="
  87. return
  88. onResults: (results) =>
  89. @hasResults = true if results.length
  90. @trigger 'results', results, @flags
  91. @flags.initialResults = false
  92. return
  93. onEnd: =>
  94. @trigger 'noresults' unless @hasResults
  95. return
  96. onClick: (event) =>
  97. if event.target is @resetLink
  98. $.stopEvent(event)
  99. @reset()
  100. app.document.onEscape()
  101. return
  102. onSubmit: (event) ->
  103. $.stopEvent(event)
  104. return
  105. onRoot: (context) =>
  106. @reset() unless context.init
  107. return
  108. afterRoute: (name, context) =>
  109. @delay @searchUrl if context.hash
  110. @autoFocus()
  111. extractHashValue: ->
  112. if (value = @getHashValue())?
  113. app.router.replaceHash()
  114. value
  115. HASH_RGX = new RegExp "^##{SEARCH_PARAM}=(.*)"
  116. getHashValue: ->
  117. try HASH_RGX.exec($.urlDecode location.hash)?[1] catch