diff --git a/app/assets/javascripts/discourse/components/composer-editor.js.es6 b/app/assets/javascripts/discourse/components/composer-editor.js.es6 index 468cbaf0f47..ae3eea03a62 100644 --- a/app/assets/javascripts/discourse/components/composer-editor.js.es6 +++ b/app/assets/javascripts/discourse/components/composer-editor.js.es6 @@ -88,7 +88,16 @@ export default Ember.Component.extend({ transformComplete: v => v.username || v.name }); - this._initInputPreviewSync($input, $preview); + if (this._enableAdvancedEditorPreviewSync()) { + this._initInputPreviewSync($input, $preview); + } else { + $input.on('scroll', () => Ember.run.throttle(this, + this._syncEditorAndPreviewScroll, + $input, + $preview, + 20 + )); + } // Focus on the body unless we have a title if (!this.get('composer.canEditTitle') && !this.capabilities.isIOS) { @@ -128,6 +137,10 @@ export default Ember.Component.extend({ } }, + _enableAdvancedEditorPreviewSync() { + return this.siteSettings.enable_advanced_editor_preview_sync; + }, + _resetShouldBuildScrollMap() { this.set('shouldBuildScrollMap', true); }, @@ -254,21 +267,44 @@ export default Ember.Component.extend({ }, _syncEditorAndPreviewScroll($input, $preview, scrollMap) { - let scrollTop; - const inputHeight = $input.height(); - const inputScrollHeight = $input[0].scrollHeight; - const inputClientHeight = $input[0].clientHeight; - const scrollable = inputScrollHeight > inputClientHeight; + if (this._enableAdvancedEditorPreviewSync()) { + let scrollTop; + const inputHeight = $input.height(); + const inputScrollHeight = $input[0].scrollHeight; + const inputClientHeight = $input[0].clientHeight; + const scrollable = inputScrollHeight > inputClientHeight; - if (scrollable && ((inputHeight + $input.scrollTop() + 100) > inputScrollHeight)) { - scrollTop = $preview[0].scrollHeight; + if (scrollable && ((inputHeight + $input.scrollTop() + 100) > inputScrollHeight)) { + scrollTop = $preview[0].scrollHeight; + } else { + const lineHeight = parseFloat($input.css('line-height')); + const lineNumber = Math.floor($input.scrollTop() / lineHeight); + scrollTop = scrollMap[lineNumber]; + } + + $preview.stop(true).animate({ scrollTop }, 100, 'linear'); } else { - const lineHeight = parseFloat($input.css('line-height')); - const lineNumber = Math.floor($input.scrollTop() / lineHeight); - scrollTop = scrollMap[lineNumber]; - } + if (!$input) { return; } - $preview.stop(true).animate({ scrollTop }, 100, 'linear'); + if ($input.scrollTop() === 0) { + $preview.scrollTop(0); + return; + } + + const inputHeight = $input[0].scrollHeight; + const previewHeight = $preview[0].scrollHeight; + + if (($input.height() + $input.scrollTop() + 100) > inputHeight) { + // cheat, special case for bottom + $preview.scrollTop(previewHeight); + return; + } + + const scrollPosition = $input.scrollTop(); + const factor = previewHeight / inputHeight; + const desired = scrollPosition * factor; + $preview.scrollTop(desired + 50); + } }, _syncPreviewAndEditorScroll($input, $preview, scrollMap) { @@ -609,7 +645,7 @@ export default Ember.Component.extend({ Ember.run.later(() => this.appEvents.trigger("composer:closed"), 400); }); - this._teardownInputPreviewSync(); + if (this._enableAdvancedEditorPreviewSync()) this._teardownInputPreviewSync(); if (this.site.mobileView) { $(window).off('resize.composer-popup-menu'); @@ -732,7 +768,14 @@ export default Ember.Component.extend({ Ember.run.debounce(this, this._loadInlineOneboxes, inline, 450); } - this._syncScroll(this._syncEditorAndPreviewScroll, this.$('.d-editor-input'), $preview); + if (this._enableAdvancedEditorPreviewSync()) { + this._syncScroll( + this._syncEditorAndPreviewScroll, + this.$('.d-editor-input'), + $preview + ); + } + this.trigger('previewRefreshed', $preview); this.sendAction('afterRefresh', $preview); }, diff --git a/app/assets/javascripts/pretty-text/engines/discourse-markdown/inject-line-number.js.es6 b/app/assets/javascripts/pretty-text/engines/discourse-markdown/inject-line-number.js.es6 index eaad9f61b6a..1f741d7df93 100644 --- a/app/assets/javascripts/pretty-text/engines/discourse-markdown/inject-line-number.js.es6 +++ b/app/assets/javascripts/pretty-text/engines/discourse-markdown/inject-line-number.js.es6 @@ -1,5 +1,7 @@ export function setup(helper) { - if (helper.getOptions().previewing) { + const opts = helper.getOptions(); + + if (opts.previewing && opts.injectLineNumbersToPreview) { helper.whiteList([ 'p.preview-sync-line', 'p[data-line-number]', diff --git a/app/assets/javascripts/pretty-text/pretty-text.js.es6 b/app/assets/javascripts/pretty-text/pretty-text.js.es6 index 002f489d7db..0d24a92aef5 100644 --- a/app/assets/javascripts/pretty-text/pretty-text.js.es6 +++ b/app/assets/javascripts/pretty-text/pretty-text.js.es6 @@ -71,6 +71,7 @@ export function buildOptions(state) { allowedHrefSchemes: siteSettings.allowed_href_schemes ? siteSettings.allowed_href_schemes.split('|') : null, allowedIframes: siteSettings.allowed_iframes ? siteSettings.allowed_iframes.split('|') : [], markdownIt: true, + injectLineNumbersToPreview: siteSettings.enable_advanced_editor_preview_sync, previewing }; diff --git a/config/site_settings.yml b/config/site_settings.yml index 143e039eb0d..d2639e964f8 100644 --- a/config/site_settings.yml +++ b/config/site_settings.yml @@ -625,6 +625,11 @@ posting: min: 1 watched_words_regular_expressions: default: false + enable_advanced_editor_preview_sync: + hidden: true + default: false + client: true + shadowed_by_global: true email: email_time_window_mins: