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