Stackedit/js/synchronizer.js

323 lines
10 KiB
JavaScript
Raw Normal View History

2013-04-21 00:07:27 +00:00
define(["jquery", "core", "dropbox-provider", "gdrive-provider", "underscore"], function($, core) {
2013-04-01 16:46:48 +00:00
var synchronizer = {};
2013-04-21 00:07:27 +00:00
// Create a map with providerId: providerObject
2013-04-20 00:14:20 +00:00
var providerMap = _.chain(arguments)
.map(function(argument) {
2013-04-20 17:40:05 +00:00
return argument && argument.providerId && [argument.providerId, argument];
2013-04-20 00:14:20 +00:00
}).compact().object().value();
2013-04-10 18:14:59 +00:00
// Used to know if user can force synchronization
var uploadPending = false;
2013-04-10 23:13:31 +00:00
// Allows external modules to update uploadPending flag
2013-04-10 18:14:59 +00:00
synchronizer.notifyChange = function(fileIndex) {
// Check that file has synchronized locations
2013-04-10 23:13:31 +00:00
if(localStorage[fileIndex + ".sync"].length !== 1) {
uploadPending = true;
synchronizer.updateSyncButton();
}
};
// Used to enable/disable the synchronization button
synchronizer.updateSyncButton = function() {
if(syncRunning === true || uploadPending === false || core.isOffline) {
$(".action-force-sync").addClass("disabled");
}
else {
$(".action-force-sync").removeClass("disabled");
2013-04-01 16:46:48 +00:00
}
};
// Run updateSyncButton on online/offline event
core.addOfflineListener(synchronizer.updateSyncButton);
2013-04-01 16:46:48 +00:00
2013-04-10 23:13:31 +00:00
// Force the synchronization
synchronizer.forceSync = function() {
lastSync = 0;
synchronizer.sync();
};
2013-04-01 16:46:48 +00:00
// Recursive function to upload a single file on multiple locations
2013-04-10 18:14:59 +00:00
var uploadFileSyncIndexList = [];
var uploadContent = undefined;
var uploadContentCRC = undefined;
var uploadTitle = undefined;
var uploadTitleCRC = undefined;
function locationUp(callback) {
// No more synchronized location for this document
if (uploadFileSyncIndexList.length === 0) {
fileUp(callback);
return;
}
// Dequeue a synchronized location
2013-04-20 00:14:20 +00:00
var syncIndex = uploadFileSyncIndexList.pop();
2013-04-20 17:40:05 +00:00
var syncAttributes = JSON.parse(localStorage[syncIndex]);
// Use the specified provider to perform the upload
providerMap[syncAttributes.provider].syncUp(
uploadContent,
uploadContentCRC,
uploadTitle,
uploadTitleCRC,
syncAttributes,
function(error, uploadFlag) {
if(uploadFlag === true) {
// If uploadFlag is true, request another upload cycle
uploadCycle = true;
2013-04-21 00:51:07 +00:00
// When page is refreshed, this flag is false but should be true here
2013-04-20 17:40:05 +00:00
uploadPending = true;
}
2013-04-20 00:14:20 +00:00
if(error) {
callback(error);
2013-04-07 15:22:13 +00:00
return;
}
2013-04-20 17:40:05 +00:00
if(uploadFlag) {
// Update syncAttributes in localStorage
localStorage[syncIndex] = JSON.stringify(syncAttributes);
2013-04-01 16:46:48 +00:00
}
2013-04-16 15:02:24 +00:00
locationUp(callback);
2013-04-20 17:40:05 +00:00
}
);
2013-04-01 16:46:48 +00:00
}
2013-04-10 18:14:59 +00:00
// Recursive function to upload multiple files
var uploadFileIndexList = [];
function fileUp(callback) {
// No more fileIndex to synchronize
if (uploadFileIndexList.length === 0) {
syncUp(callback);
return;
}
// Dequeue a fileIndex
var fileIndex = uploadFileIndexList.pop();
var fileSyncIndexes = localStorage[fileIndex + ".sync"];
2013-04-20 17:40:05 +00:00
if(fileSyncIndexes.length === 1) {
2013-04-10 18:14:59 +00:00
fileUp(callback);
2013-04-01 16:46:48 +00:00
return;
}
2013-04-10 18:14:59 +00:00
// Get document title/content
uploadContent = localStorage[fileIndex + ".content"];
uploadContentCRC = core.crc32(uploadContent);
uploadTitle = localStorage[fileIndex + ".title"];
uploadTitleCRC = core.crc32(uploadTitle);
2013-04-01 16:46:48 +00:00
2013-04-10 18:14:59 +00:00
// Parse the list of synchronized locations associated to the document
2013-04-20 00:14:20 +00:00
uploadFileSyncIndexList = _.compact(fileSyncIndexes.split(";"));
2013-04-10 18:14:59 +00:00
locationUp(callback);
}
2013-04-01 16:46:48 +00:00
2013-04-21 00:07:27 +00:00
// Entry point for up synchronization (upload changes)
2013-04-10 18:14:59 +00:00
var uploadCycle = false;
function syncUp(callback) {
if(uploadCycle === true) {
// New upload cycle
uploadCycle = false;
2013-04-20 17:40:05 +00:00
uploadFileIndexList = _.compact(localStorage["file.list"].split(";"));
2013-04-10 18:14:59 +00:00
fileUp(callback);
}
else {
callback();
}
}
2013-04-01 16:46:48 +00:00
2013-04-20 17:40:05 +00:00
// Recursive function to download changes from multiple providers
var providerList = [];
function providerDown(callback) {
if(providerList.length === 0) {
2013-04-07 15:22:13 +00:00
callback();
2013-04-21 00:07:27 +00:00
return;
2013-04-07 15:22:13 +00:00
}
2013-04-20 17:40:05 +00:00
var provider = providerList.pop();
provider.syncDown(function(error) {
if(error) {
2013-04-16 15:02:24 +00:00
callback(error);
2013-04-07 15:22:13 +00:00
return;
}
2013-04-20 17:40:05 +00:00
providerDown(callback);
2013-04-07 15:22:13 +00:00
});
}
2013-04-21 00:07:27 +00:00
// Entry point for down synchronization (download changes)
2013-04-01 16:46:48 +00:00
function syncDown(callback) {
2013-04-20 17:40:05 +00:00
providerList = _.values(providerMap);
providerDown(callback);
2013-04-01 16:46:48 +00:00
};
2013-04-21 00:07:27 +00:00
// Main entry point for synchronization
2013-04-01 16:46:48 +00:00
var syncRunning = false;
var lastSync = 0;
synchronizer.sync = function() {
// If sync is already running or timeout is not reached or offline
2013-04-02 18:42:47 +00:00
if (syncRunning || lastSync + SYNC_PERIOD > core.currentTime || core.isOffline) {
2013-04-01 16:46:48 +00:00
return;
}
syncRunning = true;
2013-04-10 18:14:59 +00:00
uploadCycle = true;
2013-04-02 18:42:47 +00:00
lastSync = core.currentTime;
2013-04-10 18:14:59 +00:00
synchronizer.updateSyncButton();
function isError(error) {
if(error !== undefined) {
2013-04-20 17:40:05 +00:00
console.error(error);
2013-04-10 18:14:59 +00:00
syncRunning = false;
synchronizer.updateSyncButton();
return true;
}
return false;
}
2013-04-01 16:46:48 +00:00
2013-04-10 18:14:59 +00:00
syncDown(function(error) {
if(isError(error)) {
return;
}
syncUp(function(error) {
if(isError(error)) {
return;
}
2013-04-01 16:46:48 +00:00
syncRunning = false;
2013-04-10 18:14:59 +00:00
uploadPending = false;
2013-04-01 16:46:48 +00:00
});
});
};
// Run sync function periodically
2013-05-04 00:05:58 +00:00
if(viewerMode === false) {
core.addPeriodicCallback(synchronizer.sync);
}
2013-04-20 00:14:20 +00:00
// Used to populate the "Manage synchronization" dialog
var lineTemplate = ['<div class="input-prepend input-append">',
'<span class="add-on" title="<%= provider.providerName %>">',
'<i class="icon-<%= provider.providerId %>"></i></span>',
'<input class="span5" type="text" value="<%= syncDesc %>" disabled />',
'</div>'].join("");
var removeButtonTemplate = '<a class="btn" title="Remove this location"><i class="icon-trash"></i></a>';
synchronizer.refreshManageSync = function() {
2013-04-21 00:07:27 +00:00
var fileIndex = core.fileManager.getCurrentFileIndex();
2013-04-20 00:14:20 +00:00
var syncIndexList = _.compact(localStorage[fileIndex + ".sync"].split(";"));
$(".msg-no-sync, .msg-sync-list").addClass("hide");
2013-05-04 00:05:58 +00:00
var syncList = $("#manage-sync-list").empty();
2013-04-20 00:14:20 +00:00
if (syncIndexList.length > 0) {
$(".msg-sync-list").removeClass("hide");
} else {
$(".msg-no-sync").removeClass("hide");
}
_.each(syncIndexList, function(syncIndex) {
var syncAttributes = JSON.parse(localStorage[syncIndex]);
var syncDesc = syncAttributes.id || syncAttributes.path;
2013-05-04 00:05:58 +00:00
var lineElement = $(_.template(lineTemplate, {
2013-04-20 00:14:20 +00:00
provider: providerMap[syncAttributes.provider],
syncDesc: syncDesc
}));
lineElement.append($(removeButtonTemplate).click(function() {
2013-04-21 00:07:27 +00:00
core.fileManager.removeSync(syncIndex);
core.fileManager.updateFileTitles();
2013-04-20 00:14:20 +00:00
}));
2013-05-04 00:05:58 +00:00
syncList.append(lineElement);
2013-04-20 00:14:20 +00:00
});
};
// Used to enable/disable provider synchronization
2013-04-20 17:40:05 +00:00
synchronizer.resetSyncFlags = function() {
_.each(providerMap, function(provider) {
provider.useSync = false;
});
};
2013-05-02 00:02:57 +00:00
synchronizer.getSyncAttributesFromFile = function(fileIndex) {
2013-04-29 21:41:10 +00:00
var syncIndexList = _.compact(localStorage[fileIndex + ".sync"].split(";"));
2013-05-02 00:02:57 +00:00
var attributesList = [];
2013-04-29 21:41:10 +00:00
_.each(syncIndexList, function(syncIndex) {
var syncAttributes = JSON.parse(localStorage[syncIndex]);
2013-05-02 00:02:57 +00:00
attributesList.push(syncAttributes);
2013-04-29 21:41:10 +00:00
providerMap[syncAttributes.provider].useSync = true;
2013-04-20 17:40:05 +00:00
});
2013-05-02 00:02:57 +00:00
return attributesList;
2013-04-20 17:40:05 +00:00
};
// Initialize the export dialog
function initExportDialog(provider) {
// Reset fields
core.resetModalInputs();
// Load preferences
var serializedPreferences = localStorage[provider.providerId + ".exportPreferences"];
if(serializedPreferences) {
var exportPreferences = JSON.parse(serializedPreferences);
_.each(provider.exportPreferencesInputIds, function(inputId) {
$("#input-sync-export-" + inputId).val(exportPreferences[inputId]);
});
}
// Open dialog box
$("#modal-upload-" + provider.providerId).modal();
}
2013-04-22 01:04:12 +00:00
core.onReady(function() {
2013-04-20 00:14:20 +00:00
// Init each provider
_.each(providerMap, function(provider) {
// Provider's import button
$(".action-sync-import-" + provider.providerId).click(function(event) {
provider.importFiles(event);
});
// Provider's export action
$(".action-sync-export-dialog-" + provider.providerId).click(function() {
initExportDialog(provider);
});
2013-04-20 00:14:20 +00:00
$(".action-sync-export-" + provider.providerId).click(function(event) {
// Perform the provider's export
2013-04-21 00:07:27 +00:00
var fileIndex = core.fileManager.getCurrentFileIndex();
2013-04-20 00:14:20 +00:00
var title = localStorage[fileIndex + ".title"];
var content = localStorage[fileIndex + ".content"];
provider.exportFile(event, title, content, function(error, syncIndex) {
if(error) {
return;
}
// Link syncIndex with fileIndex
2013-04-20 00:14:20 +00:00
localStorage[fileIndex + ".sync"] += syncIndex + ";";
synchronizer.refreshManageSync();
2013-04-21 00:07:27 +00:00
core.fileManager.updateFileTitles();
2013-04-20 00:14:20 +00:00
core.showMessage('"' + title
+ '" will now be synchronized on ' + provider.providerName + '.');
});
// Store input values as preferences for next time we open the export dialog
var exportPreferences = {};
_.each(provider.exportPreferencesInputIds, function(inputId) {
exportPreferences[inputId] = $("#input-sync-export-" + inputId).val();
});
localStorage[provider.providerId + ".exportPreferences"] = JSON.stringify(exportPreferences);
2013-04-20 00:14:20 +00:00
});
// Provider's manual export button
2013-04-20 00:14:20 +00:00
$(".action-sync-manual-" + provider.providerId).click(function(event) {
2013-04-21 00:07:27 +00:00
var fileIndex = core.fileManager.getCurrentFileIndex();
2013-04-20 00:14:20 +00:00
var title = localStorage[fileIndex + ".title"];
var content = localStorage[fileIndex + ".content"];
provider.exportManual(event, title, content, function(error, syncIndex) {
if(error) {
return;
}
localStorage[fileIndex + ".sync"] += syncIndex + ";";
synchronizer.refreshManageSync();
2013-04-21 00:07:27 +00:00
core.fileManager.updateFileTitles();
2013-04-20 00:14:20 +00:00
core.showMessage('"' + title
+ '" will now be synchronized on ' + provider.providerName + '.');
});
});
});
2013-04-10 18:14:59 +00:00
synchronizer.updateSyncButton();
$(".action-force-sync").click(function() {
if(!$(this).hasClass("disabled")) {
synchronizer.forceSync();
}
});
2013-04-21 00:07:27 +00:00
});
2013-04-10 18:14:59 +00:00
2013-04-01 16:46:48 +00:00
return synchronizer;
2013-04-02 18:42:47 +00:00
});