From 9c62f5d7f26b6f86ed6f62e2714215b71d8400a7 Mon Sep 17 00:00:00 2001 From: Thibaut Courouble Date: Sat, 15 Apr 2017 10:15:34 -0400 Subject: [PATCH 01/15] Bump Ruby 2.4.1 --- .ruby-version | 2 +- Dockerfile | 2 +- Gemfile | 2 +- Gemfile.lock | 2 +- README.md | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.ruby-version b/.ruby-version index 9183195a..58073ef8 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -2.4.0 \ No newline at end of file +2.4.1 \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index d6ce12b7..13b0918e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ -FROM ruby:2.4.0 +FROM ruby:2.4.1 MAINTAINER Conor Heine RUN apt-get update diff --git a/Gemfile b/Gemfile index 797429ca..baa850ed 100644 --- a/Gemfile +++ b/Gemfile @@ -1,5 +1,5 @@ source 'https://rubygems.org' -ruby '2.4.0' +ruby '2.4.1' gem 'rake' gem 'thor' diff --git a/Gemfile.lock b/Gemfile.lock index 855a1ae7..c49adcdb 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -129,7 +129,7 @@ DEPENDENCIES yajl-ruby RUBY VERSION - ruby 2.4.0p0 + ruby 2.4.1p111 BUNDLED WITH 1.14.1 diff --git a/README.md b/README.md index 06f27841..2b3fd6ec 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ Unless you wish to contribute to the project, I recommend using the hosted versi DevDocs is made of two pieces: a Ruby scraper that generates the documentation and metadata, and a JavaScript app powered by a small Sinatra app. -DevDocs requires Ruby 2.4.0, libcurl, and a JavaScript runtime supported by [ExecJS](https://github.com/rails/execjs#readme) (included in OS X and Windows; [Node.js](https://nodejs.org/en/) on Linux). Once you have these installed, run the following commands: +DevDocs requires Ruby 2.4.1, libcurl, and a JavaScript runtime supported by [ExecJS](https://github.com/rails/execjs#readme) (included in OS X and Windows; [Node.js](https://nodejs.org/en/) on Linux). Once you have these installed, run the following commands: ``` git clone https://github.com/Thibaut/devdocs.git && cd devdocs From 993c0d85fc6d10ee33067489f9c5332d344f39a0 Mon Sep 17 00:00:00 2001 From: Thibaut Courouble Date: Sat, 15 Apr 2017 10:17:31 -0400 Subject: [PATCH 02/15] Update gem dependencies --- Gemfile.lock | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Gemfile.lock b/Gemfile.lock index c49adcdb..9ccf18e3 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -6,7 +6,7 @@ GEM i18n (~> 0.7) minitest (~> 5.1) tzinfo (~> 1.1) - backports (3.6.8) + backports (3.7.0) better_errors (2.1.1) coderay (>= 1.0.0) erubis (>= 2.6.6) @@ -35,7 +35,7 @@ GEM mini_portile2 (2.1.0) minitest (5.10.1) multi_json (1.12.1) - nokogiri (1.7.0.1) + nokogiri (1.7.1) mini_portile2 (~> 2.1.0) options (2.3.2) progress_bar (1.1.0) @@ -76,18 +76,18 @@ GEM rack (>= 1, < 3) thor (0.19.4) thread_safe (0.3.6) - tilt (2.0.6) - tty-pager (0.5.0) + tilt (2.0.7) + tty-pager (0.7.1) tty-screen (~> 0.5.0) - tty-which (~> 0.2.0) + tty-which (~> 0.3.0) verse (~> 0.5.0) tty-screen (0.5.0) - tty-which (0.2.2) + tty-which (0.3.0) typhoeus (1.1.2) ethon (>= 0.9.0) - tzinfo (1.2.2) + tzinfo (1.2.3) thread_safe (~> 0.1) - uglifier (3.1.3) + uglifier (3.2.0) execjs (>= 0.3.0, < 3) unicode-display_width (1.1.3) unicode_utils (1.4.0) From 2cef32dfe80f8cb487708fc47bd526fb45ca76dc Mon Sep 17 00:00:00 2001 From: Thibaut Courouble Date: Sat, 15 Apr 2017 10:17:53 -0400 Subject: [PATCH 03/15] Update Raven.js --- assets/javascripts/vendor/raven.js | 134 +++++++++++++++++++---------- 1 file changed, 90 insertions(+), 44 deletions(-) diff --git a/assets/javascripts/vendor/raven.js b/assets/javascripts/vendor/raven.js index 42d71b63..fb788d81 100644 --- a/assets/javascripts/vendor/raven.js +++ b/assets/javascripts/vendor/raven.js @@ -1,4 +1,4 @@ -/*! Raven.js 3.13.1 (f55d281) | github.com/getsentry/raven-js */ +/*! Raven.js 3.14.2 (5cf57e1) | github.com/getsentry/raven-js */ /* * Includes TraceKit @@ -115,6 +115,9 @@ function Raven() { crossOrigin: 'anonymous', collectWindowErrors: true, maxMessageLength: 0, + + // By default, truncates URL values to 250 chars + maxUrlLength: 250, stackTraceLimit: 50, autoBreadcrumbs: true, sampleRate: 1 @@ -152,7 +155,7 @@ Raven.prototype = { // webpack (using a build step causes webpack #1617). Grunt verifies that // this value matches package.json during build. // See: https://github.com/getsentry/raven-js/issues/465 - VERSION: '3.13.1', + VERSION: '3.14.2', debug: false, @@ -457,9 +460,10 @@ Raven.prototype = { if (this._globalOptions.stacktrace || (options && options.stacktrace)) { var ex; - // create a stack trace from this point; just trim - // off extra frames so they don't include this function call (or - // earlier Raven.js library fn calls) + // Generate a "synthetic" stack trace from this point. + // NOTE: If you are a Sentry user, and you are seeing this stack frame, it is NOT indicative + // of a bug with Raven.js. Sentry generates synthetic traces either by configuration, + // or if it catches a thrown object without a "stack" property. try { throw new Error(msg); } catch (ex1) { @@ -473,6 +477,9 @@ Raven.prototype = { // fingerprint on msg, not stack trace (legacy behavior, could be // revisited) fingerprint: msg, + // since we know this is a synthetic trace, the top N-most frames + // MUST be from Raven.js, so mark them as in_app later by setting + // trimHeadFrames trimHeadFrames: (options.trimHeadFrames || 0) + 1 }, options); @@ -1385,9 +1392,48 @@ Raven.prototype = { exception.value = truncate(exception.value, max); } + var request = data.request; + if (request) { + if (request.url) { + request.url = truncate(request.url, this._globalOptions.maxUrlLength); + } + if (request.Referer) { + request.Referer = truncate(request.Referer, this._globalOptions.maxUrlLength); + } + } + + if (data.breadcrumbs && data.breadcrumbs.values) + this._trimBreadcrumbs(data.breadcrumbs); + return data; }, + /** + * Truncate breadcrumb values (right now just URLs) + */ + _trimBreadcrumbs: function (breadcrumbs) { + // known breadcrumb properties with urls + // TODO: also consider arbitrary prop values that start with (https?)?:// + var urlProps = ['to', 'from', 'url'], + urlProp, + crumb, + data; + + for (var i = 0; i < breadcrumbs.values.length; ++i) { + crumb = breadcrumbs.values[i]; + if (!crumb.hasOwnProperty('data') || !isObject(crumb.data)) + continue; + + data = crumb.data; + for (var j = 0; j < urlProps.length; ++j) { + urlProp = urlProps[j]; + if (data.hasOwnProperty(urlProp)) { + data[urlProp] = truncate(data[urlProp], this._globalOptions.maxUrlLength); + } + } + } + }, + _getHttpData: function() { if (!this._hasNavigator && !this._hasDocument) return; var httpData = {}; @@ -2083,20 +2129,22 @@ function isObject(what) { return typeof what === 'object' && what !== null; } -// Sorta yanked from https://github.com/joyent/node/blob/aa3b4b4/lib/util.js#L560 +// Yanked from https://git.io/vS8DV re-used under CC0 // with some tiny modifications -function isError(what) { - var toString = {}.toString.call(what); - return isObject(what) && - toString === '[object Error]' || - toString === '[object Exception]' || // Firefox NS_ERROR_FAILURE Exceptions - what instanceof Error; +function isError(value) { + switch ({}.toString.call(value)) { + case '[object Error]': return true; + case '[object Exception]': return true; + case '[object DOMException]': return true; + default: return value instanceof Error; + } } module.exports = { isObject: isObject, isError: isError }; + },{}],6:[function(_dereq_,module,exports){ (function (global){ 'use strict'; @@ -2424,27 +2472,6 @@ TraceKit.report = (function reportModuleWrapper() { * */ TraceKit.computeStackTrace = (function computeStackTraceWrapper() { - /** - * Escapes special characters, except for whitespace, in a string to be - * used inside a regular expression as a string literal. - * @param {string} text The string. - * @return {string} The escaped string literal. - */ - function escapeRegExp(text) { - return text.replace(/[\-\[\]{}()*+?.,\\\^$|#]/g, '\\$&'); - } - - /** - * Escapes special characters in a string to be used inside a regular - * expression as a string literal. Also ensures that HTML entities will - * be matched the same as their literal friends. - * @param {string} body The string. - * @return {string} The escaped string. - */ - function escapeCodeAsRegExpForMatchingInsideHTML(body) { - return escapeRegExp(body).replace('<', '(?:<|<)').replace('>', '(?:>|>)').replace('&', '(?:&|&)').replace('"', '(?:"|")').replace(/\s+/g, '\\s+'); - } - // Contents of Exception in various browsers. // // SAFARI: @@ -2491,18 +2518,31 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() { function computeStackTraceFromStackProp(ex) { if (typeof ex.stack === 'undefined' || !ex.stack) return; - var chrome = /^\s*at (.*?) ?\(((?:file|https?|blob|chrome-extension|native|eval|).*?)(?::(\d+))?(?::(\d+))?\)?\s*$/i, - gecko = /^\s*(.*?)(?:\((.*?)\))?(?:^|@)((?:file|https?|blob|chrome|resource|\[native).*?)(?::(\d+))?(?::(\d+))?\s*$/i, - winjs = /^\s*at (?:((?:\[object object\])?.+) )?\(?((?:file|ms-appx|https?|blob):.*?):(\d+)(?::(\d+))?\)?\s*$/i, + var chrome = /^\s*at (.*?) ?\(((?:file|https?|blob|chrome-extension|native|eval|webpack||\/).*?)(?::(\d+))?(?::(\d+))?\)?\s*$/i, + gecko = /^\s*(.*?)(?:\((.*?)\))?(?:^|@)((?:file|https?|blob|chrome|webpack|resource|\[native).*?)(?::(\d+))?(?::(\d+))?\s*$/i, + winjs = /^\s*at (?:((?:\[object object\])?.+) )?\(?((?:file|ms-appx|https?|webpack|blob):.*?):(\d+)(?::(\d+))?\)?\s*$/i, + + // Used to additionally parse URL/line/column from eval frames + geckoEval = /(\S+) line (\d+)(?: > eval line \d+)* > eval/i, + chromeEval = /\((\S*)(?::(\d+))(?::(\d+))\)/, + lines = ex.stack.split('\n'), stack = [], + submatch, parts, element, reference = /^(.*) is undefined$/.exec(ex.message); for (var i = 0, j = lines.length; i < j; ++i) { if ((parts = chrome.exec(lines[i]))) { - var isNative = parts[2] && parts[2].indexOf('native') !== -1; + var isNative = parts[2] && parts[2].indexOf('native') === 0; // start of line + var isEval = parts[2] && parts[2].indexOf('eval') === 0; // start of line + if (isEval && (submatch = chromeEval.exec(parts[2]))) { + // throw out eval line/column and use top-most line/column number + parts[2] = submatch[1]; // url + parts[3] = submatch[2]; // line + parts[4] = submatch[3]; // column + } element = { 'url': !isNative ? parts[2] : null, 'func': parts[1] || UNKNOWN_FUNCTION, @@ -2519,6 +2559,19 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() { 'column': parts[4] ? +parts[4] : null }; } else if ((parts = gecko.exec(lines[i]))) { + var isEval = parts[3] && parts[3].indexOf(' > eval') > -1; + if (isEval && (submatch = geckoEval.exec(parts[3]))) { + // throw out eval line/column and use top-most line number + parts[3] = submatch[1]; + parts[4] = submatch[2]; + parts[5] = null; // no column when eval + } else if (i === 0 && !parts[5] && typeof ex.columnNumber !== 'undefined') { + // FireFox uses this awesome columnNumber property for its top frame + // Also note, Firefox's column number is 0-based and everything else expects 1-based, + // so adding 1 + // NOTE: this hack doesn't work if top-most frame is eval + stack[0].column = ex.columnNumber + 1; + } element = { 'url': parts[3], 'func': parts[1] || UNKNOWN_FUNCTION, @@ -2541,13 +2594,6 @@ TraceKit.computeStackTrace = (function computeStackTraceWrapper() { return null; } - if (!stack[0].column && typeof ex.columnNumber !== 'undefined') { - // FireFox uses this awesome columnNumber property for its top frame - // Also note, Firefox's column number is 0-based and everything else expects 1-based, - // so adding 1 - stack[0].column = ex.columnNumber + 1; - } - return { 'name': ex.name, 'message': ex.message, From f9a456cefde6748caf75f8430feea0a8cd427c18 Mon Sep 17 00:00:00 2001 From: Thibaut Courouble Date: Sat, 15 Apr 2017 10:18:33 -0400 Subject: [PATCH 04/15] Fix error in $.popup --- assets/javascripts/lib/util.coffee | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/assets/javascripts/lib/util.coffee b/assets/javascripts/lib/util.coffee index 12ba49f7..e1dd3cf3 100644 --- a/assets/javascripts/lib/util.coffee +++ b/assets/javascripts/lib/util.coffee @@ -329,11 +329,11 @@ $.requestAnimationFrame = (fn) -> $.noop = -> $.popup = (value) -> - win = window.open() - if win + try + win = window.open() win.opener = null if win.opener win.location = value.href or value - else + catch window.open value.href or value, '_blank' return From 54c7c0df5c220cf6adfb8f5b15ee0e49503d0860 Mon Sep 17 00:00:00 2001 From: Thibaut Courouble Date: Sat, 15 Apr 2017 11:24:50 -0400 Subject: [PATCH 05/15] Don't autofocus search input when another input is focused --- assets/javascripts/views/search/search.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/javascripts/views/search/search.coffee b/assets/javascripts/views/search/search.coffee index 1f48ef42..e4f3900c 100644 --- a/assets/javascripts/views/search/search.coffee +++ b/assets/javascripts/views/search/search.coffee @@ -42,7 +42,7 @@ class app.views.Search extends app.View autoFocus: => unless app.isMobile() or $.isAndroid() or $.isIOS() - @input.focus() unless document.activeElement is @input + @input.focus() unless document.activeElement?.tagName is 'INPUT' return reset: -> From a8073b3a12c1d8141c727fa39a4723f1f5a44cfe Mon Sep 17 00:00:00 2001 From: Thibaut Courouble Date: Sat, 15 Apr 2017 11:30:47 -0400 Subject: [PATCH 06/15] Fix and improve doc picker tab navigation Rel #609. --- assets/javascripts/lib/util.coffee | 10 +++++++--- assets/javascripts/views/layout/settings.coffee | 5 ----- assets/javascripts/views/sidebar/doc_picker.coffee | 2 +- assets/stylesheets/components/_settings.scss | 4 ++-- views/app.erb | 11 +++++------ 5 files changed, 15 insertions(+), 17 deletions(-) diff --git a/assets/javascripts/lib/util.coffee b/assets/javascripts/lib/util.coffee index e1dd3cf3..ca1fbc7a 100644 --- a/assets/javascripts/lib/util.coffee +++ b/assets/javascripts/lib/util.coffee @@ -167,7 +167,8 @@ $.scrollTo = (el, parent, position = 'center', options = {}) -> return unless parent parentHeight = parent.clientHeight - return unless parent.scrollHeight > parentHeight + parentScrollHeight = parent.scrollHeight + return unless parentScrollHeight > parentHeight top = $.offset(el, parent).top offsetTop = parent.firstElementChild.offsetTop @@ -181,14 +182,17 @@ $.scrollTo = (el, parent, position = 'center', options = {}) -> scrollTop = parent.scrollTop height = el.offsetHeight + lastElementOffset = parent.lastElementChild.offsetTop + parent.lastElementChild.offsetHeight + offsetBottom = if lastElementOffset > 0 then parentScrollHeight - lastElementOffset else 0 + # If the target element is above the visible portion of its scrollable # ancestor, move it near the top with a gap = options.topGap * target's height. if top - offsetTop <= scrollTop + height * (options.topGap or 1) parent.scrollTop = top - offsetTop - height * (options.topGap or 1) # If the target element is below the visible portion of its scrollable # ancestor, move it near the bottom with a gap = options.bottomGap * target's height. - else if top >= scrollTop + parentHeight - height * ((options.bottomGap or 1) + 1) - parent.scrollTop = top - parentHeight + height * ((options.bottomGap or 1) + 1) + else if top + offsetBottom >= scrollTop + parentHeight - height * ((options.bottomGap or 1) + 1) + parent.scrollTop = top + offsetBottom - parentHeight + height * ((options.bottomGap or 1) + 1) return $.scrollToWithImageLock = (el, parent, args...) -> diff --git a/assets/javascripts/views/layout/settings.coffee b/assets/javascripts/views/layout/settings.coffee index d669f002..cdc790c6 100644 --- a/assets/javascripts/views/layout/settings.coffee +++ b/assets/javascripts/views/layout/settings.coffee @@ -11,7 +11,6 @@ class app.views.Settings extends app.View @events: submit: 'onSubmit' click: 'onClick' - focus: 'onFocus' @shortcuts: enter: 'onEnter' @@ -69,10 +68,6 @@ class app.views.Settings extends app.View app.router.show '/' return - onFocus: (event) => - $.scrollTo event.target, @el, 'continuous', bottomGap: 2 - return - onAppCacheProgress: (event) => if event.lengthComputable percentage = Math.round event.loaded * 100 / event.total diff --git a/assets/javascripts/views/sidebar/doc_picker.coffee b/assets/javascripts/views/sidebar/doc_picker.coffee index b0869aec..e018794f 100644 --- a/assets/javascripts/views/sidebar/doc_picker.coffee +++ b/assets/javascripts/views/sidebar/doc_picker.coffee @@ -67,7 +67,7 @@ class app.views.DocPicker extends app.View onDOMFocus: (event) => target = event.target if target.tagName is 'INPUT' - $.scrollTo target.parentNode, null, 'continuous', bottomGap: 2 + $.scrollTo target.parentNode, null, 'continuous' else if target.classList.contains(app.views.ListFold.targetClass) target.blur() unless @mouseDown and @mouseDown > Date.now() - 100 diff --git a/assets/stylesheets/components/_settings.scss b/assets/stylesheets/components/_settings.scss index 075db6e3..41d019a6 100644 --- a/assets/stylesheets/components/_settings.scss +++ b/assets/stylesheets/components/_settings.scss @@ -12,13 +12,13 @@ &._in { display: block; } ._sidebar { + display: block !important; + &:after { // padding bottom content: ''; display: block; height: $headerHeight; } - - ._sidebar-hidden & { display: block; } } ._header { justify-content: space-between; } diff --git a/views/app.erb b/views/app.erb index 534d7023..18d92f12 100644 --- a/views/app.erb +++ b/views/app.erb @@ -47,12 +47,11 @@ -
- +
+
From 655beec28c8c1ef6f44a41e3184d606af0f3c81c Mon Sep 17 00:00:00 2001 From: Thibaut Courouble Date: Sat, 15 Apr 2017 12:11:28 -0400 Subject: [PATCH 07/15] Don't auto-scroll when clicking checkbox in doc picker Rel #609. --- assets/javascripts/views/sidebar/doc_picker.coffee | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/assets/javascripts/views/sidebar/doc_picker.coffee b/assets/javascripts/views/sidebar/doc_picker.coffee index e018794f..59c53267 100644 --- a/assets/javascripts/views/sidebar/doc_picker.coffee +++ b/assets/javascripts/views/sidebar/doc_picker.coffee @@ -3,6 +3,7 @@ class app.views.DocPicker extends app.View @events: mousedown: 'onMouseDown' + mouseup: 'onMouseUp' init: -> @addSubview @listFold = new app.views.ListFold(@el) @@ -64,13 +65,18 @@ class app.views.DocPicker extends app.View @mouseDown = Date.now() return + onMouseUp: => + @mouseUp = Date.now() + return + onDOMFocus: (event) => target = event.target if target.tagName is 'INPUT' - $.scrollTo target.parentNode, null, 'continuous' + unless (@mouseDown and Date.now() < @mouseDown + 100) or (@mouseUp and Date.now() < @mouseUp + 100) + $.scrollTo target.parentNode, null, 'continuous' else if target.classList.contains(app.views.ListFold.targetClass) target.blur() - unless @mouseDown and @mouseDown > Date.now() - 100 + unless @mouseDown and Date.now() < @mouseDown + 100 if @focusEl is $('input', target.nextElementSibling) @listFold.close(target) if target.classList.contains(app.views.ListFold.activeClass) prev = target.previousElementSibling From aed1d8e56eaa48efcad04d0dfbe2295fbb9d709a Mon Sep 17 00:00:00 2001 From: Thibaut Courouble Date: Sat, 15 Apr 2017 12:40:01 -0400 Subject: [PATCH 08/15] Fix space key not activating links and checkboxes --- assets/javascripts/app/shortcuts.coffee | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/assets/javascripts/app/shortcuts.coffee b/assets/javascripts/app/shortcuts.coffee index 57c96d8f..27a7edf7 100644 --- a/assets/javascripts/app/shortcuts.coffee +++ b/assets/javascripts/app/shortcuts.coffee @@ -58,7 +58,7 @@ class app.Shortcuts when 27 @trigger 'escape' when 32 - if not @lastKeypress or @lastKeypress < Date.now() - 500 + if event.target.type is 'search' and (not @lastKeypress or @lastKeypress < Date.now() - 500) @trigger 'pageDown' false when 33 From b353d3afa18e680141a3ef13fabd88a459f5c6df Mon Sep 17 00:00:00 2001 From: Thibaut Courouble Date: Sat, 15 Apr 2017 12:40:49 -0400 Subject: [PATCH 09/15] Add form attribute to settings page inputs --- .../javascripts/templates/pages/settings_tmpl.coffee | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/assets/javascripts/templates/pages/settings_tmpl.coffee b/assets/javascripts/templates/pages/settings_tmpl.coffee index a5e36108..e1b0a6dc 100644 --- a/assets/javascripts/templates/pages/settings_tmpl.coffee +++ b/assets/javascripts/templates/pages/settings_tmpl.coffee @@ -6,13 +6,13 @@ app.templates.settingsPage = (settings) -> """
@@ -23,10 +23,10 @@ app.templates.settingsPage = (settings) -> """
From 7947f2d9ba790538cbcb3deebabae7ef87d5718b Mon Sep 17 00:00:00 2001 From: Thibaut Courouble Date: Sun, 16 Apr 2017 10:27:16 -0400 Subject: [PATCH 10/15] Fix issue when hitting escaping key in single doc mode --- assets/javascripts/app/shortcuts.coffee | 1 + 1 file changed, 1 insertion(+) diff --git a/assets/javascripts/app/shortcuts.coffee b/assets/javascripts/app/shortcuts.coffee index 27a7edf7..5bc24806 100644 --- a/assets/javascripts/app/shortcuts.coffee +++ b/assets/javascripts/app/shortcuts.coffee @@ -57,6 +57,7 @@ class app.Shortcuts @trigger 'enter' when 27 @trigger 'escape' + false when 32 if event.target.type is 'search' and (not @lastKeypress or @lastKeypress < Date.now() - 500) @trigger 'pageDown' From 4f62ccbdbf4ea27952c86dfe3fe1a5e2f14ab045 Mon Sep 17 00:00:00 2001 From: Thibaut Courouble Date: Sun, 16 Apr 2017 10:58:36 -0400 Subject: [PATCH 11/15] Fix various issues when switching from single doc to full app --- assets/javascripts/app/router.coffee | 36 +++++++++++-------- assets/javascripts/lib/page.coffee | 24 +++++++------ .../views/content/offline_page.coffee | 7 ++-- .../views/content/settings_page.coffee | 7 ++-- 4 files changed, 39 insertions(+), 35 deletions(-) diff --git a/assets/javascripts/app/router.coffee b/assets/javascripts/app/router.coffee index 91b2cfef..3b167e1b 100644 --- a/assets/javascripts/app/router.coffee +++ b/assets/javascripts/app/router.coffee @@ -34,19 +34,24 @@ class app.Router return before: (context, next) -> + previousContext = @context @context = context @trigger 'before', context - next() - return + + if res = next() + @context = previousContext + return res + else + return doc: (context, next) -> if doc = app.docs.findBySlug(context.params.doc) or app.disabledDocs.findBySlug(context.params.doc) context.doc = doc context.entry = doc.toEntry() @triggerRoute 'entry' + return else - next() - return + return next() type: (context, next) -> doc = app.docs.findBySlug(context.params.doc) @@ -55,9 +60,9 @@ class app.Router context.doc = doc context.type = type @triggerRoute 'type' + return else - next() - return + return next() entry: (context, next) -> doc = app.docs.findBySlug(context.params.doc) @@ -66,36 +71,39 @@ class app.Router context.doc = doc context.entry = entry @triggerRoute 'entry' + return else - next() - return + return next() root: -> - if app.isSingleDoc() - setTimeout (-> window.location = '/'), 0 - else - @triggerRoute 'root' + return '/' if app.isSingleDoc() + @triggerRoute 'root' return - settings: -> + settings: (context) -> + return "/#/#{context.path}" if app.isSingleDoc() @triggerRoute 'settings' return - offline: -> + offline: (context)-> + return "/#/#{context.path}" if app.isSingleDoc() @triggerRoute 'offline' return about: (context) -> + return "/#/#{context.path}" if app.isSingleDoc() context.page = 'about' @triggerRoute 'page' return news: (context) -> + return "/#/#{context.path}" if app.isSingleDoc() context.page = 'news' @triggerRoute 'page' return help: (context) -> + return "/#/#{context.path}" if app.isSingleDoc() context.page = 'help' @triggerRoute 'page' return diff --git a/assets/javascripts/lib/page.coffee b/assets/javascripts/lib/page.coffee index bfe454a0..3f40be38 100644 --- a/assets/javascripts/lib/page.coffee +++ b/assets/javascripts/lib/page.coffee @@ -38,11 +38,15 @@ page.stop = -> page.show = (path, state) -> return if path is currentState?.path context = new Context(path, state) + previousState = currentState currentState = context.state - page.dispatch(context) - context.pushState() - updateCanonicalLink() - track() + if res = page.dispatch(context) + currentState = previousState + location.assign(res) + else + context.pushState() + updateCanonicalLink() + track() context page.replace = (path, state, skipDispatch, init) -> @@ -58,10 +62,9 @@ page.replace = (path, state, skipDispatch, init) -> page.dispatch = (context) -> i = 0 next = -> - fn(context, next) if fn = callbacks[i++] - return - next() - return + res = fn(context, next) if fn = callbacks[i++] + return res + return next() page.canGoBack = -> not Context.isIntialState(currentState) @@ -116,10 +119,9 @@ class Route (context, next) => if @match context.pathname, params = [] context.params = params - fn(context, next) + return fn(context, next) else - next() - return + return next() match: (path, params) -> return unless matchData = @regexp.exec(path) diff --git a/assets/javascripts/views/content/offline_page.coffee b/assets/javascripts/views/content/offline_page.coffee index 97cae8d4..58f2a181 100644 --- a/assets/javascripts/views/content/offline_page.coffee +++ b/assets/javascripts/views/content/offline_page.coffee @@ -45,11 +45,8 @@ class app.views.OfflinePage extends app.View docEl: (doc) -> @find("[data-slug='#{doc.slug}']") - onRoute: (route) -> - if app.isSingleDoc() - window.location = "/#/#{route.path}" - else - @render() + onRoute: (context) -> + @render() return onClick: (event) => diff --git a/assets/javascripts/views/content/settings_page.coffee b/assets/javascripts/views/content/settings_page.coffee index c3671c29..d1d3d0db 100644 --- a/assets/javascripts/views/content/settings_page.coffee +++ b/assets/javascripts/views/content/settings_page.coffee @@ -58,9 +58,6 @@ class app.views.SettingsPage extends app.View @toggle input.name, input.checked return - onRoute: (route) => - if app.isSingleDoc() - window.location = "/#/#{route.path}" - else - @render() + onRoute: (context) -> + @render() return From 82743163c68ba61bbe0fc442518442eba7058299 Mon Sep 17 00:00:00 2001 From: Thibaut Courouble Date: Sun, 16 Apr 2017 11:22:52 -0400 Subject: [PATCH 12/15] Fix searching disabled doc via URL not opening first result correctly Fixes #604. --- assets/javascripts/views/search/search_scope.coffee | 2 +- assets/javascripts/views/sidebar/results.coffee | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/assets/javascripts/views/search/search_scope.coffee b/assets/javascripts/views/search/search_scope.coffee index 8ed5755d..500ba20f 100644 --- a/assets/javascripts/views/search/search_scope.coffee +++ b/assets/javascripts/views/search/search_scope.coffee @@ -70,7 +70,7 @@ class app.views.SearchScope extends app.View redirectToDoc: (doc) -> hash = location.hash app.router.replaceHash('') - window.location = doc.fullPath() + hash + location.assign doc.fullPath() + hash return reset: => diff --git a/assets/javascripts/views/sidebar/results.coffee b/assets/javascripts/views/sidebar/results.coffee index 0793c452..f5bed318 100644 --- a/assets/javascripts/views/sidebar/results.coffee +++ b/assets/javascripts/views/sidebar/results.coffee @@ -46,7 +46,7 @@ class app.views.Results extends app.View return openFirst: -> - @el.firstElementChild?.click() + setTimeout (=> @el.firstElementChild?.click()), 0 return onDocEnabled: (doc) -> From 98a6eb58cbcf0b5b19e8221cd15dc014eb3d1ebc Mon Sep 17 00:00:00 2001 From: Thibaut Courouble Date: Sun, 16 Apr 2017 11:50:43 -0400 Subject: [PATCH 13/15] Reset search field when browsing to index page in single doc mode --- assets/javascripts/app/router.coffee | 9 +++------ assets/javascripts/views/search/search.coffee | 10 +++------- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/assets/javascripts/app/router.coffee b/assets/javascripts/app/router.coffee index 3b167e1b..9a0e0b0a 100644 --- a/assets/javascripts/app/router.coffee +++ b/assets/javascripts/app/router.coffee @@ -112,18 +112,15 @@ class app.Router @triggerRoute 'notFound' return - isRoot: -> - location.pathname is '/' - - isDocIndex: -> - @context and @context.doc and @context.entry is @context.doc.toEntry() + isIndex: -> + @context.path is '/' or (app.isSingleDoc() and @context?.entry?.isIndex()) setInitialPath: -> # Remove superfluous forward slashes at the beginning of the path if (path = location.pathname.replace /^\/{2,}/g, '/') isnt location.pathname page.replace path + location.search + location.hash, null, true - if @isRoot() + if location.pathname is '/' if path = @getInitialPathFromHash() page.replace path + location.search, null, true else if path = @getInitialPathFromCookie() diff --git a/assets/javascripts/views/search/search.coffee b/assets/javascripts/views/search/search.coffee index e4f3900c..b9d72b6a 100644 --- a/assets/javascripts/views/search/search.coffee +++ b/assets/javascripts/views/search/search.coffee @@ -19,7 +19,6 @@ class app.views.Search extends app.View altS: 'stackoverflow' @routes: - root: 'onRoot' after: 'afterRoute' init: -> @@ -77,9 +76,9 @@ class app.views.Search extends app.View return searchUrl: => - if app.router.isRoot() + if location.pathname is '/' @scope.searchUrl() - else if not app.router.isDocIndex() + else if not app.router.isIndex() return return unless value = @extractHashValue() @@ -129,11 +128,8 @@ class app.views.Search extends app.View $.stopEvent(event) return - onRoot: (context) => - @reset() unless context.init - return - afterRoute: (name, context) => + @reset() if not context.init and app.router.isIndex() @delay @searchUrl if context.hash @delay @autoFocus return From ecb56f1e97e0835f39b1b497cb176e5bb45ee085 Mon Sep 17 00:00:00 2001 From: Thibaut Courouble Date: Sun, 16 Apr 2017 12:45:40 -0400 Subject: [PATCH 14/15] Fix ordering of versioned docs in sidebar and search --- assets/javascripts/collections/docs.coffee | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/assets/javascripts/collections/docs.coffee b/assets/javascripts/collections/docs.coffee index c4b2e368..d76e0f07 100644 --- a/assets/javascripts/collections/docs.coffee +++ b/assets/javascripts/collections/docs.coffee @@ -4,11 +4,19 @@ class app.collections.Docs extends app.Collection findBySlug: (slug) -> @findBy('slug', slug) or @findBy('slug_without_version', slug) + NORMALIZE_VERSION_RGX = /\.(\d)$/ + NORMALIZE_VERSION_SUB = '.0$1' sort: -> @models.sort (a, b) -> - a = a.name.toLowerCase() - b = b.name.toLowerCase() - if a < b then -1 else if a > b then 1 else 0 + if a.name is b.name + if not a.version or a.version.replace(NORMALIZE_VERSION_RGX, NORMALIZE_VERSION_SUB) > b.version.replace(NORMALIZE_VERSION_RGX, NORMALIZE_VERSION_SUB) + -1 + else + 1 + else if a.name.toLowerCase() > b.name.toLowerCase() + 1 + else + -1 # Load models concurrently. # It's not pretty but I didn't want to import a promise library only for this. From 97dbb2afde4ae4e5b1b9e0be375c2271ccaa56ca Mon Sep 17 00:00:00 2001 From: Thibaut Courouble Date: Sun, 16 Apr 2017 12:57:31 -0400 Subject: [PATCH 15/15] Don't sort already-sorted docs on boot --- assets/javascripts/app/app.coffee | 2 -- 1 file changed, 2 deletions(-) diff --git a/assets/javascripts/app/app.coffee b/assets/javascripts/app/app.coffee index 1be8136b..912c1852 100644 --- a/assets/javascripts/app/app.coffee +++ b/assets/javascripts/app/app.coffee @@ -92,8 +92,6 @@ for doc in @DOCS (if docs.indexOf(doc.slug) >= 0 then @docs else @disabledDocs).add(doc) @migrateDocs() - @docs.sort() - @disabledDocs.sort() @docs.load @start.bind(@), @onBootError.bind(@), readCache: true, writeCache: true delete @DOCS return