Fix Google Drive Realtime document import

This commit is contained in:
benweet 2013-09-19 20:48:21 +01:00
parent 91964c1240
commit d78b446722
16 changed files with 166 additions and 86 deletions

View File

@ -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"
}
}

View File

@ -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();

View File

@ -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");

View File

@ -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;

View 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;
});

View File

@ -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',

View File

@ -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;

View 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;
});

View 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

View File

@ -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: {

View File

@ -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;

View File

@ -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;
}

View File

@ -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';
}