Google Drive Realtime API
This commit is contained in:
parent
194b018cd2
commit
d1cb3db557
@ -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
|
||||
|
@ -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;
|
||||
|
43
index.html
43
index.html
@ -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>
|
||||
|
26
js/core.js
26
js/core.js
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -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;
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user