From 99e5b27fa44af41b5f2ca51ed457711ef9dea46d Mon Sep 17 00:00:00 2001 From: benweet Date: Sun, 15 Sep 2013 02:35:58 +0100 Subject: [PATCH] Switch to ACE editor --- bower.json | 2 +- res/core.js | 118 ++++++++++++++++--------- res/eventMgr.js | 70 +++++++-------- res/extensions/buttonMarkdownSyntax.js | 2 +- res/extensions/buttonViewer.js | 2 +- res/extensions/dialogAbout.js | 1 + res/extensions/markdownExtra.js | 2 +- res/extensions/partialRendering.js | 18 ++-- res/html/bodyIndex.html | 4 +- res/libs/ace_mode.js | 2 +- res/libs/ace_mode_highlight_rules.js | 6 +- res/settings.js | 2 +- res/styles/main.less | 21 +---- 13 files changed, 132 insertions(+), 118 deletions(-) diff --git a/bower.json b/bower.json index a62b8539..3fed38b4 100644 --- a/bower.json +++ b/bower.json @@ -18,6 +18,6 @@ "stacktrace": "~0.5.3", "requirejs-text": "~2.0.10", "bootstrap-tour": "~0.6.0", - "ace": "~1.1.1" + "ace": "#51b7cb67a63998c9c0b7d089a85c60e032a7cc17" } } diff --git a/res/core.js b/res/core.js index a98fde4c..227c9cbf 100644 --- a/res/core.js +++ b/res/core.js @@ -118,8 +118,8 @@ define([ utils.setInputValue("#input-settings-editor-font-family", settings.editorFontFamily); // Editor font size utils.setInputValue("#input-settings-editor-font-size", settings.editorFontSize); - // Editor max width - utils.setInputValue("#input-settings-editor-max-width", settings.editorMaxWidth); + // Max width + utils.setInputValue("#input-settings-max-width", settings.maxWidth); // Default content utils.setInputValue("#textarea-settings-default-content", settings.defaultContent); // Commit message @@ -147,8 +147,8 @@ define([ newSettings.editorFontFamily = utils.getInputTextValue("#input-settings-editor-font-family", event); // Editor font size newSettings.editorFontSize = utils.getInputIntValue("#input-settings-editor-font-size", event, 1, 99); - // Editor max width - newSettings.editorMaxWidth = utils.getInputIntValue("#input-settings-editor-max-width", event, 1); + // Max width + newSettings.maxWidth = utils.getInputIntValue("#input-settings-max-width", event, 1); // Default content newSettings.defaultContent = utils.getInputValue("#textarea-settings-default-content"); // Commit message @@ -212,52 +212,73 @@ define([ aceEditor.renderer.setPadding(EDITOR_DEFAULT_PADDING); aceEditor.session.setUseWrapMode(true); aceEditor.session.setMode("libs/ace_mode"); - // Make titles bold... + + // Make bold titles... (function(self) { + function checkLine(currentLine) { + var line = self.lines[currentLine]; + if(line.length !== 0) { + if(line[0].type.indexOf("markup.heading.multi") === 0) { + _.each(self.lines[currentLine - 1], function(previousLineObject) { + previousLineObject.type = "markup.heading.prev.multi"; + }); + } + } + } function customWorker() { - if (!self.running) { return; } + // Duplicate from background_tokenizer.js + if(!self.running) { + return; + } var workerStart = new Date(); - var startLine = self.currentLine; + var currentLine = self.currentLine; + var endLine = -1; var doc = self.doc; - var processedLines = 0; + while (self.lines[currentLine]) { + currentLine++; + } + + var startLine = currentLine; var len = doc.getLength(); - while (self.currentLine < len) { - self.$tokenizeRow(self.currentLine); - while (self.lines[self.currentLine]) { - var line = self.lines[self.currentLine]; - if(line.length !== 0 && line[0].type.indexOf("markup.heading.multi") === 0) { - _.each(self.lines[self.currentLine-1], function(previousLineObject) { - previousLineObject.type = "markup.heading.prev.multi"; - }); - } - self.currentLine++; - } + var processedLines = 0; + self.running = false; + while (currentLine < len) { + self.$tokenizeRow(currentLine); + endLine = currentLine; + do { + checkLine(currentLine); // benweet + currentLine++; + } while (self.lines[currentLine]); // only check every 5 lines - processedLines ++; - if ((processedLines % 5 == 0) && (new Date() - workerStart) > 20) { - self.fireUpdateEvent(startLine, self.currentLine-1); - self.running = setTimeout(customWorker, 20); + processedLines++; + if((processedLines % 5 == 0) && (new Date() - workerStart) > 20) { + self.running = setTimeout(customWorker, 20); // benweet + self.currentLine = currentLine; return; } } + self.currentLine = currentLine; - self.running = false; - - self.fireUpdateEvent(startLine, len - 1); + if(startLine <= endLine) + self.fireUpdateEvent(startLine, endLine); } + ; self.$worker = function() { - self.currentLine = self.currentLine ? self.currentLine - 1 : 0; + self.lines.splice(0, self.lines.length); + self.states.splice(0, self.states.length); + self.currentLine = 0; customWorker(); }; + })(aceEditor.session.bgTokenizer); + eventMgr.onAceCreated(aceEditor); - window.aceEditor = aceEditor; } - + // Create the layout function createLayout() { var layoutGlobalConfig = { @@ -291,8 +312,11 @@ define([ onresize_end: function(paneName) { if(aceEditor !== undefined && paneName == 'center') { aceEditor.resize(); + var bottomMargin = (aceEditor.renderer.$size.scrollerHeight - aceEditor.renderer.lineHeight) / 2; + bottomMargin < 0 && (bottomMargin = 0); + aceEditor.renderer.setScrollMargin(0, bottomMargin, 0, 0); setTimeout(function() { - var padding = (aceEditor.renderer.$size.scrollerWidth - settings.editorMaxWidth)/2; + var padding = (aceEditor.renderer.$size.scrollerWidth - settings.maxWidth) / 2; if(padding < EDITOR_DEFAULT_PADDING) { padding = EDITOR_DEFAULT_PADDING; } @@ -301,7 +325,7 @@ define([ aceEditor.resize(true); } }, 5); - } + } eventMgr.onLayoutResize(paneName); }, }; @@ -324,6 +348,7 @@ define([ south__minSize: 200 })); } + settings.maxWidth && $('#preview-contents').css('max-width', (settings.maxWidth + 30) + 'px'); $(".navbar").click(function() { layout.allowOverflow('north'); }); @@ -335,16 +360,16 @@ define([ // have fixed position // We also move the north toggler to the east or south resizer as the // north resizer is very small - var $previewButtonsContainerElt = $('
'); - $previewButtonsElt = $('
').appendTo($previewButtonsContainerElt); + // var $previewButtonsContainerElt = $('
'); + $previewButtonsElt = $('
'); $editorButtonsElt = $('
'); - if(settings.layoutOrientation == "horizontal") { - $('.ui-layout-resizer-north').append($previewButtonsContainerElt); + if(viewerMode || settings.layoutOrientation == "horizontal") { + $('.ui-layout-resizer-north').append($previewButtonsElt); $('.ui-layout-resizer-east').append($northTogglerElt).append($editorButtonsElt); } else { - $previewButtonsContainerElt.append($editorButtonsElt); - $('.ui-layout-resizer-south').append($previewButtonsContainerElt).append($northTogglerElt); + $('.ui-layout-resizer-south').append($previewButtonsElt).append($editorButtonsElt).append($northTogglerElt); } setPanelVisibility(); @@ -374,7 +399,7 @@ define([ else { $editorElt.val(initDocumentContent); } - + if(editor !== undefined) { // If the editor is already created aceEditor && aceEditor.selection.setSelectionRange(fileDesc.editorSelectRange); @@ -432,7 +457,7 @@ define([ eventMgr.onSectionsCreated(sectionList); return text; }); - + function checkDocumentChanges() { var newDocumentContent = $editorElt.val(); if(aceEditor !== undefined) { @@ -444,7 +469,7 @@ define([ } documentContent = newDocumentContent; } - + if(!lightMode) { editor = new Markdown.Editor(converter); // Custom insert link dialog @@ -490,7 +515,9 @@ define([ $editorElt.on("input propertychange", previewWrapper); editor = { hooks: hooks, - getConverter: function () { return converter; }, + getConverter: function() { + return converter; + }, run: previewWrapper, refreshPreview: previewWrapper }; @@ -502,10 +529,10 @@ define([ var debouncedMakePreview = _.debounce(makePreview, 500); return function() { if(documentContent === undefined) { - aceEditor.renderer.scrollToY(fileDesc.editorScrollTop); makePreview(); $previewContainerElt.scrollTop(fileDesc.previewScrollTop); _.defer(function() { + aceEditor.renderer.scrollToY(fileDesc.editorScrollTop); eventMgr.onFileOpen(fileDesc); }); } @@ -639,7 +666,7 @@ define([ // ACE editor createAceEditor(); - + // Editor's element $editorElt = $("#wmd-input").css({ // Apply editor font @@ -666,6 +693,11 @@ define([ // Other initialization that are not prioritary eventMgr.addListener("onReady", function() { + + // In vertical mode, we have to offset the editor buttons otherwise they hide the editor buttons + if(!viewerMode && settings.layoutOrientation == "vertical") { + $previewButtonsElt.css('right', parseInt($previewButtonsElt.css('right')) + $editorButtonsElt.width()); + } var isModalShown = false; $('.modal').on('show.bs.modal', function() { diff --git a/res/eventMgr.js b/res/eventMgr.js index 14542b33..f8339c50 100644 --- a/res/eventMgr.js +++ b/res/eventMgr.js @@ -7,7 +7,6 @@ define([ "settings", "text!html/settingsExtensionsAccordion.html", "extensions/partialRendering", - "extensions/userCustom", "extensions/buttonMarkdownSyntax", "extensions/googleAnalytics", "extensions/dialogAbout", @@ -33,6 +32,7 @@ define([ "extensions/buttonHtmlCode", "extensions/buttonViewer", "extensions/welcomeTour", + "extensions/userCustom", "bootstrap", "jquery-waitforimages" ], function($, _, crel, utils, Extension, settings, settingsExtensionsAccordionHTML) { @@ -136,8 +136,8 @@ define([ addEventHook("onError"); addEventHook("onOfflineChanged"); addEventHook("onUserActive"); - addEventHook("onAsyncRunning", true); - addEventHook("onPeriodicRun", true); + addEventHook("onAsyncRunning"); + addEventHook("onPeriodicRun"); // To access modules that are loaded after extensions addEventHook("onFileMgrCreated"); @@ -184,17 +184,13 @@ define([ var onPreviewFinished = createEventHook("onPreviewFinished"); var onAsyncPreviewListenerList = getExtensionListenerList("onAsyncPreview"); - // The number of times we expect tryFinished to be called - var nbAsyncPreviewListener = onAsyncPreviewListenerList.length + 1; var previewContentsElt = undefined; var $previewContentsElt = undefined; eventMgr["onAsyncPreview"] = function() { logger.log("onAsyncPreview"); logger.log("Conversion time: " + (new Date() - eventMgr.previewStartTime)); - // Call onPreviewFinished listeners when all async preview are finished - var counter = 0; - function tryFinished() { - if(++counter === nbAsyncPreviewListener) { + function recursiveCall(callbackList) { + var callback = callbackList.length ? callbackList.shift() : function() { logger.log("Preview time: " + (new Date() - eventMgr.previewStartTime)); _.defer(function() { var html = ""; @@ -203,13 +199,15 @@ define([ }); onPreviewFinished(utils.trim(html)); }); - } + }; + callback(function() { + recursiveCall(callbackList); + }); } - // We assume images are loading in the preview - $previewContentsElt.waitForImages(tryFinished); - _.each(onAsyncPreviewListenerList, function(asyncPreviewListener) { - asyncPreviewListener(tryFinished); - }); + recursiveCall(onAsyncPreviewListenerList.concat([function(callback) { + // We assume some images are loading asynchronously after the preview + $previewContentsElt.waitForImages(callback); + }])); }; var onReady = createEventHook("onReady"); @@ -255,27 +253,6 @@ define([ }); document.getElementById('extension-buttons').appendChild(extensionButtonsFragment); - // Create extension preview buttons - logger.log("onCreatePreviewButton"); - var onCreatePreviewButtonListenerList = getExtensionListenerList("onCreatePreviewButton"); - var extensionPreviewButtonsFragment = document.createDocumentFragment(); - _.each(onCreatePreviewButtonListenerList, function(listener) { - extensionPreviewButtonsFragment.appendChild(createBtn(listener)); - }); - var previewButtonsElt = document.querySelector('.extension-preview-buttons'); - previewButtonsElt.appendChild(extensionPreviewButtonsFragment); - - // A bit of jQuery... - var $previewButtonsElt = $(previewButtonsElt); - var previewButtonsWidth = $previewButtonsElt.width(); - $previewButtonsElt.find('.btn-group').each(function() { - var $btnGroupElt = $(this); - // Align dropdown to the left of the screen - $btnGroupElt.find('.dropdown-menu').css({ - right: -previewButtonsWidth + $btnGroupElt.width() + $btnGroupElt.position().left - }); - }); - // Create extension editor buttons logger.log("onCreateEditorButton"); var onCreateEditorButtonListenerList = getExtensionListenerList("onCreateEditorButton"); @@ -287,6 +264,27 @@ define([ editorButtonsElt.appendChild(extensionEditorButtonsFragment); } + // Create extension preview buttons + logger.log("onCreatePreviewButton"); + var onCreatePreviewButtonListenerList = getExtensionListenerList("onCreatePreviewButton"); + var extensionPreviewButtonsFragment = document.createDocumentFragment(); + _.each(onCreatePreviewButtonListenerList, function(listener) { + extensionPreviewButtonsFragment.appendChild(createBtn(listener)); + }); + var previewButtonsElt = document.querySelector('.extension-preview-buttons'); + previewButtonsElt.appendChild(extensionPreviewButtonsFragment); + + // A bit of jQuery... + var $previewButtonsElt = $(previewButtonsElt); + var previewButtonsWidth = $previewButtonsElt.width(); + $previewButtonsElt.find('.btn-group').each(function() { + var $btnGroupElt = $(this); + // Align dropdown to the left of the screen + $btnGroupElt.find('.dropdown-menu').css({ + right: -previewButtonsWidth + $btnGroupElt.width() + $btnGroupElt.position().left + }); + }); + // Call onReady listeners onReady(); }; diff --git a/res/extensions/buttonMarkdownSyntax.js b/res/extensions/buttonMarkdownSyntax.js index 38e8a563..3668bec0 100644 --- a/res/extensions/buttonMarkdownSyntax.js +++ b/res/extensions/buttonMarkdownSyntax.js @@ -4,7 +4,7 @@ define([ "text!html/buttonMarkdownSyntax.html", ], function($, Extension, buttonMarkdownSyntaxHTML) { - var buttonMarkdownSyntax = new Extension("buttonMarkdownSyntax", 'Button "Markdown syntax', true); + var buttonMarkdownSyntax = new Extension("buttonMarkdownSyntax", 'Button "Markdown syntax', true, true); buttonMarkdownSyntax.settingsBlock = '

Adds a "Markdown syntax" button over the preview.

'; buttonMarkdownSyntax.onCreatePreviewButton = function() { diff --git a/res/extensions/buttonViewer.js b/res/extensions/buttonViewer.js index e824b874..1c0130ad 100644 --- a/res/extensions/buttonViewer.js +++ b/res/extensions/buttonViewer.js @@ -4,7 +4,7 @@ define([ "text!html/buttonViewer.html", ], function($, Extension, buttonViewerHTML) { - var buttonViewer = new Extension("buttonViewer", 'Button "Viewer"', true); + var buttonViewer = new Extension("buttonViewer", 'Button "Viewer"', true, true); buttonViewer.settingsBlock = '

Adds a "Viewer" button over the preview.

'; buttonViewer.onCreatePreviewButton = function() { diff --git a/res/extensions/dialogAbout.js b/res/extensions/dialogAbout.js index 027d477a..68137b19 100644 --- a/res/extensions/dialogAbout.js +++ b/res/extensions/dialogAbout.js @@ -9,6 +9,7 @@ define([ var dialogAbout = new Extension("dialogAbout", 'Dialog "About"'); var libraries = { + "ACE": "http://ace.c9.io/", "Bootstrap": "http://getbootstrap.com/", "Bootstrap Tour": "http://bootstraptour.com/", "crel": "https://github.com/KoryNunn/crel", diff --git a/res/extensions/markdownExtra.js b/res/extensions/markdownExtra.js index f5ffc3ea..6b961b0f 100644 --- a/res/extensions/markdownExtra.js +++ b/res/extensions/markdownExtra.js @@ -65,7 +65,7 @@ define([ Markdown.Extra.init(converter, options); // Store extensions list in converter for partialRendering - converter.extraExtensions = markdownExtra.config.extensions; + converter.setExtraExtension && converter.setExtraExtension(markdownExtra.config.extensions); }; return markdownExtra; diff --git a/res/extensions/partialRendering.js b/res/extensions/partialRendering.js index d63cc6dc..c89f60db 100644 --- a/res/extensions/partialRendering.js +++ b/res/extensions/partialRendering.js @@ -122,7 +122,7 @@ define([ class: 'wmd-preview-section preview-content' }); var isFirst = true; - while(childNode) { + while (childNode) { var nextNode = childNode.nextSibling; if(isFirst === false && /(^| )wmd-title($| )/.test(childNode.className)) { // Stop when encountered the next wmd-title @@ -140,7 +140,8 @@ define([ sectionElt.appendChild(childNode); } childNode = nextNode; - }; + } + ; newSectionEltList.appendChild(sectionElt); }); wmdPreviewElt.innerHTML = ''; @@ -184,6 +185,11 @@ define([ editor.hooks.chain("onPreviewRefresh", function() { refreshSections(); }); + converter.setExtraExtension = function(extraExtensions) { + doFootnotes = _.some(extraExtensions, function(extension) { + return extension == "footnotes"; + }); + }; }; partialRendering.onReady = function() { @@ -199,13 +205,5 @@ define([ fileChanged = true; }; - partialRendering.onFileOpen = function() { - if(converter.extraExtensions) { - doFootnotes = _.some(converter.extraExtensions, function(extension) { - return extension == "footnotes"; - }); - } - }; - return partialRendering; }); \ No newline at end of file diff --git a/res/html/bodyIndex.html b/res/html/bodyIndex.html index 75257bfd..8b513d95 100644 --- a/res/html/bodyIndex.html +++ b/res/html/bodyIndex.html @@ -946,10 +946,10 @@
+ for="input-settings-max-width">Max width
px
diff --git a/res/libs/ace_mode.js b/res/libs/ace_mode.js index 36586dd5..e85e24af 100644 --- a/res/libs/ace_mode.js +++ b/res/libs/ace_mode.js @@ -48,7 +48,7 @@ var Mode = function() { oop.inherits(Mode, TextMode); (function() { - + this.type = "text"; this.lineCommentStart = ">"; this.getNextLineIndent = function(state, line, tab) { diff --git a/res/libs/ace_mode_highlight_rules.js b/res/libs/ace_mode_highlight_rules.js index eee961e4..d94cb744 100644 --- a/res/libs/ace_mode_highlight_rules.js +++ b/res/libs/ace_mode_highlight_rules.js @@ -113,7 +113,7 @@ var MarkdownHighlightRules = function() { }, { // list token : "markup.list", regex : "^\\s{0,3}(?:[*+-]|\\d+\\.)\\s+", - next : "listblock" + next : "listblock-start" }, { include : "basic" }], @@ -127,13 +127,11 @@ var MarkdownHighlightRules = function() { defaultToken : "markup.heading" } ], - /* don't need checkbox highlighting... "listblock-start" : [{ token : "checkbox", regex : /(?:\[[ x]\])?/, next : "listblock" }], - */ "listblock" : [ { // Lists only escape on completely blank lines. token : "empty_line", @@ -142,7 +140,7 @@ var MarkdownHighlightRules = function() { }, { // list token : "markup.list", regex : "^\\s{0,3}(?:[*+-]|\\d+\\.)\\s+", - next : "listblock" + next : "listblock-start" }, { include : "basic", noEscape: true }, { diff --git a/res/settings.js b/res/settings.js index ef0f968d..e9b315d4 100644 --- a/res/settings.js +++ b/res/settings.js @@ -8,7 +8,7 @@ define([ lazyRendering: true, editorFontFamily: 'Menlo, Consolas, "Courier New", Courier, monospace', editorFontSize: 12, - editorMaxWidth: 960, + maxWidth: 960, defaultContent: "\n\n\n> Written with [StackEdit](" + MAIN_URL + ").", commitMsg: "Published with " + MAIN_URL, template: [ diff --git a/res/styles/main.less b/res/styles/main.less index e61af49f..bc9ee618 100644 --- a/res/styles/main.less +++ b/res/styles/main.less @@ -112,7 +112,7 @@ body { #preview-contents { padding: 15px; - margin-bottom: 50px; + margin: 0 auto 50px; .ui-layout-east & { padding-left: 5px; } @@ -567,19 +567,11 @@ body { * Preview/Editor extensions buttons ********************/ -.preview-button-container { - position: absolute; - right: 0; - .extension-editor-buttons { - position: relative; - } -} - .extension-preview-buttons { - display: inline-block; - z-index: 1; + position: absolute; + z-index: 1; margin-top: 6px; - margin-right: 30px; + right: 30px; .ui-layout-resizer-south-closed & { display: none !important; } @@ -1231,11 +1223,6 @@ div.jGrowl { line-height: @input-height-base; } - #preview-contents { - max-width: 1024px; - margin: 0 auto; - } - .document-panel .search-bar { padding: 20px 20px 10px; .input-group-btn {