Google Drive Realtime API

This commit is contained in:
benweet 2013-07-19 00:30:28 +01:00
parent 194b018cd2
commit d1cb3db557
8 changed files with 182 additions and 43 deletions

View File

@ -1,5 +1,5 @@
CACHE MANIFEST
# Wed 17 Jul 2013 01:37:24 IST
# Wed 17 Jul 2013 01:37:25 IST
CACHE:
index.html

View File

@ -762,6 +762,15 @@ input[type="file"] {
z-index: -1;
}
.lock-ui {
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 1050;
}
.gecko #md-section-helper {
/* Firefox doesn't show the scrollbar if height is less than 40px */
height: 40px;

View File

@ -226,7 +226,8 @@
<h3>Delete</h3>
</div>
<div class="modal-body">
<p>Are you sure you want to delete "<span class="file-title"></span>"?
<p>
Are you sure you want to delete "<span class="file-title"></span>"?
</p>
<blockquote class="muted">
<b>NOTE:</b> This will not delete the file on synchronized
@ -247,8 +248,10 @@
</div>
<div class="modal-body">
<p>Please select your Markdown files here:</p>
<p><input type="file" id="input-file-import-harddrive-markdown"
multiple /></p>
<p>
<input type="file" id="input-file-import-harddrive-markdown"
multiple />
</p>
<p>Or drag and drop your Markdown files here:</p>
<p id="dropzone-import-harddrive-markdown" class="drop-zone">Drop
files here</p>
@ -266,13 +269,14 @@
</div>
<div class="modal-body">
<p>Please select your HTML files here:</p>
<p><input type="file" id="input-file-import-harddrive-html"
multiple /></p>
<p>
<input type="file" id="input-file-import-harddrive-html" multiple />
</p>
<p>Or drag and drop your HTML files here:</p>
<p id="dropzone-import-harddrive-html" class="drop-zone">Drop
files here</p>
<p>Or insert your HTML code here:</p> <textarea
id="input-convert-html"></textarea>
<p>Or insert your HTML code here:</p>
<textarea id="input-convert-html"></textarea>
</div>
<div class="modal-footer">
<a href="#" class="btn" data-dismiss="modal">Close</a> <a href="#"
@ -289,7 +293,8 @@
<div class="modal-body">
<blockquote class="muted">This will upload the current
document to your Google Drive account and keep it synchronized.</blockquote>
<p>Here, you can specify a <b>folder ID</b> (optional):
<p>
Here, you can specify a <b>folder ID</b> (optional):
</p>
<div class="input-prepend">
<span class="add-on"><i class="icon-gdrive"></i></span><input
@ -297,6 +302,11 @@
placeholder="FolderID"></input>
</div>
<br /> <br />
<p>
<label class="checkbox"> <input id="input-sync-export-gdrive-realtime" type="checkbox">
Create a real time collaborative document
</label>
</p>
<blockquote class="muted">
<b>NOTE:</b>
<ul>
@ -304,6 +314,8 @@
your root folder.</li>
<li>You can move or rename the file afterwards within Google
Drive.</li>
<li>Real time collaborative document can not have multiple
synchronized locations.</li>
</ul>
</blockquote>
</div>
@ -323,8 +335,8 @@
<div class="modal-body">
<blockquote class="muted">This will upload the current
document to your Dropbox account and keep it synchronized.</blockquote>
<p>Please specify a <b>file path</b> for "<span
class="file-title"></span>":
<p>
Please specify a <b>file path</b> for "<span class="file-title"></span>":
</p>
<div class="input-prepend">
<span class="add-on"><i class="icon-dropbox"></i></span><input
@ -356,8 +368,9 @@
<h3>Synchronization</h3>
</div>
<div class="modal-body">
<p class="msg-sync-list hide">"<span class="file-title"></span>"
is synchronized with the following location(s):
<p class="msg-sync-list hide">
"<span class="file-title"></span>" is synchronized with the
following location(s):
</p>
<div id="manage-sync-list"></div>
<blockquote class="msg-sync-list hide muted">
@ -600,8 +613,9 @@
<h3>Publication</h3>
</div>
<div class="modal-body">
<p class="msg-publish-list hide">"<span class="file-title"></span>"
is published on the following location(s):
<p class="msg-publish-list hide">
"<span class="file-title"></span>" is published on the following
location(s):
</p>
<div id="manage-publish-list"></div>
<blockquote class="muted">
@ -794,6 +808,7 @@
</div>
<textarea id="md-section-helper"></textarea>
<div class="lock-ui hide"></div>
<div id="dropboxjs" data-app-key="x0k2l8puemfvg0o"></div>
</body>

