History

UI improvements

Thibaut Courouble committed 9 years ago

Commit 0f238601 parent fd03ac139 files · +295 −334Browse files

Showing the changes to assets/javascripts (18 of 39 files). Show all

Unified · Split

18 files
  1. assets/javascripts/templates/pages/about_tmpl.coffee +6 −6
  2. assets/javascripts/templates/pages/help_tmpl.coffee +5 −3
  3. assets/javascripts/templates/pages/news_tmpl.coffee.erb +1 −1
  4. assets/javascripts/templates/pages/offline_tmpl.coffee +1 −1
  5. assets/javascripts/templates/pages/root_tmpl.coffee.erb +0 −9
  6. assets/javascripts/views/content/root_page.coffee +0 −1
  7. assets/javascripts/views/layout/document.coffee +11 −37
  8. assets/javascripts/views/layout/menu.coffee +24 −0
  9. assets/javascripts/views/layout/mobile.coffee +17 −18
  10. assets/javascripts/views/layout/nav.coffee +0 −26
  11. assets/javascripts/views/layout/resizer.coffee +1 −1
  12. assets/javascripts/views/list/list_focus.coffee +6 −6
  13. assets/javascripts/views/misc/notice.coffee +0 −1
  14. assets/javascripts/views/search/search.coffee +1 −1
  15. assets/javascripts/views/sidebar/doc_list.coffee +1 −1
  16. assets/javascripts/views/sidebar/results.coffee +2 −2
  17. assets/javascripts/views/sidebar/sidebar.coffee +41 −14
  18. assets/javascripts/views/view.coffee +7 −0
