Add gdrive-action.html for Chrome app support

This commit is contained in:
benweet 2013-04-02 00:12:28 +01:00
parent 01f824567b
commit f32a1e41a6
10 changed files with 268 additions and 84 deletions

View File

@ -1 +1 @@
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 js/synchronizer.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 js/synchronizer.js img/ajax-loader.gif img/dropbox.png img/gdrive.png img/glyphicons-halflings.png img/glyphicons-halflings-white.png img/stackedit-16.png img/stackedit-32.ico NETWORK: *

View File

@ -13,7 +13,7 @@ body {
/*
Override Bootstrap
*/
div, span, a, ul, li, textarea, input {
div, span, a, ul, li, textarea, input, button {
background-image: none !important;
filter: none !important;
-webkit-box-shadow: none !important;
@ -132,7 +132,7 @@ hr {
}
#menu-bar {
margin-right: 15px;
margin: 0 15px 10px 15px;
}
#wmd-button-bar {
@ -189,6 +189,7 @@ hr {
}
.icon-code {
width: 15px;
background-position: -384px -168px;
}

16
gdrive-action.html Normal file
View File

@ -0,0 +1,16 @@
<!DOCTYPE html>
<html manifest="cache.manifest">
<head>
<script type="text/javascript">
var redirectUrl = location.href.substring(0, location.href.indexOf("gdrive-action.html"));
var state = decodeURI((/state=(.+?)(&|$)/
.exec(location.search) || [ , null ])[1]);
if(state) {
localStorage["sync.gdrive.state"] = state;
}
window.location.replace(redirectUrl);
</script>
</head>
</html>

BIN
img/dropbox.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 854 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 39 KiB

View File

@ -49,13 +49,22 @@
<li><div id="wmd-button-bar"></div></li>
</ul>
<ul class="pull-right" id="menu-bar">
<li class="btn-group"><a class="btn action-create-file"
href="#" title="Create a local file"><i class="icon-file"></i></a>
<a class="btn" href="#" title="Delete the current file locally"
data-toggle="modal" data-target="#modal-remove-file-confirm"><i
class="icon-trash"></i></a> <a class="btn dropdown-toggle"
data-toggle="dropdown" href="#" title="Open a local file"><i
class="icon-folder-open"></i></a>
<li class="btn-group"><button class="btn action-force-sync"
title="Synchronize">
<i class="icon-refresh"></i>
</button></li>
<li class="btn-group"><button class="btn action-create-file"
title="Create a local file">
<i class="icon-file"></i>
</button>
<button class="btn" title="Delete the current file locally"
data-toggle="modal" data-target="#modal-remove-file-confirm">
<i class="icon-trash"></i>
</button>
<button class="btn dropdown-toggle" data-toggle="dropdown"
title="Open a local file">
<i class="icon-folder-open"></i>
</button>
<ul id="file-selector" class="dropdown-menu">
</ul></li>
<li class="btn-group"><a class="btn dropdown-toggle"
@ -78,8 +87,8 @@
<li><a href="#"
title="Change the current file synchronized locations"
data-toggle="modal" data-target="#modal-manage-sync"
class="action-refresh-manage-sync"><i
class="icon-resize-small"></i> Manage synchronization</a></li>
class="action-refresh-manage-sync"><i class="icon-refresh"></i>
Manage synchronization</a></li>
<li class="divider"></li>
<li><a href="#" title="Modify your preferences"
data-toggle="modal" data-target="#modal-settings"
@ -109,8 +118,8 @@
<div class="modal-body">
<p>Are you sure you want to remove "<span class="file-title"></span>"?
</p>
<p class="muted"><b>NOTE:</b> The file will be removed on the
local machine, not on synchronized locations.</p>
<p class="muted"><b>NOTE:</b> This will not remove the file on
synchronized locations.</p>
</div>
<div class="modal-footer">
<a href="#" class="btn" data-dismiss="modal">Cancel</a> <a href="#"
@ -133,8 +142,8 @@
<br class="msg-sync-list hide" />
<p class="msg-sync-list hide muted"><b>NOTE:</b> Removing a
synchronized location will not delete any file.</p>
<p class="msg-no-sync hide">"<span class="file-title"></span>"
is not synchronized.
<p class="msg-no-sync hide">"<span class="file-title"></span>" is
not synchronized.
</p>
<p class="msg-no-sync hide muted"><b>NOTE:</b> You can add
synchronized locations using the top-right menu.</p>

View File

@ -20,7 +20,7 @@ var asyncTaskRunner = (function() {
asyncTaskRunner.runTask = function() {
// If there is a task currently running
if(currentTaskRunning !== false) {
if(currentTaskRunning === true) {
// If the current task takes too long
var timeout = currentTask.timeout || ASYNC_TASK_DEFAULT_TIMEOUT;
if(currentTaskStartTime + timeout < currentTime) {
@ -45,34 +45,20 @@ var asyncTaskRunner = (function() {
currentTask.retryCounter = 0;
currentTask.finish = function() {
this.finished = true;
showWorkingIndicator(false);
currentTask = undefined;
currentTaskRunning = false;
asyncTaskRunner.runTask();
if(asyncTaskQueue.length === 0) {
showWorkingIndicator(false);
}
else {
asyncTaskRunner.runTask();
}
};
currentTask.success = function() {
if(this.finished === true) {
return;
}
try {
if(this.onSuccess) {
this.onSuccess();
}
} finally {
this.finish();
}
runSafe(this.onSuccess);
};
currentTask.error = function() {
if(this.finished === true) {
return;
}
try {
if(this.onError) {
this.onError();
}
} finally {
this.finish();
}
runSafe(this.onError);
};
currentTask.retry = function() {
if(this.finished === true) {
@ -98,6 +84,27 @@ var asyncTaskRunner = (function() {
}
};
function runSafe(func) {
if(currentTask.finished === true) {
return;
}
try {
if(func) {
func();
}
} finally {
currentTask.finished = true;
currentTask = undefined;
currentTaskRunning = false;
if(asyncTaskQueue.length === 0) {
showWorkingIndicator(false);
}
else {
asyncTaskRunner.runTask();
}
}
}
// Add a task in the queue
asyncTaskRunner.addTask = function(asyncTask) {
asyncTaskQueue.push(asyncTask);

View File

@ -2,6 +2,7 @@ var GOOGLE_CLIENT_ID = '241271498917-jpto9lls9fqnem1e4h6ppds9uob8rpvu.apps.googl
var SCOPES = [ 'https://www.googleapis.com/auth/drive.install',
'https://www.googleapis.com/auth/drive.file' ];
var AUTH_POPUP_TIMEOUT = 90000;
var DEFAULT_GDRIVE_FILE_TITLE = "New Markdown document";
var gdriveDelayedFunction = undefined;
function runGdriveDelayedFunction() {
@ -142,7 +143,7 @@ var gdrive = (function($) {
return;
}
var fileIndex = undefined;
var fileSyncIndex = undefined;
var asyncTask = {};
asyncTask.run = function() {
var boundary = '-------314159265358979323846';
@ -182,8 +183,8 @@ var gdrive = (function($) {
request.execute(function(response) {
if (response && response.id) {
// Upload success
fileIndex = SYNC_PROVIDER_GDRIVE + response.id;
localStorage[fileIndex + ".etag"] = response.etag;
fileSyncIndex = SYNC_PROVIDER_GDRIVE + response.id;
localStorage[fileSyncIndex + ".etag"] = response.etag;
asyncTask.success();
return;
}
@ -201,7 +202,7 @@ var gdrive = (function($) {
});
};
asyncTask.onSuccess = function() {
callback(fileIndex);
callback(fileSyncIndex);
};
asyncTaskRunner.addTask(asyncTask);
});
@ -261,6 +262,48 @@ var gdrive = (function($) {
});
};
gdrive.downloadMetadata = function(ids, callback, result) {
callback = callback || doNothing;
result = result || [];
if(ids.length === 0) {
callback(result);
return;
}
authenticate(function() {
if (connected === false) {
callback();
return;
}
var id = ids.pop();
var asyncTask = {};
asyncTask.run = function() {
var accessToken = gapi.auth.getToken().access_token;
$.ajax({
url : "https://www.googleapis.com/drive/v2/files/" + id,
headers : { "Authorization" : "Bearer " + accessToken },
dataType : "json",
timeout : AJAX_TIMEOUT
}).done(function(data, textStatus, jqXHR) {
result.push(data);
asyncTask.success();
}).fail(function(jqXHR) {
var error = {
code: jqXHR.status,
message: jqXHR.statusText
};
// Handle error
handleError(error, asyncTask, callback);
});
};
asyncTask.onSuccess = function() {
gdrive.downloadMetadata(ids, callback, result);
};
asyncTaskRunner.addTask(asyncTask);
});
};
gdrive.downloadContent = function(objects, callback, result) {
callback = callback || doNothing;
result = result || [];
@ -284,20 +327,20 @@ var gdrive = (function($) {
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",
headers : { "Authorization" : "Bearer " + accessToken },
dataType : "text",
timeout : AJAX_TIMEOUT
}).done(function(data, textStatus, jqXHR) {
file.content = data;
@ -317,7 +360,7 @@ var gdrive = (function($) {
asyncTaskRunner.addTask(asyncTask);
});
};
gdrive.createFile = function(title, content, callback) {
upload(undefined, undefined, title, content, callback);
};
@ -327,16 +370,53 @@ var gdrive = (function($) {
};
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);
});
var state = localStorage["sync.gdrive.state"];
if(state === undefined) {
return;
}
localStorage.removeItem("sync.gdrive.state");
state = JSON.parse(state);
if (state.action == "create") {
upload(undefined, state.folderId, DEFAULT_GDRIVE_FILE_TITLE,
"", function(fileSyncIndex) {
if(fileSyncIndex === undefined) {
return;
}
var fileIndex = fileManager.createFile(DEFAULT_GDRIVE_FILE_TITLE, "", [fileSyncIndex]);
fileManager.selectFile(fileIndex);
showMessage('"' + DEFAULT_GDRIVE_FILE_TITLE + '" created successfully on Google Drive.');
});
}
else if (state.action == "open") {
var importIds = [];
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 {
importIds.push(id);
}
}
} catch (e) {
gdrive.downloadMetadata(importIds, function(result) {
if(result === undefined) {
return;
}
gdrive.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 fileIndex = fileManager.createFile(file.title, file.content, [fileSyncIndex]);
fileManager.selectFile(fileIndex);
showMessage('"' + file.title + '" imported successfully from Google Drive.');
}
});
});
}
};

View File

@ -22,6 +22,7 @@ var AJAX_TIMEOUT = 5000;
var CHECK_ONLINE_PERIOD = 60000;
var offline = false;
var offlineTime = currentTime;
var offlineListeners = [];
function onOffline() {
offlineTime = currentTime;
if(offline === false) {
@ -30,13 +31,21 @@ function onOffline() {
sticky : true, close : function() {
showMessage("You are back online!", "icon-signal");
} });
for(var i=0; i<offlineListeners.length; i++) {
offlineListeners[i]();
}
}
}
function onOnline() {
$(".msg-offline").parents(".jGrowl-notification").trigger(
'jGrowl.beforeClose');
offline = false;
if(offline === true) {
$(".msg-offline").parents(".jGrowl-notification").trigger(
'jGrowl.beforeClose');
offline = false;
for(var i=0; i<offlineListeners.length; i++) {
offlineListeners[i]();
}
}
}
function checkOnline() {
@ -61,7 +70,27 @@ var fileManager = (function($) {
var save = false;
fileManager.init = function() {
gdrive.init();
synchronizer.init();
var changeSyncButtonState = function() {
if(synchronizer.isRunning() || synchronizer.isQueueEmpty() || offline) {
$(".action-force-sync").addClass("disabled");
}
else {
$(".action-force-sync").removeClass("disabled");
}
};
offlineListeners.push(changeSyncButtonState);
synchronizer.init({
onSyncBegin : changeSyncButtonState,
onSyncEnd : changeSyncButtonState,
onQueueChanged : changeSyncButtonState
});
$(".action-force-sync").click(function() {
if(!$(this).hasClass("disabled")) {
synchronizer.forceSync();
}
});
fileManager.selectFile();
// Do periodic stuff
@ -149,8 +178,9 @@ var fileManager = (function($) {
});
};
fileManager.createFile = function(title, content) {
fileManager.createFile = function(title, content, syncIndexes) {
content = content || "";
syncIndexes = syncIndexes || [];
if (!title) {
// Create a file title
title = DEFAULT_FILE_TITLE;
@ -172,7 +202,11 @@ var fileManager = (function($) {
// Create the file in the localStorage
localStorage[fileIndex + ".content"] = content;
localStorage[fileIndex + ".title"] = title;
localStorage[fileIndex + ".sync"] = ";";
var sync = ";";
for(var i=0; i<syncIndexes.length; i++) {
sync += syncIndexes[i] + ";";
}
localStorage[fileIndex + ".sync"] = sync;
localStorage["file.counter"] = fileCounter + 1;
localStorage["file.list"] += fileIndex + ";";
return fileIndex;
@ -280,6 +314,21 @@ var fileManager = (function($) {
// Remove etag
localStorage.removeItem(fileSyncIndex + ".etag");
};
// Look for local file associated to a synchronized location
fileManager.getFileIndexFromSync = function(fileSyncIndex) {
var fileIndex = undefined;
var fileIndexList = localStorage["file.list"].split(";");
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;
}
}
return fileIndex;
};
function uploadGdrive() {
$(".file-sync-indicator").removeClass("hide");
@ -456,11 +505,7 @@ var core = (function($) {
onOffline();
}
if (typeof (Storage) !== "undefined") {
fileManager.init();
} else {
showError("Local storage is not available");
}
fileManager.init();
});
})(jQuery);

View File

@ -1,18 +1,31 @@
var SYNC_PERIOD = 30000;
var SYNC_PERIOD = 60000;
var SYNC_PROVIDER_GDRIVE = "sync.gdrive.";
var syncGoogleDrive = false;
var synchronizer = (function() {
var synchronizer = {};
var onSyncBegin = undefined;
var onSyncEnd = undefined;
var onQueueChanged = undefined;
var doNothing = function() {
};
// A synchronization queue containing fileIndex that has to be synchronized
var syncUpQueue = undefined;
synchronizer.init = function() {
synchronizer.init = function(options) {
onSyncBegin = options.onSyncBegin || doNothing;
onSyncEnd = options.onSyncEnd || doNothing;
onQueueChanged = options.onQueueChanged || doNothing;
syncUpQueue = ";";
// Load the queue from localStorage in case a previous synchronization
// was aborted
if (localStorage["sync.queue"]) {
syncUpQueue = localStorage["sync.queue"];
onQueueChanged();
}
if (localStorage["sync.current"]) {
this.addFileForUpload(localStorage["sync.current"]);
@ -21,10 +34,17 @@ var synchronizer = (function() {
// Add a file to the synchronization queue
synchronizer.addFileForUpload = function(fileIndex) {
if (syncUpQueue.indexOf(";" + fileIndex + ";") === -1) {
syncUpQueue += fileIndex + ";";
localStorage["sync.queue"] = syncUpQueue;
// Check that file has synchronized locations
if(localStorage[fileIndex + ".sync"].length === 1) {
return;
}
// Check that file is not in the queue
if (syncUpQueue.indexOf(";" + fileIndex + ";") !== -1) {
return;
}
syncUpQueue += fileIndex + ";";
localStorage["sync.queue"] = syncUpQueue;
onQueueChanged();
};
// Recursive function to upload a single file on multiple locations
@ -69,6 +89,7 @@ var synchronizer = (function() {
localStorage["sync.current"] = fileIndex;
syncUpQueue = syncUpQueue.substring(separatorPos);
localStorage["sync.queue"] = syncUpQueue;
onQueueChanged();
var content = localStorage[fileIndex + ".content"];
var title = localStorage[fileIndex + ".title"];
@ -99,17 +120,7 @@ var synchronizer = (function() {
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;
}
}
var fileIndex = fileManager.getFileIndexFromSync(fileSyncIndex);
// No file corresponding (this should never happen...)
if(fileIndex === undefined) {
// We can remove the stored etag
@ -177,13 +188,28 @@ var synchronizer = (function() {
}
syncRunning = true;
lastSync = currentTime;
onSyncBegin();
syncDown(function() {
syncUp(function() {
syncRunning = false;
onSyncEnd();
});
});
};
synchronizer.forceSync = function() {
lastSync = 0;
this.sync();
};
synchronizer.isRunning = function() {
return syncRunning;
};
synchronizer.isQueueEmpty = function() {
return syncUpQueue.length === 1;
};
return synchronizer;
})();