Google Drive Realtime API
This commit is contained in:
parent
194b018cd2
commit
d1cb3db557
@ -1,5 +1,5 @@
|
|||||||
CACHE MANIFEST
|
CACHE MANIFEST
|
||||||
# Wed 17 Jul 2013 01:37:24 IST
|
# Wed 17 Jul 2013 01:37:25 IST
|
||||||
|
|
||||||
CACHE:
|
CACHE:
|
||||||
index.html
|
index.html
|
||||||
|
@ -762,6 +762,15 @@ input[type="file"] {
|
|||||||
z-index: -1;
|
z-index: -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.lock-ui {
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: 1050;
|
||||||
|
}
|
||||||
|
|
||||||
.gecko #md-section-helper {
|
.gecko #md-section-helper {
|
||||||
/* Firefox doesn't show the scrollbar if height is less than 40px */
|
/* Firefox doesn't show the scrollbar if height is less than 40px */
|
||||||
height: 40px;
|
height: 40px;
|
||||||
|
43
index.html
43
index.html
@ -226,7 +226,8 @@
|
|||||||
<h3>Delete</h3>
|
<h3>Delete</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<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>
|
</p>
|
||||||
<blockquote class="muted">
|
<blockquote class="muted">
|
||||||
<b>NOTE:</b> This will not delete the file on synchronized
|
<b>NOTE:</b> This will not delete the file on synchronized
|
||||||
@ -247,8 +248,10 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<p>Please select your Markdown files here:</p>
|
<p>Please select your Markdown files here:</p>
|
||||||
<p><input type="file" id="input-file-import-harddrive-markdown"
|
<p>
|
||||||
multiple /></p>
|
<input type="file" id="input-file-import-harddrive-markdown"
|
||||||
|
multiple />
|
||||||
|
</p>
|
||||||
<p>Or drag and drop your Markdown files here:</p>
|
<p>Or drag and drop your Markdown files here:</p>
|
||||||
<p id="dropzone-import-harddrive-markdown" class="drop-zone">Drop
|
<p id="dropzone-import-harddrive-markdown" class="drop-zone">Drop
|
||||||
files here</p>
|
files here</p>
|
||||||
@ -266,13 +269,14 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<p>Please select your HTML files here:</p>
|
<p>Please select your HTML files here:</p>
|
||||||
<p><input type="file" id="input-file-import-harddrive-html"
|
<p>
|
||||||
multiple /></p>
|
<input type="file" id="input-file-import-harddrive-html" multiple />
|
||||||
|
</p>
|
||||||
<p>Or drag and drop your HTML files here:</p>
|
<p>Or drag and drop your HTML files here:</p>
|
||||||
<p id="dropzone-import-harddrive-html" class="drop-zone">Drop
|
<p id="dropzone-import-harddrive-html" class="drop-zone">Drop
|
||||||
files here</p>
|
files here</p>
|
||||||
<p>Or insert your HTML code here:</p> <textarea
|
<p>Or insert your HTML code here:</p>
|
||||||
id="input-convert-html"></textarea>
|
<textarea id="input-convert-html"></textarea>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<a href="#" class="btn" data-dismiss="modal">Close</a> <a href="#"
|
<a href="#" class="btn" data-dismiss="modal">Close</a> <a href="#"
|
||||||
@ -289,7 +293,8 @@
|
|||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<blockquote class="muted">This will upload the current
|
<blockquote class="muted">This will upload the current
|
||||||
document to your Google Drive account and keep it synchronized.</blockquote>
|
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>
|
</p>
|
||||||
<div class="input-prepend">
|
<div class="input-prepend">
|
||||||
<span class="add-on"><i class="icon-gdrive"></i></span><input
|
<span class="add-on"><i class="icon-gdrive"></i></span><input
|
||||||
@ -297,6 +302,11 @@
|
|||||||
placeholder="FolderID"></input>
|
placeholder="FolderID"></input>
|
||||||
</div>
|
</div>
|
||||||
<br /> <br />
|
<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">
|
<blockquote class="muted">
|
||||||
<b>NOTE:</b>
|
<b>NOTE:</b>
|
||||||
<ul>
|
<ul>
|
||||||
@ -304,6 +314,8 @@
|
|||||||
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
|
||||||
|
synchronized locations.</li>
|
||||||
</ul>
|
</ul>
|
||||||
</blockquote>
|
</blockquote>
|
||||||
</div>
|
</div>
|
||||||
@ -323,8 +335,8 @@
|
|||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<blockquote class="muted">This will upload the current
|
<blockquote class="muted">This will upload the current
|
||||||
document to your Dropbox account and keep it synchronized.</blockquote>
|
document to your Dropbox account and keep it synchronized.</blockquote>
|
||||||
<p>Please specify a <b>file path</b> for "<span
|
<p>
|
||||||
class="file-title"></span>":
|
Please specify a <b>file path</b> for "<span class="file-title"></span>":
|
||||||
</p>
|
</p>
|
||||||
<div class="input-prepend">
|
<div class="input-prepend">
|
||||||
<span class="add-on"><i class="icon-dropbox"></i></span><input
|
<span class="add-on"><i class="icon-dropbox"></i></span><input
|
||||||
@ -356,8 +368,9 @@
|
|||||||
<h3>Synchronization</h3>
|
<h3>Synchronization</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<p class="msg-sync-list hide">"<span class="file-title"></span>"
|
<p class="msg-sync-list hide">
|
||||||
is synchronized with the following location(s):
|
"<span class="file-title"></span>" is synchronized with the
|
||||||
|
following location(s):
|
||||||
</p>
|
</p>
|
||||||
<div id="manage-sync-list"></div>
|
<div id="manage-sync-list"></div>
|
||||||
<blockquote class="msg-sync-list hide muted">
|
<blockquote class="msg-sync-list hide muted">
|
||||||
@ -600,8 +613,9 @@
|
|||||||
<h3>Publication</h3>
|
<h3>Publication</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<p class="msg-publish-list hide">"<span class="file-title"></span>"
|
<p class="msg-publish-list hide">
|
||||||
is published on the following location(s):
|
"<span class="file-title"></span>" is published on the following
|
||||||
|
location(s):
|
||||||
</p>
|
</p>
|
||||||
<div id="manage-publish-list"></div>
|
<div id="manage-publish-list"></div>
|
||||||
<blockquote class="muted">
|
<blockquote class="muted">
|
||||||
@ -794,6 +808,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<textarea id="md-section-helper"></textarea>
|
<textarea id="md-section-helper"></textarea>
|
||||||
|
<div class="lock-ui hide"></div>
|
||||||
<div id="dropboxjs" data-app-key="x0k2l8puemfvg0o"></div>
|
<div id="dropboxjs" data-app-key="x0k2l8puemfvg0o"></div>
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
|
24
js/core.js
24
js/core.js
@ -332,6 +332,28 @@ define([
|
|||||||
$("#wmd-redo-button").append($("<i>").addClass("icon-share-alt"));
|
$("#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
|
// onReady event callbacks
|
||||||
var readyCallbacks = [];
|
var readyCallbacks = [];
|
||||||
core.onReady = function(callback) {
|
core.onReady = function(callback) {
|
||||||
@ -401,7 +423,7 @@ define([
|
|||||||
|
|
||||||
// Configure Mousetrap
|
// Configure Mousetrap
|
||||||
mousetrap.stopCallback = function(e, element, combo) {
|
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
|
// Click events on "insert link" and "insert image" dialog buttons
|
||||||
|
@ -81,9 +81,11 @@ define([
|
|||||||
updateButtonState();
|
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) {
|
var checkSynchronization = function(fileDesc) {
|
||||||
if(_.size(fileDesc.syncLocations) !== 0) {
|
if(_.size(fileDesc.syncLocations) !== 0 && !_.some(fileDesc.syncLocations, function(syncAttributes) {
|
||||||
|
return syncAttributes.isRealtime;
|
||||||
|
})) {
|
||||||
uploadPending = true;
|
uploadPending = true;
|
||||||
updateButtonState();
|
updateButtonState();
|
||||||
}
|
}
|
||||||
|
@ -181,6 +181,47 @@ define([
|
|||||||
task.enqueue();
|
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) {
|
googleHelper.uploadImg = function(name, content, albumId, callback) {
|
||||||
var result = undefined;
|
var result = undefined;
|
||||||
var task = new AsyncTask();
|
var task = new AsyncTask();
|
||||||
|
@ -56,7 +56,6 @@ define([
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
;
|
|
||||||
|
|
||||||
gdriveProvider.importFiles = function() {
|
gdriveProvider.importFiles = function() {
|
||||||
googleHelper.picker(function(error, docs) {
|
googleHelper.picker(function(error, docs) {
|
||||||
@ -89,6 +88,18 @@ define([
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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) {
|
gdriveProvider.exportManual = function(event, title, content, callback) {
|
||||||
var id = utils.getInputTextValue("#input-sync-manual-gdrive-id", event);
|
var id = utils.getInputTextValue("#input-sync-manual-gdrive-id", event);
|
||||||
if(!id) {
|
if(!id) {
|
||||||
@ -243,30 +254,37 @@ define([
|
|||||||
return publishAttributes;
|
return publishAttributes;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Keep a link to the pagedown editor
|
||||||
var editor = undefined;
|
var editor = undefined;
|
||||||
extensionMgr.addHookCallback("onEditorConfigure", function(editorParam) {
|
extensionMgr.addHookCallback("onEditorConfigure", function(editorParam) {
|
||||||
editor = editorParam;
|
editor = editorParam;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Start realtime synchronization
|
||||||
var binding = undefined;
|
var binding = undefined;
|
||||||
gdriveProvider.onSyncStart = function(fileDesc, syncAttributes) {
|
gdriveProvider.startSync = function(content, syncAttributes, callback) {
|
||||||
console.log("onSyncStart");
|
logger.log("Starting Google Drive realtime synchronization");
|
||||||
console.log(syncAttributes);
|
googleHelper.loadRealtime(syncAttributes.id, content, function(err, doc) {
|
||||||
googleHelper.loadRealtime(syncAttributes.id, fileDesc.content, function(err, doc) {
|
|
||||||
if(err || !doc) {
|
if(err || !doc) {
|
||||||
|
callback(err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var string = doc.getModel().getRoot().get('content');
|
var string = doc.getModel().getRoot().get('content');
|
||||||
binding = gapi.drive.realtime.databinding.bindString(string, $("#wmd-input")[0]);
|
binding = gapi.drive.realtime.databinding.bindString(string, $("#wmd-input")[0]);
|
||||||
|
// Listen to
|
||||||
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);
|
||||||
|
callback();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
gdriveProvider.onSyncStop = function(syncAttributes) {
|
// Stop realtime synchronization
|
||||||
console.log("onSyncStop");
|
gdriveProvider.stopSync = function(syncAttributes) {
|
||||||
|
logger.log("Stopping Google Drive realtime synchronization");
|
||||||
if(binding !== undefined) {
|
if(binding !== undefined) {
|
||||||
binding.unbind();
|
binding.unbind();
|
||||||
|
binding = undefined;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -188,21 +188,29 @@ define([
|
|||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Used for realtime synchronization
|
||||||
function onFileOpen(fileDesc) {
|
function onFileOpen(fileDesc) {
|
||||||
_.each(fileDesc.syncLocations, function(syncAttributes) {
|
_.each(fileDesc.syncLocations, function(syncAttributes) {
|
||||||
if(_.isFunction(syncAttributes.provider.onSyncStart)) {
|
if(syncAttributes.isRealtime) {
|
||||||
syncAttributes.provider.onSyncStart(fileDesc, syncAttributes);
|
core.lockUI(true);
|
||||||
|
syncAttributes.provider.startSync(fileDesc.content, syncAttributes, function() {
|
||||||
|
core.lockUI(false);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function onFileClosed(fileDesc) {
|
function onFileClosed(fileDesc) {
|
||||||
_.each(fileDesc.syncLocations, function(syncAttributes) {
|
_.each(fileDesc.syncLocations, function(syncAttributes) {
|
||||||
if(_.isFunction(syncAttributes.provider.onSyncStop)) {
|
if(syncAttributes.isRealtime) {
|
||||||
syncAttributes.provider.onSyncStop(syncAttributes);
|
syncAttributes.provider.stopSync(syncAttributes);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
// Enable realtime synchronization
|
||||||
|
if(viewerMode === false) {
|
||||||
|
extensionMgr.addHookCallback("onFileOpen", onFileOpen);
|
||||||
|
extensionMgr.addHookCallback("onFileClosed", onFileClosed);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Initialize the export dialog
|
// Initialize the export dialog
|
||||||
@ -235,9 +243,30 @@ define([
|
|||||||
initExportDialog(provider);
|
initExportDialog(provider);
|
||||||
});
|
});
|
||||||
$(".action-sync-export-" + provider.providerId).click(function(event) {
|
$(".action-sync-export-" + provider.providerId).click(function(event) {
|
||||||
|
var isRealtime = utils.getInputChecked("#input-sync-export-" + provider.providerId + "-realtime");
|
||||||
// Perform the provider's export
|
|
||||||
var fileDesc = fileMgr.currentFile;
|
var fileDesc = fileMgr.currentFile;
|
||||||
|
|
||||||
|
if(isRealtime) {
|
||||||
|
if(_.size(fileDesc.syncLocations) > 0) {
|
||||||
|
extensionMgr.onError("Realtime collaboration document can't be synchronized with multiple locations");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 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) {
|
provider.exportFile(event, fileDesc.title, fileDesc.content, function(error, syncAttributes) {
|
||||||
if(error) {
|
if(error) {
|
||||||
return;
|
return;
|
||||||
@ -245,6 +274,7 @@ define([
|
|||||||
fileDesc.addSyncLocation(syncAttributes);
|
fileDesc.addSyncLocation(syncAttributes);
|
||||||
extensionMgr.onSyncExportSuccess(fileDesc, syncAttributes);
|
extensionMgr.onSyncExportSuccess(fileDesc, syncAttributes);
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Store input values as preferences for next time we open the
|
// Store input values as preferences for next time we open the
|
||||||
// export dialog
|
// export dialog
|
||||||
@ -257,6 +287,10 @@ define([
|
|||||||
// Provider's manual export button
|
// Provider's manual export button
|
||||||
$(".action-sync-manual-" + provider.providerId).click(function(event) {
|
$(".action-sync-manual-" + provider.providerId).click(function(event) {
|
||||||
var fileDesc = fileMgr.currentFile;
|
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) {
|
provider.exportManual(event, fileDesc.title, fileDesc.content, function(error, syncAttributes) {
|
||||||
if(error) {
|
if(error) {
|
||||||
return;
|
return;
|
||||||
@ -268,8 +302,6 @@ define([
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
extensionMgr.addHookCallback("onFileOpen", onFileOpen);
|
|
||||||
extensionMgr.addHookCallback("onFileClosed", onFileClosed);
|
|
||||||
extensionMgr.onSynchronizerCreated(synchronizer);
|
extensionMgr.onSynchronizerCreated(synchronizer);
|
||||||
return synchronizer;
|
return synchronizer;
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user