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 Override Bootstrap
*/ */
div, span, a, ul, li, textarea, input { div, span, a, ul, li, textarea, input, button {
background-image: none !important; background-image: none !important;
filter: none !important; filter: none !important;
-webkit-box-shadow: none !important; -webkit-box-shadow: none !important;
@ -132,7 +132,7 @@ hr {
} }
#menu-bar { #menu-bar {
margin-right: 15px; margin: 0 15px 10px 15px;
} }
#wmd-button-bar { #wmd-button-bar {
@ -189,6 +189,7 @@ hr {
} }
.icon-code { .icon-code {
width: 15px;
background-position: -384px -168px; 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> <li><div id="wmd-button-bar"></div></li>
</ul> </ul>
<ul class="pull-right" id="menu-bar"> <ul class="pull-right" id="menu-bar">
<li class="btn-group"><a class="btn action-create-file" <li class="btn-group"><button class="btn action-force-sync"
href="#" title="Create a local file"><i class="icon-file"></i></a> title="Synchronize">
<a class="btn" href="#" title="Delete the current file locally" <i class="icon-refresh"></i>
data-toggle="modal" data-target="#modal-remove-file-confirm"><i </button></li>
class="icon-trash"></i></a> <a class="btn dropdown-toggle" <li class="btn-group"><button class="btn action-create-file"
data-toggle="dropdown" href="#" title="Open a local file"><i title="Create a local file">
class="icon-folder-open"></i></a> <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 id="file-selector" class="dropdown-menu">
</ul></li> </ul></li>
<li class="btn-group"><a class="btn dropdown-toggle" <li class="btn-group"><a class="btn dropdown-toggle"
@ -78,8 +87,8 @@
<li><a href="#" <li><a href="#"
title="Change the current file synchronized locations" title="Change the current file synchronized locations"
data-toggle="modal" data-target="#modal-manage-sync" data-toggle="modal" data-target="#modal-manage-sync"
class="action-refresh-manage-sync"><i class="action-refresh-manage-sync"><i class="icon-refresh"></i>
class="icon-resize-small"></i> Manage synchronization</a></li> Manage synchronization</a></li>
<li class="divider"></li> <li class="divider"></li>
<li><a href="#" title="Modify your preferences" <li><a href="#" title="Modify your preferences"
data-toggle="modal" data-target="#modal-settings" data-toggle="modal" data-target="#modal-settings"
@ -109,8 +118,8 @@
<div class="modal-body"> <div class="modal-body">
<p>Are you sure you want to remove "<span class="file-title"></span>"? <p>Are you sure you want to remove "<span class="file-title"></span>"?
</p> </p>
<p class="muted"><b>NOTE:</b> The file will be removed on the <p class="muted"><b>NOTE:</b> This will not remove the file on
local machine, not on synchronized locations.</p> synchronized locations.</p>
</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="#"
@ -133,8 +142,8 @@
<br class="msg-sync-list hide" /> <br class="msg-sync-list hide" />
<p class="msg-sync-list hide muted"><b>NOTE:</b> Removing a <p class="msg-sync-list hide muted"><b>NOTE:</b> Removing a
synchronized location will not delete any file.</p> synchronized location will not delete any file.</p>
<p class="msg-no-sync hide">"<span class="file-title"></span>" <p class="msg-no-sync hide">"<span class="file-title"></span>" is
is not synchronized. not synchronized.
</p> </p>
<p class="msg-no-sync hide muted"><b>NOTE:</b> You can add <p class="msg-no-sync hide muted"><b>NOTE:</b> You can add
synchronized locations using the top-right menu.</p> synchronized locations using the top-right menu.</p>

View File

@ -20,7 +20,7 @@ var asyncTaskRunner = (function() {
asyncTaskRunner.runTask = function() { asyncTaskRunner.runTask = function() {
// If there is a task currently running // If there is a task currently running
if(currentTaskRunning !== false) { 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; var timeout = currentTask.timeout || ASYNC_TASK_DEFAULT_TIMEOUT;
if(currentTaskStartTime + timeout < currentTime) { if(currentTaskStartTime + timeout < currentTime) {
@ -45,34 +45,20 @@ var asyncTaskRunner = (function() {
currentTask.retryCounter = 0; currentTask.retryCounter = 0;
currentTask.finish = function() { currentTask.finish = function() {
this.finished = true; this.finished = true;
showWorkingIndicator(false);
currentTask = undefined; currentTask = undefined;
currentTaskRunning = false; currentTaskRunning = false;
if(asyncTaskQueue.length === 0) {
showWorkingIndicator(false);
}
else {
asyncTaskRunner.runTask(); asyncTaskRunner.runTask();
}
}; };
currentTask.success = function() { currentTask.success = function() {
if(this.finished === true) { runSafe(this.onSuccess);
return;
}
try {
if(this.onSuccess) {
this.onSuccess();
}
} finally {
this.finish();
}
}; };
currentTask.error = function() { currentTask.error = function() {
if(this.finished === true) { runSafe(this.onError);
return;
}
try {
if(this.onError) {
this.onError();
}
} finally {
this.finish();
}
}; };
currentTask.retry = function() { currentTask.retry = function() {
if(this.finished === true) { 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 // Add a task in the queue
asyncTaskRunner.addTask = function(asyncTask) { asyncTaskRunner.addTask = function(asyncTask) {
asyncTaskQueue.push(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', var SCOPES = [ 'https://www.googleapis.com/auth/drive.install',
'https://www.googleapis.com/auth/drive.file' ]; 'https://www.googleapis.com/auth/drive.file' ];
var AUTH_POPUP_TIMEOUT = 90000; var AUTH_POPUP_TIMEOUT = 90000;
var DEFAULT_GDRIVE_FILE_TITLE = "New Markdown document";
var gdriveDelayedFunction = undefined; var gdriveDelayedFunction = undefined;
function runGdriveDelayedFunction() { function runGdriveDelayedFunction() {
@ -142,7 +143,7 @@ var gdrive = (function($) {
return; return;
} }
var fileIndex = undefined; var fileSyncIndex = undefined;
var asyncTask = {}; var asyncTask = {};
asyncTask.run = function() { asyncTask.run = function() {
var boundary = '-------314159265358979323846'; var boundary = '-------314159265358979323846';
@ -182,8 +183,8 @@ var gdrive = (function($) {
request.execute(function(response) { request.execute(function(response) {
if (response && response.id) { if (response && response.id) {
// Upload success // Upload success
fileIndex = SYNC_PROVIDER_GDRIVE + response.id; fileSyncIndex = SYNC_PROVIDER_GDRIVE + response.id;
localStorage[fileIndex + ".etag"] = response.etag; localStorage[fileSyncIndex + ".etag"] = response.etag;
asyncTask.success(); asyncTask.success();
return; return;
} }
@ -201,7 +202,7 @@ var gdrive = (function($) {
}); });
}; };
asyncTask.onSuccess = function() { asyncTask.onSuccess = function() {
callback(fileIndex); callback(fileSyncIndex);
}; };
asyncTaskRunner.addTask(asyncTask); 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) { gdrive.downloadContent = function(objects, callback, result) {
callback = callback || doNothing; callback = callback || doNothing;
result = result || []; result = result || [];
@ -296,8 +339,8 @@ var gdrive = (function($) {
var accessToken = gapi.auth.getToken().access_token; var accessToken = gapi.auth.getToken().access_token;
$.ajax({ $.ajax({
url : file.downloadUrl, url : file.downloadUrl,
headers : { "Authorization" : "Bearer " headers : { "Authorization" : "Bearer " + accessToken },
+ accessToken }, dataType : "text", dataType : "text",
timeout : AJAX_TIMEOUT timeout : AJAX_TIMEOUT
}).done(function(data, textStatus, jqXHR) { }).done(function(data, textStatus, jqXHR) {
file.content = data; file.content = data;
@ -327,16 +370,53 @@ var gdrive = (function($) {
}; };
gdrive.init = function() { gdrive.init = function() {
try { var state = localStorage["sync.gdrive.state"];
var state = JSON.parse(decodeURI((/state=(.+?)(&|$)/ if(state === undefined) {
.exec(location.search) || [ , null ])[1])); return;
if (state.action == 'create') { }
upload(undefined, state.folderId, fileManager.currentFile, localStorage.removeItem("sync.gdrive.state");
fileManager.content, function(fileIndex) { state = JSON.parse(state);
console.log(fileIndex); 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.');
}); });
} }
} catch (e) { 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);
}
}
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 CHECK_ONLINE_PERIOD = 60000;
var offline = false; var offline = false;
var offlineTime = currentTime; var offlineTime = currentTime;
var offlineListeners = [];
function onOffline() { function onOffline() {
offlineTime = currentTime; offlineTime = currentTime;
if(offline === false) { if(offline === false) {
@ -30,13 +31,21 @@ function onOffline() {
sticky : true, close : function() { sticky : true, close : function() {
showMessage("You are back online!", "icon-signal"); showMessage("You are back online!", "icon-signal");
} }); } });
for(var i=0; i<offlineListeners.length; i++) {
offlineListeners[i]();
}
} }
} }
function onOnline() { function onOnline() {
if(offline === true) {
$(".msg-offline").parents(".jGrowl-notification").trigger( $(".msg-offline").parents(".jGrowl-notification").trigger(
'jGrowl.beforeClose'); 'jGrowl.beforeClose');
offline = false; offline = false;
for(var i=0; i<offlineListeners.length; i++) {
offlineListeners[i]();
}
}
} }
function checkOnline() { function checkOnline() {
@ -61,7 +70,27 @@ var fileManager = (function($) {
var save = false; var save = false;
fileManager.init = function() { fileManager.init = function() {
gdrive.init(); 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(); fileManager.selectFile();
// Do periodic stuff // Do periodic stuff
@ -149,8 +178,9 @@ var fileManager = (function($) {
}); });
}; };
fileManager.createFile = function(title, content) { fileManager.createFile = function(title, content, syncIndexes) {
content = content || ""; content = content || "";
syncIndexes = syncIndexes || [];
if (!title) { if (!title) {
// Create a file title // Create a file title
title = DEFAULT_FILE_TITLE; title = DEFAULT_FILE_TITLE;
@ -172,7 +202,11 @@ var fileManager = (function($) {
// Create the file in the localStorage // Create the file in the localStorage
localStorage[fileIndex + ".content"] = content; localStorage[fileIndex + ".content"] = content;
localStorage[fileIndex + ".title"] = title; 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.counter"] = fileCounter + 1;
localStorage["file.list"] += fileIndex + ";"; localStorage["file.list"] += fileIndex + ";";
return fileIndex; return fileIndex;
@ -281,6 +315,21 @@ var fileManager = (function($) {
localStorage.removeItem(fileSyncIndex + ".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() { function uploadGdrive() {
$(".file-sync-indicator").removeClass("hide"); $(".file-sync-indicator").removeClass("hide");
var fileIndex = localStorage["file.current"]; var fileIndex = localStorage["file.current"];
@ -456,11 +505,7 @@ var core = (function($) {
onOffline(); onOffline();
} }
if (typeof (Storage) !== "undefined") {
fileManager.init(); fileManager.init();
} else {
showError("Local storage is not available");
}
}); });
})(jQuery); })(jQuery);

View File

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