assets/javascripts/templates/pages/about_tmpl.coffee+6−6
@@ -10,7 +10,7 @@ app.templates.aboutPage = -> """ · 9 unmodified lines
1010 </ul>
1111 </nav>
1212
13− <h1 class="_lined-heading">API Documentation Browser</h1>
13+ <h1 class="_lined-heading">DevDocs: API Documentation Browser</h1>
1414 <p>DevDocs combines multiple API documentations in a fast, organized, and searchable interface.
1515 <ul>
1616 <li>Created and maintained by <a href="http://thibaut.me">Thibaut Courouble</a>
@@ -26,7 +26,7 @@ app.templates.aboutPage = -> """ · 9 unmodified lines
2626 <p class="_note _note-green">If you like DevDocs, please consider supporting my work on
2727 <a href="https://gratipay.com/devdocs/">Gratipay</a>. Thanks!<br>
2828
29− <h2 class="_lined-heading" id="credits">Credits</h2>
29+ <h2 class="_block-heading" id="credits">Credits</h2>
3030
3131 <p><strong>Special thanks to:</strong>
3232 <ul>
@@ -45,7 +45,7 @@ app.templates.aboutPage = -> """ · 12 unmodified lines
4545 #{("<tr><td>#{c[0]}<td>&copy; #{c[1]}<td><a href=\"#{c[3]}\">#{c[2]}</a>" for c in credits).join('')}
4646 </table>
4747
48− <h2 class="_lined-heading" id="faq">Questions & Answers</h2>
48+ <h2 class="_block-heading" id="faq">Questions & Answers</h2>
4949 <dl>
5050 <dt>Where can I suggest new docs and features?
5151 <dd>You can suggest and vote for new docs on the <a href="https://trello.com/b/6BmTulfx/devdocs-documentation">Trello board</a>.<br>
@@ -56,7 +56,7 @@ app.templates.aboutPage = -> """ · 4 unmodified lines
5656 </dl>
5757 <p>For anything else, feel free to email me at <a href="mailto:thibaut@devdocs.io">thibaut@devdocs.io</a>.
5858
59− <h2 class="_lined-heading" id="copyright">Copyright and License</h2>
59+ <h2 class="_block-heading" id="copyright">Copyright and License</h2>
6060 <p class="_note">
6161 <strong>Copyright 2013&ndash;2017 Thibaut Courouble and <a href="https://github.com/Thibaut/devdocs/graphs/contributors">other contributors</a></strong><br>
6262 This software is licensed under the terms of the Mozilla Public License v2.0.<br>
@@ -64,7 +64,7 @@ app.templates.aboutPage = -> """ · 1 unmodified line
6464 For more information, see the <a href="https://github.com/Thibaut/devdocs/blob/master/COPYRIGHT">COPYRIGHT</a>
6565 and <a href="https://github.com/Thibaut/devdocs/blob/master/LICENSE">LICENSE</a> files.
6666
67− <h2 class="_lined-heading" id="plugins">Plugins and Extensions</h2>
67+ <h2 class="_block-heading" id="plugins">Plugins and Extensions</h2>
6868 <ul>
6969 <li><a href="https://chrome.google.com/webstore/detail/devdocs/mnfehgbmkapmjnhcnbodoamcioleeooe">Chrome web app</a>
7070 <li><a href="https://sublime.wbond.net/packages/DevDocs">Sublime Text plugin</a>
@@ -73,7 +73,7 @@ app.templates.aboutPage = -> """ · 2 unmodified lines
7373 <li><a href="https://github.com/xuchunyang/DevDocs.el">Emacs Package</a>
7474 </ul>
7575
76− <h2 class="_lined-heading" id="privacy">Privacy Policy</h2>
76+ <h2 class="_block-heading" id="privacy">Privacy Policy</h2>
7777 <ul>
7878 <li><a href="http://devdocs.io">devdocs.io</a> ("App") is operated by Thibaut Courouble ("We").
7979 <li>We do not collect personal information.
assets/javascripts/templates/pages/help_tmpl.coffee+5−3
@@ -11,7 +11,9 @@ app.templates.helpPage = """ · 10 unmodified lines
1111 </ul>
1212 </nav>
1313
14− <h2 class="_lined-heading" id="search">Search</h2>
14+ <h1 class="_lined-heading" id="search">Help</h2>
15+
16+ <h2 class="_block-heading" id="search">Search</h2>
1517 <p>
1618 The search is case-insensitive and supports fuzzy matching (for queries longer than two characters).
1719 For example, searching <code class="_label">bgcp</code> brings up <code class="_label">background-clip</code>.<br>
@@ -42,7 +44,7 @@ app.templates.helpPage = """ · 24 unmodified lines
4244 <a href="https://support.mozilla.org/en-US/kb/how-search-from-address-bar">these instructions</a>.
4345 </dl>
4446
45− <h2 class="_lined-heading" id="shortcuts">Keyboard Shortcuts</h2>
47+ <h2 class="_block-heading" id="shortcuts">Keyboard Shortcuts</h2>
4648 <h3 class="_shortcuts-title">Selection</h3>
4749 <dl class="_shortcuts-dl">
4850 <dt class="_shortcuts-dt">
@@ -110,7 +112,7 @@ app.templates.helpPage = """ · 61 unmodified lines
110112 <strong>Tip:</strong> If the cursor is no longer in the search field, press <code class="_label">/</code> or
111113 continue to type and it will refocus the search field and start showing new results.
112114
113− <h2 class="_lined-heading" id="abbreviations">Abbreviations</h2>
115+ <h2 class="_block-heading" id="abbreviations">Abbreviations</h2>
114116 <p>Feel free to suggest new abbreviations on <a href="https://github.com/Thibaut/devdocs/issues/new">GitHub</a>.
115117 <table class="_abbreviations">
116118 <tr>
assets/javascripts/templates/pages/news_tmpl.coffee.erb+1−1
@@ -15,7 +15,7 @@ app.templates.newsList = (news, options = {}) -> · 14 unmodified lines
1515 date = new Date(value[0])
1616 if options.years isnt false and year isnt date.getUTCFullYear()
1717 year = date.getUTCFullYear()
18− result += "<h4>#{year}</h4>"
18+ result += """<h2 class="_block-heading">#{year}</h2>"""
1919 result += newsItem(date, value[1..])
2020
2121 result
assets/javascripts/templates/pages/offline_tmpl.coffee+1−1
@@ -21,7 +21,7 @@ app.templates.offlinePage = (docs) -> """ · 20 unmodified lines
2121 #{docs}
2222 </table>
2323 <p class="_note"><strong>Note:</strong> your browser may delete DevDocs's offline data if your computer is running low on disk space and you haven't used the app in a while. Load this page before going offline to make sure the data is still there.
24− <h1 class="_lined-heading">Questions & Answers</h1>
24+ <h2 class="_block-heading">Questions & Answers</h2>
2525 <dl>
2626 <dt>How does this work?
2727 <dd>Each page is cached as a key-value pair in <a href="https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API">IndexedDB</a> (downloaded from a single file).<br>
assets/javascripts/templates/pages/root_tmpl.coffee.erb+0−9
@@ -48,15 +48,6 @@ app.templates.intro = """ · 47 unmodified lines
4848 """
4949 <% end %>
5050
51−app.templates.mobileNav = """
52− <nav class="_mobile-nav">
53− <a href="/offline" class="_mobile-nav-link">Offline</a>
54− <a href="/about" class="_mobile-nav-link">About</a>
55− <a href="/news" class="_mobile-nav-link">News</a>
56− <a href="/help" class="_mobile-nav-link">Help</a>
57− </nav>
58−"""
59−
6051 app.templates.mobileIntro = """
6152 <div class="_mobile-intro">
6253 <h2 class="_intro-title">Welcome!</h2>
assets/javascripts/views/content/root_page.coffee+0−1
@@ -9,7 +9,6 @@ class app.views.RootPage extends app.View · 8 unmodified lines
99
1010 render: ->
1111 @empty()
12− @append @tmpl('mobileNav') if app.isMobile()
1312 if app.isAndroidWebview()
1413 @append @tmpl('androidWarning')
1514 else
assets/javascripts/views/layout/document.coffee+11−37
@@ -1,6 +1,6 @@
11 class app.views.Document extends app.View
2− MAX_WIDTH_CLASS = '_max-width'
3− HIDE_SIDEBAR_CLASS = '_sidebar-hidden'
2+ MAX_WIDTH_LAYOUT = '_max-width'
3+ SIDEBAR_HIDDEN_LAYOUT = '_sidebar-hidden'
44
55 @el: document
66
@@ -14,16 +14,12 @@ class app.views.Document extends app.View · 7 unmodified lines
1414 superRight: 'onForward'
1515
1616 init: ->
17− @addSubview @nav = new app.views.Nav,
17+ @addSubview @menu = new app.views.Menu,
1818 @addSubview @sidebar = new app.views.Sidebar
1919 @addSubview @resizer = new app.views.Resizer if app.views.Resizer.isSupported()
2020 @addSubview @content = new app.views.Content
2121 @addSubview @path = new app.views.Path unless app.isSingleDoc() or app.isMobile()
2222
23− @sidebar.search
24− .on 'searching', @onSearching
25− .on 'clear', @onSearchClear
26−
2723 $.on document.body, 'click', @onClick
2824
2925 @activate()
@@ -39,39 +35,17 @@ class app.views.Document extends app.View · 9 unmodified lines
3935 return
4036
4137 toggleLayout: ->
42− wantsMaxWidth = !app.el.classList.contains(MAX_WIDTH_CLASS)
43− app.el.classList[if wantsMaxWidth then 'add' else 'remove'](MAX_WIDTH_CLASS)
44− app.settings.setLayout(MAX_WIDTH_CLASS, wantsMaxWidth)
38+ wantsMaxWidth = !app.el.classList.contains(MAX_WIDTH_LAYOUT)
39+ app.el.classList[if wantsMaxWidth then 'add' else 'remove'](MAX_WIDTH_LAYOUT)
40+ app.settings.setLayout(MAX_WIDTH_LAYOUT, wantsMaxWidth)
4541 app.appCache?.updateInBackground()
4642 return
4743
48− showSidebar: (options = {}) ->
49− @toggleSidebar(options, true)
50− return
51−
52− hideSidebar: (options = {}) ->
53− @toggleSidebar(options, false)
54− return
55−
56− toggleSidebar: (options = {}, shouldShow) ->
57− shouldShow ?= if options.save then !@hasSidebar() else app.el.classList.contains(HIDE_SIDEBAR_CLASS)
58− app.el.classList[if shouldShow then 'remove' else 'add'](HIDE_SIDEBAR_CLASS)
59− if options.save
60− app.settings.setLayout(HIDE_SIDEBAR_CLASS, !shouldShow)
61− app.appCache?.updateInBackground()
62− return
63−
64− hasSidebar: ->
65− !app.settings.hasLayout(HIDE_SIDEBAR_CLASS)
66−
67− onSearching: =>
68− unless @hasSidebar()
69− @showSidebar()
70− return
71−
72− onSearchClear: =>
73− unless @hasSidebar()
74− @hideSidebar()
44+ toggleSidebarLayout: ->
45+ shouldHide = !app.settings.hasLayout(SIDEBAR_HIDDEN_LAYOUT)
46+ app.el.classList[if shouldHide then 'add' else 'remove'](SIDEBAR_HIDDEN_LAYOUT)
47+ app.settings.setLayout(SIDEBAR_HIDDEN_LAYOUT, shouldHide)
48+ app.appCache?.updateInBackground()
7549 return
7650
7751 setTitle: (title) ->
assets/javascripts/views/layout/menu.coffee+24−0
@@ -0,0 +1,24 @@
1+class app.views.Menu extends app.View
2+ @el: '._menu'
3+ @activeClass: 'active'
4+
5+ @events:
6+ click: 'onClick'
7+
8+ init: ->
9+ $.on document.body, 'click', @onGlobalClick
10+ return
11+
12+ onClick: =>
13+ prev = @el.previousElementSibling
14+ $.remove @el
15+ $.requestAnimationFrame => $.after(prev, @el)
16+ return
17+
18+ onGlobalClick: (event) =>
19+ return if event.which isnt 1
20+ if event.target.hasAttribute?('data-toggle-menu')
21+ @toggleClass @constructor.activeClass
22+ else if @hasClass @constructor.activeClass
23+ @removeClass @constructor.activeClass
24+ return
assets/javascripts/views/layout/mobile.coffee+17−18
@@ -35,44 +35,48 @@ class app.views.Mobile extends app.View · 34 unmodified lines
3535 FastClick.attach @body
3636
3737 $.on @body, 'click', @onClick
38− $.on $('._home-btn'), 'click', @onClickHome
39− $.on $('._menu-btn'), 'click', @onClickMenu
4038 $.on $('._search'), 'touchend', @onTapSearch
4139
42− @back = $('._back-btn')
40+ @toggleSidebar = $('button[data-toggle-sidebar]')
41+ @toggleSidebar.removeAttribute('hidden')
42+ $.on @toggleSidebar, 'click', @onClickToggleSidebar
43+
44+ @back = $('button[data-back]')
45+ @back.removeAttribute('hidden')
4346 $.on @back, 'click', @onClickBack
4447
45− @forward = $('._forward-btn')
48+ @forward = $('button[data-forward]')
49+ @forward.removeAttribute('hidden')
4650 $.on @forward, 'click', @onClickForward
4751
4852 app.document.sidebar.search
4953 .on 'searching', @showSidebar
50− .on 'clear', @hideSidebar
5154
5255 @activate()
5356 return
5457
5558 showSidebar: =>
5659 if @isSidebarShown()
57− @body.scrollTop = 0
60+ window.scrollTo 0, 0
5861 return
5962
60− @contentTop = @body.scrollTop
63+ @contentTop = window.scrollY
6164 @content.style.display = 'none'
6265 @sidebar.style.display = 'block'
6366
6467 if selection = @findByClass app.views.ListSelect.activeClass
65− $.scrollTo selection, @body, 'center'
68+ scrollContainer = if window.scrollY is @body.scrollTop then @body else document.documentElement
69+ $.scrollTo selection, scrollContainer, 'center'
6670 else
67− @body.scrollTop = @findByClass(app.views.ListFold.activeClass) and @sidebarTop or 0
71+ window.scrollTo 0, @findByClass(app.views.ListFold.activeClass) and @sidebarTop or 0
6872 return
6973
7074 hideSidebar: =>
7175 return unless @isSidebarShown()
72− @sidebarTop = @body.scrollTop
76+ @sidebarTop = window.scrollY
7377 @sidebar.style.display = 'none'
7478 @content.style.display = 'block'
75− @body.scrollTop = @contentTop or 0
79+ window.scrollTo 0, @contentTop or 0
7680 return
7781
7882 isSidebarShown: ->
@@ -89,17 +93,12 @@ class app.views.Mobile extends app.View · 10 unmodified lines
8993 onClickForward: =>
9094 history.forward()
9195
92− onClickHome: =>
93− app.shortcuts.trigger 'escape'
94− @hideSidebar()
95− return
96−
97− onClickMenu: =>
96+ onClickToggleSidebar: =>
9897 if @isSidebarShown() then @hideSidebar() else @showSidebar()
9998 return
10099
101100 onTapSearch: =>
102− @body.scrollTop = 0
101+ window.scrollTo 0, 0
103102
104103 afterRoute: =>
105104 @hideSidebar()
assets/javascripts/views/layout/nav.coffee+0−26
@@ -1,26 +0,0 @@
1−class app.views.Nav extends app.View
2− @el: '._nav'
3− @activeClass: '_nav-current'
4−
5− @routes:
6− after: 'afterRoute'
7−
8− select: (href) ->
9− @deselect()
10− if @current = @find "a[href='#{href}']"
11− @current.classList.add @constructor.activeClass
12− @current.setAttribute 'tabindex', '-1'
13− return
14−
15− deselect: ->
16− if @current
17− @current.classList.remove @constructor.activeClass
18− @current.removeAttribute 'tabindex'
19− @current = null
20− return
21−
22− afterRoute: (route, context) =>
23− if route in ['page', 'offline']
24− @select context.pathname
25− else
26− @deselect()
assets/javascripts/views/layout/resizer.coffee+1−1
@@ -35,7 +35,7 @@ class app.views.Resizer extends app.View · 34 unmodified lines
3535 return
3636
3737 onClick: ->
38− app.document.toggleSidebar(save: true)
38+ app.document.toggleSidebarLayout()
3939 return
4040
4141 onDragStart: (event) =>
assets/javascripts/views/list/list_focus.coffee+6−6
@@ -14,7 +14,7 @@ class app.views.ListFocus extends app.View · 13 unmodified lines
1414
1515 constructor: (@el) ->
1616 super
17− @focus = $.framify(@focus, @)
17+ @focusOnNextFrame = $.framify(@focus, @)
1818
1919 focus: (el) ->
2020 if el and not el.classList.contains @constructor.activeClass
@@ -87,22 +87,22 @@ class app.views.ListFocus extends app.View · 66 unmodified lines
8787
8888 onDown: =>
8989 if cursor = @getCursor()
90− @focus @findNext(cursor)
90+ @focusOnNextFrame @findNext(cursor)
9191 else
92− @focus @findByTag('a')
92+ @focusOnNextFrame @findByTag('a')
9393 return
9494
9595 onUp: =>
9696 if cursor = @getCursor()
97− @focus @findPrev(cursor)
97+ @focusOnNextFrame @findPrev(cursor)
9898 else
99− @focus @findLastByTag('a')
99+ @focusOnNextFrame @findLastByTag('a')
100100 return
101101
102102 onLeft: =>
103103 cursor = @getCursor()
104104 if cursor and not cursor.classList.contains(app.views.ListFold.activeClass) and cursor.parentElement isnt @el
105− @focus cursor.parentElement.previousSibling
105+ @focusOnNextFrame cursor.parentElement.previousSibling
106106 return
107107
108108 onEnter: =>
assets/javascripts/views/misc/notice.coffee+0−1
@@ -18,7 +18,6 @@ class app.views.Notice extends app.View · 17 unmodified lines
1818 return
1919
2020 show: ->
21− @addClass '_top' if @type is 'disabledDoc'
2221 @html @tmpl("#{@type}Notice", @args...)
2322 @prependTo $('._app')
2423 return
assets/javascripts/views/search/search.coffee+1−1
@@ -130,7 +130,7 @@ class app.views.Search extends app.View · 129 unmodified lines
130130 if event.target is @resetLink
131131 $.stopEvent(event)
132132 @reset()
133− @focus()
133+ app.document.onEscape()
134134 return
135135
136136 onSubmit: (event) ->
assets/javascripts/views/sidebar/doc_list.coffee+1−1
@@ -148,7 +148,7 @@ class app.views.DocList extends app.View · 147 unmodified lines
148148 return
149149
150150 scrollTo: (model) ->
151− $.scrollTo @find("a[href='#{model.fullPath()}']"), null, 'top', margin: 0
151+ $.scrollTo @find("a[href='#{model.fullPath()}']"), null, 'top', margin: if app.isMobile() then 48 else 0
152152 return
153153
154154 toggleDisabled: ->
assets/javascripts/views/sidebar/results.coffee+2−2
@@ -15,8 +15,8 @@ class app.views.Results extends app.View · 14 unmodified lines
1515 return
1616
1717 init: ->
18− @addSubview @listSelect = new app.views.ListSelect @el
1918 @addSubview @listFocus = new app.views.ListFocus @el unless app.isMobile()
19+ @addSubview @listSelect = new app.views.ListSelect @el
2020
2121 @search
2222 .on 'results', @onResults
@@ -42,7 +42,7 @@ class app.views.Results extends app.View · 19 unmodified lines
4242 return
4343
4444 focusFirst: ->
45− @listFocus?.focus @el.firstElementChild
45+ @listFocus?.focusOnNextFrame @el.firstElementChild
4646 return
4747
4848 openFirst: ->
assets/javascripts/views/sidebar/sidebar.coffee+41−14
@@ -3,6 +3,7 @@ class app.views.Sidebar extends app.View · 2 unmodified lines
33
44 @events:
55 focus: 'onFocus'
6+ select: 'onSelect'
67 click: 'onClick'
78
89 @shortcuts:
@@ -14,8 +15,8 @@ class app.views.Sidebar extends app.View · 5 unmodified lines
1415 @addSubview @search = new app.views.Search
1516
1617 @search
17− .on 'searching', @showResults
18− .on 'clear', @showDocList
18+ .on 'searching', @onSearching
19+ .on 'clear', @onSearchClear
1920 .scope
2021 .on 'change', @onScopeChange
2122
@@ -27,7 +28,15 @@ class app.views.Sidebar extends app.View · 5 unmodified lines
2728 $.on document, 'click', @onGlobalClick if @docPicker
2829 return
2930
30− show: (view) ->
31+ display: ->
32+ @el.style.display = 'block'
33+ return
34+
35+ resetDisplay: ->
36+ @el.style.display = '' unless @el.style.display is 'none'
37+ return
38+
39+ showView: (view) ->
3140 unless @view is view
3241 @hover?.hide()
3342 @saveScrollPosition()
@@ -44,28 +53,29 @@ class app.views.Sidebar extends app.View · 10 unmodified lines
4453 @append @tmpl('sidebarSettings') if @view is @docList and @docPicker
4554 return
4655
47− showDocList: (reset) =>
48− @show @docList
49− if reset is true
50− @docList.reset(revealCurrent: true)
51− @search.reset()
56+ showDocList: ->
57+ @showView @docList
5258 return
5359
5460 showDocPicker: =>
55− @show @docPicker
61+ @showView @docPicker
5662 return
5763
5864 showResults: =>
59− @show @results
65+ @showView @results
66+ return
67+
68+ reset: ->
69+ @display()
70+ @showDocList()
71+ @docList.reset()
72+ @search.reset()
6073 return
6174
6275 onReady: =>
6376 @view = @docList
6477 @render()
6578 @view.activate()
66−
67− reset: ->
68− @showDocList true
6979 return
7080
7181 onScopeChange: (newDoc, previousDoc) =>
@@ -90,15 +100,30 @@ class app.views.Sidebar extends app.View · 18 unmodified lines
90100 @el.scrollTop = 0
91101 return
92102
103+ onSearching: =>
104+ @display()
105+ @showResults()
106+ return
107+
108+ onSearchClear: =>
109+ @resetDisplay()
110+ @showDocList()
111+ return
112+
93113 onFocus: (event) =>
114+ @display()
94115 $.scrollTo event.target, @el, 'continuous', bottomGap: 2 unless event.target is @el
95116 return
96117
118+ onSelect: =>
119+ @resetDisplay()
120+ return
121+
97122 onClick: (event) =>
98123 return if event.which isnt 1
99124 if event.target.hasAttribute? 'data-reset-list'
100125 $.stopEvent(event)
101− @reset()
126+ @onAltR()
102127 else if event.target.hasAttribute? 'data-light'
103128 $.stopEvent(event)
104129 document.activeElement?.blur()
@@ -120,6 +145,8 @@ class app.views.Sidebar extends app.View · 15 unmodified lines
120145
121146 onAltR: =>
122147 @reset()
148+ @docList.reset(revealCurrent: true)
149+ @display()
123150 return
124151
125152 onEscape: =>
assets/javascripts/views/view.coffee+7−0
@@ -35,6 +35,13 @@ class app.View · 34 unmodified lines
3535 @el.classList.remove(name)
3636 return
3737
38+ toggleClass: (name) ->
39+ @el.classList.toggle(name)
40+ return
41+
42+ hasClass: (name) ->
43+ @el.classList.contains(name)
44+
3845 resetClass: ->
3946 @el.className = @originalClassName or ''
4047 if @constructor.className