Fix Google Drive Realtime document import
This commit is contained in:
parent
91964c1240
commit
d78b446722
@ -23,6 +23,7 @@
|
||||
"pagedown-extra": "git@github.com:jmcmanus/pagedown-extra.git#master",
|
||||
"crel": "git@github.com:KoryNunn/crel.git#8dbda04b129fc0aec01a2a080d1cab26816e11c1",
|
||||
"waitForImages": "git@github.com:alexanderdickson/waitForImages.git#~1.4.2",
|
||||
"to-markdown": "git@github.com:benweet/to-markdown.git#jquery"
|
||||
"to-markdown": "git@github.com:benweet/to-markdown.git#jquery",
|
||||
"js-yaml": "~2.1.0"
|
||||
}
|
||||
}
|
||||
|
28
res/core.js
28
res/core.js
@ -18,6 +18,7 @@ define([
|
||||
'libs/ace_mode',
|
||||
'ace/requirejs/text!ace/css/editor.css',
|
||||
'ace/requirejs/text!ace/theme/textmate.css',
|
||||
'ace/ext/spellcheck',
|
||||
|
||||
], function($, _, crel, ace, utils, settings, eventMgr, mousetrap, bodyIndexHTML, bodyViewerHTML, settingsTemplateTooltipHTML, settingsUserCustomExtensionTooltipHTML) {
|
||||
|
||||
@ -206,6 +207,8 @@ define([
|
||||
return;
|
||||
}
|
||||
aceEditor = ace.edit("wmd-input");
|
||||
require('ace/ext/spellcheck');
|
||||
aceEditor.setOption("spellcheck", true);
|
||||
aceEditor.renderer.setShowGutter(false);
|
||||
aceEditor.renderer.setPrintMarginColumn(false);
|
||||
aceEditor.renderer.setPadding(EDITOR_DEFAULT_PADDING);
|
||||
@ -361,13 +364,15 @@ define([
|
||||
// north resizer is very small
|
||||
// var $previewButtonsContainerElt = $('<div
|
||||
// class="preview-button-container">');
|
||||
$resizerDecorator = $('<div class="resizer-decorator">');
|
||||
$previewButtonsElt = $('<div class="extension-preview-buttons">');
|
||||
$editorButtonsElt = $('<div class="extension-editor-buttons">');
|
||||
if(viewerMode || settings.layoutOrientation == "horizontal") {
|
||||
$('.ui-layout-resizer-north').append($previewButtonsElt);
|
||||
$('.ui-layout-resizer-north').append($resizerDecorator).append($previewButtonsElt);
|
||||
$('.ui-layout-resizer-east').append($northTogglerElt).append($editorButtonsElt);
|
||||
}
|
||||
else {
|
||||
$('.ui-layout-resizer-north').append($resizerDecorator);
|
||||
$('.ui-layout-resizer-south').append($previewButtonsElt).append($editorButtonsElt).append($northTogglerElt);
|
||||
}
|
||||
|
||||
@ -435,27 +440,6 @@ define([
|
||||
|
||||
// Create the converter and the editor
|
||||
var converter = new Markdown.Converter();
|
||||
// Parse MD sections for extensions
|
||||
converter.hooks.chain("preConversion", function(text) {
|
||||
eventMgr.previewStartTime = new Date();
|
||||
var tmpText = text + "\n\n";
|
||||
var sectionList = [], offset = 0;
|
||||
// Look for titles (excluding gfm blocs)
|
||||
tmpText.replace(/^```.*\n[\s\S]*?\n```|(^.+[ \t]*\n=+[ \t]*\n+|^.+[ \t]*\n-+[ \t]*\n+|^\#{1,6}[ \t]*.+?[ \t]*\#*\n+)/gm, function(match, title, matchOffset) {
|
||||
if(title) {
|
||||
// We just found a title which means end of the previous
|
||||
// section
|
||||
// Exclude last \n of the section
|
||||
sectionList.push(tmpText.substring(offset, matchOffset));
|
||||
offset = matchOffset;
|
||||
}
|
||||
return "";
|
||||
});
|
||||
// Last section
|
||||
sectionList.push(tmpText.substring(offset, text.length));
|
||||
eventMgr.onSectionsCreated(sectionList);
|
||||
return text;
|
||||
});
|
||||
|
||||
function checkDocumentChanges() {
|
||||
var newDocumentContent = $editorElt.val();
|
||||
|
@ -6,6 +6,8 @@ define([
|
||||
"classes/Extension",
|
||||
"settings",
|
||||
"text!html/settingsExtensionsAccordion.html",
|
||||
// "extensions/yamlFrontMatterParser",
|
||||
"extensions/markdownSectionParser",
|
||||
"extensions/partialRendering",
|
||||
"extensions/buttonMarkdownSyntax",
|
||||
"extensions/googleAnalytics",
|
||||
@ -178,6 +180,8 @@ define([
|
||||
// Operations on PageDown
|
||||
addEventHook("onPagedownConfigure");
|
||||
addEventHook("onSectionsCreated");
|
||||
addEventHook("onMarkdownTrim");
|
||||
addEventHook("onExtraExtensions");
|
||||
|
||||
// Operation on ACE
|
||||
addEventHook("onAceCreated");
|
||||
|
@ -44,6 +44,11 @@ define([
|
||||
newConfig.highlighter = utils.getInputValue("#input-markdownextra-highlighter");
|
||||
};
|
||||
|
||||
var eventMgr = undefined;
|
||||
markdownExtra.onEventMgrCreated = function(eventMgrParameter) {
|
||||
eventMgr = eventMgrParameter;
|
||||
};
|
||||
|
||||
markdownExtra.onPagedownConfigure = function(editor) {
|
||||
var converter = editor.getConverter();
|
||||
var options = {
|
||||
@ -64,8 +69,8 @@ define([
|
||||
}
|
||||
Markdown.Extra.init(converter, options);
|
||||
|
||||
// Store extensions list in converter for partialRendering
|
||||
converter.setExtraExtension && converter.setExtraExtension(markdownExtra.config.extensions);
|
||||
// Send extensions list to other extensions
|
||||
eventMgr.onExtraExtensions(markdownExtra.config.extensions);
|
||||
};
|
||||
|
||||
return markdownExtra;
|
||||
|
37
res/extensions/markdownSectionParser.js
Normal file
37
res/extensions/markdownSectionParser.js
Normal file
@ -0,0 +1,37 @@
|
||||
define([
|
||||
"classes/Extension"
|
||||
], function(Extension) {
|
||||
|
||||
var markdownSectionParser = new Extension("markdownSectionParser", "Markdown section parser");
|
||||
|
||||
var eventMgr = undefined;
|
||||
markdownSectionParser.onEventMgrCreated = function(eventMgrParameter) {
|
||||
eventMgr = eventMgrParameter;
|
||||
};
|
||||
|
||||
markdownSectionParser.onPagedownConfigure = function(editor) {
|
||||
var converter = editor.getConverter();
|
||||
converter.hooks.chain("preConversion", function(text) {
|
||||
eventMgr.previewStartTime = new Date();
|
||||
var tmpText = text + "\n\n";
|
||||
var sectionList = [], offset = 0;
|
||||
// Look for titles (excluding gfm blocs)
|
||||
tmpText.replace(/^```.*\n[\s\S]*?\n```|(^.+[ \t]*\n=+[ \t]*\n+|^.+[ \t]*\n-+[ \t]*\n+|^\#{1,6}[ \t]*.+?[ \t]*\#*\n+)/gm, function(match, title, matchOffset) {
|
||||
if(title) {
|
||||
// We just found a title which means end of the previous
|
||||
// section
|
||||
// Exclude last \n of the section
|
||||
sectionList.push(tmpText.substring(offset, matchOffset));
|
||||
offset = matchOffset;
|
||||
}
|
||||
return "";
|
||||
});
|
||||
// Last section
|
||||
sectionList.push(tmpText.substring(offset, text.length));
|
||||
eventMgr.onSectionsCreated(sectionList);
|
||||
return text;
|
||||
});
|
||||
};
|
||||
|
||||
return markdownSectionParser;
|
||||
});
|
@ -189,13 +189,14 @@ define([
|
||||
editor.hooks.chain("onPreviewRefresh", function() {
|
||||
refreshSections();
|
||||
});
|
||||
converter.setExtraExtension = function(extraExtensions) {
|
||||
doFootnotes = _.some(extraExtensions, function(extension) {
|
||||
return extension == "footnotes";
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
partialRendering.onExtraExtensions = function(extraExtensions) {
|
||||
doFootnotes = _.some(extraExtensions, function(extension) {
|
||||
return extension == "footnotes";
|
||||
});
|
||||
};
|
||||
|
||||
partialRendering.onReady = function() {
|
||||
footnoteContainerElt = crel('div', {
|
||||
id: 'wmd-preview-section-footnotes',
|
||||
|
@ -17,6 +17,11 @@ define([
|
||||
scrollLink.onSectionsCreated = function(sectionListParam) {
|
||||
sectionList = sectionListParam;
|
||||
};
|
||||
|
||||
var offsetBegin = 0;
|
||||
scrollLink.onMarkdownTrim = function(offsetBeginParam) {
|
||||
offsetBegin = offsetBeginParam;
|
||||
};
|
||||
|
||||
var $previewElt = undefined;
|
||||
var mdSectionList = [];
|
||||
@ -31,8 +36,10 @@ define([
|
||||
mdSectionList = [];
|
||||
var mdTextOffset = 0;
|
||||
var mdSectionOffset = 0;
|
||||
var firstSectionOffset = offsetBegin;
|
||||
_.each(sectionList, function(sectionText) {
|
||||
mdTextOffset += sectionText.length;
|
||||
mdTextOffset += sectionText.length + firstSectionOffset;
|
||||
firstSectionOffset = 0;
|
||||
var documentPosition = aceEditor.session.doc.indexToPosition(mdTextOffset);
|
||||
var screenPosition = aceEditor.session.documentToScreenPosition(documentPosition.row, documentPosition.column);
|
||||
var newSectionOffset = screenPosition.row * aceEditor.renderer.lineHeight;
|
||||
|
35
res/extensions/yamlFrontMatterParser.js
Normal file
35
res/extensions/yamlFrontMatterParser.js
Normal file
@ -0,0 +1,35 @@
|
||||
define([
|
||||
"classes/Extension",
|
||||
"text!html/yamlFrontMatterParserSettingsBlock.html",
|
||||
"js-yaml",
|
||||
], function(Extension, yamlFrontMatterParserSettingsBlock) {
|
||||
|
||||
var yamlFrontMatterParser = new Extension("yamlFrontMatterParser", "YAML front matter", true);
|
||||
yamlFrontMatterParser.settingsBlock = yamlFrontMatterParserSettingsBlock;
|
||||
|
||||
var eventMgr = undefined;
|
||||
yamlFrontMatterParser.onEventMgrCreated = function(eventMgrParameter) {
|
||||
eventMgr = eventMgrParameter;
|
||||
};
|
||||
|
||||
yamlFrontMatterParser.onPagedownConfigure = function(editor) {
|
||||
var converter = editor.getConverter();
|
||||
converter.hooks.chain("preConversion", function(text) {
|
||||
try {
|
||||
var re = /^(\s*-{3}\s*\n([\w\W]+?)\n\s*-{3}\s*\n)?([\w\W]*)*/, results = re.exec(text), conf = {}, yaml;
|
||||
|
||||
if((yaml = results[2])) {
|
||||
conf = jsyaml.load(yaml);
|
||||
console.log(conf);
|
||||
}
|
||||
eventMgr.onMarkdownTrim(results[1].length);
|
||||
return results[3];
|
||||
}
|
||||
catch(e) {
|
||||
return text;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return yamlFrontMatterParser;
|
||||
});
|
3
res/html/yamlFrontMatterParserSettingsBlock.html
Normal file
3
res/html/yamlFrontMatterParserSettingsBlock.html
Normal file
@ -0,0 +1,3 @@
|
||||
<p>Parses YAML front matter block at the begining of the document.</p>
|
||||
<blockquote><b>Interpreted variables:</b> <i>title, published, category/categories/tags</i></blockquote>
|
||||
<span class="help-block pull-right"><a target="_blank" href="http://jekyllrb.com/docs/frontmatter/">More info</a></span>
|
Binary file not shown.
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 10 KiB |
Binary file not shown.
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
Binary file not shown.
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 4.7 KiB |
@ -49,7 +49,8 @@ requirejs.config({
|
||||
'pagedown-ace': 'bower-libs/pagedown-ace/Markdown.Editor',
|
||||
'pagedown-extra': 'bower-libs/pagedown-extra/Markdown.Extra',
|
||||
'ace/requirejs/text': 'libs/ace_text',
|
||||
'ace/commands/default_commands': 'libs/ace_commands'
|
||||
'ace/commands/default_commands': 'libs/ace_commands',
|
||||
'js-yaml': 'bower-libs/js-yaml/js-yaml'
|
||||
},
|
||||
shim: {
|
||||
underscore: {
|
||||
|
@ -285,10 +285,8 @@ define([
|
||||
aceEditor = aceEditorParam;
|
||||
// Listen to editor's changes
|
||||
aceEditor.session.on('change', function(e) {
|
||||
if(realtimeString !== undefined) {
|
||||
// Update the real time model
|
||||
realtimeString.setText(aceEditor.getValue());
|
||||
}
|
||||
// Update the real time model if any
|
||||
realtimeString && realtimeString.setText(aceEditor.getValue());
|
||||
});
|
||||
});
|
||||
|
||||
@ -308,18 +306,18 @@ define([
|
||||
logger.log("Starting Google Drive realtime synchronization");
|
||||
realtimeDocument = doc;
|
||||
var model = realtimeDocument.getModel();
|
||||
realtimeString = model.getRoot().get('content');
|
||||
var realtimeStringLocal = model.getRoot().get('content');
|
||||
|
||||
// Saves model content checksum
|
||||
function updateContentState() {
|
||||
syncAttributes.contentCRC = utils.crc32(realtimeString.getText());
|
||||
syncAttributes.contentCRC = utils.crc32(realtimeStringLocal.getText());
|
||||
utils.storeAttributes(syncAttributes);
|
||||
}
|
||||
|
||||
var debouncedRefreshPreview = _.debounce(pagedownEditor.refreshPreview, 100);
|
||||
|
||||
// Listen to insert text events
|
||||
realtimeString.addEventListener(gapi.drive.realtime.EventType.TEXT_INSERTED, function(e) {
|
||||
realtimeStringLocal.addEventListener(gapi.drive.realtime.EventType.TEXT_INSERTED, function(e) {
|
||||
if(aceEditor !== undefined && (isAceUpToDate === false || e.isLocal === false)) {
|
||||
// Update ACE editor
|
||||
var position = aceEditor.session.doc.indexToPosition(e.index);
|
||||
@ -334,7 +332,7 @@ define([
|
||||
}
|
||||
});
|
||||
// Listen to delete text events
|
||||
realtimeString.addEventListener(gapi.drive.realtime.EventType.TEXT_DELETED, function(e) {
|
||||
realtimeStringLocal.addEventListener(gapi.drive.realtime.EventType.TEXT_DELETED, function(e) {
|
||||
if(aceEditor !== undefined && (isAceUpToDate === false || e.isLocal === false)) {
|
||||
// Update ACE editor
|
||||
var range = (function(posStart, posEnd) {
|
||||
@ -361,7 +359,7 @@ define([
|
||||
// Try to merge offline modifications
|
||||
var localContent = fileDesc.content;
|
||||
var localContentChanged = syncAttributes.contentCRC != utils.crc32(localContent);
|
||||
var remoteContent = realtimeString.getText();
|
||||
var remoteContent = realtimeStringLocal.getText();
|
||||
var remoteContentCRC = utils.crc32(remoteContent);
|
||||
var remoteContentChanged = syncAttributes.contentCRC != remoteContentCRC;
|
||||
var fileContentChanged = localContent != remoteContent;
|
||||
@ -373,13 +371,13 @@ define([
|
||||
}
|
||||
else {
|
||||
// Add local modifications if no collaborators change
|
||||
realtimeString.setText(localContent);
|
||||
realtimeStringLocal.setText(localContent);
|
||||
}
|
||||
}
|
||||
|
||||
if(aceEditor === undefined) {
|
||||
// Binds model with textarea
|
||||
realtimeBinding = gapi.drive.realtime.databinding.bindString(realtimeString, document.getElementById("wmd-input"));
|
||||
realtimeBinding = gapi.drive.realtime.databinding.bindString(realtimeStringLocal, document.getElementById("wmd-input"));
|
||||
}
|
||||
|
||||
// Update content state according to collaborators changes
|
||||
@ -389,8 +387,11 @@ define([
|
||||
updateContentState();
|
||||
aceEditor === undefined && debouncedRefreshPreview();
|
||||
}
|
||||
|
||||
|
||||
if(aceEditor !== undefined) {
|
||||
// Tell ACE to update realtime string on each change
|
||||
realtimeString = realtimeStringLocal;
|
||||
|
||||
// Save undo/redo buttons actions
|
||||
undoExecute = pagedownEditor.uiManager.buttons.undo.execute;
|
||||
redoExecute = pagedownEditor.uiManager.buttons.redo.execute;
|
||||
|
@ -67,7 +67,7 @@
|
||||
@btn-success-color: @primary-color-light;
|
||||
@btn-success-bg: @navbar-default-bg;
|
||||
@btn-success-border: @transparent;
|
||||
@btn-info-bg: @body-bg;
|
||||
@btn-info-bg: @transparent;
|
||||
@btn-info-border: @transparent;
|
||||
@gray-lighter: @body-bg;
|
||||
@modal-header-border-color: @primary-bg-light;
|
||||
@ -1140,6 +1140,10 @@ div.dropdown-menu textarea {
|
||||
z-index: 1050 !important;
|
||||
}
|
||||
|
||||
.picker-dialog-bg {
|
||||
z-index: 1040 !important;
|
||||
}
|
||||
|
||||
.action-import-image-gplus {
|
||||
float: left;
|
||||
}
|
||||
|
@ -1,18 +1,24 @@
|
||||
@import "../styles/main.less";
|
||||
|
||||
@navbar-default-bg: #315A4B;
|
||||
@bg-navbar-hover: lighten(@navbar-default-bg, 8%);
|
||||
@primary-color: #174d80;
|
||||
@board-color: #385E50;
|
||||
@board-border-color: #B9AA9F;
|
||||
@blue-ink: #284F72;
|
||||
@black-ink: #444;
|
||||
@red-ink: #B56E85;
|
||||
@green-ink: #6c9c7f;
|
||||
@navbar-default-bg: darken(@board-color, 2%);
|
||||
@bg-navbar-hover: lighten(@board-color, 4%);
|
||||
@primary-color: @blue-ink;
|
||||
@primary-color-light: lighten(@primary-color, 13%);
|
||||
@primary-color-lighter: lighten(@primary-color, 20%);
|
||||
@primary-color-lightest: lighten(@primary-color, 35%);
|
||||
@primary-bg: darken(@primary-bg-light, 4%);
|
||||
@primary-bg-light: #E2DEDE;
|
||||
@primary-bg-lighter: lighten(@primary-bg-light, 4%);
|
||||
@primary-bg: lighten(@board-border-color, 16%);
|
||||
@primary-bg-light: lighten(@board-border-color, 20%);
|
||||
@primary-bg-lighter: lighten(@board-border-color, 24%);
|
||||
@btn-success-color: #eee;
|
||||
@btn-success-bg: @transparent;
|
||||
@title-base-size: 18px;
|
||||
@resizer-bg: #B1A19A;
|
||||
@panel-button-color: mix(#444, @resizer-bg, 50%);
|
||||
@panel-button-color: #666;
|
||||
|
||||
|
||||
@font-face {
|
||||
@ -26,67 +32,58 @@
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.ui-layout-resizer-north {
|
||||
background-color: @resizer-bg;
|
||||
.navbar {
|
||||
#gradient.vertical(@board-color; darken(@board-color, 2%));
|
||||
}
|
||||
|
||||
.ui-layout-resizer-north .resizer-decorator {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 7px;
|
||||
background-color: @board-border-color;
|
||||
.box-shadow(0 -1px 15px rgba(0,0,0,.3));
|
||||
z-index: 10;
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
.extension-preview-buttons {
|
||||
margin-top: 12px;
|
||||
//margin-top: 4px;
|
||||
}
|
||||
|
||||
.ui-layout-resizer-east {
|
||||
z-index: 1 !important;
|
||||
}
|
||||
|
||||
.ace-tm {
|
||||
.ace_content {
|
||||
background-image: url("../img/school-line.png");
|
||||
background-repeat: repeat;
|
||||
background-color: transparent;
|
||||
}
|
||||
.ace_marker-layer .ace_active-line {
|
||||
background-color: fade(#a2bace, 20%);
|
||||
background-color: fade(@blue-ink, 5%);
|
||||
}
|
||||
.ace_print-margin {
|
||||
background-color: fade(#b56e80, 50%);
|
||||
background-color: fade(@red-ink, 50%);
|
||||
}
|
||||
|
||||
.ace_markup.ace_heading {
|
||||
color: #444;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.ace_markup.ace_list {
|
||||
color: @primary-color-lightest;
|
||||
}
|
||||
|
||||
.ace_strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.ace_emphasis {
|
||||
color: @primary-color;
|
||||
font-style: italic;
|
||||
color: @black-ink;
|
||||
}
|
||||
|
||||
.ace_blockquote {
|
||||
color: #b56e80;
|
||||
font-style: italic;
|
||||
color: @red-ink;
|
||||
}
|
||||
|
||||
.ace_code {
|
||||
font-family: Menlo, Consolas, "Courier New", monospace;
|
||||
color: #66a26d;
|
||||
background-color: fade(@secondary-bg, 10%);
|
||||
color: @green-ink;
|
||||
}
|
||||
|
||||
.ace_code_block {
|
||||
font-family: Menlo, Consolas, "Courier New", monospace;
|
||||
color: #66a26d;
|
||||
}
|
||||
|
||||
.ace_description {
|
||||
color: @primary-color-lighter;
|
||||
color: @green-ink;
|
||||
}
|
||||
}
|
||||
|
||||
.wmd-title {
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
font-family: 'cursive_standardregular';
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user