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:
*
|
||||||
|
@ -9,7 +9,7 @@ body {
|
|||||||
cursor: progress;
|
cursor: progress;
|
||||||
}
|
}
|
||||||
|
|
||||||
.btn {
|
.btn, .dropdown-menu {
|
||||||
-webkit-user-select: none;
|
-webkit-user-select: none;
|
||||||
-moz-user-select: none;
|
-moz-user-select: none;
|
||||||
-ms-user-select: none;
|
-ms-user-select: none;
|
||||||
@ -35,6 +35,7 @@ div, span, a, ul, li, textarea, input, button {
|
|||||||
|
|
||||||
.dropdown-menu {
|
.dropdown-menu {
|
||||||
border: 1px solid #e2e2e2 !important;
|
border: 1px solid #e2e2e2 !important;
|
||||||
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
.input-prepend input,
|
.input-prepend input,
|
||||||
@ -84,7 +85,6 @@ input.error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
input[disabled], select[disabled], textarea[disabled], input[readonly], select[readonly], textarea[readonly], .input-append .add-on {
|
input[disabled], select[disabled], textarea[disabled], input[readonly], select[readonly], textarea[readonly], .input-append .add-on {
|
||||||
cursor: not-allowed;
|
|
||||||
background-color: #f5f5f5;
|
background-color: #f5f5f5;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -93,7 +93,9 @@ input[disabled], select[disabled], textarea[disabled], input[readonly], select[r
|
|||||||
.btn-primary:active,
|
.btn-primary:active,
|
||||||
.btn-primary.active,
|
.btn-primary.active,
|
||||||
.btn-primary.disabled,
|
.btn-primary.disabled,
|
||||||
.btn-primary[disabled] {
|
.btn-primary[disabled],
|
||||||
|
.btn-group.open .btn.btn-primary.dropdown-toggle {
|
||||||
|
color: #fff;
|
||||||
background-color: #888;
|
background-color: #888;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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>
|
115
index.html
115
index.html
@ -5,6 +5,9 @@
|
|||||||
<link rel="icon" href="img/stackedit-32.ico" type="image/x-icon">
|
<link rel="icon" href="img/stackedit-32.ico" type="image/x-icon">
|
||||||
<link rel="shortcut icon" href="img/stackedit-32.ico"
|
<link rel="shortcut icon" href="img/stackedit-32.ico"
|
||||||
type="image/x-icon">
|
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">
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
<link href="css/main.css" rel="stylesheet" media="screen">
|
<link href="css/main.css" rel="stylesheet" media="screen">
|
||||||
<script data-main="js/main" src="js/require.js"></script>
|
<script data-main="js/main" src="js/require.js"></script>
|
||||||
@ -47,18 +50,18 @@
|
|||||||
<li class="dropdown-submenu"><a href="#"><i
|
<li class="dropdown-submenu"><a href="#"><i
|
||||||
class="icon-gdrive"></i> Google Drive</a>
|
class="icon-gdrive"></i> Google Drive</a>
|
||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
<li><a href="#" class="action-upload-gdrive">Export to
|
<li><a href="#" class="action-download-gdrive">Import
|
||||||
Google Drive</a></li>
|
from Google Drive</a></li>
|
||||||
<li><a href="#" class="action-download-gdrive">Import from Google
|
<li><a href="#" data-toggle="modal"
|
||||||
Drive</a></li>
|
data-target="#modal-upload-gdrive">Export to Google Drive</a></li>
|
||||||
</ul></li>
|
</ul></li>
|
||||||
<li class="dropdown-submenu"><a href="#"><i
|
<li class="dropdown-submenu"><a href="#"><i
|
||||||
class="icon-dropbox"></i> Dropbox</a>
|
class="icon-dropbox"></i> Dropbox</a>
|
||||||
<ul class="dropdown-menu">
|
<ul class="dropdown-menu">
|
||||||
<li><a class="action-upload-dropbox" href="#">Export to
|
|
||||||
Dropbox</a></li>
|
|
||||||
<li><a class="action-download-dropbox" href="#">Import
|
<li><a class="action-download-dropbox" href="#">Import
|
||||||
from Dropbox</a></li>
|
from Dropbox</a></li>
|
||||||
|
<li><a href="#" data-toggle="modal"
|
||||||
|
data-target="#modal-upload-dropbox">Export to Dropbox</a></li>
|
||||||
</ul></li>
|
</ul></li>
|
||||||
<li><a href="#" data-toggle="modal"
|
<li><a href="#" data-toggle="modal"
|
||||||
data-target="#modal-manage-sync"><i class="icon-refresh"></i>
|
data-target="#modal-manage-sync"><i class="icon-refresh"></i>
|
||||||
@ -87,39 +90,43 @@
|
|||||||
|
|
||||||
<div id="modal-insert-link" class="modal hide">
|
<div id="modal-insert-link" class="modal hide">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<button type="button" class="close action-close-insert-link" data-dismiss="modal"
|
<button type="button" class="close action-close-insert-link"
|
||||||
aria-hidden="true">×</button>
|
data-dismiss="modal" aria-hidden="true">×</button>
|
||||||
<h3>Hyperlink</h3>
|
<h3>Hyperlink</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<p>Please provide the link URL and an optional title:</p>
|
<p>Please provide the link URL and an optional title:</p>
|
||||||
<div class="input-prepend">
|
<div class="input-prepend">
|
||||||
<span class="add-on"><i class="icon-globe"></i></span><input
|
<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>
|
id="input-insert-link" type="text" class="span5"
|
||||||
|
placeholder='http://example.com/ "optional title"'></input>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<a href="#" class="btn action-close-insert-link" data-dismiss="modal">Cancel</a> <a href="#"
|
<a href="#" class="btn action-close-insert-link" data-dismiss="modal">Cancel</a>
|
||||||
class="btn btn-primary action-insert-link" data-dismiss="modal">OK</a>
|
<a href="#" class="btn btn-primary action-insert-link"
|
||||||
|
data-dismiss="modal">OK</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="modal-insert-image" class="modal hide">
|
<div id="modal-insert-image" class="modal hide">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<button type="button" class="close action-close-insert-link" data-dismiss="modal"
|
<button type="button" class="close action-close-insert-link"
|
||||||
aria-hidden="true">×</button>
|
data-dismiss="modal" aria-hidden="true">×</button>
|
||||||
<h3>Image</h3>
|
<h3>Image</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<p>Please provide the image URL and an optional title:</p>
|
<p>Please provide the image URL and an optional title:</p>
|
||||||
<div class="input-prepend">
|
<div class="input-prepend">
|
||||||
<span class="add-on"><i class="icon-picture"></i></span><input
|
<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>
|
id="input-insert-image" type="text" class="span5"
|
||||||
|
placeholder='http://example.com/image.jpg "optional title"'></input>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<a href="#" class="btn action-close-insert-link" data-dismiss="modal">Cancel</a> <a href="#"
|
<a href="#" class="btn action-close-insert-link" data-dismiss="modal">Cancel</a>
|
||||||
class="btn btn-primary action-insert-image" data-dismiss="modal">OK</a>
|
<a href="#" class="btn btn-primary action-insert-image"
|
||||||
|
data-dismiss="modal">OK</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -141,6 +148,55 @@
|
|||||||
</div>
|
</div>
|
||||||
</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 id="modal-manage-sync" class="modal hide">
|
||||||
<div class="modal-header">
|
<div class="modal-header">
|
||||||
<button type="button" class="close" data-dismiss="modal"
|
<button type="button" class="close" data-dismiss="modal"
|
||||||
@ -148,11 +204,10 @@
|
|||||||
<h3>Synchronization</h3>
|
<h3>Synchronization</h3>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<div id="manage-sync-list">
|
|
||||||
<p class="msg-sync-list hide">"<span class="file-title"></span>"
|
<p class="msg-sync-list hide">"<span class="file-title"></span>"
|
||||||
is synchronized with these locations:
|
is synchronized with these locations:
|
||||||
</p>
|
</p>
|
||||||
</div>
|
<div id="manage-sync-list"></div>
|
||||||
<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>" is
|
<p class="msg-no-sync hide">"<span class="file-title"></span>" is
|
||||||
@ -161,12 +216,21 @@
|
|||||||
<p>Add a synchronized location manually:</p>
|
<p>Add a synchronized location manually:</p>
|
||||||
<div class="input-prepend input-append">
|
<div class="input-prepend input-append">
|
||||||
<span class="add-on"><i class="icon-gdrive"></i></span><input
|
<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>
|
id="manual-gdrive-fileid" type="text" class="span5"
|
||||||
<a class="btn action-manual-gdrive" data-dismiss="modal"><i class="icon-ok"></i></a>
|
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>
|
</div>
|
||||||
<p class="muted"><b>NOTE:</b> Adding a synchronized location will
|
<p class="muted"><b>NOTE:</b> Adding a synchronized location will
|
||||||
first upload the local document and overwrite the existing file on the
|
upload the local document firstly and overwrite the existing file on
|
||||||
server.</p>
|
the server.</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="modal-footer">
|
<div class="modal-footer">
|
||||||
<a href="#" class="btn btn-primary" data-dismiss="modal">Close</a>
|
<a href="#" class="btn btn-primary" data-dismiss="modal">Close</a>
|
||||||
@ -181,7 +245,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="modal-body">
|
<div class="modal-body">
|
||||||
<dl class="dl-horizontal">
|
<dl class="dl-horizontal">
|
||||||
<dt>Layout orientation</dt>
|
<dt>Layout orientation:</dt>
|
||||||
<dd>
|
<dd>
|
||||||
<label class="radio"> <input type="radio"
|
<label class="radio"> <input type="radio"
|
||||||
name="radio-layout-orientation" value="horizontal">
|
name="radio-layout-orientation" value="horizontal">
|
||||||
@ -252,8 +316,8 @@
|
|||||||
<a target="_blank" href="http://jquery.com/">jQuery</a>
|
<a target="_blank" href="http://jquery.com/">jQuery</a>
|
||||||
</dd>
|
</dd>
|
||||||
<dd>
|
<dd>
|
||||||
<a target="_blank" href="http://layout.jquery-dev.net/">jQuery UI
|
<a target="_blank" href="http://layout.jquery-dev.net/">jQuery
|
||||||
Layout</a>
|
UI Layout</a>
|
||||||
</dd>
|
</dd>
|
||||||
<dd>
|
<dd>
|
||||||
<a target="_blank" href="https://code.google.com/p/pagedown/">PageDown</a>
|
<a target="_blank" href="https://code.google.com/p/pagedown/">PageDown</a>
|
||||||
@ -272,5 +336,6 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
<div id="dropboxjs" data-app-key="x0k2l8puemfvg0o"></div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -84,7 +84,7 @@ define(["core"], function(core) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function runSafe(func) {
|
function runSafe(func) {
|
||||||
if(currentTask.finished === true) {
|
if(currentTask === undefined || currentTask.finished === true) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
@ -107,6 +107,7 @@ define(["core"], function(core) {
|
|||||||
// 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);
|
||||||
|
asyncTaskRunner.runTask();
|
||||||
};
|
};
|
||||||
|
|
||||||
return asyncTaskRunner;
|
return asyncTaskRunner;
|
||||||
|
@ -1,14 +1,17 @@
|
|||||||
var GOOGLE_SCOPES = [ 'https://www.googleapis.com/auth/drive.install',
|
var GOOGLE_SCOPES = [ 'https://www.googleapis.com/auth/drive.install',
|
||||||
'https://www.googleapis.com/auth/drive' ];
|
'https://www.googleapis.com/auth/drive' ];
|
||||||
var GOOGLE_DRIVE_APP_ID = "241271498917";
|
var GOOGLE_DRIVE_APP_ID = "241271498917";
|
||||||
|
var DROPBOX_APP_KEY = "lq6mwopab8wskas";
|
||||||
|
var DROPBOX_APP_SECRET = "851fgnucpezy84t";
|
||||||
var DEFAULT_FILE_TITLE = "Title";
|
var DEFAULT_FILE_TITLE = "Title";
|
||||||
var GDRIVE_DEFAULT_FILE_TITLE = "New Markdown document";
|
var GDRIVE_DEFAULT_FILE_TITLE = "New Markdown document";
|
||||||
var CHECK_ONLINE_PERIOD = 60000;
|
var CHECK_ONLINE_PERIOD = 60000;
|
||||||
var AJAX_TIMEOUT = 10000;
|
var AJAX_TIMEOUT = 10000;
|
||||||
var ASYNC_TASK_DEFAULT_TIMEOUT = 30000;
|
var ASYNC_TASK_DEFAULT_TIMEOUT = 30000;
|
||||||
var AUTH_POPUP_TIMEOUT = 90000;
|
var AUTH_POPUP_TIMEOUT = 90000;
|
||||||
var SYNC_PERIOD = 60000;
|
var SYNC_PERIOD = 180000;
|
||||||
var SYNC_PROVIDER_GDRIVE = "sync.gdrive.";
|
var SYNC_PROVIDER_GDRIVE = "sync.gdrive.";
|
||||||
|
var SYNC_PROVIDER_DROPBOX = "sync.dropbox.";
|
||||||
|
|
||||||
// Use by Google's client.js
|
// Use by Google's client.js
|
||||||
var delayedFunction = undefined;
|
var delayedFunction = undefined;
|
||||||
|
@ -45,7 +45,7 @@ define(["jquery", "bootstrap", "jgrowl", "layout", "Markdown.Editor"], function(
|
|||||||
core.showMessage = function(msg, iconClass, options) {
|
core.showMessage = function(msg, iconClass, options) {
|
||||||
options = options || {};
|
options = options || {};
|
||||||
iconClass = iconClass || "icon-info-sign";
|
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
|
// 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 = {};
|
var fileManager = {};
|
||||||
|
|
||||||
fileManager.init = function() {
|
fileManager.init = function() {
|
||||||
gdrive.init(fileManager);
|
gdrive.init(fileManager);
|
||||||
|
dropbox.init(fileManager);
|
||||||
|
|
||||||
var changeSyncButtonState = function() {
|
var changeSyncButtonState = function() {
|
||||||
if(synchronizer.isRunning() || synchronizer.isQueueEmpty() || core.isOffline) {
|
if(synchronizer.isRunning() || synchronizer.isQueueEmpty() || core.isOffline) {
|
||||||
@ -75,7 +77,18 @@ define(["jquery", "core", "gdrive", "synchronizer", "async-runner"], function($,
|
|||||||
+ core.encodeBase64(content);
|
+ core.encodeBase64(content);
|
||||||
window.open(uriContent, 'file');
|
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() {
|
$(".action-download-gdrive").click(function() {
|
||||||
gdrive.picker(importGdrive);
|
gdrive.picker(importGdrive);
|
||||||
});
|
});
|
||||||
@ -84,10 +97,15 @@ define(["jquery", "core", "gdrive", "synchronizer", "async-runner"], function($,
|
|||||||
manualGdrive(fileId);
|
manualGdrive(fileId);
|
||||||
});
|
});
|
||||||
$(".action-download-dropbox").click(function() {
|
$(".action-download-dropbox").click(function() {
|
||||||
core.showMessage("Sorry, Dropbox synchronization is not yet available.");
|
dropbox.picker(importDropbox);
|
||||||
});
|
});
|
||||||
$(".action-upload-dropbox").click(function() {
|
$(".action-upload-dropbox").click(function(event) {
|
||||||
core.showMessage("Sorry, Dropbox synchronization is not yet available.");
|
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 useGoogleDrive = false;
|
||||||
|
var useDropbox = false;
|
||||||
function composeTitle(fileIndex) {
|
function composeTitle(fileIndex) {
|
||||||
var result = localStorage[fileIndex + ".title"];
|
var result = " " + localStorage[fileIndex + ".title"];
|
||||||
var sync = localStorage[fileIndex + ".sync"];
|
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) {
|
if (sync.indexOf(";" + SYNC_PROVIDER_GDRIVE) !== -1) {
|
||||||
useGoogleDrive = true;
|
useGoogleDrive = true;
|
||||||
result = '<i class="icon-gdrive"></i> ' + result;
|
result = '<i class="icon-gdrive"></i>' + result;
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -245,6 +268,7 @@ define(["jquery", "core", "gdrive", "synchronizer", "async-runner"], function($,
|
|||||||
$("#file-selector").append(li);
|
$("#file-selector").append(li);
|
||||||
}
|
}
|
||||||
synchronizer.useGoogleDrive = useGoogleDrive;
|
synchronizer.useGoogleDrive = useGoogleDrive;
|
||||||
|
synchronizer.useDropbox = useDropbox;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Remove a synchronized location
|
// Remove a synchronized location
|
||||||
@ -258,8 +282,10 @@ define(["jquery", "core", "gdrive", "synchronizer", "async-runner"], function($,
|
|||||||
refreshManageSync();
|
refreshManageSync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Remove etag
|
// Remove Google Drive etag
|
||||||
localStorage.removeItem(fileSyncIndex + ".etag");
|
localStorage.removeItem(fileSyncIndex + ".etag");
|
||||||
|
// Remove Dropbox version
|
||||||
|
localStorage.removeItem(fileSyncIndex + ".version");
|
||||||
};
|
};
|
||||||
|
|
||||||
// Look for local file associated to a synchronized location
|
// Look for local file associated to a synchronized location
|
||||||
@ -277,11 +303,11 @@ define(["jquery", "core", "gdrive", "synchronizer", "async-runner"], function($,
|
|||||||
return fileIndex;
|
return fileIndex;
|
||||||
};
|
};
|
||||||
|
|
||||||
function uploadGdrive() {
|
function uploadGdrive(folderId) {
|
||||||
var fileIndex = localStorage["file.current"];
|
var fileIndex = localStorage["file.current"];
|
||||||
var content = localStorage[fileIndex + ".content"];
|
var content = localStorage[fileIndex + ".content"];
|
||||||
var title = localStorage[fileIndex + ".title"];
|
var title = localStorage[fileIndex + ".title"];
|
||||||
gdrive.createFile(title, content, function(fileSyncIndex) {
|
gdrive.upload(undefined, folderId, title, content, function(fileSyncIndex) {
|
||||||
if (fileSyncIndex === undefined) {
|
if (fileSyncIndex === undefined) {
|
||||||
return;
|
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() {
|
function refreshManageSync() {
|
||||||
var fileIndex = localStorage["file.current"];
|
var fileIndex = localStorage["file.current"];
|
||||||
var fileSyncIndexList = localStorage[fileIndex + ".sync"].split(";");
|
var fileSyncIndexList = localStorage[fileIndex + ".sync"].split(";");
|
||||||
@ -361,12 +437,18 @@ define(["jquery", "core", "gdrive", "synchronizer", "async-runner"], function($,
|
|||||||
'<i class="icon-gdrive"></i>'));
|
'<i class="icon-gdrive"></i>'));
|
||||||
line.append($("<input>").prop("type", "text").prop(
|
line.append($("<input>").prop("type", "text").prop(
|
||||||
"disabled", true).addClass("span5").val(
|
"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(
|
line.append($("<a>").addClass("btn").html(
|
||||||
'<i class="icon-trash"></i>').prop("title",
|
'<i class="icon-trash"></i>').prop("title",
|
||||||
"Remove this synchronized location").click(function() {
|
"Remove this location").click(function() {
|
||||||
fileManager.removeSync(fileSyncIndex);
|
fileManager.removeSync(fileSyncIndex);
|
||||||
fileManager.updateFileTitles();
|
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() {
|
asyncTask.onSuccess = function() {
|
||||||
delayedFunction = undefined;
|
|
||||||
connected = true;
|
connected = true;
|
||||||
callback();
|
callback();
|
||||||
};
|
};
|
||||||
asyncTask.onError = function() {
|
asyncTask.onError = function() {
|
||||||
delayedFunction = undefined;
|
|
||||||
core.setOffline();
|
core.setOffline();
|
||||||
callback();
|
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;
|
callback = callback || core.doNothing;
|
||||||
authenticate(function() {
|
authenticate(function() {
|
||||||
if (connected === false) {
|
if (connected === false) {
|
||||||
@ -172,7 +170,7 @@ define(["jquery", "core", "async-runner"], function($, core, asyncTaskRunner) {
|
|||||||
};
|
};
|
||||||
asyncTaskRunner.addTask(asyncTask);
|
asyncTaskRunner.addTask(asyncTask);
|
||||||
});
|
});
|
||||||
}
|
};
|
||||||
|
|
||||||
gdrive.checkUpdates = function(lastChangeId, callback) {
|
gdrive.checkUpdates = function(lastChangeId, callback) {
|
||||||
callback = callback || core.doNothing;
|
callback = callback || core.doNothing;
|
||||||
@ -245,10 +243,13 @@ define(["jquery", "core", "async-runner"], function($, core, asyncTaskRunner) {
|
|||||||
var id = ids.pop();
|
var id = ids.pop();
|
||||||
var asyncTask = {};
|
var asyncTask = {};
|
||||||
asyncTask.run = function() {
|
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({
|
$.ajax({
|
||||||
url : "https://www.googleapis.com/drive/v2/files/" + id,
|
url : "https://www.googleapis.com/drive/v2/files/" + id,
|
||||||
headers : { "Authorization" : "Bearer " + accessToken },
|
headers : headers,
|
||||||
dataType : "json",
|
dataType : "json",
|
||||||
timeout : AJAX_TIMEOUT
|
timeout : AJAX_TIMEOUT
|
||||||
}).done(function(data, textStatus, jqXHR) {
|
}).done(function(data, textStatus, jqXHR) {
|
||||||
@ -261,7 +262,7 @@ define(["jquery", "core", "async-runner"], function($, core, asyncTaskRunner) {
|
|||||||
};
|
};
|
||||||
// Handle error
|
// Handle error
|
||||||
if(error.code === 404) {
|
if(error.code === 404) {
|
||||||
error = "File is not available.";
|
error = 'File ID "' + id + '" does not exist on Google Drive.';
|
||||||
}
|
}
|
||||||
handleError(error, asyncTask, callback);
|
handleError(error, asyncTask, callback);
|
||||||
});
|
});
|
||||||
@ -292,7 +293,7 @@ define(["jquery", "core", "async-runner"], function($, core, asyncTaskRunner) {
|
|||||||
else if(object.kind == "drive#change") {
|
else if(object.kind == "drive#change") {
|
||||||
file = object.file;
|
file = object.file;
|
||||||
}
|
}
|
||||||
if(file === undefined) {
|
if(!file) {
|
||||||
this.downloadContent(objects, callback, result);
|
this.downloadContent(objects, callback, result);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -305,10 +306,13 @@ define(["jquery", "core", "async-runner"], function($, core, asyncTaskRunner) {
|
|||||||
|
|
||||||
var asyncTask = {};
|
var asyncTask = {};
|
||||||
asyncTask.run = function() {
|
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({
|
$.ajax({
|
||||||
url : file.downloadUrl,
|
url : file.downloadUrl,
|
||||||
headers : { "Authorization" : "Bearer " + accessToken },
|
headers : headers,
|
||||||
dataType : "text",
|
dataType : "text",
|
||||||
timeout : AJAX_TIMEOUT
|
timeout : AJAX_TIMEOUT
|
||||||
}).done(function(data, textStatus, jqXHR) {
|
}).done(function(data, textStatus, jqXHR) {
|
||||||
@ -365,7 +369,7 @@ define(["jquery", "core", "async-runner"], function($, core, asyncTaskRunner) {
|
|||||||
|
|
||||||
var pickerLoaded = false;
|
var pickerLoaded = false;
|
||||||
function loadPicker(callback) {
|
function loadPicker(callback) {
|
||||||
authenticate(function() {
|
connect(function() {
|
||||||
if (connected === false) {
|
if (connected === false) {
|
||||||
pickerLoaded = false;
|
pickerLoaded = false;
|
||||||
callback();
|
callback();
|
||||||
@ -407,7 +411,6 @@ define(["jquery", "core", "async-runner"], function($, core, asyncTaskRunner) {
|
|||||||
callback();
|
callback();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var view = new google.picker.View(google.picker.ViewId.DOCS);
|
var view = new google.picker.View(google.picker.ViewId.DOCS);
|
||||||
view.setMimeTypes("text/x-markdown,text/plain");
|
view.setMimeTypes("text/x-markdown,text/plain");
|
||||||
var pickerBuilder = new google.picker.PickerBuilder();
|
var pickerBuilder = new google.picker.PickerBuilder();
|
||||||
@ -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.importFiles = function(ids) {
|
||||||
gdrive.downloadMetadata(ids, function(result) {
|
gdrive.downloadMetadata(ids, function(result) {
|
||||||
if(result === undefined) {
|
if(result === undefined) {
|
||||||
@ -481,7 +476,7 @@ define(["jquery", "core", "async-runner"], function($, core, asyncTaskRunner) {
|
|||||||
localStorage.removeItem("sync.gdrive.state");
|
localStorage.removeItem("sync.gdrive.state");
|
||||||
state = JSON.parse(state);
|
state = JSON.parse(state);
|
||||||
if (state.action == "create") {
|
if (state.action == "create") {
|
||||||
upload(undefined, state.folderId, GDRIVE_DEFAULT_FILE_TITLE,
|
gdrive.upload(undefined, state.folderId, GDRIVE_DEFAULT_FILE_TITLE,
|
||||||
"", function(fileSyncIndex) {
|
"", function(fileSyncIndex) {
|
||||||
if(fileSyncIndex === undefined) {
|
if(fileSyncIndex === undefined) {
|
||||||
return;
|
return;
|
||||||
|
@ -6,6 +6,7 @@ if(location.hostname.indexOf("benweet.github.") === 0) {
|
|||||||
|
|
||||||
// RequireJS configuration
|
// RequireJS configuration
|
||||||
requirejs.config({
|
requirejs.config({
|
||||||
|
waitSeconds: 0,
|
||||||
paths: configPaths,
|
paths: configPaths,
|
||||||
shim: {
|
shim: {
|
||||||
'jquery-ui': ['jquery'],
|
'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 = {};
|
var synchronizer = {};
|
||||||
|
|
||||||
// Dependencies
|
// Dependencies
|
||||||
@ -6,6 +6,7 @@ define(["jquery", "core", "gdrive"], function($, core, gdrive) {
|
|||||||
|
|
||||||
// Used to know the providers we are connected to
|
// Used to know the providers we are connected to
|
||||||
synchronizer.useGoogleDrive = false;
|
synchronizer.useGoogleDrive = false;
|
||||||
|
synchronizer.useDropbox = false;
|
||||||
|
|
||||||
var onSyncBegin = undefined;
|
var onSyncBegin = undefined;
|
||||||
var onSyncEnd = undefined;
|
var onSyncEnd = undefined;
|
||||||
@ -60,7 +61,21 @@ define(["jquery", "core", "gdrive"], function($, core, gdrive) {
|
|||||||
// Try to find the provider
|
// Try to find the provider
|
||||||
if (fileSyncIndex.indexOf(SYNC_PROVIDER_GDRIVE) === 0) {
|
if (fileSyncIndex.indexOf(SYNC_PROVIDER_GDRIVE) === 0) {
|
||||||
var id = fileSyncIndex.substring(SYNC_PROVIDER_GDRIVE.length);
|
var id = fileSyncIndex.substring(SYNC_PROVIDER_GDRIVE.length);
|
||||||
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) {
|
if (result !== undefined) {
|
||||||
fileUp(fileSyncIndexList, content, title, callback);
|
fileUp(fileSyncIndexList, content, title, callback);
|
||||||
return;
|
return;
|
||||||
@ -157,7 +172,7 @@ define(["jquery", "core", "gdrive"], function($, core, gdrive) {
|
|||||||
core.showMessage('"' + file.title + '" has been updated from Google Drive.');
|
core.showMessage('"' + file.title + '" has been updated from Google Drive.');
|
||||||
if(fileIndex == localStorage["file.current"]) {
|
if(fileIndex == localStorage["file.current"]) {
|
||||||
updateFileTitles = false; // Done by next function
|
updateFileTitles = false; // Done by next function
|
||||||
fileManager.selectFile();
|
fileManager.selectFile(); // Refresh editor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Update file etag
|
// 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) {
|
function syncDown(callback) {
|
||||||
syncDownGdrive(callback);
|
syncDownGdrive(function() {
|
||||||
|
syncDownDropbox(callback);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
var syncRunning = false;
|
var syncRunning = false;
|
||||||
|
Loading…
Reference in New Issue
Block a user