Google Realtime synchronization
This commit is contained in:
parent
d1cb3db557
commit
7eaeb45806
@ -134,6 +134,7 @@ input::-webkit-input-placeholder,textarea::-webkit-input-placeholder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.navbar-inner .btn.disabled,
|
.navbar-inner .btn.disabled,
|
||||||
|
.navbar-inner .btn.blocked,
|
||||||
.navbar-inner .btn[disabled] {
|
.navbar-inner .btn[disabled] {
|
||||||
color: #333333;
|
color: #333333;
|
||||||
background-color: #ddd;
|
background-color: #ddd;
|
||||||
|
@ -33,7 +33,7 @@
|
|||||||
<script src="js/libs/require.js"></script>
|
<script src="js/libs/require.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="navbar" class="navbar navbar-fixed-top ui-layout-north">
|
<div class="navbar navbar-fixed-top ui-layout-north">
|
||||||
<div class="navbar-inner">
|
<div class="navbar-inner">
|
||||||
|
|
||||||
<ul class="nav">
|
<ul class="nav">
|
||||||
@ -303,7 +303,8 @@
|
|||||||
</div>
|
</div>
|
||||||
<br /> <br />
|
<br /> <br />
|
||||||
<p>
|
<p>
|
||||||
<label class="checkbox"> <input id="input-sync-export-gdrive-realtime" type="checkbox">
|
<label class="checkbox"> <input
|
||||||
|
id="input-sync-export-gdrive-realtime" type="checkbox">
|
||||||
Create a real time collaborative document
|
Create a real time collaborative document
|
||||||
</label>
|
</label>
|
||||||
</p>
|
</p>
|
||||||
@ -314,7 +315,9 @@
|
|||||||
your root folder.</li>
|
your root folder.</li>
|
||||||
<li>You can move or rename the file afterwards within Google
|
<li>You can move or rename the file afterwards within Google
|
||||||
Drive.</li>
|
Drive.</li>
|
||||||
<li>Real time collaborative document can not have multiple
|
<li>Real time collaborative documents can't be open outside
|
||||||
|
StackEdit</li>
|
||||||
|
<li>Real time collaborative documents can't have multiple
|
||||||
synchronized locations.</li>
|
synchronized locations.</li>
|
||||||
</ul>
|
</ul>
|
||||||
</blockquote>
|
</blockquote>
|
||||||
|
30
js/core.js
30
js/core.js
@ -197,7 +197,7 @@ define([
|
|||||||
south__minSize: 200
|
south__minSize: 200
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
$("#navbar").click(function() {
|
$(".navbar").click(function() {
|
||||||
layout.allowOverflow('north');
|
layout.allowOverflow('north');
|
||||||
});
|
});
|
||||||
$(".ui-layout-toggler-north").addClass("btn").append($("<b>").addClass("caret"));
|
$(".ui-layout-toggler-north").addClass("btn").append($("<b>").addClass("caret"));
|
||||||
@ -212,7 +212,6 @@ define([
|
|||||||
var editor = undefined;
|
var editor = undefined;
|
||||||
var fileDesc = undefined;
|
var fileDesc = undefined;
|
||||||
var documentContent = undefined;
|
var documentContent = undefined;
|
||||||
var undoManager = undefined;
|
|
||||||
core.initEditor = function(fileDescParam) {
|
core.initEditor = function(fileDescParam) {
|
||||||
if(fileDesc !== undefined) {
|
if(fileDesc !== undefined) {
|
||||||
extensionMgr.onFileClosed(fileDesc);
|
extensionMgr.onFileClosed(fileDesc);
|
||||||
@ -224,7 +223,7 @@ define([
|
|||||||
editorElt.val(initDocumentContent);
|
editorElt.val(initDocumentContent);
|
||||||
if(editor !== undefined) {
|
if(editor !== undefined) {
|
||||||
// If the editor is already created
|
// If the editor is already created
|
||||||
undoManager.reinit(initDocumentContent, fileDesc.editorStart, fileDesc.editorEnd, fileDesc.editorScrollTop);
|
editor.undoManager.reinit(initDocumentContent, fileDesc.editorStart, fileDesc.editorEnd, fileDesc.editorScrollTop);
|
||||||
editor.refreshPreview();
|
editor.refreshPreview();
|
||||||
extensionMgr.onFileOpen(fileDesc);
|
extensionMgr.onFileOpen(fileDesc);
|
||||||
return;
|
return;
|
||||||
@ -310,9 +309,8 @@ define([
|
|||||||
}
|
}
|
||||||
extensionMgr.onEditorConfigure(editor);
|
extensionMgr.onEditorConfigure(editor);
|
||||||
editor.hooks.chain("onPreviewRefresh", extensionMgr.onAsyncPreview);
|
editor.hooks.chain("onPreviewRefresh", extensionMgr.onAsyncPreview);
|
||||||
undoManager = editor.run(previewWrapper);
|
editor.run(previewWrapper);
|
||||||
undoManager.reinit(initDocumentContent, fileDesc.editorStart, fileDesc.editorEnd, fileDesc.editorScrollTop);
|
editor.undoManager.reinit(initDocumentContent, fileDesc.editorStart, fileDesc.editorEnd, fileDesc.editorScrollTop);
|
||||||
extensionMgr.onFileOpen(fileDesc);
|
|
||||||
|
|
||||||
// Hide default buttons
|
// Hide default buttons
|
||||||
$(".wmd-button-row").addClass("btn-group").find("li:not(.wmd-spacer)").addClass("btn").css("left", 0).find("span").hide();
|
$(".wmd-button-row").addClass("btn-group").find("li:not(.wmd-spacer)").addClass("btn").css("left", 0).find("span").hide();
|
||||||
@ -328,8 +326,15 @@ define([
|
|||||||
$("#wmd-ulist-button").append($("<i>").addClass("icon-list"));
|
$("#wmd-ulist-button").append($("<i>").addClass("icon-list"));
|
||||||
$("#wmd-heading-button").append($("<i>").addClass("icon-text-height"));
|
$("#wmd-heading-button").append($("<i>").addClass("icon-text-height"));
|
||||||
$("#wmd-hr-button").append($("<i>").addClass("icon-hr"));
|
$("#wmd-hr-button").append($("<i>").addClass("icon-hr"));
|
||||||
$("#wmd-undo-button").append($("<i>").addClass("icon-undo"));
|
// Create additional undo/redo button for real time synchronization
|
||||||
$("#wmd-redo-button").append($("<i>").addClass("icon-share-alt"));
|
var realtimeUndoButton = $('<li class="btn hide" id="wmd-undo-button-realtime" title="Undo - Ctrl+Z" style="left: 0px;">');
|
||||||
|
realtimeUndoButton.append($("<i>").addClass("icon-undo"));
|
||||||
|
$("#wmd-undo-button").append($("<i>").addClass("icon-undo")).after(realtimeUndoButton);
|
||||||
|
var realtimeRedoButton = $('<li class="btn hide" id="wmd-redo-button-realtime" title="Redo - Ctrl+Shift+Z" style="left: 0px;">');
|
||||||
|
realtimeRedoButton.append($("<i>").addClass("icon-share-alt"));
|
||||||
|
$("#wmd-redo-button").append($("<i>").addClass("icon-share-alt")).after(realtimeRedoButton);
|
||||||
|
|
||||||
|
extensionMgr.onFileOpen(fileDesc);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Used to lock the editor from the user interaction during asynchronous tasks
|
// Used to lock the editor from the user interaction during asynchronous tasks
|
||||||
@ -337,14 +342,7 @@ define([
|
|||||||
core.lockUI = function(param) {
|
core.lockUI = function(param) {
|
||||||
uiLocked = param;
|
uiLocked = param;
|
||||||
$("#wmd-input").prop("disabled", uiLocked);
|
$("#wmd-input").prop("disabled", uiLocked);
|
||||||
$(".btn").each(function() {
|
$(".navbar-inner .btn").toggleClass("blocked", uiLocked);
|
||||||
var classes = $(this).attr("class");
|
|
||||||
if(uiLocked) {
|
|
||||||
$(this).attr("class", classes + " disabled");
|
|
||||||
} else {
|
|
||||||
$(this).attr("class", classes.replace(" disabled", ""));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if(uiLocked) {
|
if(uiLocked) {
|
||||||
$(".lock-ui").removeClass("hide");
|
$(".lock-ui").removeClass("hide");
|
||||||
}
|
}
|
||||||
|
@ -65,15 +65,17 @@ define([
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
extensionMgr.addHookCallback = function(hookName, callback) {
|
|
||||||
hookCallbackList[hookName].push(callback);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Add a Hook to the extensionMgr
|
// Add a Hook to the extensionMgr
|
||||||
function addHook(hookName, noLog) {
|
function addHook(hookName, noLog) {
|
||||||
extensionMgr[hookName] = createHook(hookName, noLog);
|
extensionMgr[hookName] = createHook(hookName, noLog);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Used by external modules to listen to extension events
|
||||||
|
extensionMgr.addHookCallback = function(hookName, callback) {
|
||||||
|
hookCallbackList[hookName].push(callback);
|
||||||
|
};
|
||||||
|
|
||||||
// Set extension config
|
// Set extension config
|
||||||
extensionSettings = settings.extensionSettings || {};
|
extensionSettings = settings.extensionSettings || {};
|
||||||
_.each(extensionList, function(extension) {
|
_.each(extensionList, function(extension) {
|
||||||
|
@ -12,6 +12,11 @@ define([
|
|||||||
dialogManageSynchronization.onExtensionMgrCreated = function(extensionMgrParameter) {
|
dialogManageSynchronization.onExtensionMgrCreated = function(extensionMgrParameter) {
|
||||||
extensionMgr = extensionMgrParameter;
|
extensionMgr = extensionMgrParameter;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var synchronizer = undefined;
|
||||||
|
dialogManageSynchronization.onSynchronizerCreated = function(synchronizerParameter) {
|
||||||
|
synchronizer = synchronizerParameter;
|
||||||
|
};
|
||||||
|
|
||||||
var fileDesc = undefined;
|
var fileDesc = undefined;
|
||||||
var removeButtonTemplate = '<a class="btn" title="Remove this location"><i class="icon-trash"></i></a>';
|
var removeButtonTemplate = '<a class="btn" title="Remove this location"><i class="icon-trash"></i></a>';
|
||||||
@ -36,6 +41,7 @@ define([
|
|||||||
syncDesc: syncDesc
|
syncDesc: syncDesc
|
||||||
}));
|
}));
|
||||||
lineElement.append($(removeButtonTemplate).click(function() {
|
lineElement.append($(removeButtonTemplate).click(function() {
|
||||||
|
synchronizer.tryStopRealtimeSync();
|
||||||
fileDesc.removeSyncLocation(syncAttributes);
|
fileDesc.removeSyncLocation(syncAttributes);
|
||||||
extensionMgr.onSyncRemoved(fileDesc, syncAttributes);
|
extensionMgr.onSyncRemoved(fileDesc, syncAttributes);
|
||||||
}));
|
}));
|
||||||
|
@ -180,7 +180,7 @@ define([
|
|||||||
});
|
});
|
||||||
task.enqueue();
|
task.enqueue();
|
||||||
};
|
};
|
||||||
|
|
||||||
googleHelper.createRealtimeFile = function(parentId, title, callback) {
|
googleHelper.createRealtimeFile = function(parentId, title, callback) {
|
||||||
var result = undefined;
|
var result = undefined;
|
||||||
var task = new AsyncTask();
|
var task = new AsyncTask();
|
||||||
@ -189,7 +189,7 @@ define([
|
|||||||
task.onRun(function() {
|
task.onRun(function() {
|
||||||
var metadata = {
|
var metadata = {
|
||||||
title: title,
|
title: title,
|
||||||
mimeType : 'application/vnd.google-apps.drive-sdk',
|
mimeType: 'application/vnd.google-apps.drive-sdk',
|
||||||
};
|
};
|
||||||
if(parentId !== undefined) {
|
if(parentId !== undefined) {
|
||||||
// Specify the directory
|
// Specify the directory
|
||||||
@ -201,7 +201,7 @@ define([
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
var request = gapi.client.drive.files.insert({
|
var request = gapi.client.drive.files.insert({
|
||||||
'resource' : metadata
|
'resource': metadata
|
||||||
});
|
});
|
||||||
request.execute(function(response) {
|
request.execute(function(response) {
|
||||||
if(response && response.id) {
|
if(response && response.id) {
|
||||||
@ -413,6 +413,14 @@ define([
|
|||||||
task.chain(recursiveDownloadContent);
|
task.chain(recursiveDownloadContent);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// if file is a real time document
|
||||||
|
if(file.mimeType.indexOf("application/vnd.google-apps.drive-sdk") === 0) {
|
||||||
|
file.content = "";
|
||||||
|
file.isRealtime = true;
|
||||||
|
objects.shift();
|
||||||
|
task.chain(recursiveDownloadContent);
|
||||||
|
return;
|
||||||
|
}
|
||||||
var headers = {};
|
var headers = {};
|
||||||
var token = gapi.auth.getToken();
|
var token = gapi.auth.getToken();
|
||||||
if(token) {
|
if(token) {
|
||||||
@ -449,7 +457,7 @@ define([
|
|||||||
});
|
});
|
||||||
task.enqueue();
|
task.enqueue();
|
||||||
};
|
};
|
||||||
|
|
||||||
googleHelper.loadRealtime = function(fileId, content, callback) {
|
googleHelper.loadRealtime = function(fileId, content, callback) {
|
||||||
var doc = undefined;
|
var doc = undefined;
|
||||||
var task = new AsyncTask();
|
var task = new AsyncTask();
|
||||||
@ -469,9 +477,9 @@ define([
|
|||||||
handleError({
|
handleError({
|
||||||
code: err.type
|
code: err.type
|
||||||
}, task);
|
}, task);
|
||||||
});
|
});
|
||||||
task.onSuccess(function() {
|
task.onSuccess(function() {
|
||||||
callback(undefined, doc);
|
callback(undefined, doc);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
task.onError(function(error) {
|
task.onError(function(error) {
|
||||||
@ -560,7 +568,12 @@ define([
|
|||||||
pickerBuilder.setAppId(GOOGLE_DRIVE_APP_ID);
|
pickerBuilder.setAppId(GOOGLE_DRIVE_APP_ID);
|
||||||
if(!isImagePicker) {
|
if(!isImagePicker) {
|
||||||
var view = new google.picker.View(google.picker.ViewId.DOCS);
|
var view = new google.picker.View(google.picker.ViewId.DOCS);
|
||||||
view.setMimeTypes("text/x-markdown,text/plain,application/octet-stream");
|
view.setMimeTypes([
|
||||||
|
"text/x-markdown",
|
||||||
|
"text/plain",
|
||||||
|
"application/octet-stream",
|
||||||
|
"application/vnd.google-apps.drive-sdk." + GOOGLE_DRIVE_APP_ID
|
||||||
|
].join(","));
|
||||||
pickerBuilder.enableFeature(google.picker.Feature.NAV_HIDDEN);
|
pickerBuilder.enableFeature(google.picker.Feature.NAV_HIDDEN);
|
||||||
pickerBuilder.enableFeature(google.picker.Feature.MULTISELECT_ENABLED);
|
pickerBuilder.enableFeature(google.picker.Feature.MULTISELECT_ENABLED);
|
||||||
pickerBuilder.addView(view);
|
pickerBuilder.addView(view);
|
||||||
|
@ -148,7 +148,8 @@
|
|||||||
|
|
||||||
//Not necessary
|
//Not necessary
|
||||||
//forceRefresh();
|
//forceRefresh();
|
||||||
return undoManager;
|
that.undoManager = undoManager;
|
||||||
|
that.uiManager = uiManager;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -663,7 +664,7 @@
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
util.addEvent(panels.input, "keydown", handleCtrlYZ);
|
//util.addEvent(panels.input, "keydown", handleCtrlYZ);
|
||||||
util.addEvent(panels.input, "keydown", handleModeChange);
|
util.addEvent(panels.input, "keydown", handleModeChange);
|
||||||
util.addEvent(panels.input, "mousedown", function () {
|
util.addEvent(panels.input, "mousedown", function () {
|
||||||
setMode("moving");
|
setMode("moving");
|
||||||
@ -694,6 +695,7 @@
|
|||||||
inputStateObj.setInputAreaSelection();
|
inputStateObj.setInputAreaSelection();
|
||||||
saveState();
|
saveState();
|
||||||
};
|
};
|
||||||
|
this.setMode = setMode;
|
||||||
|
|
||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
@ -1251,7 +1253,7 @@
|
|||||||
util.addEvent(inputBox, keyEvent, function (key) {
|
util.addEvent(inputBox, keyEvent, function (key) {
|
||||||
|
|
||||||
// Check to see if we have a button key and, if so execute the callback.
|
// Check to see if we have a button key and, if so execute the callback.
|
||||||
if ((key.ctrlKey || key.metaKey) && !key.altKey && !key.shiftKey) {
|
if ((key.ctrlKey || key.metaKey) && !key.altKey) {
|
||||||
|
|
||||||
var keyCode = key.charCode || key.keyCode;
|
var keyCode = key.charCode || key.keyCode;
|
||||||
var keyCodeStr = String.fromCharCode(keyCode).toLowerCase();
|
var keyCodeStr = String.fromCharCode(keyCode).toLowerCase();
|
||||||
@ -1298,6 +1300,12 @@
|
|||||||
doClick(buttons.undo);
|
doClick(buttons.undo);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case "v":
|
||||||
|
undoManager.setMode("typing");
|
||||||
|
return;
|
||||||
|
case "x":
|
||||||
|
undoManager.setMode("deleting");
|
||||||
|
return;
|
||||||
default:
|
default:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -1547,6 +1555,8 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
this.setUndoRedoButtonStates = setUndoRedoButtonStates;
|
this.setUndoRedoButtonStates = setUndoRedoButtonStates;
|
||||||
|
this.buttons = buttons;
|
||||||
|
this.setButtonState = setupButton;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -42,16 +42,18 @@ define([
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var fileDescList = [];
|
var fileDescList = [];
|
||||||
|
var fileDesc = undefined;
|
||||||
_.each(result, function(file) {
|
_.each(result, function(file) {
|
||||||
var syncAttributes = createSyncAttributes(file.id, file.etag, file.content, file.title);
|
var syncAttributes = createSyncAttributes(file.id, file.etag, file.content, file.title);
|
||||||
|
syncAttributes.isRealtime = file.isRealtime;
|
||||||
var syncLocations = {};
|
var syncLocations = {};
|
||||||
syncLocations[syncAttributes.syncIndex] = syncAttributes;
|
syncLocations[syncAttributes.syncIndex] = syncAttributes;
|
||||||
var fileDesc = fileMgr.createFile(file.title, file.content, syncLocations);
|
fileDesc = fileMgr.createFile(file.title, file.content, syncLocations);
|
||||||
fileMgr.selectFile(fileDesc);
|
|
||||||
fileDescList.push(fileDesc);
|
fileDescList.push(fileDesc);
|
||||||
});
|
});
|
||||||
if(fileDescList.length !== 0) {
|
if(fileDesc !== undefined) {
|
||||||
extensionMgr.onSyncImportSuccess(fileDescList, gdriveProvider);
|
extensionMgr.onSyncImportSuccess(fileDescList, gdriveProvider);
|
||||||
|
fileMgr.selectFile(fileDesc);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -154,7 +156,9 @@ define([
|
|||||||
_.each(changes, function(change) {
|
_.each(changes, function(change) {
|
||||||
var syncIndex = createSyncIndex(change.fileId);
|
var syncIndex = createSyncIndex(change.fileId);
|
||||||
var syncAttributes = fileMgr.getSyncAttributes(syncIndex);
|
var syncAttributes = fileMgr.getSyncAttributes(syncIndex);
|
||||||
if(syncAttributes === undefined) {
|
// If file is not synchronized or it's a real time synchronized location
|
||||||
|
if(syncAttributes === undefined || syncAttributes.isRealtime === true) {
|
||||||
|
// Skip it
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Store syncAttributes to avoid 2 times searching
|
// Store syncAttributes to avoid 2 times searching
|
||||||
@ -261,31 +265,75 @@ define([
|
|||||||
});
|
});
|
||||||
|
|
||||||
// Start realtime synchronization
|
// Start realtime synchronization
|
||||||
var binding = undefined;
|
var realtimeDocument = undefined;
|
||||||
gdriveProvider.startSync = function(content, syncAttributes, callback) {
|
var realtimeBinding = undefined;
|
||||||
|
var undoExecute = undefined;
|
||||||
|
var redoExecute = undefined;
|
||||||
|
gdriveProvider.startRealtimeSync = function(content, syncAttributes, callback) {
|
||||||
logger.log("Starting Google Drive realtime synchronization");
|
logger.log("Starting Google Drive realtime synchronization");
|
||||||
googleHelper.loadRealtime(syncAttributes.id, content, function(err, doc) {
|
googleHelper.loadRealtime(syncAttributes.id, content, function(err, doc) {
|
||||||
if(err || !doc) {
|
if(err || !doc) {
|
||||||
callback(err);
|
callback(err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var string = doc.getModel().getRoot().get('content');
|
realtimeDocument = doc;
|
||||||
binding = gapi.drive.realtime.databinding.bindString(string, $("#wmd-input")[0]);
|
var model = realtimeDocument.getModel();
|
||||||
// Listen to
|
var string = model.getRoot().get('content');
|
||||||
|
realtimeBinding = gapi.drive.realtime.databinding.bindString(string, $("#wmd-input")[0]);
|
||||||
|
|
||||||
|
// Listen to text changed events
|
||||||
var debouncedRefreshPreview = _.debounce(editor.refreshPreview, 100);
|
var debouncedRefreshPreview = _.debounce(editor.refreshPreview, 100);
|
||||||
string.addEventListener(gapi.drive.realtime.EventType.TEXT_INSERTED, debouncedRefreshPreview);
|
string.addEventListener(gapi.drive.realtime.EventType.TEXT_INSERTED, debouncedRefreshPreview);
|
||||||
string.addEventListener(gapi.drive.realtime.EventType.TEXT_DELETED, debouncedRefreshPreview);
|
string.addEventListener(gapi.drive.realtime.EventType.TEXT_DELETED, debouncedRefreshPreview);
|
||||||
|
debouncedRefreshPreview();
|
||||||
|
|
||||||
|
// Add event handler for UndoRedoStateChanged events.
|
||||||
|
undoExecute = editor.uiManager.buttons.undo.execute;
|
||||||
|
redoExecute = editor.uiManager.buttons.redo.execute;
|
||||||
|
|
||||||
|
// var undoButton = $('#wmd-undo-button-realtime').removeClass("hide");
|
||||||
|
// var redoButton = $('#wmd-redo-button-realtime').removeClass("hide");
|
||||||
|
// $('#wmd-undo-button').addClass("hide");
|
||||||
|
// $('#wmd-redo-button').addClass("hide");
|
||||||
|
editor.uiManager.buttons.undo.execute = function() {
|
||||||
|
model.canUndo && model.undo();
|
||||||
|
};
|
||||||
|
editor.uiManager.buttons.redo.execute = function() {
|
||||||
|
model.canRedo && model.redo();
|
||||||
|
};
|
||||||
|
function setUndoRedoState() {
|
||||||
|
editor.uiManager.setButtonState(editor.uiManager.buttons.undo, model.canUndo);
|
||||||
|
editor.uiManager.setButtonState(editor.uiManager.buttons.redo, model.canRedo);
|
||||||
|
}
|
||||||
|
model.addEventListener(
|
||||||
|
gapi.drive.realtime.EventType.UNDO_REDO_STATE_CHANGED,
|
||||||
|
setUndoRedoState);
|
||||||
|
setUndoRedoState();
|
||||||
|
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// Stop realtime synchronization
|
// Stop realtime synchronization
|
||||||
gdriveProvider.stopSync = function(syncAttributes) {
|
gdriveProvider.stopRealtimeSync = function() {
|
||||||
logger.log("Stopping Google Drive realtime synchronization");
|
logger.log("Stopping Google Drive realtime synchronization");
|
||||||
if(binding !== undefined) {
|
if(realtimeBinding !== undefined) {
|
||||||
binding.unbind();
|
realtimeBinding.unbind();
|
||||||
binding = undefined;
|
realtimeBinding = undefined;
|
||||||
}
|
}
|
||||||
|
if(realtimeDocument !== undefined) {
|
||||||
|
realtimeDocument.close();
|
||||||
|
realtimeDocument = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
editor.uiManager.buttons.undo.execute = undoExecute;
|
||||||
|
editor.uiManager.buttons.redo.execute = redoExecute;
|
||||||
|
editor.uiManager.setUndoRedoButtonStates();
|
||||||
|
|
||||||
|
// $('#wmd-undo-button-realtime').off('click').addClass("hide");
|
||||||
|
// $('#wmd-redo-button-realtime').off('click').addClass("hide");
|
||||||
|
// $('#wmd-undo-button').removeClass("hide");
|
||||||
|
// $('#wmd-redo-button').removeClass("hide");
|
||||||
};
|
};
|
||||||
|
|
||||||
core.onReady(function() {
|
core.onReady(function() {
|
||||||
|
@ -46,6 +46,10 @@ define([
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
* Standard synchronization
|
||||||
|
**************************************************************************/
|
||||||
|
|
||||||
// Recursive function to upload a single file on multiple locations
|
// Recursive function to upload a single file on multiple locations
|
||||||
var uploadSyncAttributesList = [];
|
var uploadSyncAttributesList = [];
|
||||||
var uploadContent = undefined;
|
var uploadContent = undefined;
|
||||||
@ -63,6 +67,12 @@ define([
|
|||||||
// Dequeue a synchronized location
|
// Dequeue a synchronized location
|
||||||
var syncAttributes = uploadSyncAttributesList.pop();
|
var syncAttributes = uploadSyncAttributesList.pop();
|
||||||
|
|
||||||
|
// Skip real time synchronized location
|
||||||
|
if(syncAttributes.isRealtime === true) {
|
||||||
|
locationUp(callback);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Use the specified provider to perform the upload
|
// Use the specified provider to perform the upload
|
||||||
syncAttributes.provider.syncUp(uploadContent, uploadContentCRC, uploadTitle, uploadTitleCRC, syncAttributes, function(error, uploadFlag) {
|
syncAttributes.provider.syncUp(uploadContent, uploadContentCRC, uploadTitle, uploadTitleCRC, syncAttributes, function(error, uploadFlag) {
|
||||||
if(uploadFlag === true) {
|
if(uploadFlag === true) {
|
||||||
@ -188,30 +198,65 @@ define([
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Used for realtime synchronization
|
/***************************************************************************
|
||||||
|
* Realtime synchronization
|
||||||
|
**************************************************************************/
|
||||||
|
|
||||||
|
var realtimeFileDesc = undefined;
|
||||||
|
var realtimeSyncAttributes = undefined;
|
||||||
|
var isOnline = true;
|
||||||
|
|
||||||
|
// Determines if open file has real time sync location and tries to start
|
||||||
|
// real time sync
|
||||||
function onFileOpen(fileDesc) {
|
function onFileOpen(fileDesc) {
|
||||||
_.each(fileDesc.syncLocations, function(syncAttributes) {
|
realtimeFileDesc = _.some(fileDesc.syncLocations, function(syncAttributes) {
|
||||||
if(syncAttributes.isRealtime) {
|
realtimeSyncAttributes = syncAttributes;
|
||||||
core.lockUI(true);
|
return syncAttributes.isRealtime;
|
||||||
syncAttributes.provider.startSync(fileDesc.content, syncAttributes, function() {
|
}) ? fileDesc : undefined;
|
||||||
core.lockUI(false);
|
tryStartRealtimeSync();
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
function onFileClosed(fileDesc) {
|
|
||||||
_.each(fileDesc.syncLocations, function(syncAttributes) {
|
|
||||||
if(syncAttributes.isRealtime) {
|
|
||||||
syncAttributes.provider.stopSync(syncAttributes);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
// Enable realtime synchronization
|
|
||||||
if(viewerMode === false) {
|
|
||||||
extensionMgr.addHookCallback("onFileOpen", onFileOpen);
|
|
||||||
extensionMgr.addHookCallback("onFileClosed", onFileClosed);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tries to start/stop real time sync on online/offline event
|
||||||
|
function onOfflineChanged(isOfflineParam) {
|
||||||
|
if(isOfflineParam === false) {
|
||||||
|
isOnline = true;
|
||||||
|
tryStartRealtimeSync();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
synchronizer.tryStopRealtimeSync();
|
||||||
|
isOnline = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Starts real time synchronization if:
|
||||||
|
// 1. current file has real time sync location
|
||||||
|
// 2. we are online
|
||||||
|
function tryStartRealtimeSync() {
|
||||||
|
if(realtimeFileDesc !== undefined && isOnline === true) {
|
||||||
|
core.lockUI(true);
|
||||||
|
realtimeSyncAttributes.provider.startRealtimeSync(realtimeFileDesc.content, realtimeSyncAttributes, function() {
|
||||||
|
core.lockUI(false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stops previously started synchronization if any
|
||||||
|
synchronizer.tryStopRealtimeSync = function() {
|
||||||
|
if(realtimeFileDesc !== undefined && isOnline === true) {
|
||||||
|
realtimeSyncAttributes.provider.stopRealtimeSync();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Triggers realtime synchronization from extensionMgr events
|
||||||
|
if(viewerMode === false) {
|
||||||
|
extensionMgr.addHookCallback("onFileOpen", onFileOpen);
|
||||||
|
extensionMgr.addHookCallback("onFileClosed", synchronizer.tryStopRealtimeSync);
|
||||||
|
extensionMgr.addHookCallback("onOfflineChanged", onOfflineChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
* Initialize module
|
||||||
|
**************************************************************************/
|
||||||
|
|
||||||
// Initialize the export dialog
|
// Initialize the export dialog
|
||||||
function initExportDialog(provider) {
|
function initExportDialog(provider) {
|
||||||
@ -250,7 +295,7 @@ define([
|
|||||||
if(_.size(fileDesc.syncLocations) > 0) {
|
if(_.size(fileDesc.syncLocations) > 0) {
|
||||||
extensionMgr.onError("Realtime collaboration document can't be synchronized with multiple locations");
|
extensionMgr.onError("Realtime collaboration document can't be synchronized with multiple locations");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Perform the provider's real time export
|
// Perform the provider's real time export
|
||||||
provider.exportRealtimeFile(event, fileDesc.title, fileDesc.content, function(error, syncAttributes) {
|
provider.exportRealtimeFile(event, fileDesc.title, fileDesc.content, function(error, syncAttributes) {
|
||||||
if(error) {
|
if(error) {
|
||||||
@ -259,13 +304,18 @@ define([
|
|||||||
syncAttributes.isRealtime = true;
|
syncAttributes.isRealtime = true;
|
||||||
fileDesc.addSyncLocation(syncAttributes);
|
fileDesc.addSyncLocation(syncAttributes);
|
||||||
extensionMgr.onSyncExportSuccess(fileDesc, syncAttributes);
|
extensionMgr.onSyncExportSuccess(fileDesc, syncAttributes);
|
||||||
|
|
||||||
|
// Start the real time sync
|
||||||
|
realtimeFileDesc = fileDesc;
|
||||||
|
realtimeSyncAttributes = syncAttributes;
|
||||||
|
tryStartRealtimeSync();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if(_.size(fileDesc.syncLocations) > 0 && _.first(_.values(obj)).isRealtime) {
|
if(_.size(fileDesc.syncLocations) > 0 && _.first(_.values(obj)).isRealtime) {
|
||||||
extensionMgr.onError("Realtime collaboration document can't be synchronized with multiple locations");
|
extensionMgr.onError("Realtime collaboration document can't be synchronized with multiple locations");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Perform the provider's standard export
|
// Perform the provider's standard export
|
||||||
provider.exportFile(event, fileDesc.title, fileDesc.content, function(error, syncAttributes) {
|
provider.exportFile(event, fileDesc.title, fileDesc.content, function(error, syncAttributes) {
|
||||||
if(error) {
|
if(error) {
|
||||||
@ -290,7 +340,7 @@ define([
|
|||||||
if(_.size(fileDesc.syncLocations) > 0 && _.first(_.values(obj)).isRealtime) {
|
if(_.size(fileDesc.syncLocations) > 0 && _.first(_.values(obj)).isRealtime) {
|
||||||
extensionMgr.onError("Realtime collaboration document can't be synchronized with multiple locations");
|
extensionMgr.onError("Realtime collaboration document can't be synchronized with multiple locations");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
provider.exportManual(event, fileDesc.title, fileDesc.content, function(error, syncAttributes) {
|
provider.exportManual(event, fileDesc.title, fileDesc.content, function(error, syncAttributes) {
|
||||||
if(error) {
|
if(error) {
|
||||||
return;
|
return;
|
||||||
|
@ -44,6 +44,7 @@ textarea[disabled],
|
|||||||
}
|
}
|
||||||
|
|
||||||
.navbar-inner .btn.disabled,
|
.navbar-inner .btn.disabled,
|
||||||
|
.navbar-inner .btn.blocked,
|
||||||
.navbar-inner .btn[disabled] {
|
.navbar-inner .btn[disabled] {
|
||||||
background-color: #ced5de;
|
background-color: #ced5de;
|
||||||
}
|
}
|
||||||
|
@ -111,6 +111,7 @@ blockquote {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.navbar-inner .btn.disabled,
|
.navbar-inner .btn.disabled,
|
||||||
|
.navbar-inner .btn.blocked,
|
||||||
.navbar-inner .btn[disabled] {
|
.navbar-inner .btn[disabled] {
|
||||||
background-color: #444;
|
background-color: #444;
|
||||||
}
|
}
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
<script src="js/libs/require.js"></script>
|
<script src="js/libs/require.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body class="viewer">
|
<body class="viewer">
|
||||||
<div id="navbar" class="navbar navbar-fixed-top ui-layout-north">
|
<div class="navbar navbar-fixed-top ui-layout-north">
|
||||||
<div class="navbar-inner">
|
<div class="navbar-inner">
|
||||||
|
|
||||||
<ul class="nav pull-right hide" id="menu-bar">
|
<ul class="nav pull-right hide" id="menu-bar">
|
||||||
|
Loading…
Reference in New Issue
Block a user