diff --git a/public/res/core.js b/public/res/core.js index 34a853a1..7fb3a65f 100644 --- a/public/res/core.js +++ b/public/res/core.js @@ -519,6 +519,7 @@ define([ // If the editor is already created $editorElt.val(initDocumentContent); aceEditor && aceEditor.selection.setSelectionRange(fileDesc.editorSelectRange); + aceEditor || pagedownEditor.undoManager.reinit(initDocumentContent, fileDesc.editorStart, fileDesc.editorEnd, fileDesc.editorScrollTop); aceEditor ? aceEditor.focus() : $editorElt.focus(); //pagedownEditor.refreshPreview(); return; @@ -572,7 +573,7 @@ define([ if(documentContent == newDocumentContent) { return false; } - + if(documentContent !== undefined) { fileDesc.content = newDocumentContent; eventMgr.onContentChanged(fileDesc); @@ -589,7 +590,7 @@ define([ }); } } - + documentContent = newDocumentContent; return true; } @@ -686,7 +687,7 @@ define([ else { document.body.innerHTML = bodyIndexHTML; } - + var styleContent = ''; // Apply font @@ -710,7 +711,7 @@ define([ applyFont(16); applyFont(17, 600); applyFont(18, 1200); - + function applyMaxWidth(maxWidth, screenWidth) { styleContent += [ '@media (min-width: ' + screenWidth + 'px) {', @@ -723,7 +724,7 @@ define([ _.each(maxWidthMap, function(entry) { applyMaxWidth(entry.maxWidth, entry.screenWidth); }); - + // Apply dynamic stylesheet var style = document.createElement("style"); style.innerHTML = styleContent; @@ -832,10 +833,10 @@ define([ $('#wmd-input').replaceWith(function() { return $('
').addClass(this.className).addClass('form-control'); }); - + // Create UI layout after textarea createLayout(); - + editor.init(document.querySelector('#wmd-input'), document.querySelector('.preview-container')); } else { diff --git a/public/res/editor.js b/public/res/editor.js index 5871c438..d4373790 100644 --- a/public/res/editor.js +++ b/public/res/editor.js @@ -8,7 +8,7 @@ define([ 'crel', 'libs/prism-markdown' ], function ($, _, settings, eventMgr, Prism, crel) { - + String.prototype.splice = function (i, remove, add) { remove = +remove || 0; add = add || ''; @@ -60,23 +60,27 @@ define([ }); var previousTextContent; - function onInputChange() { + function onInputContentChange() { selectionStart = inputElt.selectionStart; selectionEnd = inputElt.selectionEnd; var currentTextContent = inputElt.textContent; - if(!/\n$/.test(currentTextContent)) { - currentTextContent += '\n'; - } if(fileChanged === false) { fileDesc.editorStart = selectionStart; fileDesc.editorEnd = selectionEnd; if(currentTextContent == previousTextContent) { return; } + if(!/\n$/.test(currentTextContent)) { + currentTextContent += '\n'; + } fileDesc.content = currentTextContent; eventMgr.onContentChanged(fileDesc); } else { + if(!/\n$/.test(currentTextContent)) { + currentTextContent += '\n'; + fileDesc.content = currentTextContent; + } eventMgr.onFileOpen(fileDesc); previewElt.scrollTop = fileDesc.previewScrollTop; selectionStart = fileDesc.editorStart; @@ -87,7 +91,59 @@ define([ } previousTextContent = currentTextContent; } - + + function adjustCursorPosition() { + setTimeout(function() { + selectionStart = inputElt.selectionStart; + selectionEnd = inputElt.selectionEnd; + + var backwards = false; + var selection = window.getSelection(); + if (!selection.isCollapsed) { + var range = document.createRange(); + range.setStart(selection.anchorNode, selection.anchorOffset); + range.setEnd(selection.focusNode, selection.focusOffset); + backwards = range.collapsed; + range.detach(); + } + + var selectionRange = selection.getRangeAt(0); + var container = backwards ? selectionRange.startContainer : selectionRange.endContainer; + var cursorY; + if(container.textContent == '\n') { + cursorY = container.parentNode.offsetTop + container.parentNode.offsetHeight / 2 - inputElt.scrollTop; + } + else { + if(selectionStart === selectionEnd) { + var selectedChar = inputElt.textContent[selectionStart]; + if(selectedChar === undefined || selectedChar == '\n') { + selectionRange = createRange(selectionStart - 1, selectionEnd); + } + else { + selectionRange = createRange(selectionStart, selectionEnd + 1); + } + } + var selectionRect = selectionRange.getBoundingClientRect(); + cursorY = selectionRect.top + selectionRect.height / 2 - inputElt.offsetTop; + selectionRange.detach(); + } + + var adjust = inputElt.offsetHeight / 2; + if(adjust > 130) { + adjust = 130; + } + var cursorMinY = adjust; + var cursorMaxY = inputElt.offsetHeight - adjust; + if(cursorY < cursorMinY) { + inputElt.scrollTop += cursorY - cursorMinY; + } + else if(cursorY > cursorMaxY) { + inputElt.scrollTop += cursorY - cursorMaxY; + } + }, 0); + } + eventMgr.addListener('onLayoutResize', adjustCursorPosition); + editor.init = function(elt1, elt2) { inputElt = elt1; previewElt = elt2; @@ -97,19 +153,19 @@ define([ }); editor.$contentElt = $(editor.contentElt); inputElt.appendChild(editor.contentElt); - + $(inputElt).scroll(function() { scrollTop = this.scrollTop; if(fileChanged === false) { fileDesc.editorScrollTop = scrollTop; } - }).bind("keyup mouseup", onInputChange); + }).bind("keyup mouseup", onInputContentChange); $(previewElt).scroll(function() { if(fileChanged === false) { fileDesc.previewScrollTop = previewElt.scrollTop; } }); - + inputElt.focus = function() { editor.$contentElt.focus(); this.setSelectionRange(selectionStart, selectionEnd); @@ -121,7 +177,7 @@ define([ editor.$contentElt.blur(function() { inputElt.focused = false; }); - + Object.defineProperty(inputElt, 'value', { get: function () { return this.textContent; @@ -151,13 +207,13 @@ define([ var replacementText = value.substring(startIndex, value.length - endIndex + 1); endIndex = currentValue.length - endIndex + 1; - var range = createRange(inputElt, startIndex, endIndex); + var range = createRange(startIndex, endIndex); range.deleteContents(); range.insertNode(document.createTextNode(replacementText)); - onInputChange(); + onInputContentChange(); } }); - + Object.defineProperty(inputElt, 'selectionStart', { get: function () { var selection = window.getSelection(); @@ -213,87 +269,10 @@ define([ configurable: true }); - function findOffset(root, ss) { - if (!root) { - return null; - } - - var offset = 0, - element = root, - container; - - do { - container = element; - element = element.firstChild; - - if (element) { - do { - var len = element.textContent.length; - - if (offset <= ss && offset + len > ss) { - break; - } - - offset += len; - } while (element = element.nextSibling); - } - - if (!element) { - // It's the container's lastChild - break; - } - } while (element && element.hasChildNodes() && element.nodeType != 3); - - if (element) { - return { - element: element, - offset: ss - offset - }; - } else if (container) { - element = container; - - while (element && element.lastChild) { - element = element.lastChild; - } - - if (element.nodeType === 3) { - return { - element: element, - offset: element.textContent.length - }; - } else { - return { - element: element, - offset: 0 - }; - } - } - - return { - element: root, - offset: 0, - error: true - }; - } - - function createRange(root, ss, se) { - var range = document.createRange(), - offset = findOffset(root, ss); - - range.setStart(offset.element, offset.offset); - - if (se && se != ss) { - offset = findOffset(root, se); - } - - range.setEnd(offset.element, offset.offset); - return range; - } - inputElt.setSelectionRange = function (ss, se) { selectionStart = ss; selectionEnd = se; - var range = createRange(editor.contentElt, ss, se); + var range = createRange(ss, se); var selection = window.getSelection(); selection.removeAllRanges(); @@ -303,6 +282,10 @@ define([ editor.$contentElt.on('keydown', function (evt) { var cmdOrCtrl = evt.metaKey || evt.ctrlKey; + if(!cmdOrCtrl && !event.altKey && !event.shiftKey) { + adjustCursorPosition(); + } + switch (evt.keyCode) { case 9: // Tab if (!cmdOrCtrl) { @@ -330,17 +313,17 @@ define([ editor.$contentElt.on('paste', function () { pagedownEditor.undoManager.setMode("paste"); setTimeout(function() { - onInputChange(); + onInputContentChange(); }, 0); }); - + editor.$contentElt.on('cut', function () { pagedownEditor.undoManager.setMode("cut"); setTimeout(function() { - onInputChange(); + onInputContentChange(); }, 0); }); - + var action = function (action, options) { options = options || {}; @@ -403,98 +386,17 @@ define([ pagedownEditor.undoManager.setMode("newlines"); - state.before += '\n'// + indent; + state.before += '\n' + indent; state.selection = ''; state.ss += indent.length + 1; state.se = state.ss; }, - - comment: function (state) { - var textAction; - var open = ''; - - var start = state.before.lastIndexOf(open), - end = state.after.indexOf(close), - closeBefore = state.before.lastIndexOf(close), - openAfter = state.after.indexOf(start); - - pagedownEditor.undoManager.setMode("typing"); - - if (start > -1 && end > -1 && (start > closeBefore || closeBefore === -1) && (end < openAfter || openAfter === -1)) { - // Uncomment - state.before = state.before.splice(start, open.length); - state.after = state.after.splice(end, close.length); - - textAction = [{ - add: '', - del: open, - start: start - }, { - add: '', - del: close, - start: state.before.length + state.selection.length + end - }]; - - state.ss -= open.length; - state.se -= open.length; - - return textAction; - } else { - // Comment - if (state.selection) { - // Comment selection - state.selection = open + state.selection + close; - - textAction = [{ - add: open, - del: '', - start: state.ss - }, { - add: close, - del: '', - start: open.length + state.se - }]; - } else { - // Comment whole line - start = state.before.lastIndexOf('\n') + 1; - end = state.after.indexOf('\n'); - - if (end === -1) { - end = state.after.length; - } - - while (/\s/.test(state.before.charAt(start))) { - start++; - } - - state.before = state.before.splice(start, 0, open); - - state.after = state.after.splice(end, 0, close); - - textAction = [{ - add: open, - del: '', - start: start - }, { - add: close, - del: '', - start: state.before.length + end - }]; - } - - state.ss += open.length; - state.se += open.length; - - return textAction; - } - } }; }; - + var sectionList = []; var sectionsToRemove = []; var modifiedSections = []; @@ -516,28 +418,38 @@ define([ // Find modified section starting from top var leftIndex = sectionList.length; _.some(sectionList, function(section, index) { - if(index >= newSectionList.length || section.text != newSectionList[index].text) { + var newSection = newSectionList[index]; + if(index >= newSectionList.length || + // Check modified + section.textWithFrontMatter != newSection.textWithFrontMatter || + // Check that section has not been detached from the DOM with backspace + !section.highlightedContent.parentNode) { leftIndex = index; return true; } }); - + // Find modified section starting from bottom var rightIndex = -sectionList.length; _.some(sectionList.slice().reverse(), function(section, index) { - var newSectionText = newSectionList[newSectionList.length - index - 1].text; - // Check also the content of the node since new lines can be added just at the beggining - if(index >= newSectionList.length || section.text != newSectionText || section.highlightedContent.textContent != newSectionText) { + var newSection = newSectionList[newSectionList.length - index - 1]; + if(index >= newSectionList.length || + // Check modified + section.textWithFrontMatter != newSection.textWithFrontMatter || + // Check that section has not been detached from the DOM with backspace + !section.highlightedContent.parentNode || + // Check also the content of the node since new lines can be added just at the beggining + section.highlightedContent.textContent != newSection.textWithFrontMatter) { 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); @@ -547,8 +459,8 @@ define([ sectionsToRemove = sectionList.slice(leftIndex, sectionList.length + rightIndex); sectionList = leftSections.concat(modifiedSections).concat(rightSections); } - - function highlightSections() { + + function highlightSections() { selectionStart = inputElt.selectionStart; selectionEnd = inputElt.selectionEnd; var newSectionEltList = document.createDocumentFragment(); @@ -568,7 +480,7 @@ define([ // section can be already removed sectionElt && editor.contentElt.removeChild(sectionElt); }); - + if(insertBeforeSection !== undefined) { var insertBeforeElt = document.getElementById("wmd-input-section-" + insertBeforeSection.id); editor.contentElt.insertBefore(newSectionEltList, insertBeforeElt); @@ -576,11 +488,9 @@ define([ else { editor.contentElt.appendChild(newSectionEltList); } - - //var dummyTextNode = document.createTextNode('\n'); - //editor.contentElt.appendChild(dummyTextNode); + inputElt.setSelectionRange(selectionStart, selectionEnd); - + // Remove textNodes created outside sections var childNode = editor.contentElt.firstChild; while(childNode) { @@ -592,49 +502,9 @@ define([ } } } -/* - 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(inputElt.textContent == text) { - selectionStart = inputElt.selectionStart; - selectionEnd = inputElt.selectionEnd; - - var newSectionEltList = document.createDocumentFragment(); - modifiedSections.forEach(function(section) { - newSectionEltList.appendChild(section.highlightedContent); - }); - - if(insertBeforeSection !== undefined) { - var insertBeforeElt = document.getElementById("wmd-input-section-" + insertBeforeSection.id); - editor.$contentElt[0].insertBefore(newSectionEltList, insertBeforeElt); - } - else { - editor.$contentElt[0].appendChild(newSectionEltList); - } - - inputElt.setSelectionRange(selectionStart, selectionEnd); - elapsedTime = Date.now() - startTime; - } - }); - } -*/ + function highlight(section) { - var text = section.text.replace(/&/g, '&').replace(/ ss) { + break; + } + + offset += len; + } while (element = element.nextSibling); + } + + if (!element) { + // It's the container's lastChild + break; + } + } while (element && element.hasChildNodes() && element.nodeType != 3); + + if (element) { + return { + element: element, + offset: ss - offset + }; + } else if (container) { + element = container; + + while (element && element.lastChild) { + element = element.lastChild; + } + + if (element.nodeType === 3) { + return { + element: element, + offset: element.textContent.length + }; + } else { + return { + element: element, + offset: 0 + }; + } + } + + return { + element: editor.contentElt, + offset: 0, + error: true + }; + } + + var range = document.createRange(), + offset = findOffset(ss); + + range.setStart(offset.element, offset.offset); + + if (se && se != ss) { + offset = findOffset(se); + } + + range.setEnd(offset.element, offset.offset); + return range; + } + return editor; -}); \ No newline at end of file +}); diff --git a/public/res/extensions/buttonFocusMode.js b/public/res/extensions/buttonFocusMode.js index 4b7134e3..f7380137 100644 --- a/public/res/extensions/buttonFocusMode.js +++ b/public/res/extensions/buttonFocusMode.js @@ -43,7 +43,8 @@ define([ var cursorMinY = coef*editorHeight; var cursorMaxY = (1-coef)*editorHeight; var cursorY = $editorElt.caret('offset').top - $editorElt.offset().top; - console.log($editorElt.caret('offset')); + //console.log($editorElt.find('.pre-content').caret('offset')); + //console.log(window.getSelection().getRangeAt(0).getBoundingClientRect()); //$positionHelper.detach(); //parentNode.normalize(); /* diff --git a/public/res/extensions/markdownSectionParser.js b/public/res/extensions/markdownSectionParser.js index e5a13af1..982fb18c 100644 --- a/public/res/extensions/markdownSectionParser.js +++ b/public/res/extensions/markdownSectionParser.js @@ -11,9 +11,9 @@ 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) { @@ -31,7 +31,7 @@ define([ regexp = '^[ \\t]*\\n\\\\?\\\\begin\\{[a-z]*\\*?\\}[\\s\\S]*?\\\\end\\{[a-z]*\\*?\\}|' + regexp; // \\begin{...} \\end{...} math block delimiters } regexp = new RegExp(regexp, 'gm'); - + var converter = editor.getConverter(); converter.hooks.chain("preConversion", function() { return _.reduce(sectionList, function(result, section) { @@ -39,8 +39,8 @@ define([ }, ''); }); }; - - var trimLen; + + var trimLen = 0; markdownSectionParser.onMarkdownTrim = function(len) { trimLen = len; }; @@ -48,13 +48,16 @@ define([ var sectionCounter = 0; function parseFileContent(fileDesc) { var text = fileDesc.content.substring(trimLen); + var frontMatter = fileDesc.content.substring(0, trimLen); var tmpText = text + "\n\n"; function addSection(startOffset, endOffset) { var sectionText = tmpText.substring(offset, endOffset); sectionList.push({ id: ++sectionCounter, - text: sectionText + text: sectionText, + textWithFrontMatter: frontMatter + sectionText }); + frontMatter = ''; } sectionList = []; var offset = 0; @@ -68,9 +71,9 @@ define([ 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/scrollLink.js b/public/res/extensions/scrollLink.js index 3a53c52c..43311164 100644 --- a/public/res/extensions/scrollLink.js +++ b/public/res/extensions/scrollLink.js @@ -17,7 +17,7 @@ define([ scrollLink.onSectionsCreated = function(sectionListParam) { sectionList = sectionListParam; }; - + var offsetBegin = 0; scrollLink.onMarkdownTrim = function(offsetBeginParam) { offsetBegin = offsetBeginParam; @@ -161,8 +161,8 @@ define([ lastPreviewScrollTop = previewScrollTop; return; } - $previewElt.stop('scrollLinkFx', true).animate({ - scrollTop: destScrollTop + scrollingHelper.stop('scrollLinkFx', true).css('value', 0).animate({ + value: destScrollTop - previewScrollTop }, { easing: 'easeOutSine', duration: 200, @@ -170,13 +170,15 @@ define([ step: function(now) { isPreviewMoving = true; lastPreviewScrollTop = previewScrollTop + now; + $previewElt.scrollTop(lastPreviewScrollTop); }, done: function() { - setTimeout(function() { + _.defer(function() { isPreviewMoving = false; - }, 100); + }); }, }).dequeue('scrollLinkFx'); + } else if(isScrollPreview === true) { if(Math.abs(previewScrollTop - lastPreviewScrollTop) <= 9) { @@ -205,43 +207,28 @@ define([ lastEditorScrollTop = editorScrollTop; return; } - if(window.lightMode) { - $editorElt.stop('scrollLinkFx', true).animate({ - scrollTop: destScrollTop - }, { - easing: 'easeOutSine', - duration: 200, - queue: 'scrollLinkFx', - step: function(now) { - isEditorMoving = true; - lastEditorScrollTop = editorScrollTop + now; - }, - done: function() { - setTimeout(function() { - isEditorMoving = false; - }, 100); - }, - }).dequeue('scrollLinkFx'); - } - else { - scrollingHelper.stop('scrollLinkFx', true).css('value', 0).animate({ - value: destScrollTop - editorScrollTop - }, { - easing: 'easeOutSine', - duration: 200, - queue: 'scrollLinkFx', - step: function(now) { - isEditorMoving = true; - lastEditorScrollTop = editorScrollTop + now; + scrollingHelper.stop('scrollLinkFx', true).css('value', 0).animate({ + value: destScrollTop - editorScrollTop + }, { + easing: 'easeOutSine', + duration: 200, + queue: 'scrollLinkFx', + step: function(now) { + isEditorMoving = true; + lastEditorScrollTop = editorScrollTop + now; + if(window.lightMode) { + $editorElt.scrollTop(lastEditorScrollTop); + } + else { aceEditor.session.setScrollTop(lastEditorScrollTop); - }, - done: function() { - _.defer(function() { - isEditorMoving = false; - }); - }, - }).dequeue('scrollLinkFx'); - } + } + }, + done: function() { + _.defer(function() { + isEditorMoving = false; + }); + }, + }).dequeue('scrollLinkFx'); } }, 100); @@ -249,7 +236,7 @@ define([ isScrollEditor = true; buildSections(); }; - + var isPreviewVisible = true; function setPreviewHidden() { isPreviewVisible = false; @@ -259,7 +246,7 @@ define([ isPreviewVisible = true; console.log(isPreviewVisible); } - + scrollLink.onLayoutConfigure = function(layoutGlobalConfig) { layoutGlobalConfig.east__onclose = setPreviewHidden; layoutGlobalConfig.south__onclose = setPreviewHidden; @@ -324,4 +311,4 @@ define([ }; return scrollLink; -}); \ No newline at end of file +}); diff --git a/public/res/extensions/yamlFrontMatterParser.js b/public/res/extensions/yamlFrontMatterParser.js index 7e98cb63..9631012e 100644 --- a/public/res/extensions/yamlFrontMatterParser.js +++ b/public/res/extensions/yamlFrontMatterParser.js @@ -17,7 +17,8 @@ define([ }; var regex = /^(\s*-{3}\s*\n([\w\W]+?)\n\s*-{3}\s*\n)?([\w\W]*)$/; - yamlFrontMatterParser.onContentChanged = function(fileDesc) { + + function parseFrontMatter(fileDesc) { var text = fileDesc.content; var results = regex.exec(text); var yaml = results[2]; @@ -34,11 +35,14 @@ define([ } catch (e) { eventMgr.onMarkdownTrim(0); - return text; + return; } } eventMgr.onMarkdownTrim((results[1] || '').length); - }; + } + + yamlFrontMatterParser.onFileOpen = parseFrontMatter; + yamlFrontMatterParser.onContentChanged = parseFrontMatter; return yamlFrontMatterParser; -}); \ No newline at end of file +}); diff --git a/public/res/html/bodyIndex.html b/public/res/html/bodyIndex.html index bb5612c3..c45b634b 100644 --- a/public/res/html/bodyIndex.html +++ b/public/res/html/bodyIndex.html @@ -123,6 +123,22 @@IMPORT