From 8cb7c307eeac358532fa1d9b61878537a09d0798 Mon Sep 17 00:00:00 2001 From: benweet Date: Wed, 19 Mar 2014 00:33:57 +0000 Subject: [PATCH] Fixes for new editor --- public/res/core.js | 15 +- public/res/editor.js | 414 ++++++++---------- public/res/extensions/buttonFocusMode.js | 3 +- .../res/extensions/markdownSectionParser.js | 19 +- public/res/extensions/scrollLink.js | 75 ++-- .../res/extensions/yamlFrontMatterParser.js | 12 +- public/res/html/bodyIndex.html | 32 +- public/res/libs/prism-markdown.js | 1 + public/res/styles/main.less | 144 +++--- 9 files changed, 328 insertions(+), 387 deletions(-) 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 @@