View File

@ -331,6 +331,28 @@ define([
$("#wmd-undo-button").append($("<i>").addClass("icon-undo"));
$("#wmd-redo-button").append($("<i>").addClass("icon-share-alt"));
};
// Used to lock the editor from the user interaction during asynchronous tasks
var uiLocked = false;
core.lockUI = function(param) {
uiLocked = param;
$("#wmd-input").prop("disabled", uiLocked);
$(".btn").each(function() {
var classes = $(this).attr("class");
if(uiLocked) {
$(this).attr("class", classes + " disabled");
} else {
$(this).attr("class", classes.replace(" disabled", ""));
}
});
if(uiLocked) {
$(".lock-ui").removeClass("hide");
}
else {
$(".lock-ui").addClass("hide");
}
};
// onReady event callbacks
var readyCallbacks = [];
@ -351,7 +373,7 @@ define([
readyCallbacks = [];
}
}
core.onReady(function() {
// Load theme list
@ -401,7 +423,7 @@ define([
// Configure Mousetrap
mousetrap.stopCallback = function(e, element, combo) {
return shownModalId || $(element).is("input, select, textarea:not(#wmd-input)");
return uiLocked || shownModalId || $(element).is("input, select, textarea:not(#wmd-input)");
};
// Click events on "insert link" and "insert image" dialog buttons

View File

@ -81,9 +81,11 @@ define([
updateButtonState();
};
// Check that a file has synchronized locations
// Check that a file has synchronized locations and no real time synchronized location
var checkSynchronization = function(fileDesc) {
if(_.size(fileDesc.syncLocations) !== 0) {
if(_.size(fileDesc.syncLocations) !== 0 && !_.some(fileDesc.syncLocations, function(syncAttributes) {
return syncAttributes.isRealtime;
})) {
uploadPending = true;
updateButtonState();
}

View File

@ -180,6 +180,47 @@ define([
});
task.enqueue();
};
googleHelper.createRealtimeFile = function(parentId, title, callback) {
var result = undefined;
var task = new AsyncTask();
connect(task);
authenticate(task);
task.onRun(function() {
var metadata = {
title: title,
mimeType : 'application/vnd.google-apps.drive-sdk',
};
if(parentId !== undefined) {
// Specify the directory
metadata.parents = [
{
kind: 'drive#fileLink',
id: parentId
}
];
}
var request = gapi.client.drive.files.insert({
'resource' : metadata
});
request.execute(function(response) {
if(response && response.id) {
// Upload success
result = response;
task.chain();
return;
}
handleError(response.error, task);
});
});
task.onSuccess(function() {
callback(undefined, result);
});
task.onError(function(error) {
callback(error);
});
task.enqueue();
};
googleHelper.uploadImg = function(name, content, albumId, callback) {
var result = undefined;

View File

@ -56,7 +56,6 @@ define([
});
});
}
;
gdriveProvider.importFiles = function() {
googleHelper.picker(function(error, docs) {
@ -88,6 +87,18 @@ define([
callback(undefined, syncAttributes);
});
};
gdriveProvider.exportRealtimeFile = function(event, title, content, callback) {
var parentId = utils.getInputTextValue("#input-sync-export-gdrive-parentid");
googleHelper.createRealtimeFile(parentId, title, function(error, result) {
if(error) {
callback(error);
return;
}
var syncAttributes = createSyncAttributes(result.id, result.etag, content, title);
callback(undefined, syncAttributes);
});
};
gdriveProvider.exportManual = function(event, title, content, callback) {
var id = utils.getInputTextValue("#input-sync-manual-gdrive-id", event);
@ -243,30 +254,37 @@ define([
return publishAttributes;
};
// Keep a link to the pagedown editor
var editor = undefined;
extensionMgr.addHookCallback("onEditorConfigure", function(editorParam) {
editor = editorParam;
});
// Start realtime synchronization
var binding = undefined;
gdriveProvider.onSyncStart = function(fileDesc, syncAttributes) {
console.log("onSyncStart");
console.log(syncAttributes);
googleHelper.loadRealtime(syncAttributes.id, fileDesc.content, function(err, doc) {
gdriveProvider.startSync = function(content, syncAttributes, callback) {
logger.log("Starting Google Drive realtime synchronization");
googleHelper.loadRealtime(syncAttributes.id, content, function(err, doc) {
if(err || !doc) {
callback(err);
return;
}
var string = doc.getModel().getRoot().get('content');
binding = gapi.drive.realtime.databinding.bindString(string, $("#wmd-input")[0]);
// Listen to
var debouncedRefreshPreview = _.debounce(editor.refreshPreview, 100);
string.addEventListener(gapi.drive.realtime.EventType.TEXT_INSERTED, debouncedRefreshPreview);
string.addEventListener(gapi.drive.realtime.EventType.TEXT_DELETED, debouncedRefreshPreview);
callback();
});
};
gdriveProvider.onSyncStop = function(syncAttributes) {
console.log("onSyncStop");
// Stop realtime synchronization
gdriveProvider.stopSync = function(syncAttributes) {
logger.log("Stopping Google Drive realtime synchronization");
if(binding !== undefined) {
binding.unbind();
binding = undefined;
}
};

View File

@ -187,23 +187,31 @@ define([
});
return true;
};
// Used for realtime synchronization
function onFileOpen(fileDesc) {
_.each(fileDesc.syncLocations, function(syncAttributes) {
if(_.isFunction(syncAttributes.provider.onSyncStart)) {
syncAttributes.provider.onSyncStart(fileDesc, syncAttributes);
if(syncAttributes.isRealtime) {
core.lockUI(true);
syncAttributes.provider.startSync(fileDesc.content, syncAttributes, function() {
core.lockUI(false);
});
}
});
}
function onFileClosed(fileDesc) {
_.each(fileDesc.syncLocations, function(syncAttributes) {
if(_.isFunction(syncAttributes.provider.onSyncStop)) {
syncAttributes.provider.onSyncStop(syncAttributes);
if(syncAttributes.isRealtime) {
syncAttributes.provider.stopSync(syncAttributes);
}
});
}
// Enable realtime synchronization
if(viewerMode === false) {
extensionMgr.addHookCallback("onFileOpen", onFileOpen);
extensionMgr.addHookCallback("onFileClosed", onFileClosed);
}
// Initialize the export dialog
function initExportDialog(provider) {
@ -235,16 +243,38 @@ define([
initExportDialog(provider);
});
$(".action-sync-export-" + provider.providerId).click(function(event) {
// Perform the provider's export
var isRealtime = utils.getInputChecked("#input-sync-export-" + provider.providerId + "-realtime");
var fileDesc = fileMgr.currentFile;
provider.exportFile(event, fileDesc.title, fileDesc.content, function(error, syncAttributes) {
if(error) {
if(isRealtime) {
if(_.size(fileDesc.syncLocations) > 0) {
extensionMgr.onError("Realtime collaboration document can't be synchronized with multiple locations");
return;
}
fileDesc.addSyncLocation(syncAttributes);
extensionMgr.onSyncExportSuccess(fileDesc, syncAttributes);
});
}
// Perform the provider's real time export
provider.exportRealtimeFile(event, fileDesc.title, fileDesc.content, function(error, syncAttributes) {
if(error) {
return;
}
syncAttributes.isRealtime = true;
fileDesc.addSyncLocation(syncAttributes);
extensionMgr.onSyncExportSuccess(fileDesc, syncAttributes);
});
}
else {
if(_.size(fileDesc.syncLocations) > 0 && _.first(_.values(obj)).isRealtime) {
extensionMgr.onError("Realtime collaboration document can't be synchronized with multiple locations");
return;
}
// Perform the provider's standard export
provider.exportFile(event, fileDesc.title, fileDesc.content, function(error, syncAttributes) {
if(error) {
return;
}
fileDesc.addSyncLocation(syncAttributes);
extensionMgr.onSyncExportSuccess(fileDesc, syncAttributes);
});
}
// Store input values as preferences for next time we open the
// export dialog
@ -257,6 +287,10 @@ define([
// Provider's manual export button
$(".action-sync-manual-" + provider.providerId).click(function(event) {
var fileDesc = fileMgr.currentFile;
if(_.size(fileDesc.syncLocations) > 0 && _.first(_.values(obj)).isRealtime) {
extensionMgr.onError("Realtime collaboration document can't be synchronized with multiple locations");
return;
}
provider.exportManual(event, fileDesc.title, fileDesc.content, function(error, syncAttributes) {
if(error) {
return;
@ -268,8 +302,6 @@ define([
});
});
extensionMgr.addHookCallback("onFileOpen", onFileOpen);
extensionMgr.addHookCallback("onFileClosed", onFileClosed);
extensionMgr.onSynchronizerCreated(synchronizer);
return synchronizer;
});