periodic down synchronization
This commit is contained in:
parent
dffc6e8829
commit
fef879fe50
@ -1 +1 @@
|
||||
CACHE MANIFEST
# v4
CACHE:
index.html
css/bootstrap.css
css/jgrowl.css
css/main.css
js/async-runner.js
js/base64.js
js/bootstrap.js
js/gdrive.js
js/jquery.jgrowl.js
js/jquery.js
js/jquery.layout.js
js/jquery-ui.custom.js
js/main.js
js/Markdown.Converter.js
js/Markdown.Editor.js
js/Markdown.Sanitizer.js
img/ajax-loader.gif
img/gdrive.png
img/glyphicons-halflings.png
img/glyphicons-halflings-white.png
img/stackedit-16.png
img/stackedit-32.ico
NETWORK:
*
|
||||
CACHE MANIFEST
# v5
CACHE:
index.html
css/bootstrap.css
css/jgrowl.css
css/main.css
js/async-runner.js
js/base64.js
js/bootstrap.js
js/gdrive.js
js/jquery.jgrowl.js
js/jquery.js
js/jquery.layout.js
js/jquery-ui.custom.js
js/main.js
js/Markdown.Converter.js
js/Markdown.Editor.js
js/Markdown.Sanitizer.js
img/ajax-loader.gif
img/gdrive.png
img/glyphicons-halflings.png
img/glyphicons-halflings-white.png
img/stackedit-16.png
img/stackedit-32.ico
NETWORK:
*
|
@ -100,7 +100,7 @@
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal"
|
||||
aria-hidden="true">×</button>
|
||||
<h3>Remove file</h3>
|
||||
<h3>Remove</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>Are you sure you want to remove "<span class="file-title"></span>"?
|
||||
|
@ -1,66 +1,101 @@
|
||||
var ASYNC_TASK_DEFAULT_TIMEOUT = 30000;
|
||||
|
||||
/**
|
||||
* Used to run any asynchronous tasks sequentially (ajax mainly)
|
||||
* An asynchronous task must be created with:
|
||||
* - a required run() function that may call success() or error()
|
||||
* - a required run() function that may call success(), error() or retry()
|
||||
* - an optional onSuccess() function
|
||||
* - an optional onError() function
|
||||
* - an optional timeout property
|
||||
* - an optional timeout field (default is 30000)
|
||||
*/
|
||||
var asyncTaskRunner = (function() {
|
||||
var asyncTaskRunner = {};
|
||||
|
||||
var asyncTaskQueue = [];
|
||||
var currentTask = undefined;
|
||||
var currentTaskStartTime = new Date().getTime();
|
||||
var currentTaskRunning = false;
|
||||
var currentTaskStartTime = currentTime;
|
||||
|
||||
// Run the next task in the queue if any and no other is running
|
||||
asyncTaskRunner.runTask = function() {
|
||||
|
||||
// If there is a task currently running
|
||||
if(currentTask !== undefined) {
|
||||
if(currentTaskRunning !== false) {
|
||||
// If the current task takes too long
|
||||
var timeout = currentTask.timeout || 30000;
|
||||
var timeout = currentTask.timeout || ASYNC_TASK_DEFAULT_TIMEOUT;
|
||||
if(currentTaskStartTime + timeout < currentTime) {
|
||||
currentTask.error();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// If no task in the queue
|
||||
if(asyncTaskQueue.length === 0) {
|
||||
return;
|
||||
}
|
||||
currentTask = asyncTaskQueue.shift();
|
||||
currentTaskStartTime = currentTime;
|
||||
showWorkingIndicator(true);
|
||||
if(currentTask === undefined) {
|
||||
// If no task in the queue
|
||||
if(asyncTaskQueue.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Set task attributes and functions
|
||||
currentTask.finished = false;
|
||||
currentTask.finish = function() {
|
||||
this.finished = true;
|
||||
showWorkingIndicator(false);
|
||||
currentTask = undefined;
|
||||
asyncTaskRunner.runTask();
|
||||
};
|
||||
currentTask.success = function() {
|
||||
if(this.finished === true) {
|
||||
return;
|
||||
}
|
||||
if(this.onSuccess) {
|
||||
this.onSuccess();
|
||||
}
|
||||
this.finish();
|
||||
};
|
||||
currentTask.error = function() {
|
||||
if(this.finished === true) {
|
||||
return;
|
||||
}
|
||||
if(this.onError) {
|
||||
this.onError();
|
||||
}
|
||||
this.finish();
|
||||
};
|
||||
currentTask.run();
|
||||
// Dequeue an enqueued task
|
||||
currentTask = asyncTaskQueue.shift();
|
||||
currentTaskStartTime = currentTime;
|
||||
showWorkingIndicator(true);
|
||||
|
||||
// Set task attributes and functions
|
||||
currentTask.finished = false;
|
||||
currentTask.retryCounter = 0;
|
||||
currentTask.finish = function() {
|
||||
this.finished = true;
|
||||
showWorkingIndicator(false);
|
||||
currentTask = undefined;
|
||||
currentTaskRunning = false;
|
||||
asyncTaskRunner.runTask();
|
||||
};
|
||||
currentTask.success = function() {
|
||||
if(this.finished === true) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if(this.onSuccess) {
|
||||
this.onSuccess();
|
||||
}
|
||||
} finally {
|
||||
this.finish();
|
||||
}
|
||||
};
|
||||
currentTask.error = function() {
|
||||
if(this.finished === true) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
if(this.onError) {
|
||||
this.onError();
|
||||
}
|
||||
} finally {
|
||||
this.finish();
|
||||
}
|
||||
};
|
||||
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++) + Math.random()) * 1000;
|
||||
console.log(delay);
|
||||
currentTaskStartTime = currentTime + delay;
|
||||
currentTaskRunning = false;
|
||||
asyncTaskRunner.runTask();
|
||||
};
|
||||
}
|
||||
|
||||
// Run the task
|
||||
if(currentTaskStartTime <= currentTime) {
|
||||
currentTaskRunning = true;
|
||||
currentTask.run();
|
||||
}
|
||||
};
|
||||
|
||||
// Add a task in the queue
|
||||
|
264
js/gdrive.js
264
js/gdrive.js
@ -1,10 +1,11 @@
|
||||
var GOOGLE_CLIENT_ID = '241271498917-jpto9lls9fqnem1e4h6ppds9uob8rpvu.apps.googleusercontent.com';
|
||||
var SCOPES = [ 'https://www.googleapis.com/auth/drive.install',
|
||||
'https://www.googleapis.com/auth/drive.file' ];
|
||||
var AUTH_POPUP_TIMEOUT = 90000;
|
||||
|
||||
var gdriveDelayedFunction = undefined;
|
||||
function runGdriveDelayedFunction() {
|
||||
if(gdriveDelayedFunction !== undefined) {
|
||||
if (gdriveDelayedFunction !== undefined) {
|
||||
gdriveDelayedFunction();
|
||||
}
|
||||
}
|
||||
@ -13,7 +14,8 @@ var gdrive = (function($) {
|
||||
|
||||
var connected = false;
|
||||
var authenticated = false;
|
||||
var doNothing = function() {};
|
||||
var doNothing = function() {
|
||||
};
|
||||
|
||||
var gdrive = {};
|
||||
|
||||
@ -22,21 +24,21 @@ var gdrive = (function($) {
|
||||
callback = callback || doNothing;
|
||||
var asyncTask = {};
|
||||
asyncTask.run = function() {
|
||||
if(connected === true) {
|
||||
if (connected === true) {
|
||||
asyncTask.success();
|
||||
return;
|
||||
}
|
||||
gdriveDelayedFunction = function() {
|
||||
asyncTask.success();
|
||||
};
|
||||
$.ajax({
|
||||
url: "https://apis.google.com/js/client.js?onload=runGdriveDelayedFunction",
|
||||
dataType: "script",
|
||||
timeout: 5000
|
||||
})
|
||||
.fail(function() {
|
||||
asyncTask.error();
|
||||
});
|
||||
$
|
||||
.ajax(
|
||||
{
|
||||
url : "https://apis.google.com/js/client.js?onload=runGdriveDelayedFunction",
|
||||
dataType : "script", timeout : AJAX_TIMEOUT }).fail(
|
||||
function() {
|
||||
asyncTask.error();
|
||||
});
|
||||
};
|
||||
asyncTask.onSuccess = function() {
|
||||
gdriveDelayedFunction = undefined;
|
||||
@ -54,27 +56,31 @@ var gdrive = (function($) {
|
||||
// Try to authenticate with Oauth
|
||||
function authenticate(callback, immediate) {
|
||||
callback = callback || doNothing;
|
||||
if(immediate === undefined) {
|
||||
if (immediate === undefined) {
|
||||
immediate = true;
|
||||
}
|
||||
connect(function() {
|
||||
if(connected === false) {
|
||||
if (connected === false) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
|
||||
var asyncTask = {};
|
||||
// If not immediate we add time for user to enter his credentials
|
||||
if(immediate === false) {
|
||||
asyncTask.timeout = 90000;
|
||||
if (immediate === false) {
|
||||
asyncTask.timeout = AUTH_POPUP_TIMEOUT;
|
||||
}
|
||||
asyncTask.run = function() {
|
||||
if(authenticated === true) {
|
||||
if (authenticated === true) {
|
||||
asyncTask.success();
|
||||
return;
|
||||
}
|
||||
gapi.auth.authorize({ 'client_id' : GOOGLE_CLIENT_ID, 'scope' : SCOPES,
|
||||
'immediate' : immediate }, function(authResult) {
|
||||
if (immediate === false) {
|
||||
showMessage("Please make sure the Google authorization popup is not blocked by your browser...");
|
||||
}
|
||||
gapi.auth.authorize({ 'client_id' : GOOGLE_CLIENT_ID,
|
||||
'scope' : SCOPES, 'immediate' : immediate }, function(
|
||||
authResult) {
|
||||
if (!authResult || authResult.error) {
|
||||
asyncTask.error();
|
||||
return;
|
||||
@ -90,7 +96,7 @@ var gdrive = (function($) {
|
||||
};
|
||||
asyncTask.onError = function() {
|
||||
// If immediate did not work retry without immediate flag
|
||||
if(connected === true && immediate === true) {
|
||||
if (connected === true && immediate === true) {
|
||||
authenticate(callback, false);
|
||||
return;
|
||||
}
|
||||
@ -100,10 +106,41 @@ var gdrive = (function($) {
|
||||
});
|
||||
}
|
||||
|
||||
function handleError(response, asyncTask, callback) {
|
||||
var errorMsg = undefined;
|
||||
if (response && response.error) {
|
||||
var error = response.error;
|
||||
// Try to analyze the error
|
||||
if (error.code >= 500 && error.code < 600) {
|
||||
errorMsg = "Google Drive is not accessible.";
|
||||
// Retry as described in Google's best practices
|
||||
asyncTask.retry();
|
||||
return;
|
||||
} else if (error.code === 401) {
|
||||
authenticated = false;
|
||||
errorMsg = "Access to Google Drive is not authorized.";
|
||||
} else if (error.code === -1) {
|
||||
connected = false;
|
||||
authenticated = false;
|
||||
onOffline();
|
||||
} else {
|
||||
errorMsg = "Google Drive error (" + error.code + ": "
|
||||
+ error.message + ").";
|
||||
}
|
||||
}
|
||||
asyncTask.onError = function() {
|
||||
if (errorMsg !== undefined) {
|
||||
showError(errorMsg);
|
||||
}
|
||||
callback();
|
||||
};
|
||||
asyncTask.error();
|
||||
}
|
||||
|
||||
function upload(fileId, parentId, title, content, callback) {
|
||||
callback = callback || doNothing;
|
||||
authenticate(function() {
|
||||
if(connected === false) {
|
||||
if (connected === false) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
@ -117,13 +154,14 @@ var gdrive = (function($) {
|
||||
|
||||
var contentType = 'text/x-markdown';
|
||||
var metadata = { title : title, mimeType : contentType };
|
||||
if (parentId) {
|
||||
if (parentId !== undefined) {
|
||||
// Specify the directory
|
||||
metadata.parents = [ { kind : 'drive#fileLink', id : parentId } ];
|
||||
metadata.parents = [ { kind : 'drive#fileLink',
|
||||
id : parentId } ];
|
||||
}
|
||||
var path = '/upload/drive/v2/files';
|
||||
var method = 'POST';
|
||||
if (fileId) {
|
||||
if (fileId !== undefined) {
|
||||
// If it's an update
|
||||
path += "/" + fileId;
|
||||
method = 'PUT';
|
||||
@ -133,59 +171,151 @@ var gdrive = (function($) {
|
||||
var multipartRequestBody = delimiter
|
||||
+ 'Content-Type: application/json\r\n\r\n'
|
||||
+ JSON.stringify(metadata) + delimiter + 'Content-Type: '
|
||||
+ contentType + '\r\n' + 'Content-Transfer-Encoding: base64\r\n'
|
||||
+ '\r\n' + base64Data + close_delim;
|
||||
+ contentType + '\r\n'
|
||||
+ 'Content-Transfer-Encoding: base64\r\n' + '\r\n'
|
||||
+ base64Data + close_delim;
|
||||
|
||||
var request = gapi.client.request({
|
||||
'path' : path,
|
||||
'method' : method,
|
||||
'params' : { 'uploadType' : 'multipart', },
|
||||
'headers' : { 'Content-Type' : 'multipart/mixed; boundary="'
|
||||
+ boundary + '"', }, 'body' : multipartRequestBody, });
|
||||
request.execute(function(file) {
|
||||
if(file.id) {
|
||||
var request = gapi.client
|
||||
.request({
|
||||
'path' : path,
|
||||
'method' : method,
|
||||
'params' : { 'uploadType' : 'multipart', },
|
||||
'headers' : { 'Content-Type' : 'multipart/mixed; boundary="'
|
||||
+ boundary + '"', }, 'body' : multipartRequestBody, });
|
||||
request.execute(function(response) {
|
||||
if (response && response.id) {
|
||||
// Upload success
|
||||
fileIndex = SYNC_PROVIDER_GDRIVE + file.id;
|
||||
localStorage[fileIndex + ".etag"] = file.etag;
|
||||
fileIndex = SYNC_PROVIDER_GDRIVE + response.id;
|
||||
localStorage[fileIndex + ".etag"] = response.etag;
|
||||
asyncTask.success();
|
||||
return
|
||||
return;
|
||||
}
|
||||
// Upload failed, try to analyse
|
||||
if(file.error.code === 401) {
|
||||
showError("Google Drive is not accessible.");
|
||||
// If file has been removed from Google Drive
|
||||
if(fileId !== undefined && response.error.code === 404) {
|
||||
showMessage('"' + title + '" has been removed from Google Drive.');
|
||||
fileManager.removeSync(SYNC_PROVIDER_GDRIVE + fileId);
|
||||
fileManager.updateFileTitles();
|
||||
// Avoid error analyzed by handleError
|
||||
response = undefined;
|
||||
}
|
||||
else {
|
||||
connected = false;
|
||||
authenticated = false;
|
||||
onOffline();
|
||||
}
|
||||
asyncTask.error();
|
||||
// Handle error
|
||||
handleError(response, asyncTask, callback);
|
||||
});
|
||||
};
|
||||
asyncTask.onSuccess = function() {
|
||||
callback(fileIndex);
|
||||
};
|
||||
asyncTask.onError = function() {
|
||||
callback();
|
||||
};
|
||||
asyncTaskRunner.addTask(asyncTask);
|
||||
});
|
||||
}
|
||||
;
|
||||
|
||||
gdrive.init = function() {
|
||||
try {
|
||||
var state = JSON.parse(decodeURI((/state=(.+?)(&|$)/
|
||||
.exec(location.search) || [ , null ])[1]));
|
||||
if (state.action == 'create') {
|
||||
upload(undefined, state.folderId,
|
||||
fileManager.currentFile, fileManager.content, function(
|
||||
fileIndex) {
|
||||
console.log(fileIndex);
|
||||
});
|
||||
gdrive.checkUpdates = function(lastChangeId, callback) {
|
||||
callback = callback || doNothing;
|
||||
authenticate(function() {
|
||||
if (connected === false) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
} catch (e) {
|
||||
|
||||
var changes = [];
|
||||
var newChangeId = lastChangeId || 0;
|
||||
function retrievePageOfChanges(request) {
|
||||
var nextPageToken = undefined;
|
||||
var asyncTask = {};
|
||||
asyncTask.run = function() {
|
||||
request
|
||||
.execute(function(response) {
|
||||
if (response && response.largestChangeId) {
|
||||
// Retrieve success
|
||||
newChangeId = response.largestChangeId;
|
||||
nextPageToken = response.nextPageToken;
|
||||
if (response.items !== undefined) {
|
||||
for ( var i = 0; i < response.items.length; i++) {
|
||||
var item = response.items[i];
|
||||
var etag = localStorage[SYNC_PROVIDER_GDRIVE
|
||||
+ item.fileId + ".etag"];
|
||||
if (etag
|
||||
&& (item.deleted === true || item.file.etag != etag)) {
|
||||
changes.push(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
asyncTask.success();
|
||||
return;
|
||||
}
|
||||
// Handle error
|
||||
handleError(response, asyncTask, callback);
|
||||
});
|
||||
};
|
||||
asyncTask.onSuccess = function() {
|
||||
if (nextPageToken !== undefined) {
|
||||
request = gapi.client.drive.changes
|
||||
.list({ 'pageToken' : nextPageToken });
|
||||
retrievePageOfChanges(request);
|
||||
} else {
|
||||
callback(changes, newChangeId);
|
||||
}
|
||||
};
|
||||
asyncTaskRunner.addTask(asyncTask);
|
||||
}
|
||||
var initialRequest = gapi.client.drive.changes
|
||||
.list({ 'startChangeId' : newChangeId + 1 });
|
||||
retrievePageOfChanges(initialRequest);
|
||||
});
|
||||
};
|
||||
|
||||
gdrive.downloadContent = function(objects, callback, result) {
|
||||
callback = callback || doNothing;
|
||||
result = result || [];
|
||||
if(objects.length === 0) {
|
||||
callback(result);
|
||||
return;
|
||||
}
|
||||
|
||||
var object = objects.pop();
|
||||
result.push(object);
|
||||
var file = undefined;
|
||||
// object may be a file
|
||||
if(object.kind == "drive#file") {
|
||||
file = object;
|
||||
}
|
||||
// object may be a change
|
||||
else if(object.kind == "drive#change") {
|
||||
file = object.file;
|
||||
}
|
||||
if(file === undefined) {
|
||||
this.downloadContent(objects, callback, result);
|
||||
return;
|
||||
}
|
||||
|
||||
authenticate(function() {
|
||||
if (connected === false) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
|
||||
var asyncTask = {};
|
||||
asyncTask.run = function() {
|
||||
var accessToken = gapi.auth.getToken().access_token;
|
||||
$
|
||||
.ajax(
|
||||
{
|
||||
url : file.downloadUrl,
|
||||
headers : { "Authorization" : "Bearer "
|
||||
+ accessToken }, dataType : "text",
|
||||
timeout : AJAX_TIMEOUT }).done(
|
||||
function(data, textStatus, jqXHR) {
|
||||
file.content = data;
|
||||
asyncTask.success();
|
||||
}).fail(function() {
|
||||
asyncTask.error();
|
||||
});
|
||||
};
|
||||
asyncTask.onSuccess = function() {
|
||||
gdrive.downloadContent(objects, callback, result);
|
||||
};
|
||||
asyncTaskRunner.addTask(asyncTask);
|
||||
});
|
||||
};
|
||||
|
||||
gdrive.createFile = function(title, content, callback) {
|
||||
@ -196,5 +326,19 @@ var gdrive = (function($) {
|
||||
upload(id, undefined, title, content, callback);
|
||||
};
|
||||
|
||||
gdrive.init = function() {
|
||||
try {
|
||||
var state = JSON.parse(decodeURI((/state=(.+?)(&|$)/
|
||||
.exec(location.search) || [ , null ])[1]));
|
||||
if (state.action == 'create') {
|
||||
upload(undefined, state.folderId, fileManager.currentFile,
|
||||
fileManager.content, function(fileIndex) {
|
||||
console.log(fileIndex);
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
}
|
||||
};
|
||||
|
||||
return gdrive;
|
||||
})(jQuery);
|
||||
|
255
js/main.js
255
js/main.js
@ -18,6 +18,8 @@ function showWorkingIndicator(show) {
|
||||
}
|
||||
}
|
||||
|
||||
var AJAX_TIMEOUT = 5000;
|
||||
var CHECK_ONLINE_PERIOD = 60000;
|
||||
var offline = false;
|
||||
var offlineTime = currentTime;
|
||||
function onOffline() {
|
||||
@ -36,21 +38,24 @@ function onOnline() {
|
||||
'jGrowl.beforeClose');
|
||||
}
|
||||
|
||||
function autoClean() {
|
||||
function checkOnline() {
|
||||
// Try to reconnect if we are offline but we have some network
|
||||
if (offline === true && navigator.onLine === true
|
||||
&& offlineTime + 60000 < currentTime) {
|
||||
&& offlineTime + CHECK_ONLINE_PERIOD < currentTime) {
|
||||
offlineTime = currentTime;
|
||||
// Try to download anything to test the connection
|
||||
$.ajax(
|
||||
{ url : "https://apis.google.com/js/client.js", timeout : 5000,
|
||||
dataType : "script" }).done(function() {
|
||||
{ url : "https://apis.google.com/js/client.js",
|
||||
timeout : AJAX_TIMEOUT, dataType : "script" }).done(function() {
|
||||
onOnline();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
var SYNC_DOWN_PERIOD = 60000;
|
||||
var SYNC_PROVIDER_GDRIVE = "sync.gdrive.";
|
||||
var syncGoogleDrive = false;
|
||||
|
||||
var synchronizer = (function($) {
|
||||
var synchronizer = {};
|
||||
|
||||
@ -76,14 +81,13 @@ var synchronizer = (function($) {
|
||||
}
|
||||
};
|
||||
|
||||
// Recursive function to run synchronization of a single file on multiple
|
||||
// locations
|
||||
function sync(fileSyncIndexList, content, title) {
|
||||
// Recursive function to upload a single file on multiple locations
|
||||
function fileUp(fileSyncIndexList, content, title) {
|
||||
if (fileSyncIndexList.length === 0) {
|
||||
localStorage.removeItem("sync.current");
|
||||
running = false;
|
||||
uploadRunning = false;
|
||||
// run the next file synchronization
|
||||
synchronizer.run();
|
||||
synchronizer.syncUp();
|
||||
return;
|
||||
}
|
||||
var fileSyncIndex = fileSyncIndexList.pop();
|
||||
@ -92,28 +96,28 @@ var synchronizer = (function($) {
|
||||
if (fileSyncIndex.indexOf(SYNC_PROVIDER_GDRIVE) === 0) {
|
||||
var id = fileSyncIndex.substring(SYNC_PROVIDER_GDRIVE.length);
|
||||
gdrive.updateFile(id, title, content, function(result) {
|
||||
if (!result && offline) {
|
||||
if (result === undefined && offline === true) {
|
||||
// If we detect offline mode we put the fileIndex back in
|
||||
// the queue
|
||||
synchronizer.addFile(localStorage["sync.current"]);
|
||||
localStorage.removeItem("sync.current");
|
||||
running = false;
|
||||
uploadRunning = false;
|
||||
return;
|
||||
}
|
||||
sync(fileSyncIndexList, content, title);
|
||||
fileUp(fileSyncIndexList, content, title);
|
||||
});
|
||||
} else {
|
||||
sync(fileSyncIndexList, content, title);
|
||||
fileUp(fileSyncIndexList, content, title);
|
||||
}
|
||||
}
|
||||
|
||||
var running = false;
|
||||
synchronizer.run = function() {
|
||||
// If synchronization is already running or nothing to synchronize
|
||||
if (running || syncQueue.length === 1 || offline) {
|
||||
var uploadRunning = false;
|
||||
synchronizer.syncUp = function() {
|
||||
// If syncUp is already running or nothing to synchronize or offline
|
||||
if (uploadRunning || syncQueue.length === 1 || offline) {
|
||||
return;
|
||||
}
|
||||
running = true;
|
||||
uploadRunning = true;
|
||||
|
||||
// Dequeue the fileIndex
|
||||
var separatorPos = syncQueue.indexOf(";", 1);
|
||||
@ -127,7 +131,101 @@ var synchronizer = (function($) {
|
||||
|
||||
// Parse the list of synchronized locations associated to the file
|
||||
var fileSyncIndexList = localStorage[fileIndex + ".sync"].split(";");
|
||||
sync(fileSyncIndexList, content, title);
|
||||
fileUp(fileSyncIndexList, content, title);
|
||||
};
|
||||
|
||||
function syncDownGdrive(callback) {
|
||||
if (syncGoogleDrive === false) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
var lastChangeId = parseInt(localStorage[SYNC_PROVIDER_GDRIVE
|
||||
+ "lastChangeId"]);
|
||||
gdrive.checkUpdates(lastChangeId, function(changes, newChangeId) {
|
||||
if (changes === undefined) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
gdrive.downloadContent(changes, function(changes) {
|
||||
if (changes === undefined) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
var updateFileTitles = false;
|
||||
for ( var i = 0; i < changes.length; i++) {
|
||||
var change = changes[i];
|
||||
var fileSyncIndex = SYNC_PROVIDER_GDRIVE + change.fileId;
|
||||
var fileIndexList = localStorage["file.list"].split(";");
|
||||
var fileIndex = undefined;
|
||||
// Look for local file associated to this synchronized location
|
||||
for ( var i = 1; i < fileIndexList.length - 1; i++) {
|
||||
var tempFileIndex = fileIndexList[i];
|
||||
var sync = localStorage[tempFileIndex + ".sync"];
|
||||
if (sync.indexOf(";" + fileSyncIndex + ";") !== -1) {
|
||||
fileIndex = tempFileIndex;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// No file corresponding (this should never happen...)
|
||||
if(fileIndex === undefined) {
|
||||
// We can remove the stored etag
|
||||
localStorage.removeItem(fileSyncIndex + ".etag");
|
||||
continue;
|
||||
}
|
||||
var title = localStorage[fileIndex + ".title"];
|
||||
// File deleted
|
||||
if (change.deleted === true) {
|
||||
fileManager.removeSync(fileSyncIndex);
|
||||
updateFileTitles = true;
|
||||
showMessage('"' + title + '" has been removed from Google Drive.');
|
||||
continue;
|
||||
}
|
||||
var content = localStorage[fileIndex + ".content"];
|
||||
var file = change.file;
|
||||
// File title changed
|
||||
if(title != file.title) {
|
||||
localStorage[fileIndex + ".title"] = file.title;
|
||||
updateFileTitles = true;
|
||||
showMessage('"' + title + '" has been renamed to "' + file.title + '" on Google Drive.');
|
||||
}
|
||||
// File content changed
|
||||
if(content != file.content) {
|
||||
localStorage[fileIndex + ".content"] = file.content;
|
||||
showMessage('"' + file.title + '" has been updated from Google Drive.');
|
||||
if(fileIndex == localStorage["file.current"]) {
|
||||
updateFileTitles = false; // Done by next function
|
||||
fileManager.selectFile();
|
||||
}
|
||||
}
|
||||
// Update file etag
|
||||
localStorage[fileSyncIndex + ".etag"] = file.etag;
|
||||
// Synchronize file to others locations
|
||||
synchronizer.addFile(fileIndex);
|
||||
}
|
||||
if(updateFileTitles) {
|
||||
fileManager.updateFileTitles();
|
||||
}
|
||||
localStorage[SYNC_PROVIDER_GDRIVE
|
||||
+ "lastChangeId"] = newChangeId;
|
||||
callback();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
var downloadRunning = false;
|
||||
var lastSyncDown = 0;
|
||||
synchronizer.syncDown = function() {
|
||||
// If syncDown is already running or timeout is not reached or offline
|
||||
if (downloadRunning || lastSyncDown + SYNC_DOWN_PERIOD > currentTime
|
||||
|| offline) {
|
||||
return;
|
||||
}
|
||||
downloadRunning = true;
|
||||
lastSyncDown = currentTime;
|
||||
|
||||
syncDownGdrive(function() {
|
||||
downloadRunning = false;
|
||||
});
|
||||
};
|
||||
|
||||
return synchronizer;
|
||||
@ -143,19 +241,21 @@ var fileManager = (function($) {
|
||||
synchronizer.init();
|
||||
fileManager.selectFile();
|
||||
|
||||
// Save file automatically and synchronize
|
||||
// Do periodic stuff
|
||||
window.setInterval(function() {
|
||||
currentTime = new Date().getTime();
|
||||
fileManager.saveFile();
|
||||
synchronizer.run();
|
||||
synchronizer.syncDown();
|
||||
synchronizer.syncUp();
|
||||
asyncTaskRunner.runTask();
|
||||
autoClean();
|
||||
checkOnline();
|
||||
}, 1000);
|
||||
|
||||
$(".action-create-file").click(function() {
|
||||
fileManager.saveFile();
|
||||
fileManager.createFile();
|
||||
fileManager.selectFile();
|
||||
$("#file-title").click();
|
||||
});
|
||||
$(".action-remove-file").click(function() {
|
||||
fileManager.deleteFile();
|
||||
@ -172,8 +272,7 @@ var fileManager = (function($) {
|
||||
var fileIndexTitle = localStorage["file.current"] + ".title";
|
||||
if (title != localStorage[fileIndexTitle]) {
|
||||
localStorage[fileIndexTitle] = title;
|
||||
updateFileDescList();
|
||||
updateFileTitleUI();
|
||||
fileManager.updateFileTitles();
|
||||
save = true;
|
||||
}
|
||||
}
|
||||
@ -205,23 +304,18 @@ var fileManager = (function($) {
|
||||
localStorage["file.counter"] = 0;
|
||||
localStorage["file.list"] = ";";
|
||||
}
|
||||
updateFileDescList();
|
||||
// If no file create one
|
||||
if (fileDescList.length === 0) {
|
||||
if (localStorage["file.list"].length === 1) {
|
||||
this.createFile();
|
||||
updateFileDescList();
|
||||
}
|
||||
// If no default file take first one
|
||||
if (!localStorage["file.current"]) {
|
||||
localStorage["file.current"] = fileDescList[0].index;
|
||||
}
|
||||
// Update the editor and the file title
|
||||
// Update the file titles
|
||||
this.updateFileTitles();
|
||||
// Update the editor
|
||||
var fileIndex = localStorage["file.current"];
|
||||
$("#wmd-input").val(localStorage[fileIndex + ".content"]);
|
||||
core.createEditor(function() {
|
||||
save = true;
|
||||
});
|
||||
updateFileTitleUI();
|
||||
};
|
||||
|
||||
fileManager.createFile = function(title) {
|
||||
@ -246,8 +340,8 @@ var fileManager = (function($) {
|
||||
// Remove synchronized locations
|
||||
var fileSyncIndexList = localStorage[fileIndex + ".sync"].split(";");
|
||||
for ( var i = 1; i < fileSyncIndexList.length - 1; i++) {
|
||||
var sync = fileSyncIndexList[i];
|
||||
fileManager.removeSync(sync);
|
||||
var fileSyncIndex = fileSyncIndexList[i];
|
||||
fileManager.removeSync(fileSyncIndex);
|
||||
}
|
||||
localStorage.removeItem(fileIndex + ".sync");
|
||||
|
||||
@ -268,33 +362,7 @@ var fileManager = (function($) {
|
||||
}
|
||||
};
|
||||
|
||||
// Remove a synchronization location associated to the file
|
||||
fileManager.removeSync = function(sync) {
|
||||
var fileIndexSync = localStorage["file.current"] + ".sync";
|
||||
localStorage[fileIndexSync] = localStorage[fileIndexSync].replace(";"
|
||||
+ sync + ";", ";");
|
||||
|
||||
localStorage.removeItem(sync + ".etag");
|
||||
};
|
||||
|
||||
function uploadGdrive() {
|
||||
$(".file-sync-indicator").removeClass("hide");
|
||||
var fileIndex = localStorage["file.current"];
|
||||
var content = localStorage[fileIndex + ".content"];
|
||||
var title = localStorage[fileIndex + ".title"];
|
||||
gdrive.createFile(title, content, function(fileSyncIndex) {
|
||||
if (fileSyncIndex) {
|
||||
localStorage[fileIndex + ".sync"] += fileSyncIndex + ";";
|
||||
updateFileTitleUI();
|
||||
showMessage('The file "' + title
|
||||
+ '" will now be synchronized on Google Drive.');
|
||||
} else {
|
||||
showError("Error while creating file on Google Drive.");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function updateFileDescList() {
|
||||
fileManager.updateFileTitles = function() {
|
||||
fileDescList = [];
|
||||
$("#file-selector").empty();
|
||||
var fileIndexList = localStorage["file.list"].split(";");
|
||||
@ -310,20 +378,24 @@ var fileManager = (function($) {
|
||||
return 1;
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
;
|
||||
|
||||
function updateFileTitleUI() {
|
||||
// If no default file take first one
|
||||
if (!localStorage["file.current"]) {
|
||||
localStorage["file.current"] = fileDescList[0].index;
|
||||
}
|
||||
|
||||
syncGoogleDrive = false;
|
||||
function composeTitle(fileIndex) {
|
||||
var result = localStorage[fileIndex + ".title"];
|
||||
var sync = localStorage[fileIndex + ".sync"];
|
||||
if (sync.indexOf(SYNC_PROVIDER_GDRIVE) !== -1) {
|
||||
if (sync.indexOf(";" + SYNC_PROVIDER_GDRIVE) !== -1) {
|
||||
syncGoogleDrive = true;
|
||||
result = '<i class="icon-gdrive"></i> ' + result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Update the editor and the file title
|
||||
// Update the the file title and the file selector
|
||||
var fileIndex = localStorage["file.current"];
|
||||
var title = localStorage[fileIndex + ".title"];
|
||||
document.title = "StackEdit - " + title;
|
||||
@ -348,8 +420,37 @@ var fileManager = (function($) {
|
||||
}
|
||||
$("#file-selector").append(li);
|
||||
}
|
||||
};
|
||||
|
||||
// Remove a synchronized location
|
||||
fileManager.removeSync = function(fileSyncIndex) {
|
||||
var fileIndexList = localStorage["file.list"].split(";");
|
||||
// Look for local files associated to this synchronized location
|
||||
for ( var i = 1; i < fileIndexList.length - 1; i++) {
|
||||
var fileIndexSync = fileIndexList[i] + ".sync";
|
||||
localStorage[fileIndexSync] = localStorage[fileIndexSync].replace(";"
|
||||
+ fileSyncIndex + ";", ";");
|
||||
}
|
||||
// Remove etag
|
||||
localStorage.removeItem(fileSyncIndex + ".etag");
|
||||
};
|
||||
|
||||
function uploadGdrive() {
|
||||
$(".file-sync-indicator").removeClass("hide");
|
||||
var fileIndex = localStorage["file.current"];
|
||||
var content = localStorage[fileIndex + ".content"];
|
||||
var title = localStorage[fileIndex + ".title"];
|
||||
gdrive.createFile(title, content, function(fileSyncIndex) {
|
||||
if (fileSyncIndex) {
|
||||
localStorage[fileIndex + ".sync"] += fileSyncIndex + ";";
|
||||
fileManager.updateFileTitles();
|
||||
showMessage('The file "' + title
|
||||
+ '" will now be synchronized on Google Drive.');
|
||||
} else {
|
||||
showError("Error while creating file on Google Drive.");
|
||||
}
|
||||
});
|
||||
}
|
||||
;
|
||||
|
||||
function refreshManageSync() {
|
||||
var fileIndex = localStorage["file.current"];
|
||||
@ -362,23 +463,24 @@ var fileManager = (function($) {
|
||||
$(".msg-no-sync").removeClass("hide");
|
||||
}
|
||||
for ( var i = 1; i < fileSyncIndexList.length - 1; i++) {
|
||||
var sync = fileSyncIndexList[i];
|
||||
(function(sync) {
|
||||
var fileSyncIndex = fileSyncIndexList[i];
|
||||
(function(fileSyncIndex) {
|
||||
var line = $("<div>").addClass("input-append");
|
||||
if (sync.indexOf(SYNC_PROVIDER_GDRIVE) === 0) {
|
||||
if (fileSyncIndex.indexOf(SYNC_PROVIDER_GDRIVE) === 0) {
|
||||
line.append($("<input>").prop("type", "text").prop(
|
||||
"disabled", true).addClass("span5").val(
|
||||
"Google Drive, FileID="
|
||||
+ sync.substring(SYNC_PROVIDER_GDRIVE.length)));
|
||||
+ fileSyncIndex.substring(SYNC_PROVIDER_GDRIVE.length)));
|
||||
line.append($("<a>").addClass("btn").html(
|
||||
'<i class="icon-trash"></i>').prop("title", "Remove this synchronized location").click(function() {
|
||||
fileManager.removeSync(sync);
|
||||
updateFileTitleUI();
|
||||
'<i class="icon-trash"></i>').prop("title",
|
||||
"Remove this synchronized location").click(function() {
|
||||
fileManager.removeSync(fileSyncIndex);
|
||||
fileManager.updateFileTitles();
|
||||
refreshManageSync();
|
||||
}));
|
||||
}
|
||||
$("#manage-sync-list").append(line);
|
||||
})(sync);
|
||||
})(fileSyncIndex);
|
||||
}
|
||||
}
|
||||
|
||||
@ -493,6 +595,7 @@ var core = (function($) {
|
||||
|
||||
$(function() {
|
||||
|
||||
$.jGrowl.defaults.life = 5000;
|
||||
$.jGrowl.defaults.closer = false;
|
||||
$.jGrowl.defaults.closeTemplate = '';
|
||||
$.jGrowl.defaults.position = 'bottom-right';
|
||||
|
Loading…
Reference in New Issue
Block a user