diff --git a/public/res/classes/FileDescriptor.js b/public/res/classes/FileDescriptor.js index 5b990492..ce0a1e89 100644 --- a/public/res/classes/FileDescriptor.js +++ b/public/res/classes/FileDescriptor.js @@ -131,7 +131,7 @@ define([ result.push(''); }); if(_.size(this.publishLocations) !== 0) { - result.push(''); + result.push(''); } result.push(_.escape(this.title)); return result.join(''); diff --git a/public/res/core.js b/public/res/core.js index 78322ee4..8b7e8eb1 100644 --- a/public/res/core.js +++ b/public/res/core.js @@ -4,7 +4,7 @@ define([ "underscore", "crel", "ace", - "classes/PreEditor", + "preEditor", "constants", "utils", "storage", @@ -26,7 +26,7 @@ define([ 'ace/ext/spellcheck', 'ace/ext/searchbox' -], function($, _, crel, ace, PreEditor, constants, utils, storage, settings, eventMgr, shortcutMgr, mousetrap, bodyIndexHTML, bodyViewerHTML, settingsTemplateTooltipHTML, settingsUserCustomExtensionTooltipHTML) { +], function($, _, crel, ace, preEditor, constants, utils, storage, settings, eventMgr, shortcutMgr, mousetrap, bodyIndexHTML, bodyViewerHTML, settingsTemplateTooltipHTML, settingsUserCustomExtensionTooltipHTML) { var core = {}; @@ -362,10 +362,10 @@ define([ north__minSize: 49, center__minWidth: 250, center__minHeight: 180, - east__onAlert: function() { + east__onalert: function() { window.location.href = 'viewer'; }, - south__onAlert: function() { + south__onalert: function() { window.location.href = 'viewer'; }, fxSettings: { @@ -385,12 +385,12 @@ define([ } }, onresize_end: function(paneName) { - if(preEditor.$preContentElt !== undefined && paneName == 'center') { + if(preEditor.$contentElt !== undefined && paneName == 'center') { var padding = ($editorElt.width() - getMaxWidth()) / 2; if(padding < constants.EDITOR_DEFAULT_PADDING) { padding = constants.EDITOR_DEFAULT_PADDING; } - preEditor.$preContentElt.css({ + preEditor.$contentElt.css({ 'padding-left': padding + 'px', 'padding-right': padding + 'px' }); @@ -499,7 +499,6 @@ define([ var fileDesc; var documentContent; var UndoManager = require("ace/undomanager").UndoManager; - var preEditor; core.initEditor = function(fileDescParam) { if(fileDesc !== undefined) { eventMgr.onFileClosed(fileDesc); @@ -587,15 +586,30 @@ define([ if(aceEditor !== undefined) { newDocumentContent = aceEditor.getValue(); } - if(documentContent === undefined) { - preEditor.highlight(); + if(documentContent == newDocumentContent) { + return false; } - else if(documentContent != newDocumentContent) { + + if(documentContent !== undefined) { fileDesc.content = newDocumentContent; eventMgr.onContentChanged(fileDesc); - preEditor.highlight(); } + else { + eventMgr.onFileOpen(fileDesc); + $previewContainerElt.scrollTop(fileDesc.previewScrollTop); + if(window.lightMode) { + $editorElt.scrollTop(fileDesc.editorScrollTop); + } + else { + preEditor.scrollTop = fileDesc.editorScrollTop; + _.defer(function() { + aceEditor.renderer.scrollToY(fileDesc.editorScrollTop); + }); + } + } + documentContent = newDocumentContent; + return true; } var previewWrapper; @@ -629,44 +643,17 @@ define([ previewWrapper = function(makePreview) { var debouncedMakePreview = _.debounce(makePreview, 500); return function() { - if(documentContent === undefined) { - makePreview(); - eventMgr.onFileOpen(fileDesc); - $previewContainerElt.scrollTop(fileDesc.previewScrollTop); - if(window.lightMode) { - preEditor.scrollTop = fileDesc.editorScrollTop; - $editorElt.scrollTop(preEditor.scrollTop); - } - else { - _.defer(function() { - aceEditor.renderer.scrollToY(fileDesc.editorScrollTop); - }); - } + var debounce = documentContent !== undefined; + if(checkDocumentChanges()) { + debounce ? debouncedMakePreview() : makePreview(); } - else { - debouncedMakePreview(); - } - checkDocumentChanges(); }; }; } else { previewWrapper = function(makePreview) { return function() { - makePreview(); - if(documentContent === undefined) { - eventMgr.onFileOpen(fileDesc); - $previewContainerElt.scrollTop(fileDesc.previewScrollTop); - if(window.lightMode) { - $editorElt.scrollTop(fileDesc.editorScrollTop); - } - else { - _.defer(function() { - aceEditor.renderer.scrollToY(fileDesc.editorScrollTop); - }); - } - } - checkDocumentChanges(); + checkDocumentChanges() && makePreview(); }; }; } @@ -861,7 +848,7 @@ define([ // In pre mode, we replace ACE with an editable pre $('#wmd-input').replaceWith(function() { var result = $('
').addClass(this.className).addClass('form-control'); - preEditor = new PreEditor(result[0]); + preEditor.init(result[0]); return result; }); @@ -1032,7 +1019,7 @@ define([ }); $(".action-import-docs-settings-confirm").click(function() { storage.clear(); - var allowedKeys = /^file\.|^focusMode$|^folder\.|^publish\.|^settings$|^sync\.|^google\.|^themeV3$|^mode$|^version$|^welcomeTour$/; + var allowedKeys = /^file\.|^folder\.|^publish\.|^settings$|^sync\.|^google\.|^themeV3$|^mode$|^version$|^welcomeTour$/; _.each(newstorage, function(value, key) { if(allowedKeys.test(key)) { storage[key] = value; diff --git a/public/res/eventMgr.js b/public/res/eventMgr.js index 631b7f60..451695aa 100644 --- a/public/res/eventMgr.js +++ b/public/res/eventMgr.js @@ -39,7 +39,7 @@ define([ "extensions/spellCheck", "extensions/userCustom", "bootstrap", - "jquery-waitforimages" + "jquery-waitforimages", ], function($, _, crel, utils, logger, Extension, settings, settingsExtensionsAccordionHTML) { var eventMgr = {}; @@ -233,7 +233,6 @@ define([ _.each(previewContentsElt.children, function(elt) { html += elt.innerHTML; }); - html = html.replace(/^<\/div>\n\n/gm, ''); var htmlWithComments = utils.trim(html); var htmlWithoutComments = htmlWithComments.replace(/ .*?<\/span> /g, ''); onPreviewFinished(htmlWithComments, htmlWithoutComments); diff --git a/public/res/extensions/buttonFocusMode.js b/public/res/extensions/buttonFocusMode.js index 890c59ee..ec83adc4 100644 --- a/public/res/extensions/buttonFocusMode.js +++ b/public/res/extensions/buttonFocusMode.js @@ -2,11 +2,10 @@ define([ "jquery", "underscore", "crel", - "storage", "classes/Extension" -], function($, _, crel, storage, Extension) { +], function($, _, crel, Extension) { - var buttonFocusMode = new Extension("buttonFocusMode", 'Button "Focus Mode"', true, true, true); + var buttonFocusMode = new Extension("buttonFocusMode", 'Button "Focus Mode"', true, true); buttonFocusMode.settingsBlock = "When typing, scrolls automatically the editor to always have the caret centered verticaly."; var aceEditor; @@ -14,48 +13,57 @@ define([ aceEditor = aceEditorParam; }; - var isFocusModeOn = false; var isMouseActive = false; function doFocusMode() { - if(isFocusModeOn === false || isMouseActive === true) { - return; + if(aceEditor) { + if(isMouseActive === true) { + return; + } + var positionInDocument = aceEditor.selection.getCursor(); + var positionInScreen = aceEditor.session.documentToScreenPosition(positionInDocument.row, positionInDocument.column); + aceEditor.session.setScrollTop((positionInScreen.row + 0.5) * aceEditor.renderer.lineHeight - aceEditor.renderer.$size.scrollerHeight / 2); } - var positionInDocument = aceEditor.selection.getCursor(); - var positionInScreen = aceEditor.session.documentToScreenPosition(positionInDocument.row, positionInDocument.column); - aceEditor.session.setScrollTop((positionInScreen.row + 0.5) * aceEditor.renderer.lineHeight - aceEditor.renderer.$size.scrollerHeight / 2); } - var $button; buttonFocusMode.onReady = function() { - aceEditor.getSession().selection.on('changeCursor', doFocusMode); - aceEditor.container.addEventListener('keydown', function() { - isMouseActive = false; - }, true); - aceEditor.container.addEventListener('mousedown', function() { - isMouseActive = true; - }, true); - if(storage.focusMode == 'on') { - $button.click(); - } - }; - - buttonFocusMode.onCreateEditorButton = function() { - $button = $([ - '' - ].join('')); - $button.click(function() { - _.defer(function() { - isFocusModeOn = $button.is('.active'); - storage.focusMode = isFocusModeOn ? 'on' : 'off'; + if(aceEditor) { + aceEditor.getSession().selection.on('changeCursor', doFocusMode); + aceEditor.container.addEventListener('keydown', function() { isMouseActive = false; - aceEditor.focus(); - doFocusMode(); - }); + }, true); + aceEditor.container.addEventListener('mousedown', function() { + isMouseActive = true; + }, true); + return; + } + var $editorElt = $('#wmd-input'); + var $positionHelper = $('').css('display', 'inline-block'); + var coef = 0.2; + $editorElt.on('keydown', function(event) { + if(event.altKey || event.ctrlKey || event.shiftKey || event.metaKey) { + return; + } + setTimeout(function() { + var range = window.getSelection().getRangeAt(0); + range.insertNode($positionHelper[0]); + var parentNode = $positionHelper[0].parentNode; + var editorHeight = $editorElt.height(); + var cursorMinY = coef*editorHeight; + var cursorMaxY = (1-coef)*editorHeight; + var cursorY = $positionHelper.offset().top - $editorElt.offset().top; + $positionHelper.detach(); + parentNode.normalize(); + if(cursorY < cursorMinY) { + $editorElt.scrollTop($editorElt.scrollTop() - cursorMinY + cursorY); + } + else if(cursorY > cursorMaxY) { + $editorElt.scrollTop($editorElt.scrollTop() + cursorY - cursorMaxY); + } + }, 0); }); - return $button[0]; }; return buttonFocusMode; -}); \ No newline at end of file +}); + + diff --git a/public/res/extensions/buttonPublish.js b/public/res/extensions/buttonPublish.js index 0fc766aa..b7e78275 100644 --- a/public/res/extensions/buttonPublish.js +++ b/public/res/extensions/buttonPublish.js @@ -37,7 +37,7 @@ define([ class: 'btn btn-success button-publish', title: 'Update document publication' }, crel('i', { - class: 'icon-share' + class: 'icon-upload' })); $button = $(button).click(function() { if(!$button.hasClass("disabled")) { diff --git a/public/res/extensions/markdownSectionParser.js b/public/res/extensions/markdownSectionParser.js index f2a5f974..e5a13af1 100644 --- a/public/res/extensions/markdownSectionParser.js +++ b/public/res/extensions/markdownSectionParser.js @@ -11,11 +11,12 @@ define([ markdownSectionParser.onEventMgrCreated = function(eventMgrParameter) { eventMgr = eventMgrParameter; }; - + + var sectionList = []; + + // Regexp to look for section delimiters + var regexp = '^.+[ \\t]*\\n=+[ \\t]*\\n+|^.+[ \\t]*\\n-+[ \\t]*\\n+|^\\#{1,6}[ \\t]*.+?[ \\t]*\\#*\\n+'; // Title delimiters markdownSectionParser.onPagedownConfigure = function(editor) { - - // Build a regexp to look for section delimiters - var regexp = '^.+[ \\t]*\\n=+[ \\t]*\\n+|^.+[ \\t]*\\n-+[ \\t]*\\n+|^\\#{1,6}[ \\t]*.+?[ \\t]*\\#*\\n+'; // Title delimiters if(markdownExtra.enabled) { if(_.some(markdownExtra.config.extensions, function(extension) { return extension == "fenced_code_gfm"; @@ -32,31 +33,44 @@ define([ regexp = new RegExp(regexp, 'gm'); var converter = editor.getConverter(); - converter.hooks.chain("preConversion", function(text) { - eventMgr.previewStartTime = new Date(); - var tmpText = text + "\n\n"; - function addSection(startOffset, endOffset) { - var sectionText = tmpText.substring(offset, endOffset); - sectionList.push({ - text: sectionText, - textWithDelimiter: '\n\n\n' + sectionText + '\n' - }); - } - var sectionList = [], offset = 0; - // Look for delimiters - tmpText.replace(regexp, function(match, matchOffset) { - // Create a new section with the text preceding the delimiter - addSection(offset, matchOffset); - offset = matchOffset; - }); - // Last section - addSection(offset, text.length); - eventMgr.onSectionsCreated(sectionList); + converter.hooks.chain("preConversion", function() { return _.reduce(sectionList, function(result, section) { - return result + section.textWithDelimiter; + return result + section.previewText; }, ''); }); }; + + var trimLen; + markdownSectionParser.onMarkdownTrim = function(len) { + trimLen = len; + }; + + var sectionCounter = 0; + function parseFileContent(fileDesc) { + var text = fileDesc.content.substring(trimLen); + var tmpText = text + "\n\n"; + function addSection(startOffset, endOffset) { + var sectionText = tmpText.substring(offset, endOffset); + sectionList.push({ + id: ++sectionCounter, + text: sectionText + }); + } + sectionList = []; + var offset = 0; + // Look for delimiters + tmpText.replace(regexp, function(match, matchOffset) { + // Create a new section with the text preceding the delimiter + addSection(offset, matchOffset); + offset = matchOffset; + }); + // Last section + addSection(offset, text.length); + eventMgr.onSectionsCreated(sectionList); + } + + markdownSectionParser.onFileOpen = parseFileContent; + markdownSectionParser.onContentChanged = parseFileContent; return markdownSectionParser; }); \ No newline at end of file diff --git a/public/res/extensions/partialRendering.js b/public/res/extensions/partialRendering.js index 32a288a6..e2d9dc7a 100644 --- a/public/res/extensions/partialRendering.js +++ b/public/res/extensions/partialRendering.js @@ -10,14 +10,51 @@ define([ partialRendering.settingsBlock = partialRenderingSettingsBlockHTML; var converter; - var sectionCounter = 0; + var doFootnotes = false; + var hasFootnotes = false; + var currentSectionList = []; + var sectionList = []; var linkDefinition; var sectionsToRemove = []; var modifiedSections = []; var insertBeforeSection; var fileChanged = false; - function updateSectionList(newSectionList, newLinkDefinition) { + function updateSectionList() { + var newSectionList = []; + var newLinkDefinition = '\n'; + hasFootnotes = false; + _.each(currentSectionList, function(section) { + var text = '\n\n\n' + section.text + '\n\n'; + + // Strip footnotes + if(doFootnotes) { + text = text.replace(/^```.*\n[\s\S]*?\n```|\n[ ]{0,3}\[\^(.+?)\]\:[ \t]*\n?([\s\S]*?)\n{1,2}((?=\n[ ]{0,3}\S)|$)/gm, function(wholeMatch, footnote) { + if(footnote) { + hasFootnotes = true; + newLinkDefinition += wholeMatch.replace(/^\s*\n/gm, '') + '\n'; + return ""; + } + return wholeMatch; + }); + } + + // Strip link definitions + text = text.replace(/^```.*\n[\s\S]*?\n```|^[ ]{0,3}\[(.+)\]:[ \t]*\n?[ \t]*(\S+?)>?(?=\s|$)[ \t]*\n?[ \t]*((\n*)["(](.+?)[")][ \t]*)?(?:\n+)/gm, function(wholeMatch, link) { + if(link) { + newLinkDefinition += wholeMatch.replace(/^\s*\n/gm, '') + '\n'; + return ""; + } + return wholeMatch; + }); + + // Add section to the newSectionList + newSectionList.push({ + id: section.id, + text: text + '\n' + }); + }); + modifiedSections = []; sectionsToRemove = []; insertBeforeSection = undefined; @@ -65,47 +102,6 @@ define([ sectionList = leftSections.concat(modifiedSections).concat(rightSections); } - var doFootnotes = false; - var hasFootnotes = false; - partialRendering.onSectionsCreated = function(sectionListParam) { - - var newSectionList = []; - var newLinkDefinition = '\n'; - hasFootnotes = false; - _.each(sectionListParam, function(section) { - var text = section.textWithDelimiter + '\n'; - - // Strip footnotes - if(doFootnotes) { - text = text.replace(/^```.*\n[\s\S]*?\n```|\n[ ]{0,3}\[\^(.+?)\]\:[ \t]*\n?([\s\S]*?)\n{1,2}((?=\n[ ]{0,3}\S)|$)/gm, function(wholeMatch, footnote) { - if(footnote) { - hasFootnotes = true; - newLinkDefinition += wholeMatch.replace(/^\s*\n/gm, '') + '\n'; - return ""; - } - return wholeMatch; - }); - } - - // Strip link definitions - text = text.replace(/^```.*\n[\s\S]*?\n```|^[ ]{0,3}\[(.+)\]:[ \t]*\n?[ \t]*(\S+?)>?(?=\s|$)[ \t]*\n?[ \t]*((\n*)["(](.+?)[")][ \t]*)?(?:\n+)/gm, function(wholeMatch, link) { - if(link) { - newLinkDefinition += wholeMatch.replace(/^\s*\n/gm, '') + '\n'; - return ""; - } - return wholeMatch; - }); - - // Add section to the newSectionList - newSectionList.push({ - id: ++sectionCounter, - text: text + '\n' - }); - }); - - updateSectionList(newSectionList, newLinkDefinition); - }; - var footnoteMap = {}; // Store one footnote elt in the footnote map function storeFootnote(footnoteElt) { @@ -133,7 +129,8 @@ define([ var isNextDelimiter = false; while (childNode) { var nextNode = childNode.nextSibling; - if(isNextDelimiter === true && childNode.tagName == 'DIV' && childNode.className == 'se-section-delimiter') { + var isDelimiter = childNode.className == 'se-preview-section-delimiter'; + if(isNextDelimiter === true && childNode.tagName == 'DIV' && isDelimiter) { // Stop when encountered the next delimiter break; } @@ -142,7 +139,7 @@ define([ _.each(childNode.querySelectorAll("ol > li"), storeFootnote); } else { - sectionElt.appendChild(childNode); + isDelimiter || sectionElt.appendChild(childNode); } childNode = nextNode; } @@ -182,9 +179,14 @@ define([ } } + partialRendering.onSectionsCreated = function(sectionListParam) { + currentSectionList = sectionListParam; + }; + partialRendering.onPagedownConfigure = function(editor) { converter = editor.getConverter(); converter.hooks.chain("preConversion", function() { + updateSectionList(); var result = _.map(modifiedSections, function(section) { return section.text; }); diff --git a/public/res/extensions/scrollLink.js b/public/res/extensions/scrollLink.js index 457babaa..9ebc86a4 100644 --- a/public/res/extensions/scrollLink.js +++ b/public/res/extensions/scrollLink.js @@ -23,68 +23,48 @@ define([ offsetBegin = offsetBeginParam; }; - var $textareaElt; - var $textareaHelperElt; + var $editorElt; var $previewElt; var mdSectionList = []; var htmlSectionList = []; var lastEditorScrollTop; var lastPreviewScrollTop; var buildSections = _.debounce(function() { - + if(!isPreviewVisible) { + return; + } mdSectionList = []; - var mdTextOffset = 0; - var mdSectionOffset = 0; + var mdSectionOffset; var firstSectionOffset = offsetBegin; - var padding = 0; - function addTextareaSection(sectionText) { - var sectionHeight = padding; - if(sectionText !== undefined) { - var textNode = document.createTextNode(sectionText); - $textareaHelperElt.empty().append(textNode); - sectionHeight += $textareaHelperElt.prop('scrollHeight'); - } - var newSectionOffset = mdSectionOffset + sectionHeight; + var scrollHeight; + if(window.lightMode) { + var editorScrollTop = $editorElt.scrollTop(); + $editorElt.find(".wmd-input-section").each(function() { + if(mdSectionOffset === undefined) { + // Force start to 0 for the first section + mdSectionOffset = 0; + return; + } + var $delimiterElt = $(this); + // Consider div scroll position + var newSectionOffset = $delimiterElt.position().top + editorScrollTop; + mdSectionList.push({ + startOffset: mdSectionOffset, + endOffset: newSectionOffset, + height: newSectionOffset - mdSectionOffset + }); + mdSectionOffset = newSectionOffset; + }); + // Last section + scrollHeight = $editorElt.prop('scrollHeight'); mdSectionList.push({ startOffset: mdSectionOffset, - endOffset: newSectionOffset, - height: sectionHeight - }); - mdSectionOffset = newSectionOffset; - } - if(window.lightMode) { - // Special treatment for light mode - $textareaHelperElt.innerWidth($textareaElt.innerWidth()); - _.each(sectionList, function(section, index) { - var sectionText = section.text; - if(index !== sectionList.length - 1) { - if(sectionText.length === 0) { - sectionText = undefined; - } - } - else { - if(/\n$/.test(sectionText)) { - // Need to add a line break to take into account a final empty line - sectionText += '\n'; - } - } - addTextareaSection(sectionText); - }); - - // Apply a coef to manage divergence in some browsers - var theoricalHeight = _.last(mdSectionList).endOffset; - var realHeight = $textareaElt[0].scrollHeight; - var coef = realHeight/theoricalHeight; - mdSectionList = _.map(mdSectionList, function(mdSection) { - return { - startOffset: mdSection.startOffset * coef, - endOffset: mdSection.endOffset * coef, - height: mdSection.height * coef, - }; + endOffset: scrollHeight, + height: scrollHeight - mdSectionOffset }); } else { - // Everything's much simpler with ACE + var mdTextOffset = 0; _.each(sectionList, function(section) { mdTextOffset += section.text.length + firstSectionOffset; firstSectionOffset = 0; @@ -101,11 +81,11 @@ define([ }); } - // Try to find corresponding sections in the preview + // Find corresponding sections in the preview htmlSectionList = []; var htmlSectionOffset; var previewScrollTop = $previewElt.scrollTop(); - $previewElt.find(".preview-content > .se-section-delimiter").each(function() { + $previewElt.find(".wmd-preview-section").each(function() { if(htmlSectionOffset === undefined) { // Force start to 0 for the first section htmlSectionOffset = 0; @@ -122,7 +102,7 @@ define([ htmlSectionOffset = newSectionOffset; }); // Last section - var scrollHeight = $previewElt.prop('scrollHeight'); + scrollHeight = $previewElt.prop('scrollHeight'); htmlSectionList.push({ startOffset: htmlSectionOffset, endOffset: scrollHeight, @@ -141,12 +121,10 @@ define([ var isPreviewMoving = false; var scrollingHelper = $(''); var doScrollLink = _.throttle(function() { - if(mdSectionList.length === 0 || mdSectionList.length !== htmlSectionList.length) { - // Delay - doScrollLink(); + if(!isPreviewVisible || mdSectionList.length === 0 || mdSectionList.length !== htmlSectionList.length) { return; } - var editorScrollTop = window.lightMode ? $textareaElt.scrollTop() : aceEditor.renderer.getScrollTop(); + var editorScrollTop = window.lightMode ? $editorElt.scrollTop() : aceEditor.renderer.getScrollTop(); editorScrollTop < 0 && (editorScrollTop = 0); var previewScrollTop = $previewElt.scrollTop(); function getDestScrollTop(srcScrollTop, srcSectionList, destSectionList) { @@ -157,7 +135,7 @@ define([ return srcScrollTop < section.endOffset; }); if(srcSection === undefined) { - // Something wrong in the algorithm... + // Something very bad happened return; } var posInSection = (srcScrollTop - srcSection.startOffset) / (srcSection.height || 1); @@ -211,7 +189,7 @@ define([ if(window.lightMode) { destScrollTop = _.min([ destScrollTop, - $textareaElt.prop('scrollHeight') - $textareaElt.outerHeight() + $editorElt.prop('scrollHeight') - $editorElt.outerHeight() ]); } else { @@ -228,7 +206,7 @@ define([ return; } if(window.lightMode) { - $textareaElt.stop('scrollLinkFx', true).animate({ + $editorElt.stop('scrollLinkFx', true).animate({ scrollTop: destScrollTop }, { easing: 'easeOutSine', @@ -271,6 +249,23 @@ define([ isScrollEditor = true; buildSections(); }; + + var isPreviewVisible = true; + function setPreviewHidden() { + isPreviewVisible = false; + console.log(isPreviewVisible); + } + function setPreviewVisible() { + isPreviewVisible = true; + console.log(isPreviewVisible); + } + + scrollLink.onLayoutConfigure = function(layoutGlobalConfig) { + layoutGlobalConfig.east__onclose = setPreviewHidden; + layoutGlobalConfig.south__onclose = setPreviewHidden; + layoutGlobalConfig.east__onopen_start = setPreviewVisible; + layoutGlobalConfig.south__onclose_start = setPreviewVisible; + }; scrollLink.onFileClosed = function() { mdSectionList = []; @@ -279,9 +274,7 @@ define([ var scrollAdjust = false; scrollLink.onReady = function() { $previewElt = $(".preview-container"); - $textareaElt = $("#wmd-input"); - // This helper is used to measure sections height in light mode - $textareaHelperElt = $('.textarea-helper'); + $editorElt = $("#wmd-input"); $previewElt.scroll(function() { if(isPreviewMoving === false && scrollAdjust === false) { @@ -299,7 +292,7 @@ define([ } }; if(window.lightMode) { - $textareaElt.scroll(handleEditorScroll); + $editorElt.scroll(handleEditorScroll); } else { aceEditor.session.on("changeScrollTop", handleEditorScroll); diff --git a/public/res/extensions/toc.js b/public/res/extensions/toc.js index c592050b..554af8f2 100644 --- a/public/res/extensions/toc.js +++ b/public/res/extensions/toc.js @@ -121,7 +121,7 @@ define([ toc.onPagedownConfigure = function(editor) { previewContentsElt = document.getElementById('preview-contents'); - var tocExp = new RegExp("^" + toc.config.marker + "$"); + var tocExp = new RegExp("^\\s*" + toc.config.marker + "\\s*$"); // Run TOC generation when conversion is finished directly on HTML editor.hooks.chain("onPreviewRefresh", function() { var tocEltList = document.querySelectorAll('.table-of-contents, .toc'); diff --git a/public/res/extensions/welcomeTour.js b/public/res/extensions/welcomeTour.js index e959d05d..a0c8860b 100644 --- a/public/res/extensions/welcomeTour.js +++ b/public/res/extensions/welcomeTour.js @@ -100,7 +100,7 @@ define([ { element: '.navbar-inner > .nav .button-publish, .navbar .right-buttons-dropdown > .nav > .btn:not(:hidden)', title: 'Update publication', - content: 'Once published, use thePublish
button to update the publication.', + content: 'Once published, use thePublish
button to update the publication.', placement: 'left', reflex: true, }, diff --git a/public/res/extensions/yamlFrontMatterParser.js b/public/res/extensions/yamlFrontMatterParser.js index 7eca45fd..7e98cb63 100644 --- a/public/res/extensions/yamlFrontMatterParser.js +++ b/public/res/extensions/yamlFrontMatterParser.js @@ -17,30 +17,27 @@ define([ }; var regex = /^(\s*-{3}\s*\n([\w\W]+?)\n\s*-{3}\s*\n)?([\w\W]*)$/; - yamlFrontMatterParser.onPagedownConfigure = function(editor) { - var converter = editor.getConverter(); - converter.hooks.chain("preConversion", function(text) { - var results = regex.exec(text); - var yaml = results[2]; + yamlFrontMatterParser.onContentChanged = function(fileDesc) { + var text = fileDesc.content; + var results = regex.exec(text); + var yaml = results[2]; - if (yaml && (!fileDesc.frontMatter || fileDesc.frontMatter._yaml != yaml)) { - fileDesc.frontMatter = undefined; - try { - fileDesc.frontMatter = YAML.parse(yaml); - if(!_.isObject(fileDesc.frontMatter)) { - fileDesc.frontMatter = undefined; - } - fileDesc.frontMatter._yaml = yaml; - fileDesc.frontMatter._frontMatter = results[1]; - } - catch (e) { - eventMgr.onMarkdownTrim(0); - return text; + if (yaml && (!fileDesc.frontMatter || fileDesc.frontMatter._yaml != yaml)) { + fileDesc.frontMatter = undefined; + try { + fileDesc.frontMatter = YAML.parse(yaml); + if(!_.isObject(fileDesc.frontMatter)) { + fileDesc.frontMatter = undefined; } + fileDesc.frontMatter._yaml = yaml; + fileDesc.frontMatter._frontMatter = results[1]; } - eventMgr.onMarkdownTrim((results[1] || '').length); - return results[3]; - }); + catch (e) { + eventMgr.onMarkdownTrim(0); + return text; + } + } + eventMgr.onMarkdownTrim((results[1] || '').length); }; return yamlFrontMatterParser; diff --git a/public/res/font/fontello.eot b/public/res/font/fontello.eot index 3899c95a..52d6a9e3 100644 Binary files a/public/res/font/fontello.eot and b/public/res/font/fontello.eot differ diff --git a/public/res/font/fontello.svg b/public/res/font/fontello.svg index cd208f44..06932346 100644 --- a/public/res/font/fontello.svg +++ b/public/res/font/fontello.svg @@ -1,7 +1,7 @@ \ No newline at end of file diff --git a/public/res/font/fontello.ttf b/public/res/font/fontello.ttf index 67268b8b..f33dbb00 100644 Binary files a/public/res/font/fontello.ttf and b/public/res/font/fontello.ttf differ diff --git a/public/res/font/fontello.woff b/public/res/font/fontello.woff index c0eb19e1..da4c5042 100644 Binary files a/public/res/font/fontello.woff and b/public/res/font/fontello.woff differ diff --git a/public/res/html/bodyIndex.html b/public/res/html/bodyIndex.html index bcf2b9be..bb5612c3 100644 --- a/public/res/html/bodyIndex.html +++ b/public/res/html/bodyIndex.html @@ -62,20 +62,6 @@EXPORT- Save... - Synchronize... @@ -87,13 +73,13 @@Publish... + class="list-group-item"> Publish... - Sharing... + Sharing links + Export to disk +IMPORTOpen from disk + class="icon-hdd"> Import from disk Open from URL + class="icon-globe"> Import from URL -Open from URL
+Import from URL
Please provide a link to a Markdown document.
@@ -485,7 +485,7 @@-Export to Dropbox
+Save on Dropbox
diff --git a/public/res/html/dialogExportGdrive.html b/public/res/html/dialogExportGdrive.html index 61a236fc..83192b7b 100644 --- a/public/res/html/dialogExportGdrive.html +++ b/public/res/html/dialogExportGdrive.html @@ -4,7 +4,7 @@
-Export to <%= providerName %>
+Save on <%= providerName %>
diff --git a/public/res/libs/layout.js b/public/res/libs/layout.js index 6adcf9b8..72de2f3a 100644 --- a/public/res/libs/layout.js +++ b/public/res/libs/layout.js @@ -793,7 +793,7 @@ $.layout.defaults = { , onswap_end: null // CALLBACK when pane ENDS being Swapped , ondrag_start: null // CALLBACK when pane STARTS being ***MANUALLY*** Resized , ondrag_end: null // CALLBACK when pane ENDS being ***MANUALLY*** Resized - , onAlert: null + , onalert: null } /* * PANE-SPECIFIC SETTINGS @@ -3479,7 +3479,7 @@ $.fn.layout = function (opts) { if (s.minSize > s.maxSize) { // INSUFFICIENT ROOM FOR PANE TO OPEN! syncPinBtns(pane, false); // make sure pin-buttons are reset if (!noAlert && o.tips.noRoomToOpen) - o.onAlert ? o.onAlert(o.tips.noRoomToOpen) : alert(o.tips.noRoomToOpen); + o.onalert ? o.onalert(o.tips.noRoomToOpen) : alert(o.tips.noRoomToOpen); return queueNext(); // ABORT } diff --git a/public/res/libs/prism-markdown.js b/public/res/libs/prism-markdown.js index 564a8b76..dc0875f4 100644 --- a/public/res/libs/prism-markdown.js +++ b/public/res/libs/prism-markdown.js @@ -100,15 +100,10 @@ Prism.languages.md = (function() { linktitle: /['\"\(][^\'\"\)]*['\"\)]/ } }; - md.tocmarker = { - pattern: /(^|(?:^|\n)\s*\n)[ \t]*\[(toc|TOC)\]($|\n($|\s*\n))$/g, - inside: { - "md md-toc": /\[(toc|TOC)\]/ - } - }; md.p = { - pattern: /^[^\n]+(?:\n|$)/gm, + pattern: /.+/g, inside: { + 'md md-toc': /^\s*\[(toc|TOC)\]\s*$/g } }; md.img = { diff --git a/public/res/classes/PreEditor.js b/public/res/preEditor.js similarity index 65% rename from public/res/classes/PreEditor.js rename to public/res/preEditor.js index 7d44b161..033b9921 100644 --- a/public/res/classes/PreEditor.js +++ b/public/res/preEditor.js @@ -1,55 +1,57 @@ /* jshint -W084, -W099 */ define([ 'jquery', + 'underscore', 'eventMgr', 'prism-core', + 'crel', 'libs/prism-markdown' -], function ($, eventMgr, Prism) { +], function ($, _, eventMgr, Prism, crel) { + + String.prototype.splice = function (i, remove, add) { + remove = +remove || 0; + add = add || ''; + return this.slice(0, i) + add + this.slice(i + remove); + }; + + var preEditor = {}; var undoManager; - eventMgr.addListener('onPagedownConfigure', function (pagedownEditor) { + eventMgr.addListener('onPagedownConfigure', function(pagedownEditor) { // Undo manager does exist at the moment setTimeout(function () { undoManager = pagedownEditor.undoManager; }, 0); }); - String.prototype.splice = function (i, remove, add) { - remove = +remove || 0; - add = add || ''; + eventMgr.addListener('onSectionsCreated', function(newSectionList) { + updateSectionList(newSectionList); + highlightSections(); + }); - return this.slice(0, i) + add + this.slice(i + remove); - }; + var fileChanged = false; + eventMgr.addListener('onFileSelected', function() { + fileChanged = true; + }); - function PreEditor(preElt) { - var preEditor = this; - preEditor.selectionStart = 0; - preEditor.selectionEnd = 0; - preEditor.scrollTop = 0; - preEditor.$preContentElt = $('
'); + preEditor.selectionStart = 0; + preEditor.selectionEnd = 0; + preEditor.scrollTop = 0; + var preElt; + preEditor.init = function(elt) { + preElt = elt; + preEditor.$contentElt = $(''); + preElt.appendChild(preEditor.$contentElt[0]); - preElt.appendChild(preEditor.$preContentElt[0]); - preEditor.highlight = function () { - setTimeout(function () { - preEditor.selectionStart = preElt.selectionStart; - preEditor.selectionEnd = preElt.selectionEnd; - var startDate = Date.now(); - Prism.highlightElement(preEditor.$preContentElt[0], false, function () { - console.log(Date.now() - startDate); - preElt.setSelectionRange(preEditor.selectionStart, preEditor.selectionEnd); - }); - }, 0); - }; - - preElt.focus = function () { - preEditor.$preContentElt.focus(); + preElt.focus = function() { + preEditor.$contentElt.focus(); this.setSelectionRange(preEditor.selectionStart, preEditor.selectionEnd); preElt.scrollTop = preEditor.scrollTop; }; - preEditor.$preContentElt.focus(function () { + preEditor.$contentElt.focus(function () { preElt.focused = true; }); - preEditor.$preContentElt.blur(function () { + preEditor.$contentElt.blur(function () { preElt.focused = false; }); Object.defineProperty(preElt, 'value', { @@ -57,7 +59,7 @@ define([ return this.textContent; }, set: function (value) { - //return preEditor.$preContentElt.text(value); + //return preEditor.$contentElt.text(value); var currentValue = this.textContent; // Find the first modified char @@ -70,7 +72,7 @@ define([ startIndex++; } if (startIndex === startIndexMax) { - return preEditor.$preContentElt.text(value); + return preEditor.$contentElt.text(value); } // Find the last modified char @@ -385,7 +387,7 @@ define([ } }; - preEditor.$preContentElt.on('keydown', function (evt) { + preEditor.$contentElt.on('keydown', function (evt) { var cmdOrCtrl = evt.metaKey || evt.ctrlKey; switch (evt.keyCode) { @@ -411,8 +413,144 @@ define([ break; } }); + }; + + var sectionList = []; + var sectionsToRemove = []; + var modifiedSections = []; + var insertBeforeSection; + function updateSectionList(newSectionList) { + + modifiedSections = []; + sectionsToRemove = []; + insertBeforeSection = undefined; + + // Render everything if file changed + if(fileChanged === true) { + sectionsToRemove = sectionList; + sectionList = newSectionList; + modifiedSections = newSectionList; + return; + } + + // Find modified section starting from top + var leftIndex = sectionList.length; + _.some(sectionList, function(section, index) { + if(index >= newSectionList.length || section.text != newSectionList[index].text) { + leftIndex = index; + return true; + } + }); + + // Find modified section starting from bottom + var rightIndex = -sectionList.length; + _.some(sectionList.slice().reverse(), function(section, index) { + if(index >= newSectionList.length || section.text != newSectionList[newSectionList.length - index - 1].text) { + rightIndex = -index; + return true; + } + }); + + if(leftIndex - rightIndex > sectionList.length) { + // Prevent overlap + rightIndex = leftIndex - sectionList.length; + } + + // Create an array composed of left unmodified, modified, right + // unmodified sections + var leftSections = sectionList.slice(0, leftIndex); + modifiedSections = newSectionList.slice(leftIndex, newSectionList.length + rightIndex); + var rightSections = sectionList.slice(sectionList.length + rightIndex, sectionList.length); + insertBeforeSection = _.first(rightSections); + sectionsToRemove = sectionList.slice(leftIndex, sectionList.length + rightIndex); + sectionList = leftSections.concat(modifiedSections).concat(rightSections); + } + + var elapsedTime = 0; + var timeoutId; + function highlightSections() { + + if(fileChanged === true) { + fileChanged = false; + // Perform a synchronous transformation + preEditor.selectionStart = preElt.selectionStart; + preEditor.selectionEnd = preElt.selectionEnd; + var newSectionEltList = document.createDocumentFragment(); + modifiedSections.forEach(function(section) { + highlight(section); + newSectionEltList.appendChild(section.highlightedContent); + }); + preEditor.$contentElt.html(''); + preEditor.$contentElt[0].appendChild(newSectionEltList); + preElt.setSelectionRange(preEditor.selectionStart, preEditor.selectionEnd); + return; + } + + // Perform an asynchronous transformation on each modified sections + clearTimeout(timeoutId); + //timeoutId = setTimeout(asyncHighlightSections, elapsedTime); + preEditor.selectionStart = preElt.selectionStart; + preEditor.selectionEnd = preElt.selectionEnd; + Prism.highlightElement(preEditor.$contentElt[0]); + //preElt.setSelectionRange(preEditor.selectionStart, preEditor.selectionEnd); } - return PreEditor; + function asyncHighlightSections() { + var startTime = Date.now(); + var deferredList = []; + modifiedSections.forEach(function(section) { + var deferred = $.Deferred(); + setTimeout(function() { + highlight(section); + deferred.resolve(); + }, 0); + deferredList.push(deferred); + }); + $.when.apply($, deferredList).then(function() { + var text = _.reduce(sectionList, function(text, section) { + return text + section.text; + }, ''); + + // Check that the editor has the actual value + if(preElt.textContent == text) { + preEditor.selectionStart = preElt.selectionStart; + preEditor.selectionEnd = preElt.selectionEnd; + + // Remove outdated sections + _.each(sectionsToRemove, function(section) { + var sectionElt = document.getElementById("wmd-input-section-" + section.id); + preEditor.$contentElt[0].removeChild(sectionElt); + }); + + var newSectionEltList = document.createDocumentFragment(); + modifiedSections.forEach(function(section) { + newSectionEltList.appendChild(section.highlightedContent); + }); + + if(insertBeforeSection !== undefined) { + var insertBeforeElt = document.getElementById("wmd-input-section-" + insertBeforeSection.id); + preEditor.$contentElt[0].insertBefore(newSectionEltList, insertBeforeElt); + } + else { + preEditor.$contentElt[0].appendChild(newSectionEltList); + } + + preElt.setSelectionRange(preEditor.selectionStart, preEditor.selectionEnd); + elapsedTime = Date.now() - startTime; + } + }); + } + + function highlight(section) { + var text = section.text.replace(/&/g, '&').replace(/