diff --git a/assets/javascripts/app/settings.coffee b/assets/javascripts/app/settings.coffee index 5fd47e5c..8d309c41 100644 --- a/assets/javascripts/app/settings.coffee +++ b/assets/javascripts/app/settings.coffee @@ -1,9 +1,23 @@ class app.Settings - DOCS_KEY = 'docs' - DARK_KEY = 'dark' - LAYOUT_KEY = 'layout' - SIZE_KEY = 'size' - TIPS_KEY = 'tips' + PREFERENCE_KEYS = [ + 'hideDisabled' + 'hideIntro' + 'manualUpdate' + 'fastScroll' + 'arrowScroll' + 'docs' + 'dark' + 'layout' + 'size' + 'tips' + ] + + INTERNAL_KEYS = [ + 'count' + 'schema' + 'version' + 'news' + ] @defaults: count: 0 @@ -32,24 +46,24 @@ class app.Settings return hasDocs: -> - try !!@store.get(DOCS_KEY) + try !!@store.get('docs') getDocs: -> - @store.get(DOCS_KEY)?.split('/') or app.config.default_docs + @store.get('docs')?.split('/') or app.config.default_docs setDocs: (docs) -> - @set DOCS_KEY, docs.join('/') + @set 'docs', docs.join('/') return getTips: -> - @store.get(TIPS_KEY)?.split('/') or [] + @store.get('tips')?.split('/') or [] setTips: (tips) -> - @set TIPS_KEY, tips.join('/') + @set 'tips', tips.join('/') return setLayout: (name, enable) -> - layout = (@store.get(LAYOUT_KEY) || '').split(' ') + layout = (@store.get('layout') || '').split(' ') $.arrayDelete(layout, '') if enable @@ -58,22 +72,34 @@ class app.Settings $.arrayDelete(layout, name) if layout.length > 0 - @set LAYOUT_KEY, layout.join(' ') + @set 'layout', layout.join(' ') else - @del LAYOUT_KEY + @del 'layout' return hasLayout: (name) -> - layout = (@store.get(LAYOUT_KEY) || '').split(' ') + layout = (@store.get('layout') || '').split(' ') layout.indexOf(name) isnt -1 setSize: (value) -> - @set SIZE_KEY, value + @set 'size', value return dump: -> @store.dump() + export: -> + data = @dump() + delete data[key] for key in INTERNAL_KEYS + data + + import: (data) -> + for key, value of @export() + @del key unless data.hasOwnProperty(key) + for key, value of data + @set key, value if PREFERENCE_KEYS.indexOf(key) isnt -1 + return + reset: -> @store.reset() @cache = {} diff --git a/assets/javascripts/lib/cookie_store.coffee b/assets/javascripts/lib/cookie_store.coffee index 9cfb4099..3b340571 100644 --- a/assets/javascripts/lib/cookie_store.coffee +++ b/assets/javascripts/lib/cookie_store.coffee @@ -14,6 +14,7 @@ class @CookieStore return value = 1 if value == true + value = parseInt(value, 10) if value and INT.test?(value) Cookies.set(key, '' + value, path: '/', expires: 1e8) @constructor.onBlocked(key, value, @get(key)) if @get(key) != value return diff --git a/assets/javascripts/news.json b/assets/javascripts/news.json index 47d694d9..e53b9f10 100644 --- a/assets/javascripts/news.json +++ b/assets/javascripts/news.json @@ -1,5 +1,8 @@ [ [ + "2017-09-10", + "Preferences can now be exported and imported." + ], [ "2017-09-03", "New documentations: D, Nim and Vulkan" ], [ diff --git a/assets/javascripts/templates/notif_tmpl.coffee b/assets/javascripts/templates/notif_tmpl.coffee index 5d284db1..94a586fd 100644 --- a/assets/javascripts/templates/notif_tmpl.coffee +++ b/assets/javascripts/templates/notif_tmpl.coffee @@ -30,6 +30,10 @@ app.templates.notifInvalidLocation = -> textNotif """ DevDocs must be loaded from #{app.config.production_host} """, """ Otherwise things are likely to break. """ +app.templates.notifImportInvalid = -> + textNotif """ Oops, an error occured. """, + """ The file you selected is invalid. """ + app.templates.notifNews = (news) -> notif 'Changelog', """
+ + + +
+ """ diff --git a/assets/javascripts/views/content/settings_page.coffee b/assets/javascripts/views/content/settings_page.coffee index 9a37ce18..d72e3eb3 100644 --- a/assets/javascripts/views/content/settings_page.coffee +++ b/assets/javascripts/views/content/settings_page.coffee @@ -5,6 +5,7 @@ class app.views.SettingsPage extends app.View @className: '_static' @events: + click: 'onClick' change: 'onChange' render: -> @@ -46,6 +47,34 @@ class app.views.SettingsPage extends app.View app.settings.set(name, enable) return + export: -> + data = new Blob([JSON.stringify(app.settings.export())], type: 'application/json') + link = document.createElement('a') + link.href = URL.createObjectURL(data) + link.download = 'devdocs.json' + link.style.display = 'none' + document.body.appendChild(link) + link.click() + document.body.removeChild(link) + return + + import: (file, input) -> + unless file and file.type is 'application/json' + new app.views.Notif 'ImportInvalid', autoHide: false + return + + reader = new FileReader() + reader.onloadend = -> + data = try JSON.parse(reader.result) + unless data and data.constructor is Object + new app.views.Notif 'ImportInvalid', autoHide: false + return + app.settings.import(data) + $.trigger input.form, 'import' + return + reader.readAsText(file) + return + onChange: (event) => input = event.target switch input.name @@ -55,10 +84,20 @@ class app.views.SettingsPage extends app.View @toggleLayout input.value, input.checked when 'smoothScroll' @toggleSmoothScroll input.checked + when 'import' + @import input.files[0], input else @toggle input.name, input.checked return + onClick: (event) => + target = $.eventTarget(event) + switch target.getAttribute('data-action') + when 'export' + $.stopEvent(event) + @export() + return + onRoute: (context) -> @render() return diff --git a/assets/javascripts/views/layout/settings.coffee b/assets/javascripts/views/layout/settings.coffee index 0d731a70..7888118a 100644 --- a/assets/javascripts/views/layout/settings.coffee +++ b/assets/javascripts/views/layout/settings.coffee @@ -9,6 +9,7 @@ class app.views.Settings extends app.View backBtn: 'button[data-back]' @events: + import: 'onImport' change: 'onChange' submit: 'onSubmit' click: 'onClick' @@ -41,11 +42,16 @@ class app.views.Settings extends app.View @addClass '_in' return - save: -> + save: (options = {}) -> unless @saving @saving = true - docs = @docPicker.getSelectedDocs() - app.settings.setDocs(docs) + + if options.import + docs = app.settings.getDocs() + else + docs = @docPicker.getSelectedDocs() + app.settings.setDocs(docs) + @saveBtn.textContent = if app.appCache then 'Downloading\u2026' else 'Saving\u2026' disabledDocs = new app.collections.Docs(doc for doc in app.docs.all() when docs.indexOf(doc.slug) is -1) disabledDocs.uninstall -> @@ -66,6 +72,11 @@ class app.views.Settings extends app.View @save() return + onImport: => + @addClass('_dirty') + @save(import: true) + return + onClick: (event) => return if event.which isnt 1 if event.target is @backBtn diff --git a/assets/stylesheets/components/_content.scss b/assets/stylesheets/components/_content.scss index bd21b5c4..bb86cc2f 100644 --- a/assets/stylesheets/components/_content.scss +++ b/assets/stylesheets/components/_content.scss @@ -398,12 +398,16 @@ } ._btn { + display: inline-block; + vertical-align: top; + line-height: normal; white-space: nowrap; padding: .375rem .675rem; background-image: linear-gradient(lighten($boxBackground, 4%), darken($boxBackground, 2%)); border: 1px solid $boxBorder; border-radius: 3px; box-shadow: 0 1px rgba($boxBorder, .08); + cursor: pointer; &:active { background-color: $boxBackground; @@ -411,6 +415,20 @@ } } +._file-btn { + position: relative; + overflow: hidden; + + > input { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + visibility: hidden; + } +} + ._btn-link { line-height: inherit; color: $linkColor;