2017-08-22 10:53:26 +03:00
import Component from '@ember/component' ;
2017-06-22 15:02:38 +03:00
import ShortcutsMixin from 'ghost-admin/mixins/shortcuts' ;
import ctrlOrCmd from 'ghost-admin/utils/ctrl-or-cmd' ;
2017-05-29 21:50:03 +03:00
import formatMarkdown from 'ghost-admin/utils/format-markdown' ;
2017-08-22 10:53:26 +03:00
import { assign } from '@ember/polyfills' ;
import { computed } from '@ember/object' ;
import { copy } from '@ember/object/internals' ;
import { htmlSafe } from '@ember/string' ;
import { isEmpty , typeOf } from '@ember/utils' ;
import { run } from '@ember/runloop' ;
2017-10-30 12:38:01 +03:00
import { inject as service } from '@ember/service' ;
2017-05-08 13:35:42 +03:00
const MOBILEDOC _VERSION = '0.3.1' ;
export const BLANK _DOC = {
version : MOBILEDOC _VERSION ,
markups : [ ] ,
atoms : [ ] ,
cards : [
[ 'card-markdown' , {
cardName : 'card-markdown' ,
markdown : ''
} ]
] ,
sections : [ [ 10 , 0 ] ]
} ;
2017-06-22 15:02:38 +03:00
export default Component . extend ( ShortcutsMixin , {
2017-05-08 13:35:42 +03:00
2017-10-30 12:38:01 +03:00
config : service ( ) ,
notifications : service ( ) ,
settings : service ( ) ,
2017-07-28 16:21:09 +03:00
2017-05-08 21:15:56 +03:00
classNames : [ 'gh-markdown-editor' ] ,
classNameBindings : [
'_isFullScreen:gh-markdown-editor-full-screen' ,
'_isSplitScreen:gh-markdown-editor-side-by-side'
] ,
2017-05-08 13:35:42 +03:00
// Public attributes
autofocus : false ,
2017-05-10 18:16:36 +03:00
imageMimeTypes : null ,
2017-05-08 21:15:56 +03:00
isFullScreen : false ,
2017-05-08 13:35:42 +03:00
mobiledoc : null ,
options : null ,
placeholder : '' ,
uploadedImageUrls : null ,
// Closure actions
onChange ( ) { } ,
2017-05-12 11:06:56 +03:00
onFullScreenToggle ( ) { } ,
2017-05-10 18:16:36 +03:00
onImageFilesSelected ( ) { } ,
2017-05-12 11:06:56 +03:00
onPreviewToggle ( ) { } ,
onSplitScreenToggle ( ) { } ,
2017-05-08 13:35:42 +03:00
showMarkdownHelp ( ) { } ,
// Internal attributes
markdown : null ,
// Private
_editor : null ,
2017-08-02 10:05:59 +03:00
_editorFocused : false ,
2017-05-08 21:15:56 +03:00
_isFullScreen : false ,
_isSplitScreen : false ,
2017-10-03 08:31:10 +03:00
_isHemingwayMode : false ,
2017-05-08 13:35:42 +03:00
_isUploading : false ,
2017-08-02 10:05:59 +03:00
_showUnsplash : false ,
2017-05-08 13:35:42 +03:00
_statusbar : null ,
_toolbar : null ,
2017-05-08 21:15:56 +03:00
_uploadedImageUrls : null ,
2017-05-08 13:35:42 +03:00
simpleMDEOptions : computed ( 'options' , function ( ) {
let options = this . get ( 'options' ) || { } ;
let defaultOptions = {
2017-05-15 19:51:19 +03:00
// use our Showdown config with sanitization for previews
previewRender ( markdown ) {
return formatMarkdown ( markdown ) ;
} ,
// Ghost-specific SimpleMDE toolbar config - allows us to create a
// bridge between SimpleMDE buttons and Ember actions
2017-05-08 13:35:42 +03:00
toolbar : [
'bold' , 'italic' , 'heading' , '|' ,
'quote' , 'unordered-list' , 'ordered-list' , '|' ,
2017-05-10 18:16:36 +03:00
'link' ,
{
name : 'image' ,
action : ( ) => {
this . _openImageFileDialog ( ) ;
} ,
className : 'fa fa-picture-o' ,
title : 'Upload Image(s)'
} ,
'|' ,
2017-05-12 11:06:56 +03:00
{
name : 'preview' ,
action : ( ) => {
this . _togglePreview ( ) ;
} ,
className : 'fa fa-eye no-disable' ,
2017-10-03 08:31:10 +03:00
title : 'Render Preview (Ctrl-Alt-R)' ,
useCtrlOnMac : true
2017-05-12 11:06:56 +03:00
} ,
2017-05-08 21:15:56 +03:00
{
name : 'side-by-side' ,
action : ( ) => {
this . send ( 'toggleSplitScreen' ) ;
} ,
className : 'fa fa-columns no-disable no-mobile' ,
2017-10-03 08:31:10 +03:00
title : 'Side-by-side Preview (Ctrl-Alt-P)' ,
useCtrlOnMac : true
2017-05-08 13:35:42 +03:00
} ,
'|' ,
2017-05-18 13:08:50 +03:00
{
name : 'spellcheck' ,
action : ( ) => {
this . _toggleSpellcheck ( ) ;
} ,
className : 'fa fa-check' ,
2017-10-03 08:31:10 +03:00
title : 'Spellcheck (Ctrl-Alt-S)' ,
useCtrlOnMac : true
2017-05-18 13:08:50 +03:00
} ,
2017-07-28 16:21:09 +03:00
{
2017-10-03 08:31:10 +03:00
name : 'hemingway' ,
2017-07-28 16:21:09 +03:00
action : ( ) => {
2017-10-03 08:31:10 +03:00
this . _toggleHemingway ( ) ;
2017-07-28 16:21:09 +03:00
} ,
className : 'fa fa-h-square' ,
2017-10-03 08:31:10 +03:00
title : 'Hemingway Mode (Ctrl-Alt-H)' ,
useCtrlOnMac : true
2017-07-28 16:21:09 +03:00
} ,
2017-05-08 13:35:42 +03:00
{
name : 'guide' ,
action : ( ) => {
this . showMarkdownHelp ( ) ;
} ,
className : 'fa fa-question-circle' ,
title : 'Markdown Guide'
}
] ,
2017-05-15 19:51:19 +03:00
// disable shortcuts for side-by-side and fullscreen because they
// trigger interal SimpleMDE methods that will result in broken
// layouts
2017-05-10 18:16:36 +03:00
shortcuts : {
2017-05-12 11:06:56 +03:00
toggleFullScreen : null ,
togglePreview : null ,
2017-06-22 18:36:40 +03:00
toggleSideBySide : null ,
drawImage : null
2017-05-10 18:16:36 +03:00
} ,
2017-05-15 19:51:19 +03:00
// only include the number of words in the status bar
2017-05-08 13:35:42 +03:00
status : [ 'words' ]
} ;
2017-09-20 13:19:48 +03:00
if ( this . get ( 'settings.unsplash.isActive' ) ) {
2017-08-02 10:05:59 +03:00
let image = defaultOptions . toolbar . findBy ( 'name' , 'image' ) ;
let index = defaultOptions . toolbar . indexOf ( image ) + 1 ;
defaultOptions . toolbar . splice ( index , 0 , {
name : 'unsplash' ,
action : ( ) => {
this . send ( 'toggleUnsplash' ) ;
} ,
className : 'fa fa-camera' ,
title : 'Add Image from Unsplash'
} ) ;
}
2017-05-08 13:35:42 +03:00
return assign ( defaultOptions , options ) ;
} ) ,
2017-06-22 15:02:38 +03:00
shortcuts : { } ,
init ( ) {
this . _super ( ... arguments ) ;
let shortcuts = this . get ( 'shortcuts' ) ;
2017-08-02 10:05:59 +03:00
shortcuts [ ` ${ ctrlOrCmd } +shift+i ` ] = { action : 'openImageFileDialog' } ;
2017-10-03 08:31:10 +03:00
shortcuts [ 'ctrl+alt+r' ] = { action : 'togglePreview' } ;
shortcuts [ 'ctrl+alt+p' ] = { action : 'toggleSplitScreen' } ;
shortcuts [ 'ctrl+alt+s' ] = { action : 'toggleSpellcheck' } ;
shortcuts [ 'ctrl+alt+h' ] = { action : 'toggleHemingway' } ;
2017-06-22 15:02:38 +03:00
} ,
2017-05-08 13:35:42 +03:00
// extract markdown content from single markdown card
didReceiveAttrs ( ) {
this . _super ( ... arguments ) ;
let mobiledoc = this . get ( 'mobiledoc' ) || copy ( BLANK _DOC , true ) ;
let uploadedImageUrls = this . get ( 'uploadedImageUrls' ) ;
if ( ! isEmpty ( uploadedImageUrls ) && uploadedImageUrls !== this . _uploadedImageUrls ) {
this . _uploadedImageUrls = uploadedImageUrls ;
// must be done afterRender to avoid double modify of mobiledoc in
// a single render
run . scheduleOnce ( 'afterRender' , this , ( ) => {
this . _insertImages ( uploadedImageUrls ) ;
} ) ;
}
// eslint-disable-next-line ember-suave/prefer-destructuring
let markdown = mobiledoc . cards [ 0 ] [ 1 ] . markdown ;
this . set ( 'markdown' , markdown ) ;
2017-05-08 21:15:56 +03:00
// use internal values to avoid updating bound values
if ( ! isEmpty ( this . get ( 'isFullScreen' ) ) ) {
this . set ( '_isFullScreen' , this . get ( 'isFullScreen' ) ) ;
}
if ( ! isEmpty ( this . get ( 'isSplitScreen' ) ) ) {
this . set ( '_isSplitScreen' , this . get ( 'isSplitScreen' ) ) ;
}
this . _updateButtonState ( ) ;
2017-05-08 13:35:42 +03:00
} ,
2017-06-22 15:02:38 +03:00
didInsertElement ( ) {
this . _super ( ... arguments ) ;
this . registerShortcuts ( ) ;
} ,
2017-05-08 13:35:42 +03:00
_insertImages ( urls ) {
let cm = this . _editor . codemirror ;
// loop through urls and generate image markdown
let images = urls . map ( ( url ) => {
2017-08-02 10:05:59 +03:00
// plain url string, so extract filename from path
if ( typeOf ( url ) === 'string' ) {
let filename = url . split ( '/' ) . pop ( ) ;
let alt = filename ;
// if we have a normal filename.ext, set alt to filename -ext
if ( filename . lastIndexOf ( '.' ) > 0 ) {
alt = filename . slice ( 0 , filename . lastIndexOf ( '.' ) ) ;
}
2017-05-17 14:27:27 +03:00
2017-08-02 10:05:59 +03:00
return ` ![ ${ alt } ]( ${ url } ) ` ;
2017-05-17 14:27:27 +03:00
2017-08-02 10:05:59 +03:00
// full url object, use attrs we're given
} else {
let image = ` ![ ${ url . alt } ]( ${ url . url } ) ` ;
if ( url . credit ) {
image += ` \n ${ url . credit } ` ;
}
return image ;
}
2017-05-08 13:35:42 +03:00
} ) ;
2017-08-02 10:05:59 +03:00
let text = images . join ( '\n\n' ) ;
2017-05-08 13:35:42 +03:00
2017-05-10 18:16:36 +03:00
// clicking the image toolbar button will lose the selection so we use
// the captured selection to re-select here
if ( this . _imageInsertSelection ) {
// we want to focus but not re-position
this . send ( 'focusEditor' , null ) ;
// re-select and clear the captured selection so drag/drop still
// inserts at the correct place
cm . setSelection (
this . _imageInsertSelection . anchor ,
this . _imageInsertSelection . head
) ;
this . _imageInsertSelection = null ;
}
2017-05-08 13:35:42 +03:00
// focus editor and place cursor at end if not already focused
if ( ! cm . hasFocus ( ) ) {
this . send ( 'focusEditor' ) ;
2017-08-02 10:05:59 +03:00
text = ` \n \n ${ text } \n \n ` ;
2017-05-08 13:35:42 +03:00
}
// insert at cursor or replace selection then position cursor at end
// of inserted text
cm . replaceSelection ( text , 'end' ) ;
} ,
2017-05-18 13:08:50 +03:00
// mark the split-pane/full-screen/spellcheck buttons active when they're active
2017-05-08 21:15:56 +03:00
_updateButtonState ( ) {
if ( this . _editor ) {
let sideBySideButton = this . _editor . toolbarElements [ 'side-by-side' ] ;
2017-05-18 13:08:50 +03:00
let spellcheckButton = this . _editor . toolbarElements . spellcheck ;
2017-10-03 08:31:10 +03:00
let hemingwayButton = this . _editor . toolbarElements . hemingway ;
2017-05-08 21:15:56 +03:00
2017-07-20 14:37:18 +03:00
if ( sideBySideButton ) {
if ( this . get ( '_isSplitScreen' ) ) {
sideBySideButton . classList . add ( 'active' ) ;
} else {
sideBySideButton . classList . remove ( 'active' ) ;
}
2017-05-08 21:15:56 +03:00
}
2017-05-18 13:08:50 +03:00
2017-07-20 14:37:18 +03:00
if ( spellcheckButton ) {
if ( this . _editor . codemirror . getOption ( 'mode' ) === 'spell-checker' ) {
spellcheckButton . classList . add ( 'active' ) ;
} else {
spellcheckButton . classList . remove ( 'active' ) ;
}
2017-05-18 13:08:50 +03:00
}
2017-07-28 16:21:09 +03:00
2017-10-03 08:31:10 +03:00
if ( hemingwayButton ) {
if ( this . _isHemingwayMode ) {
hemingwayButton . classList . add ( 'active' ) ;
2017-07-28 16:21:09 +03:00
} else {
2017-10-03 08:31:10 +03:00
hemingwayButton . classList . remove ( 'active' ) ;
2017-07-28 16:21:09 +03:00
}
}
2017-05-08 21:15:56 +03:00
}
} ,
// set up the preview auto-update and scroll sync
_connectSplitPreview ( ) {
let cm = this . _editor . codemirror ;
let editor = this . _editor ;
/* eslint-disable ember-suave/prefer-destructuring */
let editorPane = this . $ ( '.gh-markdown-editor-pane' ) [ 0 ] ;
let previewPane = this . $ ( '.gh-markdown-editor-preview' ) [ 0 ] ;
let previewContent = this . $ ( '.gh-markdown-editor-preview-content' ) [ 0 ] ;
/* eslint-enable ember-suave/prefer-destructuring */
this . _editorPane = editorPane ;
this . _previewPane = previewPane ;
this . _previewContent = previewContent ;
// from SimpleMDE -------
let sideBySideRenderingFunction = function ( ) {
previewContent . innerHTML = editor . options . previewRender (
editor . value ( ) ,
previewContent
) ;
} ;
cm . sideBySideRenderingFunction = sideBySideRenderingFunction ;
sideBySideRenderingFunction ( ) ;
cm . on ( 'update' , cm . sideBySideRenderingFunction ) ;
// Refresh to fix selection being off (#309)
cm . refresh ( ) ;
// ----------------------
this . _onEditorPaneScroll = this . _scrollHandler . bind ( this ) ;
editorPane . addEventListener ( 'scroll' , this . _onEditorPaneScroll , false ) ;
this . _scrollSync ( ) ;
} ,
_scrollHandler ( ) {
if ( ! this . _scrollSyncTicking ) {
requestAnimationFrame ( this . _scrollSync . bind ( this ) ) ;
}
this . _scrollSyncTicking = true ;
} ,
_scrollSync ( ) {
let editorPane = this . _editorPane ;
let previewPane = this . _previewPane ;
let height = editorPane . scrollHeight - editorPane . clientHeight ;
let ratio = parseFloat ( editorPane . scrollTop ) / height ;
let move = ( previewPane . scrollHeight - previewPane . clientHeight ) * ratio ;
previewPane . scrollTop = move ;
this . _scrollSyncTicking = false ;
} ,
_disconnectSplitPreview ( ) {
let cm = this . _editor . codemirror ;
cm . off ( 'update' , cm . sideBySideRenderingFunction ) ;
cm . refresh ( ) ;
this . _editorPane . removeEventListener ( 'scroll' , this . _onEditorPaneScroll , false ) ;
delete this . _previewPane ;
delete this . _previewPaneContent ;
delete this . _onEditorPaneScroll ;
} ,
2017-07-04 13:11:54 +03:00
_openImageFileDialog ( { captureSelection = true } = { } ) {
2017-06-22 15:02:38 +03:00
if ( captureSelection ) {
// capture the current selection before it's lost by clicking the
// file input button
this . _imageInsertSelection = {
anchor : this . _editor . codemirror . getCursor ( 'anchor' ) ,
head : this . _editor . codemirror . getCursor ( 'head' )
} ;
}
2017-05-10 18:16:36 +03:00
// trigger the dialog via gh-file-input, when a file is selected it will
// trigger the onImageFilesSelected closure action
this . $ ( 'input[type="file"]' ) . click ( ) ;
} ,
2017-05-12 11:06:56 +03:00
// wrap SimpleMDE's built-in preview toggle so that we can trigger a closure
// action that can apply our own classes higher up in the DOM
_togglePreview ( ) {
this . onPreviewToggle ( ! this . _editor . isPreviewActive ( ) ) ;
this . _editor . togglePreview ( ) ;
} ,
2017-05-18 13:08:50 +03:00
_toggleSpellcheck ( ) {
let cm = this . _editor . codemirror ;
if ( cm . getOption ( 'mode' ) === 'spell-checker' ) {
2017-07-20 13:59:24 +03:00
cm . setOption ( 'mode' , 'gfm' ) ;
2017-05-18 13:08:50 +03:00
} else {
cm . setOption ( 'mode' , 'spell-checker' ) ;
}
this . _updateButtonState ( ) ;
} ,
2017-10-03 08:31:10 +03:00
_toggleHemingway ( ) {
2017-07-28 16:21:09 +03:00
let cm = this . _editor . codemirror ;
let extraKeys = cm . getOption ( 'extraKeys' ) ;
let notificationText = '' ;
2017-10-03 08:31:10 +03:00
this . _isHemingwayMode = ! this . _isHemingwayMode ;
2017-07-28 16:21:09 +03:00
2017-10-03 08:31:10 +03:00
if ( this . _isHemingwayMode ) {
2017-07-28 16:21:09 +03:00
notificationText = '<span class="gh-notification-title">Hemingway Mode On:</span> Write now; edit later. Backspace disabled.' ;
extraKeys . Backspace = function ( ) { } ;
} else {
notificationText = '<span class="gh-notification-title">Hemingway Mode Off:</span> Normal editing restored.' ;
delete extraKeys . Backspace ;
}
cm . setOption ( 'extraKeys' , extraKeys ) ;
this . _updateButtonState ( ) ;
cm . focus ( ) ;
this . get ( 'notifications' ) . showNotification (
htmlSafe ( notificationText ) ,
2017-10-03 08:31:10 +03:00
{ key : 'editor.hemingwaymode' }
2017-07-28 16:21:09 +03:00
) ;
} ,
2017-05-08 21:15:56 +03:00
willDestroyElement ( ) {
if ( this . get ( '_isSplitScreen' ) ) {
this . _disconnectSplitPreview ( ) ;
}
2017-06-22 15:02:38 +03:00
this . removeShortcuts ( ) ;
2017-05-08 21:15:56 +03:00
this . _super ( ... arguments ) ;
} ,
2017-05-08 13:35:42 +03:00
actions : {
// put the markdown into a new mobiledoc card, trigger external update
updateMarkdown ( markdown ) {
let mobiledoc = copy ( BLANK _DOC , true ) ;
mobiledoc . cards [ 0 ] [ 1 ] . markdown = markdown ;
this . onChange ( mobiledoc ) ;
} ,
// store a reference to the simplemde editor so that we can handle
// focusing and image uploads
setEditor ( editor ) {
this . _editor = editor ;
// disable CodeMirror's drag/drop handling as we want to handle that
// in the parent gh-editor component
this . _editor . codemirror . setOption ( 'dragDrop' , false ) ;
2017-06-19 13:32:37 +03:00
// default to spellchecker being off
2017-07-20 13:59:24 +03:00
this . _editor . codemirror . setOption ( 'mode' , 'gfm' ) ;
2017-05-08 13:35:42 +03:00
// HACK: move the toolbar & status bar elements outside of the
// editor container so that they can be aligned in fixed positions
let container = this . $ ( ) . closest ( '.gh-editor' ) . find ( '.gh-editor-footer' ) ;
this . _toolbar = this . $ ( '.editor-toolbar' ) ;
this . _statusbar = this . $ ( '.editor-statusbar' ) ;
this . _toolbar . appendTo ( container ) ;
this . _statusbar . appendTo ( container ) ;
2017-05-08 21:15:56 +03:00
this . _updateButtonState ( ) ;
2017-05-08 13:35:42 +03:00
} ,
// used by the title input when the TAB or ENTER keys are pressed
focusEditor ( position = 'bottom' ) {
this . _editor . codemirror . focus ( ) ;
if ( position === 'bottom' ) {
this . _editor . codemirror . execCommand ( 'goDocEnd' ) ;
} else if ( position === 'top' ) {
this . _editor . codemirror . execCommand ( 'goDocStart' ) ;
}
return false ;
2017-05-08 21:15:56 +03:00
} ,
2017-08-02 10:05:59 +03:00
// HACK FIXME (PLEASE):
// - clicking toolbar buttons will cause the editor to lose focus
// - this is painful because we often want to know if the editor has focus
// so that we can insert images and so on in the correct place
// - the blur event will always fire before the button action is triggered 😞
// - to work around this we track focus state manually and set it to false
// after an arbitrary period that's long enough to allow the button action
// to trigger first
// - this _may_ well have unknown issues due to browser differences,
// variations in performance, moon cycles, sun spots, or cosmic rays
// - here be 🐲
// - (please let it work 🙏)
updateFocusState ( focused ) {
if ( focused ) {
this . _editorFocused = true ;
} else {
run . later ( this , function ( ) {
this . _editorFocused = false ;
} , 100 ) ;
}
} ,
openImageFileDialog ( ) {
2017-06-22 15:02:38 +03:00
let captureSelection = this . _editor . codemirror . hasFocus ( ) ;
this . _openImageFileDialog ( { captureSelection } ) ;
} ,
2017-08-02 10:05:59 +03:00
toggleUnsplash ( ) {
if ( this . get ( '_showUnsplash' ) ) {
return this . toggleProperty ( '_showUnsplash' ) ;
}
// capture current selection before it's lost by clicking toolbar btn
if ( this . _editorFocused ) {
this . _imageInsertSelection = {
anchor : this . _editor . codemirror . getCursor ( 'anchor' ) ,
head : this . _editor . codemirror . getCursor ( 'head' )
} ;
}
this . toggleProperty ( '_showUnsplash' ) ;
} ,
insertUnsplashPhoto ( photo ) {
let image = {
alt : photo . description || '' ,
url : photo . urls . regular ,
credit : ` <small>Photo by [ ${ photo . user . name } ]( ${ photo . user . links . html } ?utm_source=ghost&utm_medium=referral&utm_campaign=api-credit) / [Unsplash](https://unsplash.com/?utm_source=ghost&utm_medium=referral&utm_campaign=api-credit)</small> `
} ;
this . _insertImages ( [ image ] ) ;
} ,
2017-10-03 08:31:10 +03:00
togglePreview ( ) {
this . _togglePreview ( ) ;
} ,
2017-05-08 21:15:56 +03:00
toggleFullScreen ( ) {
let isFullScreen = ! this . get ( '_isFullScreen' ) ;
this . set ( '_isFullScreen' , isFullScreen ) ;
this . _updateButtonState ( ) ;
2017-05-12 11:06:56 +03:00
this . onFullScreenToggle ( isFullScreen ) ;
2017-05-08 21:15:56 +03:00
// leave split screen when exiting full screen mode
if ( ! isFullScreen && this . get ( '_isSplitScreen' ) ) {
this . send ( 'toggleSplitScreen' ) ;
}
} ,
toggleSplitScreen ( ) {
let isSplitScreen = ! this . get ( '_isSplitScreen' ) ;
2017-05-10 15:36:07 +03:00
let previewButton = this . _editor . toolbarElements . preview ;
2017-05-08 21:15:56 +03:00
this . set ( '_isSplitScreen' , isSplitScreen ) ;
this . _updateButtonState ( ) ;
// set up the preview rendering and scroll sync
// afterRender is needed so that necessary components have been
// added/removed and editor pane length has settled
if ( isSplitScreen ) {
2017-05-10 15:36:07 +03:00
// disable the normal SimpleMDE preview if it's active
if ( this . _editor . isPreviewActive ( ) ) {
let preview = this . _editor . toolbar . find ( ( button ) => {
return button . name === 'preview' ;
} ) ;
preview . action ( this . _editor ) ;
}
2017-07-20 14:37:18 +03:00
if ( previewButton ) {
previewButton . classList . add ( 'disabled' ) ;
}
2017-05-08 21:15:56 +03:00
run . scheduleOnce ( 'afterRender' , this , this . _connectSplitPreview ) ;
} else {
2017-07-20 14:37:18 +03:00
if ( previewButton ) {
previewButton . classList . remove ( 'disabled' ) ;
}
2017-05-08 21:15:56 +03:00
run . scheduleOnce ( 'afterRender' , this , this . _disconnectSplitPreview ) ;
}
2017-05-12 11:06:56 +03:00
this . onSplitScreenToggle ( isSplitScreen ) ;
2017-05-08 21:15:56 +03:00
// go fullscreen when entering split screen mode
2017-05-30 16:17:47 +03:00
this . send ( 'toggleFullScreen' ) ;
2017-05-08 21:15:56 +03:00
} ,
2017-10-03 08:31:10 +03:00
toggleSpellcheck ( ) {
this . _toggleSpellcheck ( ) ;
} ,
toggleHemingway ( ) {
this . _toggleHemingway ( ) ;
2017-07-28 16:21:09 +03:00
} ,
2017-05-08 21:15:56 +03:00
// put the toolbar/statusbar elements back so that SimpleMDE doesn't throw
// errors when it tries to remove them
destroyEditor ( ) {
let container = this . $ ( '.gh-markdown-editor-pane' ) ;
this . _toolbar . appendTo ( container ) ;
this . _statusbar . appendTo ( container ) ;
this . _editor = null ;
2017-05-08 13:35:42 +03:00
}
}
} ) ;