');
+
+ 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();
+ this.setSelectionRange(preEditor.selectionStart, preEditor.selectionEnd);
+ preElt.scrollTop = preEditor.scrollTop;
+ };
+ preEditor.$preContentElt.focus(function () {
+ preElt.focused = true;
+ });
+ preEditor.$preContentElt.blur(function () {
+ preElt.focused = false;
+ });
+ Object.defineProperty(preElt, 'value', {
+ get: function () {
+ return this.textContent;
+ },
+ set: function (value) {
+ //return preEditor.$preContentElt.text(value);
+ var currentValue = this.textContent;
+
+ // Find the first modified char
+ var startIndex = 0;
+ var startIndexMax = Math.min(currentValue.length, value.length);
+ while (startIndex < startIndexMax) {
+ if (currentValue.charCodeAt(startIndex) !== value.charCodeAt(startIndex)) {
+ break;
+ }
+ startIndex++;
+ }
+ if (startIndex === startIndexMax) {
+ return preEditor.$preContentElt.text(value);
+ }
+
+ // Find the last modified char
+ var endIndex = 1;
+ var endIndexMax = Math.min(currentValue.length - startIndex, value.length - startIndex);
+ while (endIndex <= endIndexMax) {
+ if (currentValue.charCodeAt(currentValue.length - endIndex) !== value.charCodeAt(value.length - endIndex)) {
+ break;
+ }
+ endIndex++;
+ }
+
+ var replacementText = value.substring(startIndex, value.length - endIndex + 1);
+ endIndex = currentValue.length - endIndex + 1;
+
+ var range = createRange(preElt, startIndex, endIndex);
+ range.deleteContents();
+ range.insertNode(document.createTextNode(replacementText));
+ }
+ });
+ Object.defineProperty(preElt, 'selectionStart', {
+ get: function () {
+ var selection = window.getSelection();
+
+ if (selection.rangeCount) {
+ var range = selection.getRangeAt(0),
+ element = range.startContainer,
+ container = element,
+ offset = range.startOffset;
+
+ if (!(this.compareDocumentPosition(element) & 0x10)) {
+ return 0;
+ }
+
+ do {
+ while (element = element.previousSibling) {
+ if (element.textContent) {
+ offset += element.textContent.length;
+ }
+ }
+
+ element = container = container.parentNode;
+ } while (element && element != this);
+
+ return offset;
+ } else {
+ return 0;
+ }
+ },
+ set: function (value) {
+ preElt.setSelectionRange(value, preEditor.selectionEnd);
+ },
+
+ enumerable: true,
+ configurable: true
+ });
+
+ Object.defineProperty(preElt, 'selectionEnd', {
+ get: function () {
+ var selection = window.getSelection();
+
+ if (selection.rangeCount) {
+ return this.selectionStart + (selection.getRangeAt(0) + '').length;
+ } else {
+ return 0;
+ }
+ },
+ set: function (value) {
+ preElt.setSelectionRange(preEditor.selectionStart, value);
+ },
+
+ enumerable: true,
+ 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;
+ }
+
+ preElt.setSelectionRange = function (ss, se) {
+ preEditor.selectionStart = ss;
+ preEditor.selectionEnd = se;
+ var range = createRange(this, ss, se);
+
+ var selection = window.getSelection();
+ selection.removeAllRanges();
+ selection.addRange(range);
+ };
+
+ var action = function (action, options) {
+ options = options || {};
+
+ var text = preElt.value,
+ ss = options.start || preEditor.selectionStart,
+ se = options.end || preEditor.selectionEnd,
+ state = {
+ ss: ss,
+ se: se,
+ before: text.slice(0, ss),
+ after: text.slice(se),
+ selection: text.slice(ss, se)
+ };
+
+ actions[action](state, options);
+
+ preElt.value = state.before + state.selection + state.after;
+
+ preElt.setSelectionRange(state.ss, state.se);
+
+ preElt.dispatchEvent(new window.Event('input'));
+ };
+
+ var actions = {
+ indent: function (state, options) {
+ var lf = state.before.lastIndexOf('\n') + 1;
+
+ undoManager && undoManager.setMode("typing");
+
+ if (options.inverse) {
+ if (/\s/.test(state.before.charAt(lf))) {
+ state.before = state.before.splice(lf, 1);
+
+ state.ss--;
+ state.se--;
+ }
+
+ state.selection = state.selection.replace(/^[ \t]/gm, '');
+ } else if (state.selection) {
+ state.before = state.before.splice(lf, 0, '\t');
+ state.selection = state.selection.replace(/\r?\n(?=[\s\S])/g, '\n\t');
+
+ state.ss++;
+ state.se++;
+ } else {
+ state.before += '\t';
+
+ state.ss++;
+ state.se++;
+
+ return;
+ }
+
+ state.se = state.ss + state.selection.length;
+ },
+
+ newline: function (state) {
+ var lf = state.before.lastIndexOf('\n') + 1;
+ var indent = (state.before.slice(lf).match(/^\s+/) || [''])[0];
+
+ undoManager && undoManager.setMode("newlines");
+
+ 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);
+
+ undoManager && 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;
+ }
+ }
+ };
+
+ preEditor.$preContentElt.on('keydown', function (evt) {
+ var cmdOrCtrl = evt.metaKey || evt.ctrlKey;
+
+ switch (evt.keyCode) {
+ case 9: // Tab
+ if (!cmdOrCtrl) {
+ action('indent', {
+ inverse: evt.shiftKey
+ });
+ return false;
+ }
+ break;
+ case 13:
+ action('newline');
+ return false;
+ case 191:
+ if (cmdOrCtrl && !evt.altKey) {
+ action('comment', {
+ lang: this.id
+ });
+ return false;
+ }
+
+ break;
+ }
+ });
+
+ }
+
+ return PreEditor;
+});
\ No newline at end of file
diff --git a/public/res/classes/preEditor copy.js b/public/res/classes/preEditor copy.js
new file mode 100644
index 00000000..70a7e74b
--- /dev/null
+++ b/public/res/classes/preEditor copy.js
@@ -0,0 +1,183 @@
+define(['jquery'], function($) {
+ function PreEditor(preElt) {
+ this.selectionStart = 0;
+ this.selectionEnd = 0;
+ this.scrollTop = 0;
+ this.$preContentElt = $('
');
+
+ preElt.appendChild(this.$preContentElt[0]);
+
+ preElt.focus = function() {
+ this.$preContentElt.focus();
+ this.setSelectionRange(this.selectionStart, this.selectionEnd);
+ preElt.scrollTop = this.scrollTop;
+ };
+ this.$preContentElt.focus(function() {
+ preElt.focused = true;
+ });
+ this.$preContentElt.blur(function() {
+ preElt.focused = false;
+ });
+ Object.defineProperty(preElt, 'value', {
+ get: function() {
+ return this.$preContentElt.text();
+ },
+ set: function(value) {
+ this.$preContentElt.text(value);
+ }
+ });
+
+ Object.defineProperty(preElt, 'value', {
+ get: function() {
+ return this.$preContentElt.text();
+ },
+ set: function(value) {
+ this.$preContentElt.text(value);
+ }
+ });
+ Object.defineProperty(preElt, 'selectionStart', {
+ get: function() {
+ var selection = window.getSelection();
+
+ if(selection.rangeCount) {
+ var range = selection.getRangeAt(0),
+ element = range.startContainer,
+ container = element,
+ offset = range.startOffset;
+
+ if(!(this.compareDocumentPosition(element) & 0x10)) {
+ return 0;
+ }
+
+ do {
+ while(element = element.previousSibling) {
+ if(element.textContent) {
+ offset += element.textContent.length;
+ }
+ }
+
+ element = container = container.parentNode;
+ } while(element && element != this);
+
+ return offset;
+ }
+ else {
+ return 0;
+ }
+ },
+ set: function(value) {
+ preElt.setSelectionRange(value, this.selectionEnd);
+ },
+
+ enumerable: true,
+ configurable: true
+ });
+
+ Object.defineProperty(preElt, 'selectionEnd', {
+ get: function() {
+ var selection = window.getSelection();
+
+ if(selection.rangeCount) {
+ return this.selectionStart + (selection.getRangeAt(0) + '').length;
+ }
+ else {
+ return 0;
+ }
+ },
+ set: function(value) {
+ preElt.setSelectionRange(this.selectionStart, value);
+ },
+
+ enumerable: true,
+ configurable: true
+ });
+
+ preElt.setSelectionRange = function(ss, se) {
+ this.selectionStart = ss;
+ this.selectionEnd = se;
+ 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
+ };
+ }
+
+ var range = document.createRange(),
+ offset = findOffset(this, ss);
+
+ range.setStart(offset.element, offset.offset);
+
+ if(se && se != ss) {
+ offset = findOffset(this, se);
+ }
+
+ range.setEnd(offset.element, offset.offset);
+
+ var selection = window.getSelection();
+ selection.removeAllRanges();
+ selection.addRange(range);
+ };
+
+ }
+
+
+ return PreEditor;
+});
\ No newline at end of file
diff --git a/public/res/constants.js b/public/res/constants.js
index 7d63a586..f4e816e4 100644
--- a/public/res/constants.js
+++ b/public/res/constants.js
@@ -14,7 +14,7 @@ define([], function() {
constants.DEFAULT_FILE_TITLE = "Title";
constants.DEFAULT_FOLDER_NAME = "New folder";
constants.GDRIVE_DEFAULT_FILE_TITLE = "New Markdown document";
- constants.EDITOR_DEFAULT_PADDING = 15;
+ constants.EDITOR_DEFAULT_PADDING = 30;
constants.CHECK_ONLINE_PERIOD = 120000;
constants.AJAX_TIMEOUT = 30000;
constants.ASYNC_TASK_DEFAULT_TIMEOUT = 60000;
diff --git a/public/res/core.js b/public/res/core.js
index 2295e41b..78322ee4 100644
--- a/public/res/core.js
+++ b/public/res/core.js
@@ -4,6 +4,7 @@ define([
"underscore",
"crel",
"ace",
+ "classes/PreEditor",
"constants",
"utils",
"storage",
@@ -25,7 +26,7 @@ define([
'ace/ext/spellcheck',
'ace/ext/searchbox'
-], function($, _, crel, ace, 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 = {};
@@ -384,6 +385,16 @@ define([
}
},
onresize_end: function(paneName) {
+ if(preEditor.$preContentElt !== undefined && paneName == 'center') {
+ var padding = ($editorElt.width() - getMaxWidth()) / 2;
+ if(padding < constants.EDITOR_DEFAULT_PADDING) {
+ padding = constants.EDITOR_DEFAULT_PADDING;
+ }
+ preEditor.$preContentElt.css({
+ 'padding-left': padding + 'px',
+ 'padding-right': padding + 'px'
+ });
+ }
if(aceEditor !== undefined && paneName == 'center') {
aceEditor.resize();
var bottomMargin = (aceEditor.renderer.$size.scrollerHeight - aceEditor.renderer.lineHeight) / 2;
@@ -488,6 +499,7 @@ define([
var fileDesc;
var documentContent;
var UndoManager = require("ace/undomanager").UndoManager;
+ var preEditor;
core.initEditor = function(fileDescParam) {
if(fileDesc !== undefined) {
eventMgr.onFileClosed(fileDesc);
@@ -518,11 +530,14 @@ define([
// Store editor scrollTop on scroll event
$editorElt.scroll(function() {
if(documentContent !== undefined) {
- fileDesc.editorScrollTop = $(this).scrollTop();
+ preEditor.scrollTop = this.scrollTop;
+ fileDesc.editorScrollTop = preEditor.scrollTop;
}
});
// Store editor selection on change
$editorElt.bind("keyup mouseup", function() {
+ preEditor.selectionStart = this.selectionStart;
+ preEditor.selectionEnd = this.selectionEnd;
if(documentContent !== undefined) {
fileDesc.editorStart = this.selectionStart;
fileDesc.editorEnd = this.selectionEnd;
@@ -572,9 +587,13 @@ define([
if(aceEditor !== undefined) {
newDocumentContent = aceEditor.getValue();
}
- if(documentContent !== undefined && documentContent != newDocumentContent) {
+ if(documentContent === undefined) {
+ preEditor.highlight();
+ }
+ else if(documentContent != newDocumentContent) {
fileDesc.content = newDocumentContent;
eventMgr.onContentChanged(fileDesc);
+ preEditor.highlight();
}
documentContent = newDocumentContent;
}
@@ -615,7 +634,8 @@ define([
eventMgr.onFileOpen(fileDesc);
$previewContainerElt.scrollTop(fileDesc.previewScrollTop);
if(window.lightMode) {
- $editorElt.scrollTop(fileDesc.editorScrollTop);
+ preEditor.scrollTop = fileDesc.editorScrollTop;
+ $editorElt.scrollTop(preEditor.scrollTop);
}
else {
_.defer(function() {
@@ -704,7 +724,7 @@ define([
screenWidth = screenWidth || 0;
//var codeFontSize = settings.editorFontSize;
//var codeLineHeight = Math.round(codeFontSize * 20 / 12);
- var previewFontSize = size * 13 / 12;
+ var previewFontSize = size; // * 13 / 12;
styleContent += [
'@media (min-width: ' + screenWidth + 'px) {',
'#wmd-input, .textarea-helper {',
@@ -717,9 +737,9 @@ define([
'}',
].join('\n');
}
- applyFont(15);
- applyFont(16, 600);
- applyFont(17, 1200);
+ applyFont(16);
+ applyFont(17, 600);
+ applyFont(18, 1200);
function applyMaxWidth(maxWidth, screenWidth) {
styleContent += [
@@ -837,17 +857,12 @@ define([
});
// Editor
- if(window.preMode) {
- // In light mode, we replace ACE with a textarea
- $('#wmd-input').addClass('form-control').attr('contenteditable', true);
-
- // Create UI layout after textarea
- createLayout();
- }
- else if(window.lightMode) {
- // In light mode, we replace ACE with a textarea
+ if(window.lightMode) {
+ // In pre mode, we replace ACE with an editable pre
$('#wmd-input').replaceWith(function() {
- return $('