define(["jquery", "core", "dropbox-provider", "gdrive-provider", "underscore"], function($, core) { var synchronizer = {}; // 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 if user can force synchronization var uploadPending = false; // Allows external modules to update uploadPending flag synchronizer.notifyChange = function(fileIndex) { // Check that file has synchronized locations 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"); } }; // Run updateSyncButton on online/offline event core.addOfflineListener(synchronizer.updateSyncButton); // Force the synchronization synchronizer.forceSync = function() { lastSync = 0; synchronizer.sync(); }; // Recursive function to upload a single file on multiple locations 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 var syncIndex = uploadFileSyncIndexList.pop(); 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; // When page is refreshed, this flag is false but should be true here uploadPending = true; } if(error) { callback(error); return; } if(uploadFlag) { // Update syncAttributes in localStorage localStorage[syncIndex] = JSON.stringify(syncAttributes); } locationUp(callback); } ); } // 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"]; if(fileSyncIndexes.length === 1) { fileUp(callback); return; } // Get document title/content uploadContent = localStorage[fileIndex + ".content"]; uploadContentCRC = core.crc32(uploadContent); uploadTitle = localStorage[fileIndex + ".title"]; uploadTitleCRC = core.crc32(uploadTitle); // Parse the list of synchronized locations associated to the document uploadFileSyncIndexList = _.compact(fileSyncIndexes.split(";")); locationUp(callback); } // Entry point for up synchronization (upload changes) var uploadCycle = false; function syncUp(callback) { if(uploadCycle === true) { // New upload cycle uploadCycle = false; uploadFileIndexList = _.compact(localStorage["file.list"].split(";")); fileUp(callback); } else { callback(); } } // Recursive function to download changes from multiple providers var providerList = []; function providerDown(callback) { if(providerList.length === 0) { callback(); return; } var provider = providerList.pop(); provider.syncDown(function(error) { if(error) { callback(error); return; } providerDown(callback); }); } // 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() { // If sync is already running or timeout is not reached or offline if (syncRunning || lastSync + SYNC_PERIOD > core.currentTime || core.isOffline) { return; } syncRunning = true; uploadCycle = true; lastSync = core.currentTime; synchronizer.updateSyncButton(); function isError(error) { if(error !== undefined) { console.error(error); syncRunning = false; synchronizer.updateSyncButton(); return true; } return false; } syncDown(function(error) { if(isError(error)) { return; } syncUp(function(error) { if(isError(error)) { return; } syncRunning = false; uploadPending = false; }); }); }; // Run sync function periodically core.addPeriodicCallback(synchronizer.sync); // Used to populate the "Manage synchronization" dialog var lineTemplate = ['