Added Dropbox synchronization
This commit is contained in:
parent
2b04af3610
commit
6983b74acd
@ -1 +1 @@
|
||||
CACHE MANIFEST
# v32
CACHE:
index.html
css/bootstrap.css
css/jgrowl.css
css/main.css
js/async-runner.js
js/bootstrap.js
js/config.js
js/custo.github.js
js/gdrive.js
js/jgrowl.js
js/jquery.js
js/jquery-ui.js
js/layout.js
js/main.js
js/Markdown.Converter.js
js/Markdown.Editor.js
js/Markdown.Sanitizer.js
js/require.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:
*
|
||||
CACHE MANIFEST
# v32
CACHE:
index.html
css/bootstrap.css
css/jgrowl.css
css/main.css
js/async-runner.js
js/bootstrap.js
js/config.js
js/custo.github.js
js/dropbox.js
js/gdrive.js
js/jgrowl.js
js/jquery.js
js/jquery-ui.js
js/layout.js
js/main.js
js/Markdown.Converter.js
js/Markdown.Editor.js
js/Markdown.Sanitizer.js
js/require.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:
*
|
||||
|
12
css/main.css
12
css/main.css
@ -9,7 +9,7 @@ body {
|
||||
cursor: progress;
|
||||
}
|
||||
|
||||
.btn {
|
||||
.btn, .dropdown-menu {
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
@ -35,6 +35,7 @@ div, span, a, ul, li, textarea, input, button {
|
||||
|
||||
.dropdown-menu {
|
||||
border: 1px solid #e2e2e2 !important;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.input-prepend input,
|
||||
@ -80,11 +81,10 @@ input.error {
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background-color: #777;
|
||||
background-color: #777;
|
||||
}
|
||||
|
||||
input[disabled], select[disabled], textarea[disabled], input[readonly], select[readonly], textarea[readonly], .input-append .add-on {
|
||||
cursor: not-allowed;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
@ -93,8 +93,10 @@ input[disabled], select[disabled], textarea[disabled], input[readonly], select[r
|
||||
.btn-primary:active,
|
||||
.btn-primary.active,
|
||||
.btn-primary.disabled,
|
||||
.btn-primary[disabled] {
|
||||
background-color: #888;
|
||||
.btn-primary[disabled],
|
||||
.btn-group.open .btn.btn-primary.dropdown-toggle {
|
||||
color: #fff;
|
||||
background-color: #888;
|
||||
}
|
||||
|
||||
.btn-group {
|
||||
|
11
dropbox-oauth-receiver.html
Normal file
11
dropbox-oauth-receiver.html
Normal file
@ -0,0 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<script src="js/dropbox.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
Dropbox.Drivers.Popup.oauthReceiver();
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
</body>
|
||||
</html>
|
201
index.html
201
index.html
@ -5,6 +5,9 @@
|
||||
<link rel="icon" href="img/stackedit-32.ico" type="image/x-icon">
|
||||
<link rel="shortcut icon" href="img/stackedit-32.ico"
|
||||
type="image/x-icon">
|
||||
<meta name="keywords" content="Markdown, Editor, PageDown, Stack Overflow, Stack Exchange">
|
||||
<meta name="description" content="StackEdit is a free, open-source Markdown editor based on PageDown, the Markdown library used by Stack Overflow and the other Stack Exchange sites.">
|
||||
<meta name="author" content="Benoit Schweblin">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link href="css/main.css" rel="stylesheet" media="screen">
|
||||
<script data-main="js/main" src="js/require.js"></script>
|
||||
@ -47,18 +50,18 @@
|
||||
<li class="dropdown-submenu"><a href="#"><i
|
||||
class="icon-gdrive"></i> Google Drive</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a href="#" class="action-upload-gdrive">Export to
|
||||
Google Drive</a></li>
|
||||
<li><a href="#" class="action-download-gdrive">Import from Google
|
||||
Drive</a></li>
|
||||
<li><a href="#" class="action-download-gdrive">Import
|
||||
from Google Drive</a></li>
|
||||
<li><a href="#" data-toggle="modal"
|
||||
data-target="#modal-upload-gdrive">Export to Google Drive</a></li>
|
||||
</ul></li>
|
||||
<li class="dropdown-submenu"><a href="#"><i
|
||||
class="icon-dropbox"></i> Dropbox</a>
|
||||
<ul class="dropdown-menu">
|
||||
<li><a class="action-upload-dropbox" href="#">Export to
|
||||
Dropbox</a></li>
|
||||
<li><a class="action-download-dropbox" href="#">Import
|
||||
from Dropbox</a></li>
|
||||
<li><a href="#" data-toggle="modal"
|
||||
data-target="#modal-upload-dropbox">Export to Dropbox</a></li>
|
||||
</ul></li>
|
||||
<li><a href="#" data-toggle="modal"
|
||||
data-target="#modal-manage-sync"><i class="icon-refresh"></i>
|
||||
@ -85,43 +88,47 @@
|
||||
<div class="ui-layout-east hide"></div>
|
||||
<div class="ui-layout-south hide"></div>
|
||||
|
||||
<div id="modal-insert-link" class="modal hide">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close action-close-insert-link" data-dismiss="modal"
|
||||
aria-hidden="true">×</button>
|
||||
<h3>Hyperlink</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>Please provide the link URL and an optional title:</p>
|
||||
<div class="input-prepend">
|
||||
<span class="add-on"><i class="icon-globe"></i></span><input
|
||||
id="input-insert-link" type="text" class="span5" placeholder='http://example.com/ "optional title"'></input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a href="#" class="btn action-close-insert-link" data-dismiss="modal">Cancel</a> <a href="#"
|
||||
class="btn btn-primary action-insert-link" data-dismiss="modal">OK</a>
|
||||
</div>
|
||||
</div>
|
||||
<div id="modal-insert-link" class="modal hide">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close action-close-insert-link"
|
||||
data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h3>Hyperlink</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>Please provide the link URL and an optional title:</p>
|
||||
<div class="input-prepend">
|
||||
<span class="add-on"><i class="icon-globe"></i></span><input
|
||||
id="input-insert-link" type="text" class="span5"
|
||||
placeholder='http://example.com/ "optional title"'></input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a href="#" class="btn action-close-insert-link" data-dismiss="modal">Cancel</a>
|
||||
<a href="#" class="btn btn-primary action-insert-link"
|
||||
data-dismiss="modal">OK</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="modal-insert-image" class="modal hide">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close action-close-insert-link" data-dismiss="modal"
|
||||
aria-hidden="true">×</button>
|
||||
<h3>Image</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>Please provide the image URL and an optional title:</p>
|
||||
<div class="input-prepend">
|
||||
<span class="add-on"><i class="icon-picture"></i></span><input
|
||||
id="input-insert-image" type="text" class="span5" placeholder='http://example.com/image.jpg "optional title"'></input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a href="#" class="btn action-close-insert-link" data-dismiss="modal">Cancel</a> <a href="#"
|
||||
class="btn btn-primary action-insert-image" data-dismiss="modal">OK</a>
|
||||
</div>
|
||||
</div>
|
||||
<div id="modal-insert-image" class="modal hide">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close action-close-insert-link"
|
||||
data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h3>Image</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>Please provide the image URL and an optional title:</p>
|
||||
<div class="input-prepend">
|
||||
<span class="add-on"><i class="icon-picture"></i></span><input
|
||||
id="input-insert-image" type="text" class="span5"
|
||||
placeholder='http://example.com/image.jpg "optional title"'></input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a href="#" class="btn action-close-insert-link" data-dismiss="modal">Cancel</a>
|
||||
<a href="#" class="btn btn-primary action-insert-image"
|
||||
data-dismiss="modal">OK</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="modal-remove-file-confirm" class="modal hide">
|
||||
<div class="modal-header">
|
||||
@ -141,6 +148,55 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="modal-upload-gdrive" class="modal hide">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal"
|
||||
aria-hidden="true">×</button>
|
||||
<h3>Export</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>This will upload the current document into your Google Drive
|
||||
root folder and keep it synchronized.</p>
|
||||
<p class="muted"><b>NOTE:</b> You can move or rename the file
|
||||
within Google Drive afterwards.</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a href="#" class="btn" data-dismiss="modal">Cancel</a> <a href="#"
|
||||
data-dismiss="modal"
|
||||
class="btn btn-primary action-upload-gdrive-root">OK</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="modal-upload-dropbox" class="modal hide">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal"
|
||||
aria-hidden="true">×</button>
|
||||
<h3>Export</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>This will upload the current document to your Dropbox account
|
||||
and keep it synchronized.</p>
|
||||
<p>Please specify a file path for "<span class="file-title"></span>":
|
||||
</p>
|
||||
<div class="input-prepend">
|
||||
<span class="add-on"><i class="icon-dropbox"></i></span><input
|
||||
id="upload-dropbox-path" type="text" class="span5"
|
||||
placeholder="/path/to/My Document.md"></input>
|
||||
</div>
|
||||
<br /> <br /> <b class="muted">NOTE:</b>
|
||||
<ul class="muted">
|
||||
<li>Dropbox file path does not depend on document title.</li>
|
||||
<li>The title of your document will not be synchronized.</li>
|
||||
<li>Destination folder must exist.</li>
|
||||
<li>Any existing file at this location will be overwritten.</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a href="#" class="btn" data-dismiss="modal">Cancel</a> <a href="#"
|
||||
data-dismiss="modal" class="btn btn-primary action-upload-dropbox">OK</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="modal-manage-sync" class="modal hide">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal"
|
||||
@ -148,11 +204,10 @@
|
||||
<h3>Synchronization</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div id="manage-sync-list">
|
||||
<p class="msg-sync-list hide">"<span class="file-title"></span>"
|
||||
is synchronized with these locations:
|
||||
</p>
|
||||
</div>
|
||||
<p class="msg-sync-list hide">"<span class="file-title"></span>"
|
||||
is synchronized with these locations:
|
||||
</p>
|
||||
<div id="manage-sync-list"></div>
|
||||
<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
|
||||
@ -161,12 +216,21 @@
|
||||
<p>Add a synchronized location manually:</p>
|
||||
<div class="input-prepend input-append">
|
||||
<span class="add-on"><i class="icon-gdrive"></i></span><input
|
||||
id="manual-gdrive-fileid" type="text" class="span5" placeholder="Google Drive file ID"></input>
|
||||
<a class="btn action-manual-gdrive" data-dismiss="modal"><i class="icon-ok"></i></a>
|
||||
id="manual-gdrive-fileid" type="text" class="span5"
|
||||
placeholder="Google Drive file ID"></input> <a
|
||||
class="btn action-manual-gdrive" title="Add this location"
|
||||
data-dismiss="modal"><i class="icon-ok"></i></a>
|
||||
</div>
|
||||
<div class="input-prepend input-append">
|
||||
<span class="add-on"><i class="icon-dropbox"></i></span><input
|
||||
id="manual-dropbox-path" type="text" class="span5"
|
||||
placeholder="Dropbox file path"></input> <a
|
||||
class="btn action-manual-dropbox" title="Add this location"
|
||||
data-dismiss="modal"><i class="icon-ok"></i></a>
|
||||
</div>
|
||||
<p class="muted"><b>NOTE:</b> Adding a synchronized location will
|
||||
first upload the local document and overwrite the existing file on the
|
||||
server.</p>
|
||||
upload the local document firstly and overwrite the existing file on
|
||||
the server.</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a href="#" class="btn btn-primary" data-dismiss="modal">Close</a>
|
||||
@ -181,7 +245,7 @@
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<dl class="dl-horizontal">
|
||||
<dt>Layout orientation</dt>
|
||||
<dt>Layout orientation:</dt>
|
||||
<dd>
|
||||
<label class="radio"> <input type="radio"
|
||||
name="radio-layout-orientation" value="horizontal">
|
||||
@ -239,25 +303,25 @@
|
||||
</dl>
|
||||
<dl>
|
||||
<dt>Credit:</dt>
|
||||
<dd>
|
||||
<a target="_blank" href="http://twitter.github.com/bootstrap/">Bootstrap</a>
|
||||
</dd>
|
||||
<dd>
|
||||
<a target="_blank" href="http://glyphicons.com/">GLYPHICONS</a>
|
||||
</dd>
|
||||
<dd>
|
||||
<a target="_blank" href="https://github.com/stanlemon/jGrowl/">jGrowl</a>
|
||||
</dd>
|
||||
<dd>
|
||||
<a target="_blank" href="http://twitter.github.com/bootstrap/">Bootstrap</a>
|
||||
</dd>
|
||||
<dd>
|
||||
<a target="_blank" href="http://glyphicons.com/">GLYPHICONS</a>
|
||||
</dd>
|
||||
<dd>
|
||||
<a target="_blank" href="https://github.com/stanlemon/jGrowl/">jGrowl</a>
|
||||
</dd>
|
||||
<dd>
|
||||
<a target="_blank" href="http://jquery.com/">jQuery</a>
|
||||
</dd>
|
||||
<dd>
|
||||
<a target="_blank" href="http://layout.jquery-dev.net/">jQuery UI
|
||||
Layout</a>
|
||||
</dd>
|
||||
<dd>
|
||||
<a target="_blank" href="https://code.google.com/p/pagedown/">PageDown</a>
|
||||
</dd>
|
||||
<dd>
|
||||
<a target="_blank" href="http://layout.jquery-dev.net/">jQuery
|
||||
UI Layout</a>
|
||||
</dd>
|
||||
<dd>
|
||||
<a target="_blank" href="https://code.google.com/p/pagedown/">PageDown</a>
|
||||
</dd>
|
||||
<dd>
|
||||
<a target="_blank" href="http://requirejs.org/">RequireJS</a>
|
||||
</dd>
|
||||
@ -272,5 +336,6 @@
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div id="dropboxjs" data-app-key="x0k2l8puemfvg0o"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -84,7 +84,7 @@ define(["core"], function(core) {
|
||||
};
|
||||
|
||||
function runSafe(func) {
|
||||
if(currentTask.finished === true) {
|
||||
if(currentTask === undefined || currentTask.finished === true) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
@ -107,6 +107,7 @@ define(["core"], function(core) {
|
||||
// Add a task in the queue
|
||||
asyncTaskRunner.addTask = function(asyncTask) {
|
||||
asyncTaskQueue.push(asyncTask);
|
||||
asyncTaskRunner.runTask();
|
||||
};
|
||||
|
||||
return asyncTaskRunner;
|
||||
|
@ -1,14 +1,17 @@
|
||||
var GOOGLE_SCOPES = [ 'https://www.googleapis.com/auth/drive.install',
|
||||
'https://www.googleapis.com/auth/drive' ];
|
||||
var GOOGLE_DRIVE_APP_ID = "241271498917";
|
||||
var DROPBOX_APP_KEY = "lq6mwopab8wskas";
|
||||
var DROPBOX_APP_SECRET = "851fgnucpezy84t";
|
||||
var DEFAULT_FILE_TITLE = "Title";
|
||||
var GDRIVE_DEFAULT_FILE_TITLE = "New Markdown document";
|
||||
var CHECK_ONLINE_PERIOD = 60000;
|
||||
var AJAX_TIMEOUT = 10000;
|
||||
var ASYNC_TASK_DEFAULT_TIMEOUT = 30000;
|
||||
var AUTH_POPUP_TIMEOUT = 90000;
|
||||
var SYNC_PERIOD = 60000;
|
||||
var SYNC_PERIOD = 180000;
|
||||
var SYNC_PROVIDER_GDRIVE = "sync.gdrive.";
|
||||
var SYNC_PROVIDER_DROPBOX = "sync.dropbox.";
|
||||
|
||||
// Use by Google's client.js
|
||||
var delayedFunction = undefined;
|
||||
|
@ -45,7 +45,7 @@ define(["jquery", "bootstrap", "jgrowl", "layout", "Markdown.Editor"], function(
|
||||
core.showMessage = function(msg, iconClass, options) {
|
||||
options = options || {};
|
||||
iconClass = iconClass || "icon-info-sign";
|
||||
$.jGrowl("<i class='icon-white " + iconClass + "'></i> " + msg, options);
|
||||
$.jGrowl("<i class='icon-white " + iconClass + "'></i> " + $("<div>").text(msg).html(), options);
|
||||
};
|
||||
|
||||
// Used to show an error message
|
||||
|
389
js/dropbox.js
Normal file
389
js/dropbox.js
Normal file
@ -0,0 +1,389 @@
|
||||
define(["jquery", "core", "async-runner"], function($, core, asyncTaskRunner) {
|
||||
|
||||
// Dependencies
|
||||
var fileManager = undefined;
|
||||
|
||||
var client = undefined;
|
||||
var authenticated = false;
|
||||
|
||||
var dropbox = {};
|
||||
|
||||
// Try to connect dropbox by downloading client.js
|
||||
function connect(callback) {
|
||||
callback = callback || core.doNothing;
|
||||
var asyncTask = {};
|
||||
asyncTask.run = function() {
|
||||
if(core.isOffline === true) {
|
||||
client = undefined;
|
||||
core.showMessage("Operation not available in offline mode.");
|
||||
asyncTask.error();
|
||||
return;
|
||||
}
|
||||
if (client !== undefined) {
|
||||
asyncTask.success();
|
||||
return;
|
||||
}
|
||||
$.ajax({
|
||||
url : "js/dropbox.min.js",
|
||||
dataType : "script", timeout : AJAX_TIMEOUT
|
||||
}).done(function() {
|
||||
asyncTask.success();
|
||||
}).fail(function() {
|
||||
asyncTask.error();
|
||||
});
|
||||
};
|
||||
asyncTask.onSuccess = function() {
|
||||
client = new Dropbox.Client({
|
||||
key: DROPBOX_APP_KEY,
|
||||
secret: DROPBOX_APP_SECRET
|
||||
});
|
||||
client.authDriver(new Dropbox.Drivers.Popup({
|
||||
receiverUrl: "http://localhost/dropbox-oauth-receiver.html",
|
||||
rememberUser: true
|
||||
}));
|
||||
callback();
|
||||
};
|
||||
asyncTask.onError = function() {
|
||||
core.setOffline();
|
||||
callback();
|
||||
};
|
||||
asyncTaskRunner.addTask(asyncTask);
|
||||
}
|
||||
|
||||
// Try to authenticate with Oauth
|
||||
function authenticate(callback, immediate) {
|
||||
callback = callback || core.doNothing;
|
||||
if (immediate === undefined) {
|
||||
immediate = true;
|
||||
}
|
||||
connect(function() {
|
||||
if (client === undefined) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
|
||||
var asyncTask = {};
|
||||
asyncTask.run = function() {
|
||||
if (authenticated === true) {
|
||||
asyncTask.success();
|
||||
return;
|
||||
}
|
||||
if (immediate === false) {
|
||||
core.showMessage("Please make sure the Dropbox authorization popup is not blocked by your browser.");
|
||||
}
|
||||
client.authenticate({interactive: !immediate}, function(error, client) {
|
||||
if (client.authState !== Dropbox.Client.DONE) {
|
||||
// Handle error
|
||||
asyncTask.error();
|
||||
return;
|
||||
}
|
||||
|
||||
asyncTask.success();
|
||||
});
|
||||
};
|
||||
asyncTask.onSuccess = function() {
|
||||
callback();
|
||||
};
|
||||
asyncTask.onError = function() {
|
||||
// If immediate did not work retry without immediate flag
|
||||
if (client !== undefined && immediate === true) {
|
||||
authenticate(callback, false);
|
||||
return;
|
||||
}
|
||||
callback();
|
||||
};
|
||||
asyncTaskRunner.addTask(asyncTask);
|
||||
});
|
||||
}
|
||||
|
||||
dropbox.upload = function(path, content, callback) {
|
||||
callback = callback || core.doNothing;
|
||||
authenticate(function() {
|
||||
if (client === undefined) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
|
||||
var fileSyncIndex = undefined;
|
||||
var asyncTask = {};
|
||||
asyncTask.run = function() {
|
||||
client.writeFile(path, content, function(error, stat) {
|
||||
if (!error) {
|
||||
fileSyncIndex = SYNC_PROVIDER_DROPBOX + encodeURIComponent(stat.path.toLowerCase());
|
||||
localStorage[fileSyncIndex + ".version"] = stat.versionTag;
|
||||
asyncTask.success();
|
||||
return;
|
||||
}
|
||||
// Handle error
|
||||
if(error.status === Dropbox.ApiError.INVALID_PARAM) {
|
||||
error = 'Could not upload document into path "' + path + '".';
|
||||
}
|
||||
handleError(error, asyncTask, callback);
|
||||
});
|
||||
};
|
||||
asyncTask.onSuccess = function() {
|
||||
callback(fileSyncIndex);
|
||||
};
|
||||
asyncTaskRunner.addTask(asyncTask);
|
||||
});
|
||||
};
|
||||
|
||||
dropbox.checkUpdates = function(lastChangeId, callback) {
|
||||
callback = callback || core.doNothing;
|
||||
authenticate(function() {
|
||||
if (client === undefined) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
|
||||
var changes = [];
|
||||
var newChangeId = lastChangeId || 0;
|
||||
function retrievePageOfChanges(changeId) {
|
||||
var shouldPullAgain = false;
|
||||
var asyncTask = {};
|
||||
asyncTask.run = function() {
|
||||
client.pullChanges(changeId, function(error, pullChanges) {
|
||||
if (pullChanges && pullChanges.cursorTag) {
|
||||
// Retrieve success
|
||||
newChangeId = pullChanges.cursor();
|
||||
shouldPullAgain = pullChanges.shouldPullAgain;
|
||||
if(pullChanges.changes !== undefined) {
|
||||
for(var i=0; i<pullChanges.changes.length; i++) {
|
||||
var item = pullChanges.changes[i];
|
||||
var version = localStorage[SYNC_PROVIDER_DROPBOX
|
||||
+ encodeURIComponent(item.path.toLowerCase()) + ".version"];
|
||||
if(version && (item.wasRemoved || item.stat.versionTag != version)) {
|
||||
changes.push(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
asyncTask.success();
|
||||
return;
|
||||
}
|
||||
// Handle error
|
||||
handleError(error, asyncTask, callback);
|
||||
});
|
||||
};
|
||||
asyncTask.onSuccess = function() {
|
||||
if (shouldPullAgain === true) {
|
||||
retrievePageOfChanges(newChangeId);
|
||||
} else {
|
||||
callback(changes, newChangeId);
|
||||
}
|
||||
};
|
||||
asyncTaskRunner.addTask(asyncTask);
|
||||
}
|
||||
retrievePageOfChanges(newChangeId);
|
||||
});
|
||||
};
|
||||
|
||||
dropbox.downloadMetadata = function(paths, callback, result) {
|
||||
callback = callback || core.doNothing;
|
||||
result = result || [];
|
||||
if(paths.length === 0) {
|
||||
callback(result);
|
||||
return;
|
||||
}
|
||||
|
||||
authenticate(function() {
|
||||
if (client === undefined) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
|
||||
var path = paths.pop();
|
||||
var asyncTask = {};
|
||||
asyncTask.run = function() {
|
||||
client.stat(path, function(error, stat) {
|
||||
if(stat) {
|
||||
result.push(stat);
|
||||
asyncTask.success();
|
||||
return;
|
||||
}
|
||||
handleError(error, asyncTask, callback);
|
||||
});
|
||||
};
|
||||
asyncTask.onSuccess = function() {
|
||||
dropbox.downloadMetadata(paths, callback, result);
|
||||
};
|
||||
asyncTaskRunner.addTask(asyncTask);
|
||||
});
|
||||
};
|
||||
|
||||
dropbox.downloadContent = function(objects, callback, result) {
|
||||
callback = callback || core.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.isFile === true) {
|
||||
file = object;
|
||||
}
|
||||
// object may be a change
|
||||
else if(object.wasRemoved !== undefined) {
|
||||
file = object.stat;
|
||||
}
|
||||
if(!file) {
|
||||
this.downloadContent(objects, callback, result);
|
||||
return;
|
||||
}
|
||||
|
||||
authenticate(function() {
|
||||
if (client === undefined) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
|
||||
var asyncTask = {};
|
||||
asyncTask.run = function() {
|
||||
client.readFile(file.path, function(error, data) {
|
||||
if(data) {
|
||||
file.content = data;
|
||||
asyncTask.success();
|
||||
return;
|
||||
}
|
||||
handleError(error, asyncTask, callback);
|
||||
});
|
||||
};
|
||||
asyncTask.onSuccess = function() {
|
||||
dropbox.downloadContent(objects, callback, result);
|
||||
};
|
||||
asyncTaskRunner.addTask(asyncTask);
|
||||
});
|
||||
};
|
||||
|
||||
function handleError(error, asyncTask, callback) {
|
||||
var errorMsg = undefined;
|
||||
asyncTask.onError = function() {
|
||||
if (errorMsg !== undefined) {
|
||||
core.showError(errorMsg);
|
||||
}
|
||||
callback();
|
||||
};
|
||||
if (error) {
|
||||
// Try to analyze the error
|
||||
if (typeof error === "string") {
|
||||
errorMsg = error;
|
||||
} else if (error.status === Dropbox.ApiError.INVALID_TOKEN
|
||||
|| error.status === Dropbox.ApiError.OAUTH_ERROR) {
|
||||
authenticated = false;
|
||||
errorMsg = "Access to Dropbox is not authorized.";
|
||||
} else if (error.status === Dropbox.ApiError.NETWORK_ERROR) {
|
||||
client = undefined;
|
||||
authenticated = false;
|
||||
core.setOffline();
|
||||
} else {
|
||||
errorMsg = "Dropbox error ("
|
||||
+ error.status + ").";
|
||||
}
|
||||
}
|
||||
asyncTask.error();
|
||||
}
|
||||
|
||||
var pickerLoaded = false;
|
||||
function loadPicker(callback) {
|
||||
connect(function() {
|
||||
if (client === undefined) {
|
||||
pickerLoaded = false;
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
|
||||
var asyncTask = {};
|
||||
asyncTask.run = function() {
|
||||
if (pickerLoaded === true) {
|
||||
asyncTask.success();
|
||||
return;
|
||||
}
|
||||
$.ajax({
|
||||
url : "https://www.dropbox.com/static/api/1/dropbox.js",
|
||||
dataType : "script", timeout : AJAX_TIMEOUT
|
||||
}).done(function() {
|
||||
asyncTask.success();
|
||||
}).fail(function() {
|
||||
asyncTask.error();
|
||||
});
|
||||
};
|
||||
asyncTask.onSuccess = function() {
|
||||
pickerLoaded = true;
|
||||
callback();
|
||||
};
|
||||
asyncTask.onError = function() {
|
||||
core.setOffline();
|
||||
callback();
|
||||
};
|
||||
asyncTaskRunner.addTask(asyncTask);
|
||||
});
|
||||
}
|
||||
|
||||
dropbox.picker = function(callback) {
|
||||
callback = callback || core.doNothing;
|
||||
loadPicker(function() {
|
||||
if (pickerLoaded === false) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
var options = {};
|
||||
options.multiselect = true;
|
||||
options.linkType = "direct";
|
||||
options.success = function(files) {
|
||||
var paths = [];
|
||||
for(var i=0; i<files.length; i++) {
|
||||
var path = files[i].link;
|
||||
path = path.replace(/.*\/view\/[^\/]*/, "");
|
||||
paths.push(decodeURI(path));
|
||||
}
|
||||
callback(paths);
|
||||
};
|
||||
options.cancel = function() {
|
||||
callback();
|
||||
};
|
||||
Dropbox.choose(options);
|
||||
core.showMessage("Please make sure the Dropbox chooser popup is not blocked by your browser.");
|
||||
});
|
||||
};
|
||||
|
||||
dropbox.importFiles = function(paths) {
|
||||
dropbox.downloadMetadata(paths, function(result) {
|
||||
if(result === undefined) {
|
||||
return;
|
||||
}
|
||||
dropbox.downloadContent(result, function(result) {
|
||||
if(result === undefined) {
|
||||
return;
|
||||
}
|
||||
for(var i=0; i<result.length; i++) {
|
||||
var file = result[i];
|
||||
fileSyncIndex = SYNC_PROVIDER_DROPBOX + encodeURIComponent(file.path.toLowerCase());
|
||||
localStorage[fileSyncIndex + ".version"] = file.versionTag;
|
||||
var fileIndex = fileManager.createFile(file.name, file.content, [fileSyncIndex]);
|
||||
fileManager.selectFile(fileIndex);
|
||||
core.showMessage('"' + file.name + '" imported successfully from Dropbox.');
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
dropbox.init = function(fileManagerModule) {
|
||||
fileManager = fileManagerModule;
|
||||
};
|
||||
|
||||
dropbox.checkPath = function(path) {
|
||||
if(!path.match(/^[^\\<>:"\|?\*]+$/)) {
|
||||
core.showError('"' + path + '" contains invalid characters.');
|
||||
return undefined;
|
||||
}
|
||||
if(path.indexOf("/") !== 0) {
|
||||
return "/" + path;
|
||||
}
|
||||
return path;
|
||||
};
|
||||
|
||||
return dropbox;
|
||||
});
|
5
js/dropbox.min.js
vendored
Normal file
5
js/dropbox.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@ -1,9 +1,11 @@
|
||||
define(["jquery", "core", "gdrive", "synchronizer", "async-runner"], function($, core, gdrive, synchronizer, asyncTaskRunner) {
|
||||
define(["jquery", "core", "gdrive", "dropbox", "synchronizer", "async-runner"],
|
||||
function($, core, gdrive, dropbox, synchronizer, asyncTaskRunner) {
|
||||
|
||||
var fileManager = {};
|
||||
|
||||
fileManager.init = function() {
|
||||
gdrive.init(fileManager);
|
||||
dropbox.init(fileManager);
|
||||
|
||||
var changeSyncButtonState = function() {
|
||||
if(synchronizer.isRunning() || synchronizer.isQueueEmpty() || core.isOffline) {
|
||||
@ -75,7 +77,18 @@ define(["jquery", "core", "gdrive", "synchronizer", "async-runner"], function($,
|
||||
+ core.encodeBase64(content);
|
||||
window.open(uriContent, 'file');
|
||||
});
|
||||
$(".action-upload-gdrive").click(uploadGdrive);
|
||||
$(".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 yet
|
||||
gdrive.picker(function(ids) {
|
||||
if(ids !== undefined && ids.length !== 0) {
|
||||
uploadGdrive(ids[0]);
|
||||
}
|
||||
}, true);
|
||||
});
|
||||
$(".action-download-gdrive").click(function() {
|
||||
gdrive.picker(importGdrive);
|
||||
});
|
||||
@ -84,10 +97,15 @@ define(["jquery", "core", "gdrive", "synchronizer", "async-runner"], function($,
|
||||
manualGdrive(fileId);
|
||||
});
|
||||
$(".action-download-dropbox").click(function() {
|
||||
core.showMessage("Sorry, Dropbox synchronization is not yet available.");
|
||||
dropbox.picker(importDropbox);
|
||||
});
|
||||
$(".action-upload-dropbox").click(function() {
|
||||
core.showMessage("Sorry, Dropbox synchronization is not yet available.");
|
||||
$(".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);
|
||||
});
|
||||
};
|
||||
|
||||
@ -209,12 +227,17 @@ define(["jquery", "core", "gdrive", "synchronizer", "async-runner"], function($,
|
||||
}
|
||||
|
||||
var useGoogleDrive = false;
|
||||
var useDropbox = false;
|
||||
function composeTitle(fileIndex) {
|
||||
var result = localStorage[fileIndex + ".title"];
|
||||
var result = " " + localStorage[fileIndex + ".title"];
|
||||
var sync = localStorage[fileIndex + ".sync"];
|
||||
if (sync.indexOf(";" + SYNC_PROVIDER_DROPBOX) !== -1) {
|
||||
useDropbox = true;
|
||||
result = '<i class="icon-dropbox"></i>' + result;
|
||||
}
|
||||
if (sync.indexOf(";" + SYNC_PROVIDER_GDRIVE) !== -1) {
|
||||
useGoogleDrive = true;
|
||||
result = '<i class="icon-gdrive"></i> ' + result;
|
||||
result = '<i class="icon-gdrive"></i>' + result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -245,6 +268,7 @@ define(["jquery", "core", "gdrive", "synchronizer", "async-runner"], function($,
|
||||
$("#file-selector").append(li);
|
||||
}
|
||||
synchronizer.useGoogleDrive = useGoogleDrive;
|
||||
synchronizer.useDropbox = useDropbox;
|
||||
};
|
||||
|
||||
// Remove a synchronized location
|
||||
@ -258,8 +282,10 @@ define(["jquery", "core", "gdrive", "synchronizer", "async-runner"], function($,
|
||||
refreshManageSync();
|
||||
}
|
||||
}
|
||||
// Remove etag
|
||||
// Remove Google Drive etag
|
||||
localStorage.removeItem(fileSyncIndex + ".etag");
|
||||
// Remove Dropbox version
|
||||
localStorage.removeItem(fileSyncIndex + ".version");
|
||||
};
|
||||
|
||||
// Look for local file associated to a synchronized location
|
||||
@ -277,11 +303,11 @@ define(["jquery", "core", "gdrive", "synchronizer", "async-runner"], function($,
|
||||
return fileIndex;
|
||||
};
|
||||
|
||||
function uploadGdrive() {
|
||||
function uploadGdrive(folderId) {
|
||||
var fileIndex = localStorage["file.current"];
|
||||
var content = localStorage[fileIndex + ".content"];
|
||||
var title = localStorage[fileIndex + ".title"];
|
||||
gdrive.createFile(title, content, function(fileSyncIndex) {
|
||||
gdrive.upload(undefined, folderId, title, content, function(fileSyncIndex) {
|
||||
if (fileSyncIndex === undefined) {
|
||||
return;
|
||||
}
|
||||
@ -342,6 +368,56 @@ define(["jquery", "core", "gdrive", "synchronizer", "async-runner"], function($,
|
||||
});
|
||||
}
|
||||
|
||||
function manualDropbox(path) {
|
||||
if(!path) {
|
||||
return;
|
||||
}
|
||||
path = dropbox.checkPath(path);
|
||||
if(path === undefined) {
|
||||
return;
|
||||
}
|
||||
// Check that file is not synchronized with an other one
|
||||
var fileSyncIndex = SYNC_PROVIDER_DROPBOX + encodeURIComponent(path.toLowerCase());
|
||||
var fileIndex = fileManager.getFileIndexFromSync(fileSyncIndex);
|
||||
if(fileIndex !== undefined) {
|
||||
var title = localStorage[fileIndex + ".title"];
|
||||
core.showError('Path "' + path + '" is already synchronized with "' + title + '"');
|
||||
return;
|
||||
}
|
||||
var fileIndex = localStorage["file.current"];
|
||||
var content = localStorage[fileIndex + ".content"];
|
||||
var title = localStorage[fileIndex + ".title"];
|
||||
dropbox.upload(path, content, function(fileSyncIndex) {
|
||||
if (fileSyncIndex === undefined) {
|
||||
return;
|
||||
}
|
||||
localStorage[fileIndex + ".sync"] += fileSyncIndex + ";";
|
||||
refreshManageSync();
|
||||
fileManager.updateFileTitles();
|
||||
core.showMessage('"' + title
|
||||
+ '" will now be synchronized on Dropbox.');
|
||||
});
|
||||
}
|
||||
|
||||
function importDropbox(paths) {
|
||||
if(paths === undefined) {
|
||||
return;
|
||||
}
|
||||
var importPaths = [];
|
||||
for(var i=0; i<paths.length; i++) {
|
||||
var filePath = paths[i];
|
||||
var fileSyncIndex = SYNC_PROVIDER_DROPBOX + encodeURIComponent(filePath.toLowerCase());
|
||||
var fileIndex = fileManager.getFileIndexFromSync(fileSyncIndex);
|
||||
if(fileIndex !== undefined) {
|
||||
var title = localStorage[fileIndex + ".title"];
|
||||
core.showError('"' + title + '" was already imported');
|
||||
continue;
|
||||
}
|
||||
importPaths.push(filePath);
|
||||
}
|
||||
dropbox.importFiles(importPaths);
|
||||
}
|
||||
|
||||
function refreshManageSync() {
|
||||
var fileIndex = localStorage["file.current"];
|
||||
var fileSyncIndexList = localStorage[fileIndex + ".sync"].split(";");
|
||||
@ -361,12 +437,18 @@ define(["jquery", "core", "gdrive", "synchronizer", "async-runner"], function($,
|
||||
'<i class="icon-gdrive"></i>'));
|
||||
line.append($("<input>").prop("type", "text").prop(
|
||||
"disabled", true).addClass("span5").val(
|
||||
"ID="
|
||||
+ fileSyncIndex.substring(SYNC_PROVIDER_GDRIVE.length)));
|
||||
fileSyncIndex.substring(SYNC_PROVIDER_GDRIVE.length)));
|
||||
}
|
||||
if (fileSyncIndex.indexOf(SYNC_PROVIDER_DROPBOX) === 0) {
|
||||
line.append($("<span>").addClass("add-on").html(
|
||||
'<i class="icon-dropbox"></i>'));
|
||||
line.append($("<input>").prop("type", "text").prop(
|
||||
"disabled", true).addClass("span5").val(
|
||||
decodeURIComponent(fileSyncIndex.substring(SYNC_PROVIDER_DROPBOX.length))));
|
||||
}
|
||||
line.append($("<a>").addClass("btn").html(
|
||||
'<i class="icon-trash"></i>').prop("title",
|
||||
"Remove this synchronized location").click(function() {
|
||||
"Remove this location").click(function() {
|
||||
fileManager.removeSync(fileSyncIndex);
|
||||
fileManager.updateFileTitles();
|
||||
}));
|
||||
|
37
js/gdrive.js
37
js/gdrive.js
@ -34,12 +34,10 @@ define(["jquery", "core", "async-runner"], function($, core, asyncTaskRunner) {
|
||||
});
|
||||
};
|
||||
asyncTask.onSuccess = function() {
|
||||
delayedFunction = undefined;
|
||||
connected = true;
|
||||
callback();
|
||||
};
|
||||
asyncTask.onError = function() {
|
||||
delayedFunction = undefined;
|
||||
core.setOffline();
|
||||
callback();
|
||||
};
|
||||
@ -99,7 +97,7 @@ define(["jquery", "core", "async-runner"], function($, core, asyncTaskRunner) {
|
||||
});
|
||||
}
|
||||
|
||||
function upload(fileId, parentId, title, content, callback) {
|
||||
gdrive.upload = function(fileId, parentId, title, content, callback) {
|
||||
callback = callback || core.doNothing;
|
||||
authenticate(function() {
|
||||
if (connected === false) {
|
||||
@ -172,7 +170,7 @@ define(["jquery", "core", "async-runner"], function($, core, asyncTaskRunner) {
|
||||
};
|
||||
asyncTaskRunner.addTask(asyncTask);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
gdrive.checkUpdates = function(lastChangeId, callback) {
|
||||
callback = callback || core.doNothing;
|
||||
@ -245,10 +243,13 @@ define(["jquery", "core", "async-runner"], function($, core, asyncTaskRunner) {
|
||||
var id = ids.pop();
|
||||
var asyncTask = {};
|
||||
asyncTask.run = function() {
|
||||
var accessToken = gapi.auth.getToken().access_token;
|
||||
var token = gapi.auth.getToken();
|
||||
var headers = {
|
||||
Authorization : token ? "Bearer " + token.access_token: null
|
||||
};
|
||||
$.ajax({
|
||||
url : "https://www.googleapis.com/drive/v2/files/" + id,
|
||||
headers : { "Authorization" : "Bearer " + accessToken },
|
||||
headers : headers,
|
||||
dataType : "json",
|
||||
timeout : AJAX_TIMEOUT
|
||||
}).done(function(data, textStatus, jqXHR) {
|
||||
@ -261,7 +262,7 @@ define(["jquery", "core", "async-runner"], function($, core, asyncTaskRunner) {
|
||||
};
|
||||
// Handle error
|
||||
if(error.code === 404) {
|
||||
error = "File is not available.";
|
||||
error = 'File ID "' + id + '" does not exist on Google Drive.';
|
||||
}
|
||||
handleError(error, asyncTask, callback);
|
||||
});
|
||||
@ -292,7 +293,7 @@ define(["jquery", "core", "async-runner"], function($, core, asyncTaskRunner) {
|
||||
else if(object.kind == "drive#change") {
|
||||
file = object.file;
|
||||
}
|
||||
if(file === undefined) {
|
||||
if(!file) {
|
||||
this.downloadContent(objects, callback, result);
|
||||
return;
|
||||
}
|
||||
@ -305,10 +306,13 @@ define(["jquery", "core", "async-runner"], function($, core, asyncTaskRunner) {
|
||||
|
||||
var asyncTask = {};
|
||||
asyncTask.run = function() {
|
||||
var accessToken = gapi.auth.getToken().access_token;
|
||||
var token = gapi.auth.getToken();
|
||||
var headers = {
|
||||
Authorization : token ? "Bearer " + token.access_token: null
|
||||
};
|
||||
$.ajax({
|
||||
url : file.downloadUrl,
|
||||
headers : { "Authorization" : "Bearer " + accessToken },
|
||||
headers : headers,
|
||||
dataType : "text",
|
||||
timeout : AJAX_TIMEOUT
|
||||
}).done(function(data, textStatus, jqXHR) {
|
||||
@ -365,7 +369,7 @@ define(["jquery", "core", "async-runner"], function($, core, asyncTaskRunner) {
|
||||
|
||||
var pickerLoaded = false;
|
||||
function loadPicker(callback) {
|
||||
authenticate(function() {
|
||||
connect(function() {
|
||||
if (connected === false) {
|
||||
pickerLoaded = false;
|
||||
callback();
|
||||
@ -407,7 +411,6 @@ define(["jquery", "core", "async-runner"], function($, core, asyncTaskRunner) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
|
||||
var view = new google.picker.View(google.picker.ViewId.DOCS);
|
||||
view.setMimeTypes("text/x-markdown,text/plain");
|
||||
var pickerBuilder = new google.picker.PickerBuilder();
|
||||
@ -443,14 +446,6 @@ define(["jquery", "core", "async-runner"], function($, core, asyncTaskRunner) {
|
||||
});
|
||||
};
|
||||
|
||||
gdrive.createFile = function(title, content, callback) {
|
||||
upload(undefined, undefined, title, content, callback);
|
||||
};
|
||||
|
||||
gdrive.updateFile = function(id, title, content, callback) {
|
||||
upload(id, undefined, title, content, callback);
|
||||
};
|
||||
|
||||
gdrive.importFiles = function(ids) {
|
||||
gdrive.downloadMetadata(ids, function(result) {
|
||||
if(result === undefined) {
|
||||
@ -481,7 +476,7 @@ define(["jquery", "core", "async-runner"], function($, core, asyncTaskRunner) {
|
||||
localStorage.removeItem("sync.gdrive.state");
|
||||
state = JSON.parse(state);
|
||||
if (state.action == "create") {
|
||||
upload(undefined, state.folderId, GDRIVE_DEFAULT_FILE_TITLE,
|
||||
gdrive.upload(undefined, state.folderId, GDRIVE_DEFAULT_FILE_TITLE,
|
||||
"", function(fileSyncIndex) {
|
||||
if(fileSyncIndex === undefined) {
|
||||
return;
|
||||
|
@ -6,6 +6,7 @@ if(location.hostname.indexOf("benweet.github.") === 0) {
|
||||
|
||||
// RequireJS configuration
|
||||
requirejs.config({
|
||||
waitSeconds: 0,
|
||||
paths: configPaths,
|
||||
shim: {
|
||||
'jquery-ui': ['jquery'],
|
||||
|
@ -1,4 +1,4 @@
|
||||
define(["jquery", "core", "gdrive"], function($, core, gdrive) {
|
||||
define(["jquery", "core", "gdrive", "dropbox"], function($, core, gdrive, dropbox) {
|
||||
var synchronizer = {};
|
||||
|
||||
// Dependencies
|
||||
@ -6,6 +6,7 @@ define(["jquery", "core", "gdrive"], function($, core, gdrive) {
|
||||
|
||||
// Used to know the providers we are connected to
|
||||
synchronizer.useGoogleDrive = false;
|
||||
synchronizer.useDropbox = false;
|
||||
|
||||
var onSyncBegin = undefined;
|
||||
var onSyncEnd = undefined;
|
||||
@ -60,7 +61,21 @@ define(["jquery", "core", "gdrive"], function($, core, gdrive) {
|
||||
// Try to find the provider
|
||||
if (fileSyncIndex.indexOf(SYNC_PROVIDER_GDRIVE) === 0) {
|
||||
var id = fileSyncIndex.substring(SYNC_PROVIDER_GDRIVE.length);
|
||||
gdrive.updateFile(id, title, content, function(result) {
|
||||
gdrive.upload(id, undefined, title, content, function(result) {
|
||||
if (result !== undefined) {
|
||||
fileUp(fileSyncIndexList, content, title, callback);
|
||||
return;
|
||||
}
|
||||
// If error we put the fileIndex back in the queue
|
||||
synchronizer.addFileForUpload(localStorage["sync.current"]);
|
||||
localStorage.removeItem("sync.current");
|
||||
callback();
|
||||
return;
|
||||
});
|
||||
} else if (fileSyncIndex.indexOf(SYNC_PROVIDER_DROPBOX) === 0) {
|
||||
var path = fileSyncIndex.substring(SYNC_PROVIDER_DROPBOX.length);
|
||||
path = decodeURIComponent(path);
|
||||
dropbox.upload(path, content, function(result) {
|
||||
if (result !== undefined) {
|
||||
fileUp(fileSyncIndexList, content, title, callback);
|
||||
return;
|
||||
@ -157,7 +172,7 @@ define(["jquery", "core", "gdrive"], function($, core, gdrive) {
|
||||
core.showMessage('"' + file.title + '" has been updated from Google Drive.');
|
||||
if(fileIndex == localStorage["file.current"]) {
|
||||
updateFileTitles = false; // Done by next function
|
||||
fileManager.selectFile();
|
||||
fileManager.selectFile(); // Refresh editor
|
||||
}
|
||||
}
|
||||
// Update file etag
|
||||
@ -175,8 +190,78 @@ define(["jquery", "core", "gdrive"], function($, core, gdrive) {
|
||||
});
|
||||
}
|
||||
|
||||
function syncDownDropbox(callback) {
|
||||
if (synchronizer.useDropbox === false) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
var lastChangeId = localStorage[SYNC_PROVIDER_DROPBOX + "lastChangeId"];
|
||||
dropbox.checkUpdates(lastChangeId, function(changes, newChangeId) {
|
||||
if (changes === undefined) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
dropbox.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_DROPBOX + encodeURIComponent(change.path.toLowerCase());
|
||||
var fileIndex = fileManager.getFileIndexFromSync(fileSyncIndex);
|
||||
// No file corresponding (this should never happen...)
|
||||
if(fileIndex === undefined) {
|
||||
// We can remove the stored version
|
||||
localStorage.removeItem(fileSyncIndex + ".version");
|
||||
continue;
|
||||
}
|
||||
var title = localStorage[fileIndex + ".title"];
|
||||
// File deleted
|
||||
if (change.wasRemoved === true) {
|
||||
fileManager.removeSync(fileSyncIndex);
|
||||
updateFileTitles = true;
|
||||
core.showMessage('"' + title + '" has been removed from Dropbox.');
|
||||
continue;
|
||||
}
|
||||
var content = localStorage[fileIndex + ".content"];
|
||||
var file = change.stat;
|
||||
var contentChanged = content != file.content;
|
||||
// If file is in the upload queue we have a conflict
|
||||
if (contentChanged && syncUpQueue.indexOf(";" + fileIndex + ";") !== -1) {
|
||||
fileManager.createFile(title + " (backup)", content);
|
||||
updateFileTitles = true;
|
||||
core.showMessage('Conflict detected on "' + title + '". A backup has been created locally.');
|
||||
}
|
||||
// If file content changed
|
||||
if(contentChanged) {
|
||||
localStorage[fileIndex + ".content"] = file.content;
|
||||
core.showMessage('"' + title + '" has been updated from Dropbox.');
|
||||
if(fileIndex == localStorage["file.current"]) {
|
||||
updateFileTitles = false; // Done by next function
|
||||
fileManager.selectFile(); // Refresh editor
|
||||
}
|
||||
}
|
||||
// Update file version
|
||||
localStorage[fileSyncIndex + ".version"] = file.versionTag;
|
||||
// Synchronize file to others locations
|
||||
synchronizer.addFileForUpload(fileIndex);
|
||||
}
|
||||
if(updateFileTitles) {
|
||||
fileManager.updateFileTitles();
|
||||
}
|
||||
localStorage[SYNC_PROVIDER_DROPBOX
|
||||
+ "lastChangeId"] = newChangeId;
|
||||
callback();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function syncDown(callback) {
|
||||
syncDownGdrive(callback);
|
||||
syncDownGdrive(function() {
|
||||
syncDownDropbox(callback);
|
||||
});
|
||||
};
|
||||
|
||||
var syncRunning = false;
|
||||
|
Loading…
Reference in New Issue
Block a user