New synchronize pattern

This commit is contained in:
benweet 2013-04-21 01:07:27 +01:00
parent 2f1471af05
commit 425a7f0528
20 changed files with 5860 additions and 263 deletions

View File

@ -1 +1 @@
CACHE MANIFEST # v41 CACHE: index.html css/bootstrap.css css/jgrowl.css css/main.css css/prettify.css js/main-min.js js/require.js img/ajax-loader.gif img/glyphicons-halflings.png img/glyphicons-halflings-white.png img/icons.png img/stackedit-32.ico img/stackedit-promo.png NETWORK: *
CACHE MANIFEST # v1 CACHE: index.html css/main-css.css js/main-min.js js/require.js img/ajax-loader.gif img/glyphicons-halflings.png img/glyphicons-halflings-white.png img/icons.png img/stackedit-32.ico img/stackedit-promo.png NETWORK: *

5647
css/main-min.css vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -7,7 +7,7 @@ var redirectUrl = location.href.substring(0, location.href.indexOf("gdrive-actio
var state = decodeURI((/state=(.+?)(&|$)/
.exec(location.search) || [ , null ])[1]);
if(state) {
localStorage["sync.gdrive.state"] = state;
localStorage["gdrive.state"] = state;
}
window.location.replace(redirectUrl);

View File

@ -11,8 +11,9 @@
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">
<link href="css/main-min.css" rel="stylesheet" media="screen">
<script>
// http://.../?debug to serve original JavaScript files for debug
var dep = "main-min";
if (location.search.indexOf("debug") !== -1) {
dep = "main";
@ -184,13 +185,13 @@
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"
aria-hidden="true">&times;</button>
<h3>Google Drive export</h3>
<h3>Export to Google Drive</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>
afterwards within Google Drive.</p>
</div>
<div class="modal-footer">
<a href="#" class="btn" data-dismiss="modal">Cancel</a> <a href="#"
@ -203,7 +204,7 @@
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal"
aria-hidden="true">&times;</button>
<h3>Dropbox export</h3>
<h3>Export to Dropbox</h3>
</div>
<div class="modal-body">
<p>This will upload the current document to your Dropbox account
@ -225,7 +226,8 @@
</div>
<div class="modal-footer">
<a href="#" class="btn" data-dismiss="modal">Cancel</a> <a href="#"
data-dismiss="modal" class="btn btn-primary action-sync-export-dropbox">OK</a>
data-dismiss="modal"
class="btn btn-primary action-sync-export-dropbox">OK</a>
</div>
</div>
@ -260,9 +262,8 @@
class="btn action-sync-manual-dropbox" title="Add location"
data-dismiss="modal"><i class="icon-ok"></i></a>
</div>
<p class="muted"><b>NOTE:</b> Adding a synchronized location will
upload the local document firstly and overwrite the existing file on
the server.</p>
<p class="muted"><b>NOTE:</b> This will 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>
@ -335,12 +336,24 @@
</div>
<div class="control-group modal-publish-gdrive">
<div class="controls muted">
<b>NOTE:</b> If no file ID
is supplied, the file will be created into your Google Drive root
folder. You can move the file within Google Drive afterwards.
If no file ID is supplied, the file will be created
into your Google Drive root folder. You can move the file afterwards within
Google Drive.
</div>
</div>
<div class="control-group modal-publish-gdrive">
<label class="control-label" for="input-publish-gdrive-filename">Force file name (optional)</label>
<div class="controls">
<input type="text" id="input-publish-gdrive-filename"
placeholder="File name">
</div>
</div>
<div class="control-group modal-publish-gdrive">
<div class="controls muted">
If no filename is supplied, the document title will be used.
</div>
</div>
<div class="control-group">
<div class="control-label">Format</div>
<div class="controls">

View File

@ -1,14 +1,11 @@
/**
* Used to run asynchronous tasks sequentially (ajax mainly) An asynchronous
* Used to run asynchronous tasks sequentially (ajax mainly). An asynchronous
* task is composed of different callback types: onRun, onSuccess, onError
*/
define([ "underscore" ], function() {
define([ "core", "underscore" ], function(core) {
var asyncRunner = {};
// Dependencies
var core = undefined;
var taskQueue = [];
var currentTask = undefined;
var currentTaskRunning = false;
@ -21,8 +18,8 @@ define([ "underscore" ], function() {
task.retryCounter = 0;
/**
* onRun callbacks are called by chain(). These callbacks have to call
* chain() themselves to chain with next callback or to finish the task
* and call onSuccess callbacks.
* chain() themselves to chain with next onRun callback or error() to
* throw an exception or retry() to restart the task.
*/
// Run callbacks
task.runCallbacks = [];
@ -75,7 +72,8 @@ define([ "underscore" ], function() {
runCallback();
};
/**
* error() calls the onError callbacks.
* error() calls the onError callbacks with the error parameter and ends
* the task by throwing an exception.
*/
task.error = function(error) {
if (task.finished === true) {
@ -177,9 +175,5 @@ define([ "underscore" ], function() {
}
};
asyncRunner.init = function(coreModule) {
core = coreModule;
};
return asyncRunner;
});

View File

@ -1,7 +1,4 @@
define(["jquery", "google-helper"], function($, googleHelper) {
// Dependencies
var core = undefined;
define(["jquery", "core", "google-helper"], function($, core, googleHelper) {
var bloggerProvider = {
providerId: PROVIDER_BLOGGER,
@ -33,9 +30,5 @@ define(["jquery", "google-helper"], function($, googleHelper) {
return publishAttributes;
};
bloggerProvider.init = function(coreModule) {
core = coreModule;
};
return bloggerProvider;
});

View File

@ -1,16 +1,10 @@
define(
[ "jquery", "file-manager", "google-helper", "dropbox-helper",
"github-helper", "synchronizer", "publisher", "async-runner",
"bootstrap", "jgrowl", "layout", "Markdown.Editor", "config",
[ "jquery", "bootstrap", "jgrowl", "layout", "Markdown.Editor", "config",
"underscore" ],
function($, fileManager, googleHelper, dropboxHelper, githubHelper,
synchronizer, publisher, asyncRunner) {
function($) {
var core = {};
// The interval Id for periodic tasks
var intervalId = undefined;
// Usage: callback = callback || core.doNothing;
core.doNothing = function() {};
@ -20,6 +14,13 @@ define(
core.currentTime = new Date().getTime();
}
// Used for periodic tasks
var intervalId = undefined;
var periodicCallbacks = [];
core.addPeriodicCallback = function(callback) {
periodicCallbacks.push(callback);
};
// Used to detect user activity
var userReal = false;
var userActive = false;
@ -118,6 +119,10 @@ define(
if(!msg) {
return;
}
var endOfMsg = msg.indexOf("|");
if(endOfMsg !== -1) {
msg = msg.substring(0, endOfMsg);
}
options = options || {};
iconClass = iconClass || "icon-info-sign";
$.jGrowl("<i class='icon-white " + iconClass + "'></i> " + _.escape(msg), options);
@ -132,7 +137,9 @@ define(
core.isOffline = false;
var offlineTime = core.currentTime;
var offlineListeners = [];
core.addOfflineListener = function(callback) {
offlineListeners.push(callback);
};
core.setOffline = function() {
offlineTime = core.currentTime;
if(core.isOffline === false) {
@ -148,7 +155,6 @@ define(
}
}
};
core.setOnline = function() {
if(core.isOffline === true) {
$(".msg-offline").parents(".jGrowl-notification").trigger(
@ -159,7 +165,6 @@ define(
}
}
};
function checkOnline() {
// Try to reconnect if we are offline but we have some network
if (core.isOffline === true && navigator.onLine === true
@ -469,7 +474,6 @@ define(
function setupLocalStorage() {
if (localStorage["file.list"] === undefined) {
localStorage["file.list"] = ";";
localStorage["version"] = "v2";
}
}
@ -488,12 +492,12 @@ define(
var fileIndexList = _.compact(localStorage["file.list"].split(";"));
_.each(fileIndexList, function(fileIndex) {
localStorage[fileIndex + ".publish"] = ";";
var fileSyncIndexList = _.compact(localStorage[fileIndex + ".sync"].split(";"));
_.each(fileSyncIndexList, function(fileSyncIndex) {
localStorage[fileSyncIndex + ".contentCRC"] = "0";
var syncIndexList = _.compact(localStorage[fileIndex + ".sync"].split(";"));
_.each(syncIndexList, function(syncIndex) {
localStorage[syncIndex + ".contentCRC"] = "0";
// We store title CRC only for Google Drive synchronization
if(localStorage[fileSyncIndex + ".etag"] !== undefined) {
localStorage[fileSyncIndex + ".titleCRC"] = "0";
if(localStorage[syncIndex + ".etag"] !== undefined) {
localStorage[syncIndex + ".titleCRC"] = "0";
}
});
});
@ -503,37 +507,41 @@ define(
// from v1 to v2
if(version == "v1") {
var gdriveLastChangeId = localStorage["sync.gdrive.lastChangeId"];
localStorage["gdrive.lastChangeId"] = gdriveLastChangeId;
localStorage.removeItem("sync.gdrive.lastChangeId");
if(gdriveLastChangeId) {
localStorage["gdrive.lastChangeId"] = gdriveLastChangeId;
localStorage.removeItem("sync.gdrive.lastChangeId");
}
var dropboxLastChangeId = localStorage["sync.dropbox.lastChangeId"];
localStorage["dropbox.lastChangeId"] = dropboxLastChangeId;
localStorage.removeItem("sync.dropbox.lastChangeId");
if(dropboxLastChangeId) {
localStorage["dropbox.lastChangeId"] = dropboxLastChangeId;
localStorage.removeItem("sync.dropbox.lastChangeId");
}
var SYNC_PROVIDER_GDRIVE = "sync." + PROVIDER_GDRIVE + ".";
var SYNC_PROVIDER_DROPBOX = "sync." + PROVIDER_DROPBOX + ".";
var fileIndexList = _.compact(localStorage["file.list"].split(";"));
_.each(fileIndexList, function(fileIndex) {
var fileSyncIndexList = _.compact(localStorage[fileIndex + ".sync"].split(";"));
_.each(fileSyncIndexList, function(fileSyncIndex) {
var syncIndexList = _.compact(localStorage[fileIndex + ".sync"].split(";"));
_.each(syncIndexList, function(syncIndex) {
var syncAttributes = {};
if (fileSyncIndex.indexOf(SYNC_PROVIDER_GDRIVE) === 0) {
if (syncIndex.indexOf(SYNC_PROVIDER_GDRIVE) === 0) {
syncAttributes.provider = PROVIDER_GDRIVE;
syncAttributes.id = fileSyncIndex.substring(SYNC_PROVIDER_GDRIVE.length);
syncAttributes.etag = localStorage[fileSyncIndex + ".etag"];
syncAttributes.contentCRC = localStorage[fileSyncIndex + ".contentCRC"];
syncAttributes.titleCRC = localStorage[fileSyncIndex + ".titleCRC"];
syncAttributes.id = syncIndex.substring(SYNC_PROVIDER_GDRIVE.length);
syncAttributes.etag = localStorage[syncIndex + ".etag"];
syncAttributes.contentCRC = localStorage[syncIndex + ".contentCRC"];
syncAttributes.titleCRC = localStorage[syncIndex + ".titleCRC"];
}
else if (fileSyncIndex.indexOf(SYNC_PROVIDER_DROPBOX) === 0) {
else if (syncIndex.indexOf(SYNC_PROVIDER_DROPBOX) === 0) {
syncAttributes.provider = PROVIDER_DROPBOX;
syncAttributes.path = decodeURIComponent(fileSyncIndex.substring(SYNC_PROVIDER_DROPBOX.length));
syncAttributes.version = localStorage[fileSyncIndex + ".version"];
syncAttributes.contentCRC = localStorage[fileSyncIndex + ".contentCRC"];
syncAttributes.path = decodeURIComponent(syncIndex.substring(SYNC_PROVIDER_DROPBOX.length));
syncAttributes.version = localStorage[syncIndex + ".version"];
syncAttributes.contentCRC = localStorage[syncIndex + ".contentCRC"];
}
localStorage[fileSyncIndex] = JSON.stringify(syncAttributes);
localStorage.remoteItem(fileSyncIndex + ".etag");
localStorage.remoteItem(fileSyncIndex + ".version");
localStorage.remoteItem(fileSyncIndex + ".contentCRC");
localStorage.remoteItem(fileSyncIndex + ".titleCRC");
localStorage[syncIndex] = JSON.stringify(syncAttributes);
localStorage.removeItem(syncIndex + ".etag");
localStorage.removeItem(syncIndex + ".version");
localStorage.removeItem(syncIndex + ".contentCRC");
localStorage.removeItem(syncIndex + ".titleCRC");
});
});
version = "v2";
@ -559,7 +567,7 @@ define(
+ left);
};
core.init = function() {
$(function() {
setupLocalStorage();
upgradeLocalStorage();
@ -626,38 +634,18 @@ define(
core.resetModalInputs();
});
// Init asyncRunner
asyncRunner.init(core);
// Init helpers
googleHelper.init(core);
dropboxHelper.init(core);
githubHelper.init(core);
// Init synchronizer
synchronizer.init(core, fileManager);
// Init publisher
publisher.init(core, fileManager);
offlineListeners.push(synchronizer.updateSyncButton);
offlineListeners.push(publisher.updatePublishButton);
// Init file manager
fileManager.init(core);
// Do periodic tasks
intervalId = window.setInterval(function() {
updateCurrentTime();
core.checkWindowUnique();
if(isUserActive() === false) {
return;
if(isUserActive() === true) {
_.each(periodicCallbacks, function(callback) {
callback();
});
checkOnline();
}
synchronizer.sync();
asyncRunner.runTask();
checkOnline();
}, 1000);
};
});
return core;
});

View File

@ -1,7 +1,4 @@
define(["jquery", "async-runner"], function($, asyncRunner) {
// Dependencies
var core = undefined;
define(["jquery", "core", "async-runner"], function($, core, asyncRunner) {
var client = undefined;
var authenticated = false;
@ -317,9 +314,5 @@ define(["jquery", "async-runner"], function($, asyncRunner) {
asyncRunner.addTask(task);
};
dropboxHelper.init = function(coreModule) {
core = coreModule;
};
return dropboxHelper;
});

View File

@ -1,8 +1,4 @@
define(["jquery", "dropbox-helper"], function($, dropboxHelper) {
// Dependencies
var core = undefined;
var fileManager = undefined;
define(["jquery", "core", "dropbox-helper"], function($, core, dropboxHelper) {
var dropboxProvider = {
providerId: PROVIDER_DROPBOX,
@ -31,7 +27,7 @@ define(["jquery", "dropbox-helper"], function($, dropboxHelper) {
syncAttributes.path = path;
syncAttributes.version = versionTag;
syncAttributes.contentCRC = core.crc32(content);
var syncIndex = "sync." + PROVIDER_DROPBOX + "." + encodeURIComponent(file.path.toLowerCase());
var syncIndex = "sync." + PROVIDER_DROPBOX + "." + encodeURIComponent(path.toLowerCase());
localStorage[syncIndex] = JSON.stringify(syncAttributes);
return syncIndex;
}
@ -47,8 +43,8 @@ define(["jquery", "dropbox-helper"], function($, dropboxHelper) {
}
_.each(result, function(file) {
var syncIndex = createSyncAttributes(file.path, file.versionTag, file.content);
var fileIndex = fileManager.createFile(file.name, file.content, [syncIndex]);
fileManager.selectFile(fileIndex);
var fileIndex = core.fileManager.createFile(file.name, file.content, [syncIndex]);
core.fileManager.selectFile(fileIndex);
core.showMessage('"' + file.name + '" imported successfully from Dropbox.');
});
});
@ -63,7 +59,7 @@ define(["jquery", "dropbox-helper"], function($, dropboxHelper) {
var importPaths = [];
_.each(paths, function(path) {
var syncIndex = "sync." + PROVIDER_DROPBOX + "." + encodeURIComponent(path.toLowerCase());
var fileIndex = fileManager.getFileIndexFromSync(syncIndex);
var fileIndex = core.fileManager.getFileIndexFromSync(syncIndex);
if(fileIndex !== undefined) {
var title = localStorage[fileIndex + ".title"];
core.showError('"' + title + '" was already imported');
@ -71,19 +67,19 @@ define(["jquery", "dropbox-helper"], function($, dropboxHelper) {
}
importPaths.push(path);
});
dropboxHelper.importFiles(importPaths);
importFilesFromPaths(importPaths);
});
};
function exportFileToPath(path, title, content, callback) {
path = dropboxHelper.checkPath(path);
path = checkPath(path);
if(path === undefined) {
callback(true);
return;
}
// Check that file is not synchronized with an other one
var syncIndex = "sync." + PROVIDER_DROPBOX + "." + encodeURIComponent(path.toLowerCase());
var fileIndex = fileManager.getFileIndexFromSync(syncIndex);
var fileIndex = core.fileManager.getFileIndexFromSync(syncIndex);
if(fileIndex !== undefined) {
var existingTitle = localStorage[fileIndex + ".title"];
core.showError('File path is already synchronized with "' + existingTitle + '"');
@ -102,12 +98,12 @@ define(["jquery", "dropbox-helper"], function($, dropboxHelper) {
dropboxProvider.exportFile = function(event, title, content, callback) {
var path = core.getInputValue($("#input-sync-export-dropbox-path"), event);
exportFileToPath(path);
exportFileToPath(path, title, content, callback);
};
dropboxProvider.exportManual = function(event, title, content, callback) {
var path = core.getInputValue($("#input-sync-manual-dropbox-path"), event);
exportFileToPath(path);
exportFileToPath(path, title, content, callback);
};
dropboxProvider.syncUp = function(uploadContent, uploadContentCRC, uploadTitle, uploadTitleCRC, syncAttributes, callback) {
@ -128,12 +124,12 @@ define(["jquery", "dropbox-helper"], function($, dropboxHelper) {
});
};
function syncDown(callback) {
dropboxProvider.syncDown = function(callback) {
if (dropboxProvider.useSync === false) {
callback();
return;
}
var lastChangeId = parseInt(localStorage[PROVIDER_DROPBOX + ".lastChangeId"]);
var lastChangeId = localStorage[PROVIDER_DROPBOX + ".lastChangeId"];
dropboxHelper.checkChanges(lastChangeId, function(error, changes, newChangeId) {
if (error) {
callback(error);
@ -141,7 +137,7 @@ define(["jquery", "dropbox-helper"], function($, dropboxHelper) {
}
var interestingChanges = [];
_.each(changes, function(change) {
var syncIndex = "sync." + PROVIDER_DROPBOX + "." + encodeURIComponent(file.path.toLowerCase());
var syncIndex = "sync." + PROVIDER_DROPBOX + "." + encodeURIComponent(change.path.toLowerCase());
var serializedAttributes = localStorage[syncIndex];
if(serializedAttributes === undefined) {
return;
@ -161,7 +157,7 @@ define(["jquery", "dropbox-helper"], function($, dropboxHelper) {
change.syncAttributes = syncAttributes;
}
});
dropboxHelper.downloadContent(changes, function(error, changes) {
dropboxHelper.downloadContent(interestingChanges, function(error, changes) {
if (error) {
callback(error);
return;
@ -169,16 +165,16 @@ define(["jquery", "dropbox-helper"], function($, dropboxHelper) {
var updateFileTitles = false;
_.each(changes, function(change) {
var syncIndex = change.syncIndex;
var fileIndex = fileManager.getFileIndexFromSync(syncIndex);
var fileIndex = core.fileManager.getFileIndexFromSync(syncIndex);
// No file corresponding (file may have been deleted locally)
if(fileIndex === undefined) {
fileManager.removeSync(syncIndex);
core.fileManager.removeSync(syncIndex);
return;
}
var localTitle = localStorage[fileIndex + ".title"];
// File deleted
if (change.wasRemoved === true) {
fileManager.removeSync(syncIndex);
core.fileManager.removeSync(syncIndex);
updateFileTitles = true;
core.showMessage('"' + localTitle + '" has been removed from Dropbox.');
return;
@ -190,7 +186,7 @@ define(["jquery", "dropbox-helper"], function($, dropboxHelper) {
var fileContentChanged = localContent != file.content;
// Conflict detection
if (fileContentChanged === true && localContentChanged === true) {
fileManager.createFile(localTitle + " (backup)", localContent);
core.fileManager.createFile(localTitle + " (backup)", localContent);
updateFileTitles = true;
core.showMessage('Conflict detected on "' + localTitle + '". A backup has been created locally.');
}
@ -198,9 +194,9 @@ define(["jquery", "dropbox-helper"], function($, dropboxHelper) {
if(fileContentChanged) {
localStorage[fileIndex + ".content"] = file.content;
core.showMessage('"' + localTitle + '" has been updated from Dropbox.');
if(fileManager.isCurrentFileIndex(fileIndex)) {
if(core.fileManager.isCurrentFileIndex(fileIndex)) {
updateFileTitles = false; // Done by next function
fileManager.selectFile(); // Refresh editor
core.fileManager.selectFile(); // Refresh editor
}
}
// Update syncAttributes
@ -209,13 +205,13 @@ define(["jquery", "dropbox-helper"], function($, dropboxHelper) {
localStorage[syncIndex] = JSON.stringify(syncAttributes);
});
if(updateFileTitles) {
fileManager.updateFileTitles();
core.fileManager.updateFileTitles();
}
localStorage[PROVIDER_DROPBOX + ".lastChangeId"] = newChangeId;
callback();
});
});
}
};
dropboxProvider.publish = function(publishAttributes, title, content, callback) {
var path = checkPath(publishAttributes.path);
@ -235,10 +231,5 @@ define(["jquery", "dropbox-helper"], function($, dropboxHelper) {
return publishAttributes;
};
dropboxProvider.init = function(coreModule, fileManagerModule) {
core = coreModule;
fileManager = fileManagerModule;
};
return dropboxProvider;
});

View File

@ -1,11 +1,10 @@
define(["jquery", "google-helper", "dropbox-helper", "github-helper", "synchronizer", "publisher", "underscore"],
function($, googleHelper, dropboxHelper, githubHelper, synchronizer, publisher) {
define(["jquery", "core", "synchronizer", "publisher", "underscore"],
function($, core, synchronizer, publisher) {
var fileManager = {};
// To avoid circle inclusion
core.fileManager = fileManager;
// Dependencies
var core = undefined;
// Defines the current file
var currentFileIndex = localStorage["file.current"];
fileManager.getCurrentFileIndex = function() {
@ -141,7 +140,7 @@ define(["jquery", "google-helper", "dropbox-helper", "github-helper", "synchroni
var result = " " + localStorage[fileIndex + ".title"];
var providerIdList = synchronizer.getSyncProvidersFromFile(fileIndex);
_.each(providerIdList, function(providerId) {
result = '<i class="icon-' + providerId '"></i>' + result;
result = '<i class="icon-' + providerId + '"></i>' + result;
});
return result;
}
@ -217,9 +216,7 @@ define(["jquery", "google-helper", "dropbox-helper", "github-helper", "synchroni
}).value();
};
fileManager.init = function(coreModule) {
core = coreModule;
$(function() {
fileManager.selectFile();
$(".action-create-file").click(function() {
@ -280,7 +277,7 @@ define(["jquery", "google-helper", "dropbox-helper", "github-helper", "synchroni
+ core.encodeBase64(content);
window.open(uriContent, 'file');
});
};
});
return fileManager;
});

View File

@ -1,12 +1,8 @@
define(["jquery", "google-helper", "underscore"], function($, googleHelper) {
// Dependencies
var core = undefined;
var fileManager = undefined;
define(["jquery", "core", "google-helper", "underscore"], function($, core, googleHelper) {
var gdriveProvider = {
providerId: PROVIDER_GDRIVE,
providerName: "Gdrive",
providerName: "Google Drive",
defaultPublishFormat: "template",
useSync: false
};
@ -18,7 +14,7 @@ define(["jquery", "google-helper", "underscore"], function($, googleHelper) {
syncAttributes.etag = etag;
syncAttributes.contentCRC = core.crc32(content);
syncAttributes.titleCRC = core.crc32(title);
var syncIndex = "sync." + PROVIDER_GDRIVE + "." + file.id;
var syncIndex = "sync." + PROVIDER_GDRIVE + "." + id;
localStorage[syncIndex] = JSON.stringify(syncAttributes);
return syncIndex;
}
@ -34,8 +30,8 @@ define(["jquery", "google-helper", "underscore"], function($, googleHelper) {
}
_.each(result, function(file) {
var syncIndex = createSyncAttributes(file.id, file.etag, file.content, file.title);
var fileIndex = fileManager.createFile(file.title, file.content, [syncIndex]);
fileManager.selectFile(fileIndex);
var fileIndex = core.fileManager.createFile(file.title, file.content, [syncIndex]);
core.fileManager.selectFile(fileIndex);
core.showMessage('"' + file.title + '" imported successfully from Google Drive.');
});
});
@ -50,7 +46,7 @@ define(["jquery", "google-helper", "underscore"], function($, googleHelper) {
var importIds = [];
_.each(ids, function(id) {
var syncIndex = "sync." + PROVIDER_GDRIVE + "." + id;
var fileIndex = fileManager.getFileIndexFromSync(syncIndex);
var fileIndex = core.fileManager.getFileIndexFromSync(syncIndex);
if(fileIndex !== undefined) {
var title = localStorage[fileIndex + ".title"];
core.showError('"' + title + '" was already imported');
@ -80,7 +76,7 @@ define(["jquery", "google-helper", "underscore"], function($, googleHelper) {
}
// Check that file is not synchronized with an other one
var syncIndex = "sync." + PROVIDER_GDRIVE + "." + id;
var fileIndex = fileManager.getFileIndexFromSync(syncIndex);
var fileIndex = core.fileManager.getFileIndexFromSync(syncIndex);
if(fileIndex !== undefined) {
var existingTitle = localStorage[fileIndex + ".title"];
core.showError('File ID is already synchronized with "' + existingTitle + '"');
@ -117,7 +113,7 @@ define(["jquery", "google-helper", "underscore"], function($, googleHelper) {
});
};
function syncDown(callback) {
gdriveProvider.syncDown = function(callback) {
if (gdriveProvider.useSync === false) {
callback();
return;
@ -130,7 +126,7 @@ define(["jquery", "google-helper", "underscore"], function($, googleHelper) {
}
var interestingChanges = [];
_.each(changes, function(change) {
var syncIndex = "sync." + PROVIDER_GDRIVE + "." + file.id;
var syncIndex = "sync." + PROVIDER_GDRIVE + "." + change.fileId;
var serializedAttributes = localStorage[syncIndex];
if(serializedAttributes === undefined) {
return;
@ -150,7 +146,7 @@ define(["jquery", "google-helper", "underscore"], function($, googleHelper) {
change.syncAttributes = syncAttributes;
}
});
googleHelper.downloadContent(changes, function(error, changes) {
googleHelper.downloadContent(interestingChanges, function(error, changes) {
if (error) {
callback(error);
return;
@ -158,16 +154,16 @@ define(["jquery", "google-helper", "underscore"], function($, googleHelper) {
var updateFileTitles = false;
_.each(changes, function(change) {
var syncIndex = change.syncIndex;
var fileIndex = fileManager.getFileIndexFromSync(syncIndex);
var fileIndex = core.fileManager.getFileIndexFromSync(syncIndex);
// No file corresponding (file may have been deleted locally)
if(fileIndex === undefined) {
fileManager.removeSync(syncIndex);
core.fileManager.removeSync(syncIndex);
return;
}
var localTitle = localStorage[fileIndex + ".title"];
// File deleted
if (change.deleted === true) {
fileManager.removeSync(syncIndex);
core.fileManager.removeSync(syncIndex);
updateFileTitles = true;
core.showMessage('"' + localTitle + '" has been removed from Google Drive.');
return;
@ -182,7 +178,7 @@ define(["jquery", "google-helper", "underscore"], function($, googleHelper) {
// Conflict detection
if ((fileTitleChanged === true && localTitleChanged === true)
|| (fileContentChanged === true && localContentChanged === true)) {
fileManager.createFile(localTitle + " (backup)", localContent);
core.fileManager.createFile(localTitle + " (backup)", localContent);
updateFileTitles = true;
core.showMessage('Conflict detected on "' + localTitle + '". A backup has been created locally.');
}
@ -196,9 +192,9 @@ define(["jquery", "google-helper", "underscore"], function($, googleHelper) {
if(fileContentChanged) {
localStorage[fileIndex + ".content"] = file.content;
core.showMessage('"' + file.title + '" has been updated from Google Drive.');
if(fileManager.isCurrentFileIndex(fileIndex)) {
if(core.fileManager.isCurrentFileIndex(fileIndex)) {
updateFileTitles = false; // Done by next function
fileManager.selectFile(); // Refresh editor
core.fileManager.selectFile(); // Refresh editor
}
}
// Update syncAttributes
@ -208,36 +204,48 @@ define(["jquery", "google-helper", "underscore"], function($, googleHelper) {
localStorage[syncIndex] = JSON.stringify(syncAttributes);
});
if(updateFileTitles) {
fileManager.updateFileTitles();
core.fileManager.updateFileTitles();
}
localStorage[PROVIDER_GDRIVE + ".lastChangeId"] = newChangeId;
callback();
});
});
}
};
gdriveProvider.publish = function(publishAttributes, title, content, callback) {
googleHelper.upload(publishAttributes.fileId, undefined, title, content, undefined, callback);
googleHelper.upload(
publishAttributes.fileId,
undefined,
publishAttributes.fileName || title,
content,
undefined,
function(error, result) {
if(error) {
callback(error);
return;
}
publishAttributes.fileId = result.id;
callback();
}
);
};
gdriveProvider.newPublishAttributes = function(event) {
var publishAttributes = {};
publishAttributes.fileId = $("#input-publish-gdrive-fileid").val() || undefined;
publishAttributes.fileName = $("#input-publish-gdrive-filename").val() || undefined;
if(event.isPropagationStopped()) {
return undefined;
}
return publishAttributes;
};
gdriveProvider.init = function(coreModule, fileManagerModule) {
core = coreModule;
fileManager = fileManagerModule;
var state = localStorage["sync.gdrive.state"];
$(function() {
var state = localStorage[PROVIDER_GDRIVE + ".state"];
if(state === undefined) {
return;
}
localStorage.removeItem("sync.gdrive.state");
localStorage.removeItem(PROVIDER_GDRIVE + ".state");
state = JSON.parse(state);
if (state.action == "create") {
googleHelper.upload(undefined, state.folderId, GDRIVE_DEFAULT_FILE_TITLE,
@ -265,7 +273,7 @@ define(["jquery", "google-helper", "underscore"], function($, googleHelper) {
});
importFilesFromIds(importIds);
}
};
});
return gdriveProvider;
});

View File

@ -1,7 +1,4 @@
define(["jquery", "async-runner"], function($, asyncRunner) {
// Dependencies
var core = undefined;
define(["jquery", "core", "async-runner"], function($, core, asyncRunner) {
var connected = undefined;
var github = undefined;
@ -10,7 +7,6 @@ define(["jquery", "async-runner"], function($, asyncRunner) {
// Try to connect github by downloading js file
function connect(task) {
callback = callback || core.doNothing;
task.onRun(function() {
if(core.isOffline === true) {
connected = false;
@ -161,9 +157,5 @@ define(["jquery", "async-runner"], function($, asyncRunner) {
asyncRunner.addTask(task);
};
githubHelper.init = function(coreModule) {
core = coreModule;
};
return githubHelper;
});

View File

@ -1,7 +1,4 @@
define(["jquery", "github-helper"], function($, githubHelper) {
// Dependencies
var core = undefined;
define(["jquery", "core", "github-helper"], function($, core, githubHelper) {
var githubProvider = {
providerId: PROVIDER_GITHUB,
@ -25,9 +22,5 @@ define(["jquery", "github-helper"], function($, githubHelper) {
return publishAttributes;
};
githubProvider.init = function(coreModule) {
core = coreModule;
};
return githubProvider;
});

View File

@ -1,7 +1,4 @@
define(["jquery", "async-runner"], function($, asyncRunner) {
// Dependencies
var core = undefined;
define(["jquery", "core", "async-runner"], function($, core, asyncRunner) {
var connected = false;
var authenticated = false;
@ -132,7 +129,7 @@ define(["jquery", "async-runner"], function($, asyncRunner) {
// Handle error
if(error !== undefined && fileId !== undefined) {
if(error.code === 404) {
error = 'File ID "' + fileId + '" does not exist on Google Drive.';
error = 'File ID "' + fileId + '" not found on Google Drive.|removePublish';
}
else if(error.code === 412) {
// We may have missed a file update
@ -235,7 +232,7 @@ define(["jquery", "async-runner"], function($, asyncRunner) {
};
// Handle error
if(error.code === 404) {
error = 'File ID "' + id + '" does not exist on Google Drive.';
error = 'File ID "' + id + '" not found on Google Drive.';
}
handleError(error, task, callback);
});
@ -458,7 +455,7 @@ define(["jquery", "async-runner"], function($, asyncRunner) {
};
// Handle error
if(error.code === 404 && postId !== undefined) {
error = 'Post ' + postId + ' not found on Blogger.';
error = 'Post ' + postId + ' not found on Blogger.|removePublish';
}
handleError(error, task, callback);
});
@ -483,25 +480,21 @@ define(["jquery", "async-runner"], function($, asyncRunner) {
};
// Handle error
if(error.code === 404) {
error = 'Blog "' + blogUrl + '" not found on Blogger.';
error = 'Blog "' + blogUrl + '" not found on Blogger.|removePublish';
}
handleError(error, task, callback);
});
}
task.chain(getBlogId);
});
task.onSuccess = function() {
task.onSuccess(function() {
callback(undefined, blogId, postId);
};
task.onError = function(error) {
});
task.onError(function(error) {
callback(error);
};
});
asyncRunner.addTask(task);
};
googleHelper.init = function(coreModule) {
core = coreModule;
};
return googleHelper;
});

10
js/main-min.js vendored

File diff suppressed because one or more lines are too long

View File

@ -12,9 +12,8 @@ requirejs.config({
}
});
require(["jquery", "core"], function($, core) {
require(["jquery", "file-manager", "synchronizer", "publisher"], function($) {
$(function() {
// If browser has detected a new application cache.
if (window.applicationCache) {
window.applicationCache.addEventListener('updateready', function(e) {
@ -24,7 +23,5 @@ require(["jquery", "core"], function($, core) {
}
}, false);
}
core.init();
});
});

View File

@ -1,12 +1,8 @@
define(["jquery", "github-provider", "blogger-provider", "dropbox-provider", "gdrive-provider", "underscore"], function($) {
// Dependencies
var core = undefined;
var fileManager = undefined;
define(["jquery", "core", "github-provider", "blogger-provider", "dropbox-provider", "gdrive-provider", "underscore"], function($, core) {
var publisher = {};
// Create a map with providerName: providerObject
// Create a map with providerId: providerObject
var providerMap = _.chain(arguments)
.map(function(argument) {
return argument && argument.providerId && [argument.providerId, argument];
@ -17,7 +13,7 @@ define(["jquery", "github-provider", "blogger-provider", "dropbox-provider", "gd
// Allows external modules to update hasPublications flag
publisher.notifyPublish = function() {
var fileIndex = fileManager.getCurrentFileIndex();
var fileIndex = core.fileManager.getCurrentFileIndex();
// Check that file has publications
if(localStorage[fileIndex + ".publish"].length === 1) {
@ -42,7 +38,7 @@ define(["jquery", "github-provider", "blogger-provider", "dropbox-provider", "gd
// Apply template to the current document
publisher.applyTemplate = function(publishAttributes) {
var fileIndex = fileManager.getCurrentFileIndex();
var fileIndex = core.fileManager.getCurrentFileIndex();
try {
return _.template(core.settings.template, {
documentTitle: localStorage[fileIndex + ".title"],
@ -91,6 +87,10 @@ define(["jquery", "github-provider", "blogger-provider", "dropbox-provider", "gd
// Call the provider
var provider = providerMap[publishAttributes.provider];
provider.publish(publishAttributes, publishTitle, content, function(error) {
if(error !== undefined && error.toString().indexOf("|removePublish") !== -1) {
core.fileManager.removePublish(publishIndex);
core.showMessage(provider.providerName + " publish location has been removed.");
}
publishLocation(callback, errorFlag || error );
});
}
@ -104,7 +104,7 @@ define(["jquery", "github-provider", "blogger-provider", "dropbox-provider", "gd
publishRunning = true;
publisher.updatePublishButton();
var fileIndex = fileManager.getCurrentFileIndex();
var fileIndex = core.fileManager.getCurrentFileIndex();
publishTitle = localStorage[fileIndex + ".title"];
publishIndexList = _.compact(localStorage[fileIndex + ".publish"].split(";"));
publishLocation(function(errorFlag) {
@ -151,7 +151,7 @@ define(["jquery", "github-provider", "blogger-provider", "dropbox-provider", "gd
if(publishAttributes === undefined) {
return;
}
var fileIndex = fileManager.getCurrentFileIndex();
var fileIndex = core.fileManager.getCurrentFileIndex();
var title = localStorage[fileIndex + ".title"];
var content = getPublishContent(publishAttributes);
provider.publish(publishAttributes, title, content, function(error) {
@ -173,7 +173,7 @@ define(["jquery", "github-provider", "blogger-provider", "dropbox-provider", "gd
'</div>'].join("");
var removeButtonTemplate = '<a class="btn" title="Remove this location"><i class="icon-trash"></i></a>';
publisher.refreshManagePublish = function() {
var fileIndex = fileManager.getCurrentFileIndex();
var fileIndex = core.fileManager.getCurrentFileIndex();
var publishIndexList = _.compact(localStorage[fileIndex + ".publish"].split(";"));
$(".msg-no-publish, .msg-publish-list").addClass("hide");
$("#manage-publish-list .input-append").remove();
@ -190,19 +190,17 @@ define(["jquery", "github-provider", "blogger-provider", "dropbox-provider", "gd
publishDesc: publishDesc
}));
lineElement.append($(removeButtonTemplate).click(function() {
fileManager.removePublish(publishIndex);
core.fileManager.removePublish(publishIndex);
}));
$("#manage-publish-list").append(lineElement);
});
};
publisher.init = function(coreModule, fileManagerModule) {
core = coreModule;
fileManager = fileManagerModule;
$(function() {
core.addOfflineListener(publisher.updatePublishButton);
// Init each provider
_.each(providerMap, function(provider) {
provider.init(core, fileManager);
// Publish provider button
$(".action-publish-" + provider.providerId).click(function() {
initNewLocation(provider);
@ -243,7 +241,7 @@ define(["jquery", "github-provider", "blogger-provider", "dropbox-provider", "gd
$(document).click(function(e) {
$(".tooltip-template").tooltip('hide');
});
};
});
return publisher;
});

View File

@ -1,20 +1,12 @@
define(["jquery", "google-helper", "dropbox-helper", "dropbox-provider", "gdrive-provider"], function($, googleHelper, dropboxHelper) {
define(["jquery", "core", "dropbox-provider", "gdrive-provider", "underscore"], function($, core) {
var synchronizer = {};
// Dependencies
var core = undefined;
var fileManager = undefined;
// Create a map with providerName: providerObject
// Create a map with providerId: providerObject
var providerMap = _.chain(arguments)
.map(function(argument) {
return argument && argument.providerId && [argument.providerId, argument];
}).compact().object().value();
// Used to know the providers we are connected to
synchronizer.useGoogleDrive = false;
synchronizer.useDropbox = false;
// Used to know if user can force synchronization
var uploadPending = false;
@ -116,7 +108,7 @@ define(["jquery", "google-helper", "dropbox-helper", "dropbox-provider", "gdrive
locationUp(callback);
}
// Used to upload document changes from local storage
// Entry point for up synchronization (upload changes)
var uploadCycle = false;
function syncUp(callback) {
if(uploadCycle === true) {
@ -135,6 +127,7 @@ define(["jquery", "google-helper", "dropbox-helper", "dropbox-provider", "gdrive
function providerDown(callback) {
if(providerList.length === 0) {
callback();
return;
}
var provider = providerList.pop();
provider.syncDown(function(error) {
@ -146,11 +139,13 @@ define(["jquery", "google-helper", "dropbox-helper", "dropbox-provider", "gdrive
});
}
// Entry point for down synchronization (download changes)
function syncDown(callback) {
providerList = _.values(providerMap);
providerDown(callback);
};
// Main entry point for synchronization
var syncRunning = false;
var lastSync = 0;
synchronizer.sync = function() {
@ -195,7 +190,7 @@ define(["jquery", "google-helper", "dropbox-helper", "dropbox-provider", "gdrive
'</div>'].join("");
var removeButtonTemplate = '<a class="btn" title="Remove this location"><i class="icon-trash"></i></a>';
synchronizer.refreshManageSync = function() {
var fileIndex = fileManager.getCurrentFileIndex();
var fileIndex = core.fileManager.getCurrentFileIndex();
var syncIndexList = _.compact(localStorage[fileIndex + ".sync"].split(";"));
$(".msg-no-sync, .msg-sync-list").addClass("hide");
$("#manage-sync-list .input-append").remove();
@ -212,19 +207,19 @@ define(["jquery", "google-helper", "dropbox-helper", "dropbox-provider", "gdrive
syncDesc: syncDesc
}));
lineElement.append($(removeButtonTemplate).click(function() {
fileManager.removeSync(syncIndex);
fileManager.updateFileTitles();
core.fileManager.removeSync(syncIndex);
core.fileManager.updateFileTitles();
}));
$("#manage-sync-list").append(lineElement);
});
};
// Used to enable/disable providers' synchronization
synchronizer.resetSyncFlags = function() {
_.each(providerMap, function(provider) {
provider.useSync = false;
});
};
synchronizer.getSyncProvidersFromFile = function(fileIndex) {
var sync = localStorage[fileIndex + ".sync"];
var providerIdList = [];
@ -237,20 +232,19 @@ define(["jquery", "google-helper", "dropbox-helper", "dropbox-provider", "gdrive
return providerIdList;
};
synchronizer.init = function(coreModule, fileManagerModule) {
core = coreModule;
fileManager = fileManagerModule;
$(function() {
core.addOfflineListener(synchronizer.updateSyncButton);
core.addPeriodicCallback(synchronizer.sync);
// Init each provider
_.each(providerMap, function(provider) {
provider.init(core, fileManager);
// Provider's import button
$(".action-sync-import-" + provider.providerId).click(function(event) {
provider.importFiles(event);
});
// Provider's export button
$(".action-sync-export-" + provider.providerId).click(function(event) {
var fileIndex = fileManager.getCurrentFileIndex();
var fileIndex = core.fileManager.getCurrentFileIndex();
var title = localStorage[fileIndex + ".title"];
var content = localStorage[fileIndex + ".content"];
provider.exportFile(event, title, content, function(error, syncIndex) {
@ -259,14 +253,14 @@ define(["jquery", "google-helper", "dropbox-helper", "dropbox-provider", "gdrive
}
localStorage[fileIndex + ".sync"] += syncIndex + ";";
synchronizer.refreshManageSync();
fileManager.updateFileTitles();
core.fileManager.updateFileTitles();
core.showMessage('"' + title
+ '" will now be synchronized on ' + provider.providerName + '.');
});
});
// Provider's manual sync button
$(".action-sync-manual-" + provider.providerId).click(function(event) {
var fileIndex = fileManager.getCurrentFileIndex();
var fileIndex = core.fileManager.getCurrentFileIndex();
var title = localStorage[fileIndex + ".title"];
var content = localStorage[fileIndex + ".content"];
provider.exportManual(event, title, content, function(error, syncIndex) {
@ -275,7 +269,7 @@ define(["jquery", "google-helper", "dropbox-helper", "dropbox-provider", "gdrive
}
localStorage[fileIndex + ".sync"] += syncIndex + ";";
synchronizer.refreshManageSync();
fileManager.updateFileTitles();
core.fileManager.updateFileTitles();
core.showMessage('"' + title
+ '" will now be synchronized on ' + provider.providerName + '.');
});
@ -288,7 +282,7 @@ define(["jquery", "google-helper", "dropbox-helper", "dropbox-provider", "gdrive
synchronizer.forceSync();
}
});
};
});
return synchronizer;
});

6
optimize-css.json Normal file
View File

@ -0,0 +1,6 @@
// RequireJS optimizer configuration file
// Usage: node r.js -o optimize-css.json
({
cssIn: "css/main.css",
out: "css/main-min.css"
})

View File

@ -1,8 +1,8 @@
// RequireJS optimizer configuration file
// Usage: node r.js -o optimize.json
// Usage: node r.js -o optimize-js.json
({
baseUrl: "js",
name: "main",
out: "js/main-min.js",
mainConfigFile: 'js/main.js'
mainConfigFile: 'js/main.js',
})