New asyncRunner
This commit is contained in:
parent
51ba5930af
commit
2177785ca5
47
index.html
47
index.html
@ -64,7 +64,7 @@
|
||||
<li class="dropdown-submenu"><a href="#"><i
|
||||
class="icon-gdrive"></i> Google Drive</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="#" class="action-download-gdrive">Import
|
||||
<li><a href="#" class="action-sync-import-gdrive">Import
|
||||
from Google Drive</a></li>
|
||||
<li><a href="#" data-toggle="modal"
|
||||
data-target="#modal-upload-gdrive" class="action-reset-input">Export
|
||||
@ -73,7 +73,7 @@
|
||||
<li class="dropdown-submenu"><a href="#"><i
|
||||
class="icon-dropbox"></i> Dropbox</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a class="action-download-dropbox" href="#">Import
|
||||
<li><a class="action-sync-import-dropbox" href="#">Import
|
||||
from Dropbox</a></li>
|
||||
<li><a href="#" data-toggle="modal"
|
||||
data-target="#modal-upload-dropbox" class="action-reset-input">Export
|
||||
@ -92,6 +92,8 @@
|
||||
class="icon-dropbox"></i> Dropbox</a></li>
|
||||
<li><a href="#" class="action-publish-github"><i
|
||||
class="icon-github"></i> GitHub</a></li>
|
||||
<li><a href="#" class="action-publish-gdrive"><i
|
||||
class="icon-gdrive"></i> Google Drive</a></li>
|
||||
</ul></li>
|
||||
<li><a href="#" data-toggle="modal"
|
||||
data-target="#modal-manage-publish" class="action-reset-input"><i
|
||||
@ -182,7 +184,7 @@
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal"
|
||||
aria-hidden="true">×</button>
|
||||
<h3>Export</h3>
|
||||
<h3>Google Drive export</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>This will upload the current document into your Google Drive
|
||||
@ -193,7 +195,7 @@
|
||||
<div class="modal-footer">
|
||||
<a href="#" class="btn" data-dismiss="modal">Cancel</a> <a href="#"
|
||||
data-dismiss="modal"
|
||||
class="btn btn-primary action-upload-gdrive-root">OK</a>
|
||||
class="btn btn-primary action-sync-export-gdrive">OK</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -201,7 +203,7 @@
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal"
|
||||
aria-hidden="true">×</button>
|
||||
<h3>Export</h3>
|
||||
<h3>Dropbox export</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>This will upload the current document to your Dropbox account
|
||||
@ -210,7 +212,7 @@
|
||||
</p>
|
||||
<div class="input-prepend">
|
||||
<span class="add-on"><i class="icon-dropbox"></i></span><input
|
||||
id="upload-dropbox-path" type="text" class="span5"
|
||||
id="input-sync-export-dropbox-path" type="text" class="span5"
|
||||
placeholder="/path/to/My Document.md"></input>
|
||||
</div>
|
||||
<br /> <br /> <b class="muted">NOTE:</b>
|
||||
@ -223,7 +225,7 @@
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a href="#" class="btn" data-dismiss="modal">Cancel</a> <a href="#"
|
||||
data-dismiss="modal" class="btn btn-primary action-upload-dropbox">OK</a>
|
||||
data-dismiss="modal" class="btn btn-primary action-sync-export-dropbox">OK</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -246,16 +248,16 @@
|
||||
<p>Add a synchronized location manually:</p>
|
||||
<div class="input-prepend input-append">
|
||||
<span class="add-on" title="Google Drive"><i
|
||||
class="icon-gdrive"></i></span><input id="manual-gdrive-fileid"
|
||||
class="icon-gdrive"></i></span><input id="input-sync-manual-gdrive-id"
|
||||
type="text" class="span5" placeholder="Google Drive file ID"></input>
|
||||
<a class="btn action-manual-gdrive" title="Add location"
|
||||
<a class="btn action-sync-manual-gdrive" title="Add location"
|
||||
data-dismiss="modal"><i class="icon-ok"></i></a>
|
||||
</div>
|
||||
<div class="input-prepend input-append">
|
||||
<span class="add-on" title="Dropbox"><i class="icon-dropbox"></i></span><input
|
||||
id="manual-dropbox-path" type="text" class="span5"
|
||||
id="input-sync-manual-dropbox-path" type="text" class="span5"
|
||||
placeholder="Dropbox file path"></input> <a
|
||||
class="btn action-manual-dropbox" title="Add location"
|
||||
class="btn action-sync-manual-dropbox" title="Add location"
|
||||
data-dismiss="modal"><i class="icon-ok"></i></a>
|
||||
</div>
|
||||
<p class="muted"><b>NOTE:</b> Adding a synchronized location will
|
||||
@ -271,7 +273,9 @@
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal"
|
||||
aria-hidden="true">×</button>
|
||||
<h3>Publish on <span class="publish-provider-name"></span></h3>
|
||||
<h3>
|
||||
Publish on <span class="publish-provider-name"></span>
|
||||
</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="form-horizontal">
|
||||
@ -314,12 +318,29 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group modal-publish-dropbox">
|
||||
<label class="control-label" for="input-publish-dropbox-path">File path</label>
|
||||
<label class="control-label" for="input-publish-dropbox-path">File
|
||||
path</label>
|
||||
<div class="controls">
|
||||
<input type="text" id="input-publish-dropbox-path"
|
||||
placeholder="/path/to/My Document.html">
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group modal-publish-gdrive">
|
||||
<label class="control-label" for="input-publish-gdrive-fileid">File
|
||||
ID (optional)</label>
|
||||
<div class="controls">
|
||||
<input type="text" id="input-publish-gdrive-fileid"
|
||||
placeholder="File ID">
|
||||
</div>
|
||||
</div>
|
||||
<div class="control-group modal-publish-gdrive">
|
||||
<div class="controls muted">
|
||||
<b>NOTE:</b> If no file ID
|
||||
is supplied, the file will be created into your Google Drive root
|
||||
folder. You can move the file within Google Drive afterwards.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<div class="control-label">Format</div>
|
||||
<div class="controls">
|
||||
|
@ -1,121 +1,185 @@
|
||||
/**
|
||||
* Used to run asynchronous tasks sequentially (ajax mainly)
|
||||
* An asynchronous task must be created with:
|
||||
* - a required run() function that may call success(), error() or retry()
|
||||
* - an optional onSuccess() function
|
||||
* - an optional onError() function
|
||||
* - an optional timeout field (default is 30000)
|
||||
* Used to run asynchronous tasks sequentially (ajax mainly) An asynchronous
|
||||
* task is composed of different callback types: onRun, onSuccess, onError
|
||||
*/
|
||||
define([ "underscore" ], function() {
|
||||
|
||||
var asyncTaskRunner = {};
|
||||
var asyncRunner = {};
|
||||
|
||||
// Dependencies
|
||||
var core = undefined;
|
||||
|
||||
var asyncTaskQueue = [];
|
||||
var taskQueue = [];
|
||||
var currentTask = undefined;
|
||||
var currentTaskRunning = false;
|
||||
var currentTaskStartTime = 0;
|
||||
|
||||
// Run the next task in the queue if any and no other is running
|
||||
asyncRunner.createTask = function() {
|
||||
var task = {};
|
||||
task.finished = false;
|
||||
task.timeout = ASYNC_TASK_DEFAULT_TIMEOUT;
|
||||
task.retryCounter = 0;
|
||||
/**
|
||||
* onRun callbacks are called by chain(). These callbacks have to call
|
||||
* chain() themselves to chain with next callback or to finish the task
|
||||
* and call onSuccess callbacks.
|
||||
*/
|
||||
// Run callbacks
|
||||
task.runCallbacks = [];
|
||||
task.onRun = function(callback) {
|
||||
task.runCallbacks.push(callback);
|
||||
};
|
||||
/**
|
||||
* onSuccess callbacks are called when every onRun callbacks have
|
||||
* succeed.
|
||||
*/
|
||||
task.successCallbacks = [];
|
||||
task.onSuccess = function(callback) {
|
||||
task.successCallbacks.push(callback);
|
||||
};
|
||||
/**
|
||||
* onError callbacks are called when error() is called in a onRun
|
||||
* callback.
|
||||
*/
|
||||
task.errorCallbacks = [];
|
||||
task.onError = function(callback) {
|
||||
task.errorCallbacks.push(callback);
|
||||
};
|
||||
/**
|
||||
* chain() calls the next onRun callback or the onSuccess callbacks when
|
||||
* finished. The optional callback parameter can be used to add a onRun
|
||||
* callback during execution.
|
||||
*/
|
||||
task.chain = function(callback) {
|
||||
if (task.finished === true) {
|
||||
return;
|
||||
}
|
||||
// If first execution
|
||||
if (task.queue === undefined) {
|
||||
// Create a copy of the onRun callbacks
|
||||
task.queue = task.runCallbacks.slice();
|
||||
}
|
||||
// If a callback is passed as a parameter
|
||||
if(callback !== undefined) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
// If all callbacks have been run
|
||||
if (task.queue.length === 0) {
|
||||
// Run the onSuccess callbacks
|
||||
runSafe(task, task.successCallbacks);
|
||||
return;
|
||||
}
|
||||
// Run the next callback
|
||||
var runCallback = task.queue.shift();
|
||||
runCallback();
|
||||
};
|
||||
/**
|
||||
* error() calls the onError callbacks.
|
||||
*/
|
||||
task.error = function(error) {
|
||||
if (task.finished === true) {
|
||||
return;
|
||||
}
|
||||
error = error || "Unknown error";
|
||||
runSafe(task, task.errorCallbacks, error);
|
||||
// Exit the current call stack
|
||||
throw error;
|
||||
};
|
||||
/**
|
||||
* retry() can be called in an onRun callback to restart the task
|
||||
*/
|
||||
task.retry = function() {
|
||||
if (task.finished === true) {
|
||||
return;
|
||||
}
|
||||
task.queue = undefined;
|
||||
if (task.retryCounter === 5) {
|
||||
task.error(new Error("Maximum retry number reached"));
|
||||
return;
|
||||
}
|
||||
// Implement an exponential backoff
|
||||
var delay = Math.pow(2, task.retryCounter++) * 1000;
|
||||
currentTaskStartTime = core.currentTime + delay;
|
||||
currentTaskRunning = false;
|
||||
asyncRunner.runTask();
|
||||
};
|
||||
return task;
|
||||
};
|
||||
|
||||
// Run the next task in the queue if any and no other running
|
||||
function runTask() {
|
||||
|
||||
// If there is a task currently running
|
||||
if (currentTaskRunning === true) {
|
||||
// If the current task takes too long
|
||||
var timeout = currentTask.timeout || ASYNC_TASK_DEFAULT_TIMEOUT;
|
||||
if(currentTaskStartTime + timeout < core.currentTime) {
|
||||
currentTask.error();
|
||||
if (currentTaskStartTime + currentTask.timeout < core.currentTime) {
|
||||
var errorMsg = "A timeout occurred.";
|
||||
core.showError(errorMsg);
|
||||
currentTask.error(new Error(errorMsg));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentTask === undefined) {
|
||||
// If no task in the queue
|
||||
if(asyncTaskQueue.length === 0) {
|
||||
if (taskQueue.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Dequeue an enqueued task
|
||||
currentTask = asyncTaskQueue.shift();
|
||||
currentTask = taskQueue.shift();
|
||||
currentTaskStartTime = core.currentTime;
|
||||
core.showWorkingIndicator(true);
|
||||
|
||||
// Set task attributes and functions
|
||||
currentTask.finished = false;
|
||||
currentTask.retryCounter = 0;
|
||||
currentTask.success = function() {
|
||||
runSafe(this.onSuccess);
|
||||
};
|
||||
currentTask.error = function() {
|
||||
runSafe(this.onError);
|
||||
};
|
||||
currentTask.retry = function() {
|
||||
if(this.finished === true) {
|
||||
return;
|
||||
}
|
||||
if(currentTask.retryCounter === 5) {
|
||||
this.error();
|
||||
return;
|
||||
}
|
||||
// Implement an exponential backoff
|
||||
var delay = Math.pow(2, currentTask.retryCounter++) * 1000;
|
||||
currentTaskStartTime = core.currentTime + delay;
|
||||
currentTaskRunning = false;
|
||||
asyncTaskRunner.runTask();
|
||||
};
|
||||
}
|
||||
|
||||
// Run the task
|
||||
if (currentTaskStartTime <= core.currentTime) {
|
||||
currentTaskRunning = true;
|
||||
currentTask.run();
|
||||
currentTask.chain();
|
||||
}
|
||||
}
|
||||
|
||||
asyncTaskRunner.runTask = function() {
|
||||
asyncRunner.runTask = function() {
|
||||
// Use defer to avoid stack overflow
|
||||
_.defer(runTask);
|
||||
};
|
||||
|
||||
function runSafe(func) {
|
||||
if(currentTask === undefined || currentTask.finished === true) {
|
||||
return;
|
||||
}
|
||||
function runSafe(task, callbacks, param) {
|
||||
try {
|
||||
if(func) {
|
||||
func();
|
||||
}
|
||||
_.each(callbacks, function(callback) {
|
||||
callback(param);
|
||||
});
|
||||
} finally {
|
||||
currentTask.finished = true;
|
||||
task.finished = true;
|
||||
if (currentTask === task) {
|
||||
currentTask = undefined;
|
||||
currentTaskRunning = false;
|
||||
if(asyncTaskQueue.length === 0) {
|
||||
core.showWorkingIndicator(false);
|
||||
}
|
||||
else {
|
||||
asyncTaskRunner.runTask();
|
||||
if (taskQueue.length === 0) {
|
||||
core.showWorkingIndicator(false);
|
||||
} else {
|
||||
asyncRunner.runTask();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add a task into the queue
|
||||
asyncTaskRunner.addTask = function(asyncTask) {
|
||||
asyncTaskQueue.push(asyncTask);
|
||||
asyncTaskRunner.runTask();
|
||||
// Add a task to the queue
|
||||
asyncRunner.addTask = function(task) {
|
||||
taskQueue.push(task);
|
||||
asyncRunner.runTask();
|
||||
};
|
||||
|
||||
// Change current task timeout
|
||||
asyncTaskRunner.setCurrentTaskTimeout = function(timeout) {
|
||||
asyncRunner.setCurrentTaskTimeout = function(timeout) {
|
||||
if (currentTask !== undefined) {
|
||||
currentTask.timeout = timeout;
|
||||
}
|
||||
};
|
||||
|
||||
asyncTaskRunner.init = function(coreModule) {
|
||||
asyncRunner.init = function(coreModule) {
|
||||
core = coreModule;
|
||||
};
|
||||
|
||||
return asyncTaskRunner;
|
||||
return asyncRunner;
|
||||
});
|
||||
|
@ -13,9 +13,9 @@ define(["jquery", "google-helper"], function($, googleHelper) {
|
||||
bloggerProvider.publish = function(publishAttributes, title, content, callback) {
|
||||
googleHelper.uploadBlogger(publishAttributes.blogUrl,
|
||||
publishAttributes.blogId, publishAttributes.postId, title, content,
|
||||
function(blogId, postId) {
|
||||
if(blogId === undefined || postId === undefined) {
|
||||
callback(true);
|
||||
function(error, blogId, postId) {
|
||||
if(error) {
|
||||
callback(error);
|
||||
return;
|
||||
}
|
||||
publishAttributes.blogId = blogId;
|
||||
@ -27,10 +27,7 @@ define(["jquery", "google-helper"], function($, googleHelper) {
|
||||
bloggerProvider.newPublishAttributes = function(event) {
|
||||
var publishAttributes = {};
|
||||
publishAttributes.blogUrl = core.getInputValue($("#input-publish-blogger-url"), event);
|
||||
var postId = $("#input-publish-blogger-postid").val();
|
||||
if(postId) {
|
||||
publishAttributes.postId = postId;
|
||||
}
|
||||
publishAttributes.postId = $("#input-publish-blogger-postid").val() || undefined;
|
||||
if(event.isPropagationStopped()) {
|
||||
return undefined;
|
||||
}
|
||||
|
@ -9,14 +9,16 @@ var GDRIVE_DEFAULT_FILE_TITLE = "New Markdown document";
|
||||
var CHECK_ONLINE_PERIOD = 60000;
|
||||
var AJAX_TIMEOUT = 10000;
|
||||
var ASYNC_TASK_DEFAULT_TIMEOUT = 30000;
|
||||
var AUTH_POPUP_TIMEOUT = 90000;
|
||||
var ASYNC_TASK_LONG_TIMEOUT = 90000;
|
||||
var SYNC_PERIOD = 180000;
|
||||
var USER_IDLE_THRESHOLD = 300000;
|
||||
var SYNC_PROVIDER_GDRIVE = "sync.gdrive.";
|
||||
var SYNC_PROVIDER_DROPBOX = "sync.dropbox.";
|
||||
var PROVIDER_TYPE_PUBLISH_FLAG = 1;
|
||||
var PROVIDER_TYPE_SYNC_FLAG = 1;
|
||||
var PROVIDER_TYPE_PUBLISH_FLAG = 2;
|
||||
var PROVIDER_BLOGGER = "blogger";
|
||||
var PROVIDER_DROPBOX = "dropbox";
|
||||
var PROVIDER_GDRIVE = "gdrive";
|
||||
var PROVIDER_GITHUB = "github";
|
||||
|
||||
// Use by Google's client.js
|
||||
|
68
js/core.js
68
js/core.js
@ -4,7 +4,7 @@ define(
|
||||
"bootstrap", "jgrowl", "layout", "Markdown.Editor", "config",
|
||||
"underscore" ],
|
||||
function($, fileManager, googleHelper, dropboxHelper, githubHelper,
|
||||
synchronizer, publisher, asyncTaskRunner) {
|
||||
synchronizer, publisher, asyncRunner) {
|
||||
|
||||
var core = {};
|
||||
|
||||
@ -102,7 +102,7 @@ define(
|
||||
$(".modal input[type=text]:not([disabled])").val("");
|
||||
};
|
||||
|
||||
// Used by asyncTaskRunner
|
||||
// Used by asyncRunner
|
||||
core.showWorkingIndicator = function(show) {
|
||||
if (show === false) {
|
||||
$(".working-indicator").addClass("hide");
|
||||
@ -115,6 +115,9 @@ define(
|
||||
|
||||
// Used to show a notification message
|
||||
core.showMessage = function(msg, iconClass, options) {
|
||||
if(!msg) {
|
||||
return;
|
||||
}
|
||||
options = options || {};
|
||||
iconClass = iconClass || "icon-info-sign";
|
||||
$.jGrowl("<i class='icon-white " + iconClass + "'></i> " + _.escape(msg), options);
|
||||
@ -466,7 +469,7 @@ define(
|
||||
function setupLocalStorage() {
|
||||
if (localStorage["file.list"] === undefined) {
|
||||
localStorage["file.list"] = ";";
|
||||
localStorage["version"] = "v1";
|
||||
localStorage["version"] = "v2";
|
||||
}
|
||||
}
|
||||
|
||||
@ -482,22 +485,53 @@ define(
|
||||
localStorage.removeItem("sync.current");
|
||||
localStorage.removeItem("file.counter");
|
||||
|
||||
var fileIndexList = localStorage["file.list"].split(";");
|
||||
for ( var i = 1; i < fileIndexList.length - 1; i++) {
|
||||
var fileIndex = fileIndexList[i];
|
||||
var fileIndexList = _.compact(localStorage["file.list"].split(";"));
|
||||
_.each(fileIndexList, function(fileIndex) {
|
||||
localStorage[fileIndex + ".publish"] = ";";
|
||||
var fileSyncIndexList = localStorage[fileIndex + ".sync"].split(";");
|
||||
for ( var j = 1; j < fileSyncIndexList.length - 1; j++) {
|
||||
var fileSyncIndex = fileSyncIndexList[j];
|
||||
var fileSyncIndexList = _.compact(localStorage[fileIndex + ".sync"].split(";"));
|
||||
_.each(fileSyncIndexList, function(fileSyncIndex) {
|
||||
localStorage[fileSyncIndex + ".contentCRC"] = "0";
|
||||
// We store title CRC only for Google Drive synchronization
|
||||
if(localStorage[fileSyncIndex + ".etag"] !== undefined) {
|
||||
localStorage[fileSyncIndex + ".titleCRC"] = "0";
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
version = "v1";
|
||||
}
|
||||
|
||||
// from v1 to v2
|
||||
if(version == "v1") {
|
||||
|
||||
var SYNC_PROVIDER_GDRIVE = "sync." + PROVIDER_GDRIVE + ".";
|
||||
var SYNC_PROVIDER_DROPBOX = "sync." + PROVIDER_DROPBOX + ".";
|
||||
var fileIndexList = _.compact(localStorage["file.list"].split(";"));
|
||||
_.each(fileIndexList, function(fileIndex) {
|
||||
var fileSyncIndexList = _.compact(localStorage[fileIndex + ".sync"].split(";"));
|
||||
_.each(fileSyncIndexList, function(fileSyncIndex) {
|
||||
var syncAttributes = {};
|
||||
if (fileSyncIndex.indexOf(SYNC_PROVIDER_GDRIVE) === 0) {
|
||||
syncAttributes.provider = PROVIDER_GDRIVE;
|
||||
syncAttributes.id = fileSyncIndex.substring(SYNC_PROVIDER_GDRIVE.length);
|
||||
syncAttributes.etag = localStorage[fileSyncIndex + ".etag"];
|
||||
syncAttributes.contentCRC = localStorage[fileSyncIndex + ".contentCRC"];
|
||||
syncAttributes.titleCRC = localStorage[fileSyncIndex + ".titleCRC"];
|
||||
}
|
||||
else if (fileSyncIndex.indexOf(SYNC_PROVIDER_DROPBOX) === 0) {
|
||||
syncAttributes.provider = PROVIDER_DROPBOX;
|
||||
syncAttributes.path = decodeURIComponent(fileSyncIndex.substring(SYNC_PROVIDER_DROPBOX.length));
|
||||
syncAttributes.version = localStorage[fileSyncIndex + ".version"];
|
||||
syncAttributes.contentCRC = localStorage[fileSyncIndex + ".contentCRC"];
|
||||
}
|
||||
localStorage[fileSyncIndex] = JSON.stringify(syncAttributes);
|
||||
localStorage.remoteItem(fileSyncIndex + ".etag");
|
||||
localStorage.remoteItem(fileSyncIndex + ".version");
|
||||
localStorage.remoteItem(fileSyncIndex + ".contentCRC");
|
||||
localStorage.remoteItem(fileSyncIndex + ".titleCRC");
|
||||
});
|
||||
});
|
||||
version = "v2";
|
||||
}
|
||||
localStorage["version"] = version;
|
||||
}
|
||||
|
||||
@ -586,13 +620,13 @@ define(
|
||||
core.resetModalInputs();
|
||||
});
|
||||
|
||||
// Init asyncTaskRunner
|
||||
asyncTaskRunner.init(core);
|
||||
// Init asyncRunner
|
||||
asyncRunner.init(core);
|
||||
|
||||
// Init helpers
|
||||
googleHelper.init(core, fileManager);
|
||||
dropboxHelper.init(core, fileManager);
|
||||
githubHelper.init(core, fileManager);
|
||||
googleHelper.init(core);
|
||||
dropboxHelper.init(core);
|
||||
githubHelper.init(core);
|
||||
|
||||
// Init synchronizer
|
||||
synchronizer.init(core, fileManager);
|
||||
@ -614,7 +648,7 @@ define(
|
||||
return;
|
||||
}
|
||||
synchronizer.sync();
|
||||
asyncTaskRunner.runTask();
|
||||
asyncRunner.runTask();
|
||||
checkOnline();
|
||||
}, 1000);
|
||||
};
|
||||
|
@ -1,8 +1,7 @@
|
||||
define(["jquery", "async-runner"], function($, asyncTaskRunner) {
|
||||
define(["jquery", "async-runner"], function($, asyncRunner) {
|
||||
|
||||
// Dependencies
|
||||
var core = undefined;
|
||||
var fileManager = undefined;
|
||||
|
||||
var client = undefined;
|
||||
var authenticated = false;
|
||||
@ -10,16 +9,16 @@ define(["jquery", "async-runner"], function($, asyncTaskRunner) {
|
||||
var dropboxHelper = {};
|
||||
|
||||
// Try to connect dropbox by downloading client.js
|
||||
function connect(callback) {
|
||||
callback = callback || core.doNothing;
|
||||
function connect(task) {
|
||||
task.onRun(function() {
|
||||
if(core.isOffline === true) {
|
||||
client = undefined;
|
||||
core.showMessage("Operation not available in offline mode.");
|
||||
callback(true);
|
||||
task.error();
|
||||
return;
|
||||
}
|
||||
if (client !== undefined) {
|
||||
callback();
|
||||
task.chain();
|
||||
return;
|
||||
}
|
||||
$.ajax({
|
||||
@ -34,106 +33,95 @@ define(["jquery", "async-runner"], function($, asyncTaskRunner) {
|
||||
receiverUrl: BASE_URL + "dropbox-oauth-receiver.html",
|
||||
rememberUser: true
|
||||
}));
|
||||
callback();
|
||||
task.chain();
|
||||
}).fail(function() {
|
||||
core.setOffline();
|
||||
callback(true);
|
||||
task.error(new Error("Network timeout"));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Try to authenticate with Oauth
|
||||
function authenticate(callback, immediate) {
|
||||
callback = callback || core.doNothing;
|
||||
if (immediate === undefined) {
|
||||
immediate = true;
|
||||
}
|
||||
connect(function(error) {
|
||||
if (error) {
|
||||
callback(error);
|
||||
return;
|
||||
}
|
||||
function authenticate(task) {
|
||||
task.onRun(function() {
|
||||
if (authenticated === true) {
|
||||
callback();
|
||||
task.chain();
|
||||
return;
|
||||
}
|
||||
var immediate = true;
|
||||
function localAuthenticate() {
|
||||
if (immediate === false) {
|
||||
core.showMessage("Please make sure the Dropbox authorization popup is not blocked by your browser.");
|
||||
asyncTaskRunner.setCurrentTaskTimeout(AUTH_POPUP_TIMEOUT);
|
||||
// If not immediate we add time for user to enter his credentials
|
||||
task.timeout = ASYNC_TASK_LONG_TIMEOUT;
|
||||
}
|
||||
client.reset();
|
||||
client.authenticate({interactive: !immediate}, function(error, client) {
|
||||
// Success
|
||||
if (client.authState === Dropbox.Client.DONE) {
|
||||
callback();
|
||||
authenticated = true;
|
||||
task.chain();
|
||||
return;
|
||||
}
|
||||
|
||||
// If immediate did not work retry without immediate flag
|
||||
if (client !== undefined && immediate === true) {
|
||||
authenticate(callback, false);
|
||||
if (immediate === true) {
|
||||
immediate = false;
|
||||
task.chain(localAuthenticate);
|
||||
return;
|
||||
}
|
||||
// Notify error
|
||||
callback(true);
|
||||
// Error
|
||||
var errorMsg = "Access to Dropbox account is not authorized.";
|
||||
core.showError(errorMsg);
|
||||
task.error(new Error(errorMsg));
|
||||
});
|
||||
}
|
||||
task.chain(localAuthenticate);
|
||||
});
|
||||
}
|
||||
|
||||
dropboxHelper.upload = function(path, content, callback) {
|
||||
callback = callback || core.doNothing;
|
||||
var syncIndex = undefined;
|
||||
var asyncTask = {};
|
||||
asyncTask.run = function() {
|
||||
authenticate(function(error) {
|
||||
if (error) {
|
||||
handleError(error, asyncTask, callback);
|
||||
return;
|
||||
}
|
||||
|
||||
var result = undefined;
|
||||
var task = asyncRunner.createTask();
|
||||
connect(task);
|
||||
authenticate(task);
|
||||
task.onRun(function() {
|
||||
client.writeFile(path, content, function(error, stat) {
|
||||
if (!error) {
|
||||
syncIndex = SYNC_PROVIDER_DROPBOX + encodeURIComponent(stat.path.toLowerCase());
|
||||
localStorage[syncIndex + ".version"] = stat.versionTag;
|
||||
asyncTask.success();
|
||||
result = stat;
|
||||
task.chain();
|
||||
return;
|
||||
}
|
||||
// Handle error
|
||||
if(error.status === Dropbox.ApiError.INVALID_PARAM) {
|
||||
error = 'Could not upload document into path "' + path + '".';
|
||||
}
|
||||
handleError(error, asyncTask, callback);
|
||||
handleError(error, task, callback);
|
||||
});
|
||||
});
|
||||
};
|
||||
asyncTask.onSuccess = function() {
|
||||
callback(undefined, syncIndex);
|
||||
};
|
||||
asyncTask.onError = function() {
|
||||
callback(true);
|
||||
};
|
||||
asyncTaskRunner.addTask(asyncTask);
|
||||
task.onSuccess(function() {
|
||||
callback(undefined, result);
|
||||
});
|
||||
task.onError(function(error) {
|
||||
callback(error);
|
||||
});
|
||||
asyncRunner.addTask(task);
|
||||
};
|
||||
|
||||
dropboxHelper.checkUpdates = function(lastChangeId, callback) {
|
||||
callback = callback || core.doNothing;
|
||||
var changes = [];
|
||||
var newChangeId = lastChangeId || 0;
|
||||
var asyncTask = {};
|
||||
asyncTask.run = function() {
|
||||
function retrievePageOfChanges(changeId) {
|
||||
if(asyncTask.finished === true) {
|
||||
return;
|
||||
}
|
||||
authenticate(function(error) {
|
||||
var task = asyncRunner.createTask();
|
||||
connect(task);
|
||||
authenticate(task);
|
||||
task.onRun(function() {
|
||||
function retrievePageOfChanges() {
|
||||
client.pullChanges(newChangeId, function(error, pullChanges) {
|
||||
if (error) {
|
||||
handleError(error, asyncTask, callback);
|
||||
handleError(error, task, callback);
|
||||
return;
|
||||
}
|
||||
|
||||
client.pullChanges(changeId, function(error, pullChanges) {
|
||||
if (error) {
|
||||
handleError(error, asyncTask, callback);
|
||||
return;
|
||||
}
|
||||
|
||||
// Retrieve success
|
||||
newChangeId = pullChanges.cursor();
|
||||
if(pullChanges.changes !== undefined) {
|
||||
@ -147,82 +135,68 @@ define(["jquery", "async-runner"], function($, asyncTaskRunner) {
|
||||
}
|
||||
}
|
||||
if (pullChanges.shouldPullAgain) {
|
||||
retrievePageOfChanges(newChangeId);
|
||||
task.chain(retrievePageOfChanges);
|
||||
} else {
|
||||
asyncTask.success();
|
||||
task.chain();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
retrievePageOfChanges(newChangeId);
|
||||
};
|
||||
asyncTask.onSuccess = function() {
|
||||
task.chain(retrievePageOfChanges);
|
||||
});
|
||||
task.onSuccess(function() {
|
||||
callback(undefined, changes, newChangeId);
|
||||
};
|
||||
asyncTask.onError = function() {
|
||||
callback(true);
|
||||
};
|
||||
asyncTaskRunner.addTask(asyncTask);
|
||||
});
|
||||
task.onError(function(error) {
|
||||
callback(error);
|
||||
});
|
||||
asyncRunner.addTask(task);
|
||||
};
|
||||
|
||||
dropboxHelper.downloadMetadata = function(paths, callback) {
|
||||
callback = callback || core.doNothing;
|
||||
result = result || [];
|
||||
|
||||
var path = paths.pop();
|
||||
var asyncTask = {};
|
||||
asyncTask.run = function() {
|
||||
var result = [];
|
||||
var task = asyncRunner.createTask();
|
||||
connect(task);
|
||||
authenticate(task);
|
||||
task.onRun(function() {
|
||||
function recursiveDownloadMetadata() {
|
||||
if(asyncTask.finished === true) {
|
||||
return;
|
||||
}
|
||||
if(paths.length === 0) {
|
||||
asyncTask.success();
|
||||
task.chain();
|
||||
return;
|
||||
}
|
||||
authenticate(function(error) {
|
||||
if (error) {
|
||||
handleError(error, asyncTask, callback);
|
||||
return;
|
||||
}
|
||||
|
||||
var path = paths.pop();
|
||||
client.stat(path, function(error, stat) {
|
||||
if(stat) {
|
||||
result.push(stat);
|
||||
recursiveDownloadMetadata();
|
||||
task.chain(recursiveDownloadMetadata);
|
||||
return;
|
||||
}
|
||||
handleError(error, asyncTask, callback);
|
||||
});
|
||||
handleError(error, task, callback);
|
||||
});
|
||||
}
|
||||
recursiveDownloadMetadata();
|
||||
};
|
||||
asyncTask.onSuccess = function() {
|
||||
task.chain(recursiveDownloadMetadata);
|
||||
});
|
||||
task.onSuccess(function() {
|
||||
callback(undefined, result);
|
||||
};
|
||||
asyncTask.onError = function() {
|
||||
callback(true);
|
||||
};
|
||||
asyncTaskRunner.addTask(asyncTask);
|
||||
});
|
||||
task.onError(function(error) {
|
||||
callback(error);
|
||||
});
|
||||
asyncRunner.addTask(task);
|
||||
};
|
||||
|
||||
dropboxHelper.downloadContent = function(objects, callback, result) {
|
||||
dropboxHelper.downloadContent = function(objects, callback) {
|
||||
callback = callback || core.doNothing;
|
||||
result = result || [];
|
||||
|
||||
var asyncTask = {};
|
||||
asyncTask.run = function() {
|
||||
|
||||
var result = [];
|
||||
var task = asyncRunner.createTask();
|
||||
connect(task);
|
||||
authenticate(task);
|
||||
task.onRun(function() {
|
||||
function recursiveDownloadContent() {
|
||||
if(asyncTask.finished === true) {
|
||||
return;
|
||||
}
|
||||
if(objects.length === 0) {
|
||||
asyncTask.success();
|
||||
task.chain();
|
||||
return;
|
||||
}
|
||||
|
||||
var object = objects.pop();
|
||||
result.push(object);
|
||||
var file = undefined;
|
||||
@ -235,44 +209,31 @@ define(["jquery", "async-runner"], function($, asyncTaskRunner) {
|
||||
file = object.stat;
|
||||
}
|
||||
if(!file) {
|
||||
recursiveDownloadContent();
|
||||
task.chain(recursiveDownloadContent);
|
||||
return;
|
||||
}
|
||||
authenticate(function(error) {
|
||||
if (error) {
|
||||
handleError(error, asyncTask, callback);
|
||||
return;
|
||||
}
|
||||
|
||||
client.readFile(file.path, function(error, data) {
|
||||
if(data) {
|
||||
file.content = data;
|
||||
recursiveDownloadContent();
|
||||
return;
|
||||
}
|
||||
handleError(error, asyncTask, callback);
|
||||
});
|
||||
handleError(error, task, callback);
|
||||
});
|
||||
}
|
||||
recursiveDownloadContent();
|
||||
};
|
||||
asyncTask.onSuccess = function() {
|
||||
task.chain(recursiveDownloadContent);
|
||||
});
|
||||
task.onSuccess(function() {
|
||||
callback(undefined, result);
|
||||
};
|
||||
asyncTask.onError = function() {
|
||||
callback(true);
|
||||
};
|
||||
asyncTaskRunner.addTask(asyncTask);
|
||||
});
|
||||
task.onError(function(error) {
|
||||
callback(error);
|
||||
});
|
||||
asyncRunner.addTask(task);
|
||||
};
|
||||
|
||||
function handleError(error, asyncTask, callback) {
|
||||
var errorMsg = undefined;
|
||||
asyncTask.onError = function() {
|
||||
if (errorMsg !== undefined) {
|
||||
core.showError(errorMsg);
|
||||
}
|
||||
callback(errorMsg);
|
||||
};
|
||||
function handleError(error, task, callback) {
|
||||
var errorMsg = true;
|
||||
if (error) {
|
||||
console.error(error);
|
||||
// Try to analyze the error
|
||||
@ -281,7 +242,20 @@ define(["jquery", "async-runner"], function($, asyncTaskRunner) {
|
||||
} else if (error.status === Dropbox.ApiError.INVALID_TOKEN
|
||||
|| error.status === Dropbox.ApiError.OAUTH_ERROR) {
|
||||
authenticated = false;
|
||||
errorMsg = "Access to Dropbox is not authorized.";
|
||||
task.retry();
|
||||
return;
|
||||
} else if(error.status === Dropbox.ApiError.INVALID_PARAM && error.responseText
|
||||
.indexOf("oauth_nonce") !== -1) {
|
||||
// A bug I guess...
|
||||
_.each(_.keys(localStorage), function(key) {
|
||||
// We have to remove the Oauth cache from the localStorage
|
||||
if(key.indexOf("dropbox-auth") === 0) {
|
||||
localStorage.removeItem(key);
|
||||
}
|
||||
});
|
||||
authenticated = false;
|
||||
task.retry();
|
||||
return;
|
||||
} else if (error.status === Dropbox.ApiError.NETWORK_ERROR) {
|
||||
client = undefined;
|
||||
authenticated = false;
|
||||
@ -290,31 +264,27 @@ define(["jquery", "async-runner"], function($, asyncTaskRunner) {
|
||||
errorMsg = "Dropbox error ("
|
||||
+ error.status + ").";
|
||||
}
|
||||
core.showError(errorMsg);
|
||||
}
|
||||
asyncTask.error();
|
||||
task.error(new Error(errorMsg));
|
||||
}
|
||||
|
||||
var pickerLoaded = false;
|
||||
function loadPicker(callback) {
|
||||
function loadPicker(task) {
|
||||
task.onRun(function() {
|
||||
if (pickerLoaded === true) {
|
||||
callback();
|
||||
task.chain();
|
||||
return;
|
||||
}
|
||||
connect(function(error) {
|
||||
if (error) {
|
||||
pickerLoaded = false;
|
||||
callback(error);
|
||||
return;
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
url : "https://www.dropbox.com/static/api/1/dropbox.js",
|
||||
dataType : "script", timeout : AJAX_TIMEOUT
|
||||
}).done(function() {
|
||||
pickerLoaded = true;
|
||||
callback();
|
||||
task.chain();
|
||||
}).fail(function() {
|
||||
callback(true);
|
||||
core.setOffline();
|
||||
task.error();
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -322,14 +292,12 @@ define(["jquery", "async-runner"], function($, asyncTaskRunner) {
|
||||
dropboxHelper.picker = function(callback) {
|
||||
callback = callback || core.doNothing;
|
||||
var paths = [];
|
||||
|
||||
var asyncTask = {};
|
||||
asyncTask.run = function() {
|
||||
loadPicker(function(error) {
|
||||
if (error) {
|
||||
handleError(error, asyncTask, callback);
|
||||
return;
|
||||
}
|
||||
var task = asyncRunner.createTask();
|
||||
// Add some time for user to choose his files
|
||||
task.timeout = ASYNC_TASK_LONG_TIMEOUT;
|
||||
connect(task);
|
||||
loadPicker(task);
|
||||
task.onRun(function() {
|
||||
var options = {};
|
||||
options.multiselect = true;
|
||||
options.linkType = "direct";
|
||||
@ -339,61 +307,25 @@ define(["jquery", "async-runner"], function($, asyncTaskRunner) {
|
||||
path = path.replace(/.*\/view\/[^\/]*/, "");
|
||||
paths.push(decodeURI(path));
|
||||
}
|
||||
asyncTask.success();
|
||||
task.chain();
|
||||
};
|
||||
options.cancel = function() {
|
||||
asyncTask.error();
|
||||
task.chain();
|
||||
};
|
||||
Dropbox.choose(options);
|
||||
core.showMessage("Please make sure the Dropbox chooser popup is not blocked by your browser.");
|
||||
});
|
||||
};
|
||||
asyncTask.onSuccess = function() {
|
||||
task.onSuccess(function() {
|
||||
callback(undefined, paths);
|
||||
};
|
||||
asyncTask.onError = function() {
|
||||
callback(true);
|
||||
};
|
||||
asyncTaskRunner.addTask(asyncTask);
|
||||
});
|
||||
task.onError(function(error) {
|
||||
callback(error);
|
||||
});
|
||||
asyncRunner.addTask(task);
|
||||
};
|
||||
|
||||
dropboxHelper.importFiles = function(paths) {
|
||||
dropboxHelper.downloadMetadata(paths, function(error, result) {
|
||||
if(error) {
|
||||
return;
|
||||
}
|
||||
dropboxHelper.downloadContent(result, function(error, result) {
|
||||
if(error) {
|
||||
return;
|
||||
}
|
||||
for(var i=0; i<result.length; i++) {
|
||||
var file = result[i];
|
||||
syncIndex = SYNC_PROVIDER_DROPBOX + encodeURIComponent(file.path.toLowerCase());
|
||||
localStorage[syncIndex + ".version"] = file.versionTag;
|
||||
var contentCRC = core.crc32(file.content);
|
||||
localStorage[syncIndex + ".contentCRC"] = contentCRC;
|
||||
var fileIndex = fileManager.createFile(file.name, file.content, [syncIndex]);
|
||||
fileManager.selectFile(fileIndex);
|
||||
core.showMessage('"' + file.name + '" imported successfully from Dropbox.');
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
dropboxHelper.init = function(coreModule, fileManagerModule) {
|
||||
dropboxHelper.init = function(coreModule) {
|
||||
core = coreModule;
|
||||
fileManager = fileManagerModule;
|
||||
};
|
||||
|
||||
dropboxHelper.checkPath = function(path) {
|
||||
if(!path.match(/^[^\\<>:"\|?\*]+$/)) {
|
||||
core.showError('"' + path + '" contains invalid characters.');
|
||||
return undefined;
|
||||
}
|
||||
if(path.indexOf("/") !== 0) {
|
||||
return "/" + path;
|
||||
}
|
||||
return path;
|
||||
};
|
||||
|
||||
return dropboxHelper;
|
||||
|
@ -2,16 +2,116 @@ define(["jquery", "dropbox-helper"], function($, dropboxHelper) {
|
||||
|
||||
// Dependencies
|
||||
var core = undefined;
|
||||
var fileManager = undefined;
|
||||
|
||||
var dropboxProvider = {
|
||||
providerType: PROVIDER_TYPE_PUBLISH_FLAG,
|
||||
providerType: PROVIDER_TYPE_SYNC_FLAG | PROVIDER_TYPE_PUBLISH_FLAG,
|
||||
providerId: PROVIDER_DROPBOX,
|
||||
providerName: "Dropbox",
|
||||
defaultPublishFormat: "template"
|
||||
};
|
||||
|
||||
function checkPath(path) {
|
||||
if(path === undefined) {
|
||||
return undefined;
|
||||
}
|
||||
if(!path.match(/^[^\\<>:"\|?\*]+$/)) {
|
||||
core.showError('"' + path + '" contains invalid characters.');
|
||||
return undefined;
|
||||
}
|
||||
if(path.indexOf("/") !== 0) {
|
||||
return "/" + path;
|
||||
}
|
||||
return path;
|
||||
}
|
||||
|
||||
function createSyncAttributes(path, versionTag, content) {
|
||||
var syncAttributes = {};
|
||||
syncAttributes.provider = PROVIDER_DROPBOX;
|
||||
syncAttributes.path = path;
|
||||
syncAttributes.version = versionTag;
|
||||
syncAttributes.contentCRC = core.crc32(content);
|
||||
var syncIndex = "sync." + PROVIDER_DROPBOX + "." + encodeURIComponent(file.path.toLowerCase());
|
||||
localStorage[syncIndex] = JSON.stringify(syncAttributes);
|
||||
return syncIndex;
|
||||
}
|
||||
|
||||
function importFilesFromPaths(paths) {
|
||||
dropboxHelper.downloadMetadata(paths, function(error, result) {
|
||||
if(error) {
|
||||
return;
|
||||
}
|
||||
dropboxHelper.downloadContent(result, function(error, result) {
|
||||
if(error) {
|
||||
return;
|
||||
}
|
||||
_.each(result, function(file) {
|
||||
var syncIndex = createSyncAttributes(file.path, file.versionTag, file.content);
|
||||
var fileIndex = fileManager.createFile(file.name, file.content, [syncIndex]);
|
||||
fileManager.selectFile(fileIndex);
|
||||
core.showMessage('"' + file.name + '" imported successfully from Dropbox.');
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
dropboxProvider.importFiles = function() {
|
||||
dropboxHelper.picker(function(error, paths) {
|
||||
if(error) {
|
||||
return;
|
||||
}
|
||||
var importPaths = [];
|
||||
_.each(paths, function(path) {
|
||||
var syncIndex = "sync." + PROVIDER_DROPBOX + "." + encodeURIComponent(path.toLowerCase());
|
||||
var fileIndex = fileManager.getFileIndexFromSync(syncIndex);
|
||||
if(fileIndex !== undefined) {
|
||||
var title = localStorage[fileIndex + ".title"];
|
||||
core.showError('"' + title + '" was already imported');
|
||||
return;
|
||||
}
|
||||
importPaths.push(path);
|
||||
});
|
||||
dropboxHelper.importFiles(importPaths);
|
||||
});
|
||||
};
|
||||
|
||||
function exportFileToPath(path, title, content, callback) {
|
||||
path = dropboxHelper.checkPath(path);
|
||||
if(path === undefined) {
|
||||
callback(true);
|
||||
return;
|
||||
}
|
||||
// Check that file is not synchronized with an other one
|
||||
var syncIndex = "sync." + PROVIDER_DROPBOX + "." + encodeURIComponent(path.toLowerCase());
|
||||
var fileIndex = fileManager.getFileIndexFromSync(syncIndex);
|
||||
if(fileIndex !== undefined) {
|
||||
var existingTitle = localStorage[fileIndex + ".title"];
|
||||
core.showError('File path is already synchronized with "' + existingTitle + '"');
|
||||
callback(true);
|
||||
return;
|
||||
}
|
||||
dropboxHelper.upload(path, content, function(error, result) {
|
||||
if (error) {
|
||||
callback(error);
|
||||
return;
|
||||
}
|
||||
syncIndex = createSyncAttributes(result.path, result.versionTag, content);
|
||||
callback(undefined, syncIndex);
|
||||
});
|
||||
}
|
||||
|
||||
dropboxProvider.exportFile = function(event, title, content, callback) {
|
||||
var path = core.getInputValue($("#input-sync-export-dropbox-path"), event);
|
||||
exportFileToPath(path);
|
||||
};
|
||||
|
||||
dropboxProvider.exportManual = function(event, title, content, callback) {
|
||||
var path = core.getInputValue($("#input-sync-manual-dropbox-path"), event);
|
||||
exportFileToPath(path);
|
||||
};
|
||||
|
||||
dropboxProvider.publish = function(publishAttributes, title, content, callback) {
|
||||
var path = dropboxHelper.checkPath(publishAttributes.path);
|
||||
var path = checkPath(publishAttributes.path);
|
||||
if(path === undefined) {
|
||||
callback(true);
|
||||
return;
|
||||
@ -28,8 +128,9 @@ define(["jquery", "dropbox-helper"], function($, dropboxHelper) {
|
||||
return publishAttributes;
|
||||
};
|
||||
|
||||
dropboxProvider.init = function(coreModule) {
|
||||
dropboxProvider.init = function(coreModule, fileManagerModule) {
|
||||
core = coreModule;
|
||||
fileManager = fileManagerModule;
|
||||
};
|
||||
|
||||
return dropboxProvider;
|
||||
|
@ -40,7 +40,7 @@ define(["jquery", "google-helper", "dropbox-helper", "github-helper", "synchroni
|
||||
|
||||
// Update the file titles
|
||||
fileManager.updateFileTitles();
|
||||
refreshManageSync();
|
||||
synchronizer.refreshManageSync();
|
||||
publisher.notifyPublish();
|
||||
|
||||
// Recreate the editor
|
||||
@ -186,14 +186,11 @@ define(["jquery", "google-helper", "dropbox-helper", "github-helper", "synchroni
|
||||
localStorage[fileIndex + ".sync"] = localStorage[fileIndex + ".sync"].replace(";"
|
||||
+ syncIndex + ";", ";");
|
||||
if(fileManager.isCurrentFileIndex(fileIndex)) {
|
||||
refreshManageSync();
|
||||
synchronizer.refreshManageSync();
|
||||
}
|
||||
}
|
||||
// Remove ETAG, version, CRCs (if any)
|
||||
localStorage.removeItem(syncIndex + ".etag");
|
||||
localStorage.removeItem(syncIndex + ".version");
|
||||
localStorage.removeItem(syncIndex + ".contentCRC");
|
||||
localStorage.removeItem(syncIndex + ".titleCRC");
|
||||
// Remove sync attributes
|
||||
localStorage.removeItem(syncIndex);
|
||||
};
|
||||
|
||||
// Get the fileIndex associated to a syncIndex
|
||||
@ -215,7 +212,7 @@ define(["jquery", "google-helper", "dropbox-helper", "github-helper", "synchroni
|
||||
publisher.notifyPublish();
|
||||
}
|
||||
}
|
||||
// Remove publish object
|
||||
// Remove publish attributes
|
||||
localStorage.removeItem(publishIndex);
|
||||
};
|
||||
|
||||
@ -228,148 +225,6 @@ define(["jquery", "google-helper", "dropbox-helper", "github-helper", "synchroni
|
||||
}).value();
|
||||
};
|
||||
|
||||
function uploadGdrive(fileId, folderId) {
|
||||
var fileIndex = fileManager.getCurrentFileIndex();
|
||||
var content = localStorage[fileIndex + ".content"];
|
||||
var title = localStorage[fileIndex + ".title"];
|
||||
googleHelper.upload(fileId, folderId, title, content, function(syncIndex) {
|
||||
if (syncIndex === undefined) {
|
||||
return;
|
||||
}
|
||||
var contentCRC = core.crc32(content);
|
||||
localStorage[syncIndex + ".contentCRC"] = contentCRC;
|
||||
var titleCRC = core.crc32(title);
|
||||
localStorage[syncIndex + ".titleCRC"] = titleCRC;
|
||||
localStorage[fileIndex + ".sync"] += syncIndex + ";";
|
||||
refreshManageSync();
|
||||
fileManager.updateFileTitles();
|
||||
core.showMessage('"' + title
|
||||
+ '" will now be synchronized on Google Drive.');
|
||||
});
|
||||
}
|
||||
|
||||
function manualGdrive(fileId) {
|
||||
if(!fileId) {
|
||||
return;
|
||||
}
|
||||
// Check that file is not synchronized with an other one
|
||||
var syncIndex = SYNC_PROVIDER_GDRIVE + fileId;
|
||||
var fileIndex = fileManager.getFileIndexFromSync(syncIndex);
|
||||
if(fileIndex !== undefined) {
|
||||
var title = localStorage[fileIndex + ".title"];
|
||||
core.showError('File ID is already synchronized with "' + title + '"');
|
||||
return;
|
||||
}
|
||||
uploadGdrive(fileId);
|
||||
}
|
||||
|
||||
function importGdrive(ids) {
|
||||
if(ids === undefined) {
|
||||
return;
|
||||
}
|
||||
var importIds = [];
|
||||
for(var i=0; i<ids.length; i++) {
|
||||
var fileId = ids[i];
|
||||
var syncIndex = SYNC_PROVIDER_GDRIVE + fileId;
|
||||
var fileIndex = fileManager.getFileIndexFromSync(syncIndex);
|
||||
if(fileIndex !== undefined) {
|
||||
var title = localStorage[fileIndex + ".title"];
|
||||
core.showError('"' + title + '" was already imported');
|
||||
continue;
|
||||
}
|
||||
importIds.push(fileId);
|
||||
}
|
||||
googleHelper.importFiles(importIds);
|
||||
}
|
||||
|
||||
function manualDropbox(path) {
|
||||
if(!path) {
|
||||
return;
|
||||
}
|
||||
path = dropboxHelper.checkPath(path);
|
||||
if(path === undefined) {
|
||||
return;
|
||||
}
|
||||
// Check that file is not synchronized with an other one
|
||||
var syncIndex = SYNC_PROVIDER_DROPBOX + encodeURIComponent(path.toLowerCase());
|
||||
var fileIndex = fileManager.getFileIndexFromSync(syncIndex);
|
||||
if(fileIndex !== undefined) {
|
||||
var title = localStorage[fileIndex + ".title"];
|
||||
core.showError('Path "' + path + '" is already synchronized with "' + title + '"');
|
||||
return;
|
||||
}
|
||||
var fileIndex = fileManager.getCurrentFileIndex();
|
||||
var content = localStorage[fileIndex + ".content"];
|
||||
var title = localStorage[fileIndex + ".title"];
|
||||
dropboxHelper.upload(path, content, function(error, syncIndex) {
|
||||
if (error) {
|
||||
return;
|
||||
}
|
||||
var contentCRC = core.crc32(content);
|
||||
localStorage[syncIndex + ".contentCRC"] = contentCRC;
|
||||
localStorage[fileIndex + ".sync"] += syncIndex + ";";
|
||||
refreshManageSync();
|
||||
fileManager.updateFileTitles();
|
||||
core.showMessage('"' + title
|
||||
+ '" will now be synchronized on Dropbox.');
|
||||
});
|
||||
}
|
||||
|
||||
function importDropbox(error, paths) {
|
||||
if(error) {
|
||||
return;
|
||||
}
|
||||
var importPaths = [];
|
||||
for(var i=0; i<paths.length; i++) {
|
||||
var filePath = paths[i];
|
||||
var syncIndex = SYNC_PROVIDER_DROPBOX + encodeURIComponent(filePath.toLowerCase());
|
||||
var fileIndex = fileManager.getFileIndexFromSync(syncIndex);
|
||||
if(fileIndex !== undefined) {
|
||||
var title = localStorage[fileIndex + ".title"];
|
||||
core.showError('"' + title + '" was already imported');
|
||||
continue;
|
||||
}
|
||||
importPaths.push(filePath);
|
||||
}
|
||||
dropboxHelper.importFiles(importPaths);
|
||||
}
|
||||
|
||||
function refreshManageSync() {
|
||||
var fileIndex = fileManager.getCurrentFileIndex();
|
||||
var syncIndexList = _.compact(localStorage[fileIndex + ".sync"].split(";"));
|
||||
$(".msg-no-sync, .msg-sync-list").addClass("hide");
|
||||
$("#manage-sync-list .input-append").remove();
|
||||
if (syncIndexList.length > 0) {
|
||||
$(".msg-sync-list").removeClass("hide");
|
||||
} else {
|
||||
$(".msg-no-sync").removeClass("hide");
|
||||
}
|
||||
_.each(syncIndexList, function(syncIndex) {
|
||||
var line = $("<div>").addClass("input-prepend input-append");
|
||||
if (syncIndex.indexOf(SYNC_PROVIDER_GDRIVE) === 0) {
|
||||
line.append($("<span>").addClass("add-on").prop("title", "Google Drive").html(
|
||||
'<i class="icon-gdrive"></i>'));
|
||||
line.append($("<input>").prop("type", "text").prop(
|
||||
"disabled", true).addClass("span5").val(
|
||||
syncIndex.substring(SYNC_PROVIDER_GDRIVE.length)));
|
||||
}
|
||||
else if (syncIndex.indexOf(SYNC_PROVIDER_DROPBOX) === 0) {
|
||||
line.append($("<span>").addClass("add-on").prop("title", "Dropbox").html(
|
||||
'<i class="icon-dropbox"></i>'));
|
||||
line.append($("<input>").prop("type", "text").prop(
|
||||
"disabled", true).addClass("span5").val(
|
||||
decodeURIComponent(syncIndex.substring(SYNC_PROVIDER_DROPBOX.length))));
|
||||
}
|
||||
line.append($("<a>").addClass("btn").html(
|
||||
'<i class="icon-trash"></i>').prop("title",
|
||||
"Remove this location").click(function() {
|
||||
fileManager.removeSync(syncIndex);
|
||||
fileManager.updateFileTitles();
|
||||
}));
|
||||
$("#manage-sync-list").append(line);
|
||||
});
|
||||
}
|
||||
|
||||
fileManager.init = function(coreModule) {
|
||||
core = coreModule;
|
||||
|
||||
@ -388,18 +243,29 @@ define(["jquery", "google-helper", "dropbox-helper", "github-helper", "synchroni
|
||||
$(this).hide();
|
||||
$("#file-title-input").show().focus();
|
||||
});
|
||||
$("#file-title-input").blur(function() {
|
||||
var title = $.trim($(this).val());
|
||||
if (title) {
|
||||
function applyTitle(input) {
|
||||
var title = $.trim(input.val());
|
||||
var fileIndexTitle = fileManager.getCurrentFileIndex() + ".title";
|
||||
if (title) {
|
||||
if (title != localStorage[fileIndexTitle]) {
|
||||
localStorage[fileIndexTitle] = title;
|
||||
fileManager.updateFileTitles();
|
||||
fileManager.saveFile();
|
||||
}
|
||||
}
|
||||
$(this).hide();
|
||||
input.hide().val(localStorage[fileIndexTitle]);
|
||||
$("#file-title").show();
|
||||
}
|
||||
$("#file-title-input").blur(function() {
|
||||
applyTitle($(this));
|
||||
}).keyup(function(e) {
|
||||
if (e.keyCode == 13) {
|
||||
applyTitle($(this));
|
||||
}
|
||||
if (e.keyCode == 27) {
|
||||
$(this).val("");
|
||||
applyTitle($(this));
|
||||
}
|
||||
});
|
||||
$(".action-download-md").click(
|
||||
function() {
|
||||
@ -422,38 +288,6 @@ define(["jquery", "google-helper", "dropbox-helper", "github-helper", "synchroni
|
||||
+ core.encodeBase64(content);
|
||||
window.open(uriContent, 'file');
|
||||
});
|
||||
|
||||
// Synchronize actions
|
||||
$(".action-upload-gdrive-root").click(function() {
|
||||
uploadGdrive();
|
||||
});
|
||||
$(".action-upload-gdrive-select").click(function() {
|
||||
// This action is not available because picker does not support
|
||||
// folder selection
|
||||
googleHelper.picker(function(ids) {
|
||||
if(ids !== undefined && ids.length !== 0) {
|
||||
uploadGdrive(undefined, ids[0]);
|
||||
}
|
||||
}, true);
|
||||
});
|
||||
$(".action-download-gdrive").click(function() {
|
||||
googleHelper.picker(importGdrive);
|
||||
});
|
||||
$(".action-manual-gdrive").click(function(event) {
|
||||
var fileId = core.getInputValue($("#manual-gdrive-fileid"), event);
|
||||
manualGdrive(fileId);
|
||||
});
|
||||
$(".action-download-dropbox").click(function() {
|
||||
dropboxHelper.picker(importDropbox);
|
||||
});
|
||||
$(".action-upload-dropbox").click(function(event) {
|
||||
var path = core.getInputValue($("#upload-dropbox-path"), event);
|
||||
manualDropbox(path);
|
||||
});
|
||||
$(".action-manual-dropbox").click(function(event) {
|
||||
var path = core.getInputValue($("#manual-dropbox-path"), event);
|
||||
manualDropbox(path);
|
||||
});
|
||||
};
|
||||
|
||||
return fileManager;
|
||||
|
152
js/gdrive-provider.js
Normal file
152
js/gdrive-provider.js
Normal file
@ -0,0 +1,152 @@
|
||||
define(["jquery", "google-helper", "underscore"], function($, googleHelper) {
|
||||
|
||||
// Dependencies
|
||||
var core = undefined;
|
||||
var fileManager = undefined;
|
||||
|
||||
var gdriveProvider = {
|
||||
providerType: PROVIDER_TYPE_SYNC_FLAG | PROVIDER_TYPE_PUBLISH_FLAG,
|
||||
providerId: PROVIDER_GDRIVE,
|
||||
providerName: "Gdrive",
|
||||
defaultPublishFormat: "template"
|
||||
};
|
||||
|
||||
function createSyncAttributes(id, etag, content, title) {
|
||||
var syncAttributes = {};
|
||||
syncAttributes.provider = PROVIDER_GDRIVE;
|
||||
syncAttributes.id = id;
|
||||
syncAttributes.etag = etag;
|
||||
syncAttributes.contentCRC = core.crc32(content);
|
||||
syncAttributes.titleCRC = core.crc32(title);
|
||||
var syncIndex = "sync." + PROVIDER_GDRIVE + "." + file.id;
|
||||
localStorage[syncIndex] = JSON.stringify(syncAttributes);
|
||||
return syncIndex;
|
||||
}
|
||||
|
||||
function importFilesFromIds(ids) {
|
||||
googleHelper.downloadMetadata(ids, function(error, result) {
|
||||
if(error) {
|
||||
return;
|
||||
}
|
||||
googleHelper.downloadContent(result, function(error, result) {
|
||||
if(error) {
|
||||
return;
|
||||
}
|
||||
_.each(result, function(file) {
|
||||
var syncIndex = createSyncAttributes(file.id, file.etag, file.content, file.title);
|
||||
var fileIndex = fileManager.createFile(file.title, file.content, [syncIndex]);
|
||||
fileManager.selectFile(fileIndex);
|
||||
core.showMessage('"' + file.title + '" imported successfully from Google Drive.');
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
gdriveProvider.importFiles = function() {
|
||||
googleHelper.picker(function(error, ids) {
|
||||
if(ids === undefined) {
|
||||
return;
|
||||
}
|
||||
var importIds = [];
|
||||
_.each(ids, function(id) {
|
||||
var syncIndex = "sync." + PROVIDER_GDRIVE + "." + id;
|
||||
var fileIndex = fileManager.getFileIndexFromSync(syncIndex);
|
||||
if(fileIndex !== undefined) {
|
||||
var title = localStorage[fileIndex + ".title"];
|
||||
core.showError('"' + title + '" was already imported');
|
||||
return;
|
||||
}
|
||||
importIds.push(id);
|
||||
});
|
||||
importFilesFromIds(importIds);
|
||||
});
|
||||
};
|
||||
|
||||
gdriveProvider.exportFile = function(event, title, content, callback) {
|
||||
googleHelper.upload(undefined, undefined, title, content, function(error, result) {
|
||||
if (error) {
|
||||
callback(error);
|
||||
return;
|
||||
}
|
||||
var syncIndex = createSyncAttributes(result.id, result.etag, content, title);
|
||||
callback(undefined, syncIndex);
|
||||
});
|
||||
};
|
||||
|
||||
gdriveProvider.exportManual = function(event, title, content, callback) {
|
||||
var id = core.getInputValue($("#input-sync-manual-gdrive-id"), event);
|
||||
if(!id) {
|
||||
return;
|
||||
}
|
||||
// Check that file is not synchronized with an other one
|
||||
var syncIndex = "sync." + PROVIDER_GDRIVE + "." + id;
|
||||
var fileIndex = fileManager.getFileIndexFromSync(syncIndex);
|
||||
if(fileIndex !== undefined) {
|
||||
var existingTitle = localStorage[fileIndex + ".title"];
|
||||
core.showError('File ID is already synchronized with "' + existingTitle + '"');
|
||||
callback(true);
|
||||
return;
|
||||
}
|
||||
googleHelper.upload(id, undefined, title, content, function(error, result) {
|
||||
if (error) {
|
||||
callback(error);
|
||||
return;
|
||||
}
|
||||
var syncIndex = createSyncAttributes(result.id, result.etag, content, title);
|
||||
callback(undefined, syncIndex);
|
||||
});
|
||||
};
|
||||
|
||||
gdriveProvider.publish = function(publishAttributes, title, content, callback) {
|
||||
googleHelper.upload(publishAttributes.fileId, undefined, title, content, callback);
|
||||
};
|
||||
|
||||
gdriveProvider.newPublishAttributes = function(event) {
|
||||
var publishAttributes = {};
|
||||
publishAttributes.fileId = $("#input-publish-gdrive-fileid").val() || undefined;
|
||||
if(event.isPropagationStopped()) {
|
||||
return undefined;
|
||||
}
|
||||
return publishAttributes;
|
||||
};
|
||||
|
||||
gdriveProvider.init = function(coreModule, fileManagerModule) {
|
||||
core = coreModule;
|
||||
fileManager = fileManagerModule;
|
||||
|
||||
var state = localStorage["sync.gdrive.state"];
|
||||
if(state === undefined) {
|
||||
return;
|
||||
}
|
||||
localStorage.removeItem("sync.gdrive.state");
|
||||
state = JSON.parse(state);
|
||||
if (state.action == "create") {
|
||||
googleHelper.upload(undefined, state.folderId, GDRIVE_DEFAULT_FILE_TITLE,
|
||||
"", function(error, file) {
|
||||
if(error) {
|
||||
return;
|
||||
}
|
||||
var syncIndex = createSyncAttributes(file.id, file.etag, file.content, file.title);
|
||||
var fileIndex = fileManager.createFile(file.title, file.content, [syncIndex]);
|
||||
fileManager.selectFile(fileIndex);
|
||||
core.showMessage('"' + file.title + '" created successfully on Google Drive.');
|
||||
});
|
||||
}
|
||||
else if (state.action == "open") {
|
||||
var importIds = [];
|
||||
_.each(state.ids, function(id) {
|
||||
var syncIndex = "sync." + PROVIDER_GDRIVE + "." + id;
|
||||
var fileIndex = fileManager.getFileIndexFromSync(syncIndex);
|
||||
if(fileIndex !== undefined) {
|
||||
fileManager.selectFile(fileIndex);
|
||||
}
|
||||
else {
|
||||
importIds.push(id);
|
||||
}
|
||||
});
|
||||
importFilesFromIds(importIds);
|
||||
}
|
||||
};
|
||||
|
||||
return gdriveProvider;
|
||||
});
|
@ -1,4 +1,4 @@
|
||||
define(["jquery", "async-runner"], function($, asyncTaskRunner) {
|
||||
define(["jquery", "async-runner"], function($, asyncRunner) {
|
||||
|
||||
// Dependencies
|
||||
var core = undefined;
|
||||
@ -9,18 +9,17 @@ define(["jquery", "async-runner"], function($, asyncTaskRunner) {
|
||||
var githubHelper = {};
|
||||
|
||||
// Try to connect github by downloading js file
|
||||
function connect(callback) {
|
||||
function connect(task) {
|
||||
callback = callback || core.doNothing;
|
||||
var asyncTask = {};
|
||||
asyncTask.run = function() {
|
||||
task.onRun(function() {
|
||||
if(core.isOffline === true) {
|
||||
connected = false;
|
||||
core.showMessage("Operation not available in offline mode.");
|
||||
asyncTask.error();
|
||||
task.error();
|
||||
return;
|
||||
}
|
||||
if (connected === true) {
|
||||
asyncTask.success();
|
||||
task.chain();
|
||||
return;
|
||||
}
|
||||
$.ajax({
|
||||
@ -28,159 +27,138 @@ define(["jquery", "async-runner"], function($, asyncTaskRunner) {
|
||||
dataType : "script", timeout : AJAX_TIMEOUT
|
||||
}).done(function() {
|
||||
connected = true;
|
||||
asyncTask.success();
|
||||
task.chain();
|
||||
}).fail(function() {
|
||||
asyncTask.error();
|
||||
});
|
||||
};
|
||||
asyncTask.onSuccess = function() {
|
||||
callback();
|
||||
};
|
||||
asyncTask.onError = function() {
|
||||
core.setOffline();
|
||||
callback();
|
||||
};
|
||||
asyncTaskRunner.addTask(asyncTask);
|
||||
task.error(new Error("Network timeout"));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Try to authenticate with Oauth
|
||||
function authenticate(callback, immediate) {
|
||||
callback = callback || core.doNothing;
|
||||
connect(function() {
|
||||
if (connected === false) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
|
||||
var intervalId = undefined;
|
||||
function authenticate(task) {
|
||||
var authWindow = undefined;
|
||||
var token = localStorage["githubToken"];
|
||||
var errorMsg = "Access to GitHub is not authorized.";
|
||||
var asyncTask = {};
|
||||
asyncTask.run = function() {
|
||||
var intervalId = undefined;
|
||||
task.onRun(function() {
|
||||
if (github !== undefined) {
|
||||
asyncTask.success();
|
||||
task.chain();
|
||||
return;
|
||||
}
|
||||
|
||||
var token = localStorage["githubToken"];
|
||||
if(token !== undefined) {
|
||||
github = new Github({
|
||||
token: token,
|
||||
auth: "oauth"
|
||||
});
|
||||
asyncTask.success();
|
||||
task.chain();
|
||||
return;
|
||||
}
|
||||
if(immediate === true) {
|
||||
core.showError();
|
||||
asyncTask.error();
|
||||
return;
|
||||
}
|
||||
// We add time for user to enter his credentials
|
||||
asyncTask.timeout = AUTH_POPUP_TIMEOUT;
|
||||
core.showMessage("Please make sure the Github authorization popup is not blocked by your browser.");
|
||||
// We add time for user to enter his credentials
|
||||
task.timeout = ASYNC_TASK_LONG_TIMEOUT;
|
||||
var code = undefined;
|
||||
function getCode() {
|
||||
localStorage.removeItem("githubCode");
|
||||
authWindow = core.popupWindow(
|
||||
'github-oauth-client.html?client_id=' + GITHUB_CLIENT_ID,
|
||||
'stackedit-github-oauth', 960, 600);
|
||||
authWindow.focus();
|
||||
intervalId = setInterval(function() {
|
||||
var code = localStorage["githubCode"];
|
||||
if(authWindow.closed === true || code !== undefined) {
|
||||
localStorage.removeItem("githubCode");
|
||||
if(authWindow.closed === true) {
|
||||
clearInterval(intervalId);
|
||||
authWindow = undefined;
|
||||
intervalId = undefined;
|
||||
code = localStorage["githubCode"];
|
||||
if(code === undefined) {
|
||||
asyncTask.error();
|
||||
task.error();
|
||||
return;
|
||||
}
|
||||
$.getJSON(GATEKEEPER_URL + "authenticate/" + code, function(data) {
|
||||
if(data.token !== undefined) {
|
||||
localStorage["githubToken"] = data.token;
|
||||
asyncTask.success();
|
||||
}
|
||||
else {
|
||||
asyncTask.error();
|
||||
localStorage.removeItem("githubCode");
|
||||
task.chain(getToken);
|
||||
}
|
||||
});
|
||||
}
|
||||
}, 500);
|
||||
};
|
||||
asyncTask.onSuccess = function() {
|
||||
if(intervalId !== undefined) {
|
||||
clearInterval(intervalId);
|
||||
function getToken() {
|
||||
$.getJSON(GATEKEEPER_URL + "authenticate/" + code, function(data) {
|
||||
if(data.token !== undefined) {
|
||||
token = data.token;
|
||||
localStorage["githubToken"] = token;
|
||||
github = new Github({
|
||||
token: token,
|
||||
auth: "oauth"
|
||||
});
|
||||
task.chain();
|
||||
}
|
||||
if (github !== undefined) {
|
||||
callback();
|
||||
return;
|
||||
else {
|
||||
task.error();
|
||||
}
|
||||
authenticate(callback, true);
|
||||
};
|
||||
asyncTask.onError = function() {
|
||||
});
|
||||
}
|
||||
task.chain(getCode);
|
||||
});
|
||||
task.onError(function() {
|
||||
if(intervalId !== undefined) {
|
||||
clearInterval(intervalId);
|
||||
}
|
||||
if(authWindow !== undefined) {
|
||||
authWindow.close();
|
||||
}
|
||||
core.showError(errorMsg);
|
||||
callback();
|
||||
};
|
||||
asyncTaskRunner.addTask(asyncTask);
|
||||
});
|
||||
}
|
||||
|
||||
githubHelper.upload = function(reponame, branch, path, content, commitMsg, callback) {
|
||||
callback = callback || core.doNothing;
|
||||
authenticate(function() {
|
||||
if (github === undefined) {
|
||||
callback("error");
|
||||
return;
|
||||
}
|
||||
|
||||
var error = undefined;
|
||||
var asyncTask = {};
|
||||
asyncTask.run = function() {
|
||||
var task = asyncRunner.createTask();
|
||||
connect(task);
|
||||
authenticate(task);
|
||||
task.onRun(function() {
|
||||
var userLogin = undefined;
|
||||
function getUserLogin() {
|
||||
var user = github.getUser();
|
||||
user.show(undefined, function(err, result) {
|
||||
if(err) {
|
||||
error = err.error;
|
||||
asyncTask.error();
|
||||
task.error(err);
|
||||
return;
|
||||
}
|
||||
var repo = github.getRepo(result.login, reponame);
|
||||
userLogin = result.login;
|
||||
task.chain(write);
|
||||
});
|
||||
}
|
||||
function write() {
|
||||
var repo = github.getRepo(userLogin, reponame);
|
||||
repo.write(branch, path, content, commitMsg, function(err) {
|
||||
if(err) {
|
||||
error = err.error;
|
||||
asyncTask.error();
|
||||
task.error(err);
|
||||
return;
|
||||
}
|
||||
asyncTask.success();
|
||||
task.chain();
|
||||
});
|
||||
});
|
||||
};
|
||||
asyncTask.onSuccess = function() {
|
||||
callback(error);
|
||||
};
|
||||
asyncTask.onError = function() {
|
||||
if(error !== undefined) {
|
||||
console.error(error);
|
||||
}
|
||||
task.chain(getUserLogin);
|
||||
});
|
||||
task.onSuccess(function() {
|
||||
callback();
|
||||
});
|
||||
task.onError(function(err) {
|
||||
var errorMsg = "Could not publish on GitHub.";
|
||||
if(error === 401 || error === 403) {
|
||||
if(err !== undefined) {
|
||||
console.error(err);
|
||||
if(err.error === 401 || err.error === 403) {
|
||||
github = undefined;
|
||||
// Token must be renewed
|
||||
localStorage.removeItem("githubToken");
|
||||
errorMsg = "Access to GitHub is not authorized.";
|
||||
}
|
||||
else if(error === 0) {
|
||||
else if(err.error === 0) {
|
||||
connected = false;
|
||||
github = undefined;
|
||||
core.setOffline();
|
||||
}
|
||||
}
|
||||
core.showError(errorMsg);
|
||||
callback(error);
|
||||
};
|
||||
asyncTaskRunner.addTask(asyncTask);
|
||||
callback(errorMsg);
|
||||
});
|
||||
asyncRunner.addTask(task);
|
||||
};
|
||||
|
||||
githubHelper.init = function(coreModule) {
|
||||
|
@ -1,8 +1,7 @@
|
||||
define(["jquery", "async-runner"], function($, asyncTaskRunner) {
|
||||
define(["jquery", "async-runner"], function($, asyncRunner) {
|
||||
|
||||
// Dependencies
|
||||
var core = undefined;
|
||||
var fileManager = undefined;
|
||||
|
||||
var connected = false;
|
||||
var authenticated = false;
|
||||
@ -10,109 +9,83 @@ define(["jquery", "async-runner"], function($, asyncTaskRunner) {
|
||||
var googleHelper = {};
|
||||
|
||||
// Try to connect Gdrive by downloading client.js
|
||||
function connect(callback) {
|
||||
callback = callback || core.doNothing;
|
||||
var asyncTask = {};
|
||||
asyncTask.run = function() {
|
||||
function connect(task) {
|
||||
task.onRun(function() {
|
||||
if(core.isOffline === true) {
|
||||
connected = false;
|
||||
core.showMessage("Operation not available in offline mode.");
|
||||
asyncTask.error();
|
||||
task.error();
|
||||
return;
|
||||
}
|
||||
if (connected === true) {
|
||||
asyncTask.success();
|
||||
task.chain();
|
||||
return;
|
||||
}
|
||||
delayedFunction = function() {
|
||||
asyncTask.success();
|
||||
connected = true;
|
||||
task.chain();
|
||||
};
|
||||
$.ajax({
|
||||
url : "https://apis.google.com/js/client.js?onload=runDelayedFunction",
|
||||
dataType : "script", timeout : AJAX_TIMEOUT
|
||||
}).fail(function() {
|
||||
asyncTask.error();
|
||||
});
|
||||
};
|
||||
asyncTask.onSuccess = function() {
|
||||
connected = true;
|
||||
callback();
|
||||
};
|
||||
asyncTask.onError = function() {
|
||||
core.setOffline();
|
||||
callback();
|
||||
};
|
||||
asyncTaskRunner.addTask(asyncTask);
|
||||
task.error(new Error("Network timeout"));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// Try to authenticate with Oauth
|
||||
function authenticate(callback, immediate) {
|
||||
callback = callback || core.doNothing;
|
||||
if (immediate === undefined) {
|
||||
immediate = true;
|
||||
}
|
||||
connect(function() {
|
||||
if (connected === false) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
|
||||
var asyncTask = {};
|
||||
// If not immediate we add time for user to enter his credentials
|
||||
if (immediate === false) {
|
||||
asyncTask.timeout = AUTH_POPUP_TIMEOUT;
|
||||
}
|
||||
asyncTask.run = function() {
|
||||
function authenticate(task) {
|
||||
task.onRun(function() {
|
||||
if (authenticated === true) {
|
||||
asyncTask.success();
|
||||
task.chain();
|
||||
return;
|
||||
}
|
||||
var immediate = true;
|
||||
function localAuthenticate() {
|
||||
if (immediate === false) {
|
||||
core.showMessage("Please make sure the Google authorization popup is not blocked by your browser.");
|
||||
// If not immediate we add time for user to enter his credentials
|
||||
task.timeout = ASYNC_TASK_LONG_TIMEOUT;
|
||||
}
|
||||
gapi.auth.authorize({ 'client_id' : GOOGLE_CLIENT_ID,
|
||||
'scope' : GOOGLE_SCOPES, 'immediate' : immediate }, function(
|
||||
authResult) {
|
||||
gapi.client.load('drive', 'v2', function() {
|
||||
if (!authResult || authResult.error) {
|
||||
asyncTask.error();
|
||||
return;
|
||||
}
|
||||
authenticated = true;
|
||||
asyncTask.success();
|
||||
});
|
||||
});
|
||||
};
|
||||
asyncTask.onSuccess = function() {
|
||||
callback();
|
||||
};
|
||||
asyncTask.onError = function() {
|
||||
// If immediate did not work retry without immediate flag
|
||||
if (connected === true && immediate === true) {
|
||||
authenticate(callback, false);
|
||||
immediate = false;
|
||||
task.chain(localAuthenticate);
|
||||
return;
|
||||
}
|
||||
callback();
|
||||
};
|
||||
asyncTaskRunner.addTask(asyncTask);
|
||||
// Error
|
||||
var errorMsg = "Access to Google account is not authorized.";
|
||||
core.showError(errorMsg);
|
||||
task.error(new Error(errorMsg));
|
||||
return;
|
||||
}
|
||||
// Success
|
||||
authenticated = true;
|
||||
task.chain();
|
||||
});
|
||||
});
|
||||
}
|
||||
task.chain(localAuthenticate);
|
||||
});
|
||||
}
|
||||
|
||||
googleHelper.upload = function(fileId, parentId, title, content, callback) {
|
||||
callback = callback || core.doNothing;
|
||||
authenticate(function() {
|
||||
if (connected === false) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
|
||||
var fileSyncIndex = undefined;
|
||||
var asyncTask = {};
|
||||
asyncTask.run = function() {
|
||||
var result = undefined;
|
||||
var task = asyncRunner.createTask();
|
||||
connect(task);
|
||||
authenticate(task);
|
||||
task.onRun(function() {
|
||||
var boundary = '-------314159265358979323846';
|
||||
var delimiter = "\r\n--" + boundary + "\r\n";
|
||||
var close_delim = "\r\n--" + boundary + "--";
|
||||
|
||||
var contentType = 'text/x-markdown';
|
||||
var metadata = { title : title, mimeType : contentType };
|
||||
if (parentId !== undefined) {
|
||||
@ -154,9 +127,8 @@ define(["jquery", "async-runner"], function($, asyncTaskRunner) {
|
||||
request.execute(function(response) {
|
||||
if (response && response.id) {
|
||||
// Upload success
|
||||
fileSyncIndex = SYNC_PROVIDER_GDRIVE + response.id;
|
||||
localStorage[fileSyncIndex + ".etag"] = response.etag;
|
||||
asyncTask.success();
|
||||
result = response;
|
||||
task.chain();
|
||||
return;
|
||||
}
|
||||
var error = response.error;
|
||||
@ -171,35 +143,44 @@ define(["jquery", "async-runner"], function($, asyncTaskRunner) {
|
||||
error = 'Conflict on file ID "' + fileId + '". Please restart the synchronization.';
|
||||
}
|
||||
}
|
||||
handleError(error, asyncTask, callback);
|
||||
handleError(error, task, callback);
|
||||
});
|
||||
};
|
||||
asyncTask.onSuccess = function() {
|
||||
callback(fileSyncIndex);
|
||||
};
|
||||
asyncTask.onError = function() {
|
||||
callback();
|
||||
};
|
||||
asyncTaskRunner.addTask(asyncTask);
|
||||
});
|
||||
task.onSuccess(function() {
|
||||
callback(undefined, result);
|
||||
});
|
||||
task.onError(function(error) {
|
||||
callback(error);
|
||||
});
|
||||
asyncRunner.addTask(task);
|
||||
};
|
||||
|
||||
googleHelper.checkUpdates = function(lastChangeId, callback) {
|
||||
callback = callback || core.doNothing;
|
||||
authenticate(function() {
|
||||
if (connected === false) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
|
||||
var changes = [];
|
||||
var newChangeId = lastChangeId || 0;
|
||||
function retrievePageOfChanges(request) {
|
||||
var task = asyncRunner.createTask();
|
||||
connect(task);
|
||||
authenticate(task);
|
||||
task.onRun(function() {
|
||||
var nextPageToken = undefined;
|
||||
var asyncTask = {};
|
||||
asyncTask.run = function() {
|
||||
function retrievePageOfChanges() {
|
||||
var request = undefined;
|
||||
if(nextPageToken === undefined) {
|
||||
request = gapi.client.drive.changes
|
||||
.list({ 'startChangeId' : newChangeId + 1 });
|
||||
}
|
||||
else {
|
||||
request = gapi.client.drive.changes
|
||||
.list({ 'pageToken' : nextPageToken });
|
||||
}
|
||||
|
||||
request.execute(function(response) {
|
||||
if (response && response.largestChangeId) {
|
||||
if (!response || !response.largestChangeId) {
|
||||
// Handle error
|
||||
handleError(response.error, task, callback);
|
||||
return;
|
||||
}
|
||||
// Retrieve success
|
||||
newChangeId = response.largestChangeId;
|
||||
nextPageToken = response.nextPageToken;
|
||||
@ -214,50 +195,38 @@ define(["jquery", "async-runner"], function($, asyncTaskRunner) {
|
||||
}
|
||||
}
|
||||
}
|
||||
asyncTask.success();
|
||||
return;
|
||||
}
|
||||
// Handle error
|
||||
handleError(response.error, asyncTask, callback);
|
||||
});
|
||||
};
|
||||
asyncTask.onSuccess = function() {
|
||||
if (nextPageToken !== undefined) {
|
||||
request = gapi.client.drive.changes
|
||||
.list({ 'pageToken' : nextPageToken });
|
||||
retrievePageOfChanges(request);
|
||||
} else {
|
||||
callback(changes, newChangeId);
|
||||
task.chain(retrievePageOfChanges);
|
||||
}
|
||||
};
|
||||
asyncTask.onError = function() {
|
||||
callback();
|
||||
};
|
||||
asyncTaskRunner.addTask(asyncTask);
|
||||
else {
|
||||
task.chain();
|
||||
}
|
||||
var initialRequest = gapi.client.drive.changes
|
||||
.list({ 'startChangeId' : newChangeId + 1 });
|
||||
retrievePageOfChanges(initialRequest);
|
||||
});
|
||||
}
|
||||
task.chain(retrievePageOfChanges);
|
||||
});
|
||||
task.onSuccess(function() {
|
||||
callback(undefined, changes, newChangeId);
|
||||
});
|
||||
task.onError(function(error) {
|
||||
callback(error);
|
||||
});
|
||||
asyncRunner.addTask(task);
|
||||
};
|
||||
|
||||
googleHelper.downloadMetadata = function(ids, callback, result) {
|
||||
googleHelper.downloadMetadata = function(ids, callback) {
|
||||
callback = callback || core.doNothing;
|
||||
result = result || [];
|
||||
var result = [];
|
||||
var task = asyncRunner.createTask();
|
||||
connect(task);
|
||||
authenticate(task);
|
||||
task.onRun(function() {
|
||||
function recursiveDownloadMetadata() {
|
||||
if(ids.length === 0) {
|
||||
callback(result);
|
||||
task.chain();
|
||||
return;
|
||||
}
|
||||
|
||||
authenticate(function() {
|
||||
if (connected === false) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
|
||||
var id = ids.pop();
|
||||
var asyncTask = {};
|
||||
asyncTask.run = function() {
|
||||
var token = gapi.auth.getToken();
|
||||
var headers = {
|
||||
Authorization : token ? "Bearer " + token.access_token: null
|
||||
@ -269,7 +238,7 @@ define(["jquery", "async-runner"], function($, asyncTaskRunner) {
|
||||
timeout : AJAX_TIMEOUT
|
||||
}).done(function(data, textStatus, jqXHR) {
|
||||
result.push(data);
|
||||
asyncTask.success();
|
||||
task.chain(recursiveDownloadMetadata);
|
||||
}).fail(function(jqXHR) {
|
||||
var error = {
|
||||
code: jqXHR.status,
|
||||
@ -279,27 +248,34 @@ define(["jquery", "async-runner"], function($, asyncTaskRunner) {
|
||||
if(error.code === 404) {
|
||||
error = 'File ID "' + id + '" does not exist on Google Drive.';
|
||||
}
|
||||
handleError(error, asyncTask, callback);
|
||||
handleError(error, task, callback);
|
||||
});
|
||||
};
|
||||
asyncTask.onSuccess = function() {
|
||||
googleHelper.downloadMetadata(ids, callback, result);
|
||||
};
|
||||
asyncTask.onError = function() {
|
||||
callback();
|
||||
};
|
||||
asyncTaskRunner.addTask(asyncTask);
|
||||
}
|
||||
task.chain(recursiveDownloadMetadata);
|
||||
});
|
||||
task.onSuccess(function() {
|
||||
callback(undefined, result);
|
||||
});
|
||||
task.onError(function(error) {
|
||||
callback(error);
|
||||
});
|
||||
asyncRunner.addTask(task);
|
||||
};
|
||||
|
||||
googleHelper.downloadContent = function(objects, callback, result) {
|
||||
googleHelper.downloadContent = function(objects, callback) {
|
||||
callback = callback || core.doNothing;
|
||||
result = result || [];
|
||||
var result = [];
|
||||
var task = asyncRunner.createTask();
|
||||
// Add some time for user to choose his files
|
||||
task.timeout = ASYNC_TASK_LONG_TIMEOUT;
|
||||
connect(task);
|
||||
authenticate(task);
|
||||
task.onRun(function() {
|
||||
function recursiveDownloadContent() {
|
||||
if(objects.length === 0) {
|
||||
callback(result);
|
||||
task.chain();
|
||||
return;
|
||||
}
|
||||
|
||||
var object = objects.pop();
|
||||
result.push(object);
|
||||
var file = undefined;
|
||||
@ -312,18 +288,9 @@ define(["jquery", "async-runner"], function($, asyncTaskRunner) {
|
||||
file = object.file;
|
||||
}
|
||||
if(!file) {
|
||||
this.downloadContent(objects, callback, result);
|
||||
task.chain(recursiveDownloadContent);
|
||||
return;
|
||||
}
|
||||
|
||||
authenticate(function() {
|
||||
if (connected === false) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
|
||||
var asyncTask = {};
|
||||
asyncTask.run = function() {
|
||||
var token = gapi.auth.getToken();
|
||||
var headers = {
|
||||
Authorization : token ? "Bearer " + token.access_token: null
|
||||
@ -335,35 +302,29 @@ define(["jquery", "async-runner"], function($, asyncTaskRunner) {
|
||||
timeout : AJAX_TIMEOUT
|
||||
}).done(function(data, textStatus, jqXHR) {
|
||||
file.content = data;
|
||||
asyncTask.success();
|
||||
task.chain(recursiveDownloadContent);
|
||||
}).fail(function(jqXHR) {
|
||||
var error = {
|
||||
code: jqXHR.status,
|
||||
message: jqXHR.statusText
|
||||
};
|
||||
// Handle error
|
||||
handleError(error, asyncTask, callback);
|
||||
handleError(error, task, callback);
|
||||
});
|
||||
};
|
||||
asyncTask.onSuccess = function() {
|
||||
googleHelper.downloadContent(objects, callback, result);
|
||||
};
|
||||
asyncTask.onError = function() {
|
||||
callback();
|
||||
};
|
||||
asyncTaskRunner.addTask(asyncTask);
|
||||
}
|
||||
task.chain(recursiveDownloadContent);
|
||||
});
|
||||
task.onSuccess(function() {
|
||||
callback(undefined, result);
|
||||
});
|
||||
task.onError(function(error) {
|
||||
callback(error);
|
||||
});
|
||||
asyncRunner.addTask(task);
|
||||
};
|
||||
|
||||
function handleError(error, asyncTask, callback, serviceName) {
|
||||
serviceName = serviceName || "Google Drive";
|
||||
function handleError(error, task, callback) {
|
||||
var errorMsg = undefined;
|
||||
asyncTask.onError = function() {
|
||||
if (errorMsg !== undefined) {
|
||||
core.showError(errorMsg);
|
||||
}
|
||||
callback();
|
||||
};
|
||||
if (error) {
|
||||
console.error(error);
|
||||
// Try to analyze the error
|
||||
@ -371,38 +332,31 @@ define(["jquery", "async-runner"], function($, asyncTaskRunner) {
|
||||
errorMsg = error;
|
||||
}
|
||||
else if (error.code >= 500 && error.code < 600) {
|
||||
errorMsg = serviceName + " is not accessible.";
|
||||
// Retry as described in Google's best practices
|
||||
asyncTask.retry();
|
||||
task.retry();
|
||||
return;
|
||||
} else if (error.code === 401 || error.code === 403) {
|
||||
authenticated = false;
|
||||
errorMsg = "Access to " + serviceName + " is not authorized.";
|
||||
task.retry();
|
||||
return;
|
||||
} else if (error.code <= 0) {
|
||||
connected = false;
|
||||
authenticated = false;
|
||||
core.setOffline();
|
||||
} else {
|
||||
errorMsg = serviceName + " error (" + error.code + ": "
|
||||
errorMsg = "Google error (" + error.code + ": "
|
||||
+ error.message + ").";
|
||||
}
|
||||
core.showError(errorMsg);
|
||||
}
|
||||
asyncTask.error();
|
||||
task.error(new Error(errorMsg));
|
||||
}
|
||||
|
||||
var pickerLoaded = false;
|
||||
function loadPicker(callback) {
|
||||
connect(function() {
|
||||
if (connected === false) {
|
||||
pickerLoaded = false;
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
|
||||
var asyncTask = {};
|
||||
asyncTask.run = function() {
|
||||
function loadPicker(task) {
|
||||
task.onRun(function() {
|
||||
if (pickerLoaded === true) {
|
||||
asyncTask.success();
|
||||
task.chain();
|
||||
return;
|
||||
}
|
||||
$.ajax({
|
||||
@ -410,30 +364,23 @@ define(["jquery", "async-runner"], function($, asyncTaskRunner) {
|
||||
data : {key: GOOGLE_KEY},
|
||||
dataType : "script", timeout : AJAX_TIMEOUT
|
||||
}).done(function() {
|
||||
asyncTask.success();
|
||||
}).fail(function() {
|
||||
asyncTask.error();
|
||||
});
|
||||
};
|
||||
asyncTask.onSuccess = function() {
|
||||
google.load('picker', '1', {callback: callback});
|
||||
google.load('picker', '1', {callback: task.chain});
|
||||
pickerLoaded = true;
|
||||
};
|
||||
asyncTask.onError = function() {
|
||||
}).fail(function() {
|
||||
core.setOffline();
|
||||
callback();
|
||||
};
|
||||
asyncTaskRunner.addTask(asyncTask);
|
||||
task.error(new Error("Network timeout"));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
googleHelper.picker = function(callback) {
|
||||
callback = callback || core.doNothing;
|
||||
loadPicker(function() {
|
||||
if (pickerLoaded === false) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
var ids = [];
|
||||
var picker = undefined;
|
||||
var task = asyncRunner.createTask();
|
||||
connect(task);
|
||||
loadPicker(task);
|
||||
task.onRun(function() {
|
||||
var view = new google.picker.View(google.picker.ViewId.DOCS);
|
||||
view.setMimeTypes("text/x-markdown,text/plain");
|
||||
var pickerBuilder = new google.picker.PickerBuilder();
|
||||
@ -449,88 +396,46 @@ define(["jquery", "async-runner"], function($, asyncTaskRunner) {
|
||||
pickerBuilder.setCallback(function(data) {
|
||||
if (data.action == google.picker.Action.PICKED ||
|
||||
data.action == google.picker.Action.CANCEL) {
|
||||
var ids = [];
|
||||
if(data.action == google.picker.Action.PICKED) {
|
||||
for(var i=0; i<data.docs.length; i++) {
|
||||
ids.push(data.docs[i].id);
|
||||
}
|
||||
}
|
||||
$(".modal-backdrop, .picker").remove();
|
||||
callback(ids);
|
||||
task.chain();
|
||||
}
|
||||
});
|
||||
var picker = pickerBuilder.build();
|
||||
picker = pickerBuilder.build();
|
||||
$("body").append($("<div>").addClass("modal-backdrop").click(function() {
|
||||
picker.setVisible(false);
|
||||
$(".modal-backdrop, .picker").remove();
|
||||
callback();
|
||||
task.chain();
|
||||
}));
|
||||
picker.setVisible(true);
|
||||
});
|
||||
};
|
||||
|
||||
googleHelper.importFiles = function(ids) {
|
||||
googleHelper.downloadMetadata(ids, function(result) {
|
||||
if(result === undefined) {
|
||||
return;
|
||||
}
|
||||
googleHelper.downloadContent(result, function(result) {
|
||||
if(result === undefined) {
|
||||
return;
|
||||
}
|
||||
for(var i=0; i<result.length; i++) {
|
||||
var file = result[i];
|
||||
fileSyncIndex = SYNC_PROVIDER_GDRIVE + file.id;
|
||||
localStorage[fileSyncIndex + ".etag"] = file.etag;
|
||||
var contentCRC = core.crc32(file.content);
|
||||
localStorage[fileSyncIndex + ".contentCRC"] = contentCRC;
|
||||
var titleCRC = core.crc32(file.title);
|
||||
localStorage[fileSyncIndex + ".titleCRC"] = titleCRC;
|
||||
var fileIndex = fileManager.createFile(file.title, file.content, [fileSyncIndex]);
|
||||
fileManager.selectFile(fileIndex);
|
||||
core.showMessage('"' + file.title + '" imported successfully from Google Drive.');
|
||||
}
|
||||
task.onSuccess(function() {
|
||||
callback(undefined, ids);
|
||||
});
|
||||
task.onError(function(error) {
|
||||
if(picker !== undefined) {
|
||||
picker.setVisible(false);
|
||||
$(".modal-backdrop, .picker").remove();
|
||||
task.chain();
|
||||
}
|
||||
callback(error);
|
||||
});
|
||||
asyncRunner.addTask(task);
|
||||
};
|
||||
|
||||
googleHelper.uploadBlogger = function(blogUrl, blogId, postId, title, content, callback) {
|
||||
authenticate(function() {
|
||||
if (connected === false) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
|
||||
var asyncTask = {};
|
||||
asyncTask.run = function() {
|
||||
var task = asyncRunner.createTask();
|
||||
connect(task);
|
||||
authenticate(task);
|
||||
task.onRun(function() {
|
||||
var token = gapi.auth.getToken();
|
||||
var headers = {
|
||||
Authorization : token ? "Bearer " + token.access_token: null
|
||||
};
|
||||
|
||||
function getBlogId(localCallback) {
|
||||
$.ajax({
|
||||
url : "https://www.googleapis.com/blogger/v3/blogs/byurl",
|
||||
data: { url: blogUrl },
|
||||
headers : headers,
|
||||
dataType : "json",
|
||||
timeout : AJAX_TIMEOUT
|
||||
}).done(function(blog, textStatus, jqXHR) {
|
||||
blogId = blog.id;
|
||||
localCallback();
|
||||
}).fail(function(jqXHR) {
|
||||
var error = {
|
||||
code: jqXHR.status,
|
||||
message: jqXHR.statusText
|
||||
};
|
||||
// Handle error
|
||||
if(error.code === 404) {
|
||||
error = 'Blog "' + blogUrl + '" not found on Blogger.';
|
||||
}
|
||||
handleError(error, asyncTask, callback, "Blogger");
|
||||
});
|
||||
}
|
||||
|
||||
function publish() {
|
||||
var url = "https://www.googleapis.com/blogger/v3/blogs/" + blogId + "/posts/";
|
||||
var data = {
|
||||
@ -556,7 +461,7 @@ define(["jquery", "async-runner"], function($, asyncTaskRunner) {
|
||||
timeout : AJAX_TIMEOUT
|
||||
}).done(function(post, textStatus, jqXHR) {
|
||||
postId = post.id;
|
||||
asyncTask.success();
|
||||
task.chain();
|
||||
}).fail(function(jqXHR) {
|
||||
var error = {
|
||||
code: jqXHR.status,
|
||||
@ -566,65 +471,47 @@ define(["jquery", "async-runner"], function($, asyncTaskRunner) {
|
||||
if(error.code === 404 && postId !== undefined) {
|
||||
error = 'Post ' + postId + ' not found on Blogger.';
|
||||
}
|
||||
handleError(error, asyncTask, callback, "Blogger");
|
||||
handleError(error, task, callback);
|
||||
});
|
||||
}
|
||||
|
||||
if(blogId === undefined) {
|
||||
getBlogId(publish);
|
||||
function getBlogId() {
|
||||
if(blogId !== undefined) {
|
||||
task.chain(publish);
|
||||
}
|
||||
else {
|
||||
publish();
|
||||
$.ajax({
|
||||
url : "https://www.googleapis.com/blogger/v3/blogs/byurl",
|
||||
data: { url: blogUrl },
|
||||
headers : headers,
|
||||
dataType : "json",
|
||||
timeout : AJAX_TIMEOUT
|
||||
}).done(function(blog, textStatus, jqXHR) {
|
||||
blogId = blog.id;
|
||||
task.chain(publish);
|
||||
}).fail(function(jqXHR) {
|
||||
var error = {
|
||||
code: jqXHR.status,
|
||||
message: jqXHR.statusText
|
||||
};
|
||||
// Handle error
|
||||
if(error.code === 404) {
|
||||
error = 'Blog "' + blogUrl + '" not found on Blogger.';
|
||||
}
|
||||
};
|
||||
asyncTask.onSuccess = function() {
|
||||
callback(blogId, postId);
|
||||
};
|
||||
asyncTask.onError = function() {
|
||||
callback();
|
||||
};
|
||||
asyncTaskRunner.addTask(asyncTask);
|
||||
handleError(error, task, callback);
|
||||
});
|
||||
}
|
||||
task.chain(getBlogId);
|
||||
});
|
||||
task.onSuccess = function() {
|
||||
callback(undefined, blogId, postId);
|
||||
};
|
||||
task.onError = function(error) {
|
||||
callback(error);
|
||||
};
|
||||
asyncRunner.addTask(task);
|
||||
};
|
||||
|
||||
googleHelper.init = function(coreModule, fileManagerModule) {
|
||||
googleHelper.init = function(coreModule) {
|
||||
core = coreModule;
|
||||
fileManager = fileManagerModule;
|
||||
var state = localStorage["sync.gdrive.state"];
|
||||
if(state === undefined) {
|
||||
return;
|
||||
}
|
||||
localStorage.removeItem("sync.gdrive.state");
|
||||
state = JSON.parse(state);
|
||||
if (state.action == "create") {
|
||||
googleHelper.upload(undefined, state.folderId, GDRIVE_DEFAULT_FILE_TITLE,
|
||||
"", function(fileSyncIndex) {
|
||||
if(fileSyncIndex === undefined) {
|
||||
return;
|
||||
}
|
||||
var contentCRC = core.crc32("");
|
||||
localStorage[fileSyncIndex + ".contentCRC"] = contentCRC;
|
||||
var titleCRC = core.crc32(GDRIVE_DEFAULT_FILE_TITLE);
|
||||
localStorage[fileSyncIndex + ".titleCRC"] = titleCRC;
|
||||
var fileIndex = fileManager.createFile(GDRIVE_DEFAULT_FILE_TITLE, "", [fileSyncIndex]);
|
||||
fileManager.selectFile(fileIndex);
|
||||
core.showMessage('"' + GDRIVE_DEFAULT_FILE_TITLE + '" created successfully on Google Drive.');
|
||||
});
|
||||
}
|
||||
else if (state.action == "open") {
|
||||
var ids = [];
|
||||
for(var i=0; i<state.ids.length; i++) {
|
||||
var id = state.ids[i];
|
||||
var fileSyncIndex = SYNC_PROVIDER_GDRIVE + id;
|
||||
var fileIndex = fileManager.getFileIndexFromSync(fileSyncIndex);
|
||||
if(fileIndex !== undefined) {
|
||||
fileManager.selectFile(fileIndex);
|
||||
} else {
|
||||
ids.push(id);
|
||||
}
|
||||
}
|
||||
googleHelper.importFiles(ids);
|
||||
}
|
||||
};
|
||||
|
||||
return googleHelper;
|
||||
|
@ -1,4 +1,4 @@
|
||||
define(["jquery", "github-provider", "blogger-provider", "dropbox-provider", "underscore"], function($) {
|
||||
define(["jquery", "github-provider", "blogger-provider", "dropbox-provider", "gdrive-provider", "underscore"], function($) {
|
||||
|
||||
// Dependencies
|
||||
var core = undefined;
|
||||
@ -183,8 +183,7 @@ define(["jquery", "github-provider", "blogger-provider", "dropbox-provider", "un
|
||||
$(".msg-no-publish").removeClass("hide");
|
||||
}
|
||||
_.each(publishIndexList, function(publishIndex) {
|
||||
var serializedObject = localStorage[publishIndex];
|
||||
var publishAttributes = JSON.parse(serializedObject);
|
||||
var publishAttributes = JSON.parse(localStorage[publishIndex]);
|
||||
var publishDesc = JSON.stringify(publishAttributes).replace(/{|}|"/g, "");
|
||||
lineElement = $(_.template(lineTemplate, {
|
||||
provider: providerMap[publishAttributes.provider],
|
||||
@ -203,7 +202,7 @@ define(["jquery", "github-provider", "blogger-provider", "dropbox-provider", "un
|
||||
|
||||
// Init each provider
|
||||
_.each(providerMap, function(provider) {
|
||||
provider.init(core);
|
||||
provider.init(core, fileManager);
|
||||
// Publish provider button
|
||||
$(".action-publish-" + provider.providerId).click(function() {
|
||||
initNewLocation(provider);
|
||||
|
@ -1,10 +1,16 @@
|
||||
define(["jquery", "google-helper", "dropbox-helper"], function($, googleHelper, dropboxHelper) {
|
||||
define(["jquery", "google-helper", "dropbox-helper", "dropbox-provider", "gdrive-provider"], function($, googleHelper, dropboxHelper) {
|
||||
var synchronizer = {};
|
||||
|
||||
// Dependencies
|
||||
var core = undefined;
|
||||
var fileManager = undefined;
|
||||
|
||||
// Create a map with providerName: providerObject
|
||||
var providerMap = _.chain(arguments)
|
||||
.map(function(argument) {
|
||||
return argument && argument.providerType & PROVIDER_TYPE_SYNC_FLAG && [argument.providerId, argument];
|
||||
}).compact().object().value();
|
||||
|
||||
// Used to know the providers we are connected to
|
||||
synchronizer.useGoogleDrive = false;
|
||||
synchronizer.useDropbox = false;
|
||||
@ -52,15 +58,12 @@ define(["jquery", "google-helper", "dropbox-helper"], function($, googleHelper,
|
||||
}
|
||||
|
||||
// Dequeue a synchronized location
|
||||
var fileSyncIndex = uploadFileSyncIndexList.pop();
|
||||
if(!fileSyncIndex) {
|
||||
locationUp(callback);
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip if CRC has not changed
|
||||
var syncIndex = uploadFileSyncIndexList.pop();
|
||||
var syncAttributes = JSON.parse(localStorage[fileSyncIndex]);
|
||||
=
|
||||
var syncContentCRC = localStorage[fileSyncIndex + ".contentCRC"];
|
||||
var syncTitleCRC = localStorage[fileSyncIndex + ".titleCRC"];
|
||||
// Skip if CRC has not changed
|
||||
if(uploadContentCRC == syncContentCRC && (syncTitleCRC === undefined || uploadTitleCRC == syncTitleCRC)) {
|
||||
locationUp(callback);
|
||||
return;
|
||||
@ -74,17 +77,15 @@ define(["jquery", "google-helper", "dropbox-helper"], function($, googleHelper,
|
||||
// Try to find the provider
|
||||
if (fileSyncIndex.indexOf(SYNC_PROVIDER_GDRIVE) === 0) {
|
||||
var id = fileSyncIndex.substring(SYNC_PROVIDER_GDRIVE.length);
|
||||
googleHelper.upload(id, undefined, uploadTitle, uploadContent, function(result) {
|
||||
if (result !== undefined) {
|
||||
googleHelper.upload(id, undefined, uploadTitle, uploadContent, function(error, result) {
|
||||
if(error) {
|
||||
// If error we abort the synchronization (retry later)
|
||||
callback(error);
|
||||
return;
|
||||
}
|
||||
localStorage[fileSyncIndex + ".contentCRC"] = uploadContentCRC;
|
||||
localStorage[fileSyncIndex + ".titleCRC"] = uploadTitleCRC;
|
||||
locationUp(callback);
|
||||
return;
|
||||
}
|
||||
|
||||
// If error we abort the synchronization (retry later)
|
||||
callback("abort");
|
||||
return;
|
||||
});
|
||||
} else if (fileSyncIndex.indexOf(SYNC_PROVIDER_DROPBOX) === 0) {
|
||||
var path = fileSyncIndex.substring(SYNC_PROVIDER_DROPBOX.length);
|
||||
@ -130,7 +131,7 @@ define(["jquery", "google-helper", "dropbox-helper"], function($, googleHelper,
|
||||
uploadTitleCRC = core.crc32(uploadTitle);
|
||||
|
||||
// Parse the list of synchronized locations associated to the document
|
||||
uploadFileSyncIndexList = fileSyncIndexes.split(";");
|
||||
uploadFileSyncIndexList = _.compact(fileSyncIndexes.split(";"));
|
||||
locationUp(callback);
|
||||
}
|
||||
|
||||
@ -156,14 +157,14 @@ define(["jquery", "google-helper", "dropbox-helper"], function($, googleHelper,
|
||||
}
|
||||
var lastChangeId = parseInt(localStorage[SYNC_PROVIDER_GDRIVE
|
||||
+ "lastChangeId"]);
|
||||
googleHelper.checkUpdates(lastChangeId, function(changes, newChangeId) {
|
||||
if (changes === undefined) {
|
||||
callback("error");
|
||||
googleHelper.checkUpdates(lastChangeId, function(error, changes, newChangeId) {
|
||||
if (error) {
|
||||
callback(error);
|
||||
return;
|
||||
}
|
||||
googleHelper.downloadContent(changes, function(changes) {
|
||||
if (changes === undefined) {
|
||||
callback("error");
|
||||
googleHelper.downloadContent(changes, function(error, changes) {
|
||||
if (error) {
|
||||
callback(error);
|
||||
return;
|
||||
}
|
||||
var updateFileTitles = false;
|
||||
@ -336,10 +337,83 @@ define(["jquery", "google-helper", "dropbox-helper"], function($, googleHelper,
|
||||
});
|
||||
};
|
||||
|
||||
// Used to populate the "Manage synchronization" dialog
|
||||
var lineTemplate = ['<div class="input-prepend input-append">',
|
||||
'<span class="add-on" title="<%= provider.providerName %>">',
|
||||
'<i class="icon-<%= provider.providerId %>"></i></span>',
|
||||
'<input class="span5" type="text" value="<%= syncDesc %>" disabled />',
|
||||
'</div>'].join("");
|
||||
var removeButtonTemplate = '<a class="btn" title="Remove this location"><i class="icon-trash"></i></a>';
|
||||
synchronizer.refreshManageSync = function() {
|
||||
var fileIndex = fileManager.getCurrentFileIndex();
|
||||
var syncIndexList = _.compact(localStorage[fileIndex + ".sync"].split(";"));
|
||||
$(".msg-no-sync, .msg-sync-list").addClass("hide");
|
||||
$("#manage-sync-list .input-append").remove();
|
||||
if (syncIndexList.length > 0) {
|
||||
$(".msg-sync-list").removeClass("hide");
|
||||
} else {
|
||||
$(".msg-no-sync").removeClass("hide");
|
||||
}
|
||||
_.each(syncIndexList, function(syncIndex) {
|
||||
var syncAttributes = JSON.parse(localStorage[syncIndex]);
|
||||
var syncDesc = syncAttributes.id || syncAttributes.path;
|
||||
lineElement = $(_.template(lineTemplate, {
|
||||
provider: providerMap[syncAttributes.provider],
|
||||
syncDesc: syncDesc
|
||||
}));
|
||||
lineElement.append($(removeButtonTemplate).click(function() {
|
||||
fileManager.removeSync(syncIndex);
|
||||
fileManager.updateFileTitles();
|
||||
}));
|
||||
$("#manage-sync-list").append(lineElement);
|
||||
});
|
||||
};
|
||||
|
||||
synchronizer.init = function(coreModule, fileManagerModule) {
|
||||
core = coreModule;
|
||||
fileManager = fileManagerModule;
|
||||
|
||||
// Init each provider
|
||||
_.each(providerMap, function(provider) {
|
||||
provider.init(core, fileManager);
|
||||
// Provider's import button
|
||||
$(".action-sync-import-" + provider.providerId).click(function(event) {
|
||||
provider.importFiles(event);
|
||||
});
|
||||
// Provider's export button
|
||||
$(".action-sync-export-" + provider.providerId).click(function(event) {
|
||||
var fileIndex = fileManager.getCurrentFileIndex();
|
||||
var title = localStorage[fileIndex + ".title"];
|
||||
var content = localStorage[fileIndex + ".content"];
|
||||
provider.exportFile(event, title, content, function(error, syncIndex) {
|
||||
if(error) {
|
||||
return;
|
||||
}
|
||||
localStorage[fileIndex + ".sync"] += syncIndex + ";";
|
||||
synchronizer.refreshManageSync();
|
||||
fileManager.updateFileTitles();
|
||||
core.showMessage('"' + title
|
||||
+ '" will now be synchronized on ' + provider.providerName + '.');
|
||||
});
|
||||
});
|
||||
// Provider's manual sync button
|
||||
$(".action-sync-manual-" + provider.providerId).click(function(event) {
|
||||
var fileIndex = fileManager.getCurrentFileIndex();
|
||||
var title = localStorage[fileIndex + ".title"];
|
||||
var content = localStorage[fileIndex + ".content"];
|
||||
provider.exportManual(event, title, content, function(error, syncIndex) {
|
||||
if(error) {
|
||||
return;
|
||||
}
|
||||
localStorage[fileIndex + ".sync"] += syncIndex + ";";
|
||||
synchronizer.refreshManageSync();
|
||||
fileManager.updateFileTitles();
|
||||
core.showMessage('"' + title
|
||||
+ '" will now be synchronized on ' + provider.providerName + '.');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
synchronizer.updateSyncButton();
|
||||
$(".action-force-sync").click(function() {
|
||||
if(!$(this).hasClass("disabled")) {
|
||||
|
Loading…
Reference in New Issue
Block a user