New extension pattern
This commit is contained in:
parent
56fa0c5873
commit
d636e80646
@ -6,7 +6,7 @@ index.html
|
||||
viewer.html
|
||||
css/main-min.css
|
||||
js/main-min.js
|
||||
js/require.js
|
||||
js/lib/require.js
|
||||
img/ajax-loader.gif
|
||||
img/glyphicons-halflings.png
|
||||
img/glyphicons-halflings-white.png
|
||||
|
47
css/main-min.css
vendored
47
css/main-min.css
vendored
@ -5513,6 +5513,12 @@ code {
|
||||
h1 {
|
||||
margin: 30px 0 30px;
|
||||
}
|
||||
h4, h5, h6 {
|
||||
line-height: 40px;
|
||||
}
|
||||
.toc ul {
|
||||
list-style-type: none;
|
||||
}
|
||||
p, pre, blockquote {
|
||||
margin: 0 0 20px;
|
||||
}
|
||||
@ -5727,9 +5733,48 @@ div.dropdown-menu i {
|
||||
height: 80px;
|
||||
max-width: 206px;
|
||||
}
|
||||
.tooltip-inner {
|
||||
#modal-settings .accordion-group {
|
||||
border: 0;
|
||||
border-bottom: 1px solid #eee;
|
||||
-webkit-border-radius: inherit;
|
||||
-moz-border-radius: inherit;
|
||||
border-radius: inherit;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
#modal-settings .accordion-heading .checkbox {
|
||||
padding: 8px 15px;
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
#modal-settings .accordion-inner {
|
||||
border: 0;
|
||||
padding: 10px 40px;
|
||||
}
|
||||
#modal-settings .accordion-inner .form-horizontal .control-group {
|
||||
color: #999;
|
||||
}
|
||||
#modal-settings .accordion-inner .form-horizontal .control-label {
|
||||
text-align: left;
|
||||
}
|
||||
.accordion-toggle {
|
||||
cursor: help;
|
||||
}
|
||||
.nav-tabs > .active > a, .nav-tabs > .active > a:hover, .nav-tabs > .active > a:focus {
|
||||
color: #fff;
|
||||
background-color: #777;
|
||||
border-color: #777;
|
||||
border-bottom-color: transparent;
|
||||
}
|
||||
.nav-tabs {
|
||||
border-bottom-color: #eee;
|
||||
}
|
||||
.nav > li > a:hover,
|
||||
.nav > li > a:focus {
|
||||
background-color: #eee;
|
||||
}
|
||||
.nav-tabs > li > a:hover,
|
||||
.nav-tabs > li > a:focus {
|
||||
border-color: #eee;
|
||||
}
|
||||
table {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
@ -19,13 +19,12 @@
|
||||
document.write('<link href="css/main' + suffix + '.css" rel="stylesheet">');
|
||||
var theme = localStorage.theme;
|
||||
if (theme) {
|
||||
document
|
||||
.write('<link href="themes/' + theme + '/' + theme + '.css" rel="stylesheet">');
|
||||
document.write('<link href="themes/' + theme + '/' + theme + '.css" rel="stylesheet">');
|
||||
}
|
||||
var require = { baseUrl : "js", deps : [ "main" + suffix ] };
|
||||
var viewerMode = false;
|
||||
</script>
|
||||
<script src="js/require.js"></script>
|
||||
<script src="js/lib/require.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="navbar" class="navbar navbar-fixed-top ui-layout-north">
|
||||
|
@ -2,7 +2,12 @@
|
||||
* Used to run asynchronous tasks sequentially (ajax mainly). An asynchronous
|
||||
* task is composed of different callback types: onRun, onSuccess, onError
|
||||
*/
|
||||
define([ "core", "underscore" ], function(core) {
|
||||
define([
|
||||
"underscore",
|
||||
"core",
|
||||
"utils",
|
||||
"extension-manager"
|
||||
], function(_, core, utils, extensionMgr) {
|
||||
|
||||
var asyncRunner = {};
|
||||
|
||||
@ -81,7 +86,7 @@ define([ "core", "underscore" ], function(core) {
|
||||
}
|
||||
error = error || new Error("Unknown error");
|
||||
if(error.message) {
|
||||
core.showError(error);
|
||||
extensionMgr.onError(error);
|
||||
}
|
||||
runSafe(task, task.errorCallbacks, error);
|
||||
// Exit the current call stack
|
||||
@ -102,7 +107,7 @@ define([ "core", "underscore" ], function(core) {
|
||||
}
|
||||
// Implement an exponential backoff
|
||||
var delay = Math.pow(2, task.retryCounter++) * 1000;
|
||||
currentTaskStartTime = core.currentTime + delay;
|
||||
currentTaskStartTime = utils.currentTime + delay;
|
||||
currentTaskRunning = false;
|
||||
asyncRunner.runTask();
|
||||
};
|
||||
@ -117,7 +122,7 @@ define([ "core", "underscore" ], function(core) {
|
||||
// If there is a task currently running
|
||||
if (currentTaskRunning === true) {
|
||||
// If the current task takes too long
|
||||
if (currentTaskStartTime + currentTask.timeout < core.currentTime) {
|
||||
if (currentTaskStartTime + currentTask.timeout < utils.currentTime) {
|
||||
currentTask.error(new Error("A timeout occurred."));
|
||||
}
|
||||
return;
|
||||
@ -131,12 +136,12 @@ define([ "core", "underscore" ], function(core) {
|
||||
|
||||
// Dequeue an enqueued task
|
||||
currentTask = taskQueue.shift();
|
||||
currentTaskStartTime = core.currentTime;
|
||||
core.showWorkingIndicator(true);
|
||||
currentTaskStartTime = utils.currentTime;
|
||||
extensionMgr.onAsyncRunning(true);
|
||||
}
|
||||
|
||||
// Run the task
|
||||
if (currentTaskStartTime <= core.currentTime) {
|
||||
if (currentTaskStartTime <= utils.currentTime) {
|
||||
currentTaskRunning = true;
|
||||
currentTask.chain();
|
||||
}
|
||||
@ -157,7 +162,7 @@ define([ "core", "underscore" ], function(core) {
|
||||
currentTaskRunning = false;
|
||||
}
|
||||
if (taskQueue.length === 0) {
|
||||
core.showWorkingIndicator(false);
|
||||
extensionMgr.onAsyncRunning(false);
|
||||
} else {
|
||||
asyncRunner.runTask();
|
||||
}
|
||||
|
@ -1,4 +1,8 @@
|
||||
define(["utils", "google-helper"], function(utils, googleHelper) {
|
||||
define([
|
||||
"underscore",
|
||||
"utils",
|
||||
"google-helper"
|
||||
], function(_, utils, googleHelper) {
|
||||
|
||||
var PROVIDER_BLOGGER = "blogger";
|
||||
|
||||
@ -39,10 +43,11 @@ define(["utils", "google-helper"], function(utils, googleHelper) {
|
||||
publishAttributes.labelList = [];
|
||||
var labels = utils.getInputTextValue("#input-publish-labels");
|
||||
if(labels !== undefined) {
|
||||
publishAttributes.labelList = _.chain(labels.split(","))
|
||||
.map(function(label) {
|
||||
return utils.trim(label);
|
||||
}).compact().value();
|
||||
publishAttributes.labelList = _.chain(
|
||||
labels.split(",")
|
||||
).map(function(label) {
|
||||
return utils.trim(label);
|
||||
}).compact().value();
|
||||
}
|
||||
if(event.isPropagationStopped()) {
|
||||
return undefined;
|
||||
|
219
js/core.js
219
js/core.js
@ -1,28 +1,18 @@
|
||||
define([
|
||||
"jquery",
|
||||
"underscore",
|
||||
"utils",
|
||||
"settings",
|
||||
"extension-manager",
|
||||
"bootstrap",
|
||||
"layout",
|
||||
"Markdown.Editor",
|
||||
"storage",
|
||||
"config",
|
||||
"underscore",
|
||||
"FileSaver",
|
||||
"css_browser_selector"
|
||||
], function($, utils, extensionManager) {
|
||||
"lib/bootstrap",
|
||||
"lib/layout",
|
||||
"lib/Markdown.Editor"
|
||||
], function($, _, utils, settings, extensionMgr) {
|
||||
|
||||
var core = {};
|
||||
|
||||
// For convenience
|
||||
core.doNothing = function() {};
|
||||
|
||||
// Time shared by others modules
|
||||
function updateCurrentTime() {
|
||||
core.currentTime = new Date().getTime();
|
||||
}
|
||||
updateCurrentTime();
|
||||
|
||||
// Used for periodic tasks
|
||||
var intervalId = undefined;
|
||||
var periodicCallbacks = [];
|
||||
@ -38,11 +28,11 @@ define([
|
||||
function setUserActive() {
|
||||
userReal = true;
|
||||
userActive = true;
|
||||
userLastActivity = core.currentTime;
|
||||
userLastActivity = utils.currentTime;
|
||||
};
|
||||
function isUserActive() {
|
||||
if(userActive === true
|
||||
&& core.currentTime - userLastActivity > USER_IDLE_THRESHOLD) {
|
||||
&& utils.currentTime - userLastActivity > USER_IDLE_THRESHOLD) {
|
||||
userActive = false;
|
||||
}
|
||||
return userActive && windowUnique;
|
||||
@ -50,7 +40,7 @@ define([
|
||||
|
||||
// Used to only have 1 window of the application in the same browser
|
||||
var windowId = undefined;
|
||||
core.checkWindowUnique = function() {
|
||||
function checkWindowUnique() {
|
||||
if(userReal === false || windowUnique === false) {
|
||||
return;
|
||||
}
|
||||
@ -70,136 +60,65 @@ define([
|
||||
keyboard: false
|
||||
});
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Export data on disk
|
||||
core.saveFile = function(content, filename) {
|
||||
if(saveAs !== undefined) {
|
||||
var blob = new Blob([content], {type: "text/plain;charset=utf-8"});
|
||||
saveAs(blob, filename);
|
||||
}
|
||||
else {
|
||||
var uriContent = "data:application/octet-stream;base64,"
|
||||
+ utils.encodeBase64(content);
|
||||
window.open(uriContent, 'file');
|
||||
}
|
||||
};
|
||||
|
||||
// Used by asyncRunner
|
||||
core.showWorkingIndicator = function(show) {
|
||||
if (show === false) {
|
||||
$(".working-indicator").removeClass("show");
|
||||
$("body").removeClass("working");
|
||||
} else {
|
||||
$(".working-indicator").addClass("show");
|
||||
$("body").addClass("working");
|
||||
}
|
||||
};
|
||||
|
||||
// Log a message
|
||||
core.showMessage = function(message) {
|
||||
console.log(message);
|
||||
extensionManager.onMessage(message);
|
||||
};
|
||||
|
||||
// Log an error
|
||||
core.showError = function(error) {
|
||||
console.error(error);
|
||||
if(_.isString(error)) {
|
||||
extensionManager.onMessage(error);
|
||||
}
|
||||
else if(_.isObject(error)) {
|
||||
extensionManager.onMessage(error.message);
|
||||
}
|
||||
};
|
||||
|
||||
// Offline management
|
||||
core.isOffline = false;
|
||||
var offlineTime = core.currentTime;
|
||||
var offlineListeners = [];
|
||||
core.addOfflineListener = function(callback) {
|
||||
offlineListeners.push(callback);
|
||||
};
|
||||
var offlineTime = utils.currentTime;
|
||||
core.setOffline = function() {
|
||||
offlineTime = core.currentTime;
|
||||
offlineTime = utils.currentTime;
|
||||
if(core.isOffline === false) {
|
||||
core.isOffline = true;
|
||||
extensionManager.onOfflineChanged(true);
|
||||
_.each(offlineListeners, function(listener) {
|
||||
listener();
|
||||
});
|
||||
extensionMgr.onOfflineChanged(true);
|
||||
}
|
||||
};
|
||||
core.setOnline = function() {
|
||||
function setOnline() {
|
||||
if(core.isOffline === true) {
|
||||
core.isOffline = false;
|
||||
extensionManager.onOfflineChanged(false);
|
||||
_.each(offlineListeners, function(listener) {
|
||||
listener();
|
||||
});
|
||||
extensionMgr.onOfflineChanged(false);
|
||||
}
|
||||
};
|
||||
}
|
||||
function checkOnline() {
|
||||
// Try to reconnect if we are offline but we have some network
|
||||
if (core.isOffline === true && navigator.onLine === true
|
||||
&& offlineTime + CHECK_ONLINE_PERIOD < core.currentTime) {
|
||||
offlineTime = core.currentTime;
|
||||
&& offlineTime + CHECK_ONLINE_PERIOD < utils.currentTime) {
|
||||
offlineTime = utils.currentTime;
|
||||
// Try to download anything to test the connection
|
||||
$.ajax({
|
||||
url : "//www.google.com/jsapi",
|
||||
timeout : AJAX_TIMEOUT, dataType : "script"
|
||||
}).done(function() {
|
||||
core.setOnline();
|
||||
setOnline();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Setting management
|
||||
core.settings = {
|
||||
layoutOrientation : "horizontal",
|
||||
lazyRendering : true,
|
||||
editorFontSize : 14,
|
||||
defaultContent: "\n\n\n> Written with [StackEdit](http://benweet.github.io/stackedit/).",
|
||||
commitMsg : "Published by http://benweet.github.io/stackedit",
|
||||
template : ['<!DOCTYPE html>\n',
|
||||
'<html>\n',
|
||||
'<head>\n',
|
||||
'<title><%= documentTitle %></title>\n',
|
||||
'</head>\n',
|
||||
'<body><%= documentHTML %></body>\n',
|
||||
'</html>'].join(""),
|
||||
sshProxy : SSH_PROXY_URL,
|
||||
extensionSettings: {}
|
||||
};
|
||||
if (_.has(localStorage, "settings")) {
|
||||
_.extend(core.settings, JSON.parse(localStorage.settings));
|
||||
}
|
||||
extensionManager.init(core.settings.extensionSettings);
|
||||
|
||||
core.loadSettings = function() {
|
||||
// Load settings in settings dialog
|
||||
function loadSettings() {
|
||||
|
||||
// Layout orientation
|
||||
utils.setInputRadio("radio-layout-orientation", core.settings.layoutOrientation);
|
||||
utils.setInputRadio("radio-layout-orientation", settings.layoutOrientation);
|
||||
// Theme
|
||||
utils.setInputValue("#input-settings-theme", localStorage.theme);
|
||||
// Lazy rendering
|
||||
utils.setInputChecked("#input-settings-lazy-rendering", core.settings.lazyRendering);
|
||||
utils.setInputChecked("#input-settings-lazy-rendering", settings.lazyRendering);
|
||||
// Editor font size
|
||||
utils.setInputValue("#input-settings-editor-font-size", core.settings.editorFontSize);
|
||||
utils.setInputValue("#input-settings-editor-font-size", settings.editorFontSize);
|
||||
// Default content
|
||||
utils.setInputValue("#textarea-settings-default-content", core.settings.defaultContent);
|
||||
utils.setInputValue("#textarea-settings-default-content", settings.defaultContent);
|
||||
// Commit message
|
||||
utils.setInputValue("#input-settings-publish-commit-msg", core.settings.commitMsg);
|
||||
utils.setInputValue("#input-settings-publish-commit-msg", settings.commitMsg);
|
||||
// Template
|
||||
utils.setInputValue("#textarea-settings-publish-template", core.settings.template);
|
||||
utils.setInputValue("#textarea-settings-publish-template", settings.template);
|
||||
// SSH proxy
|
||||
utils.setInputValue("#input-settings-ssh-proxy", core.settings.sshProxy);
|
||||
utils.setInputValue("#input-settings-ssh-proxy", settings.sshProxy);
|
||||
|
||||
// Load extension settings
|
||||
extensionManager.onLoadSettings();
|
||||
};
|
||||
extensionMgr.onLoadSettings();
|
||||
}
|
||||
|
||||
core.saveSettings = function(event) {
|
||||
// Save settings from settings dialog
|
||||
function saveSettings(event) {
|
||||
var newSettings = {};
|
||||
|
||||
// Layout orientation
|
||||
@ -221,14 +140,14 @@ define([
|
||||
|
||||
// Save extension settings
|
||||
newSettings.extensionSettings = {};
|
||||
extensionManager.onSaveSettings(newSettings.extensionSettings, event);
|
||||
extensionMgr.onSaveSettings(newSettings.extensionSettings, event);
|
||||
|
||||
if(!event.isPropagationStopped()) {
|
||||
$.extend(core.settings, newSettings);
|
||||
localStorage.settings = JSON.stringify(newSettings);
|
||||
$.extend(settings, newSettings);
|
||||
localStorage.settings = JSON.stringify(settings);
|
||||
localStorage.theme = theme;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Create the layout
|
||||
var layout = undefined;
|
||||
@ -250,8 +169,8 @@ define([
|
||||
center__minWidth : 200,
|
||||
center__minHeight : 200
|
||||
};
|
||||
extensionManager.onLayoutConfigure(layoutGlobalConfig);
|
||||
if (core.settings.layoutOrientation == "horizontal") {
|
||||
extensionMgr.onLayoutConfigure(layoutGlobalConfig);
|
||||
if (settings.layoutOrientation == "horizontal") {
|
||||
$(".ui-layout-south").remove();
|
||||
$(".ui-layout-east").addClass("well").prop("id", "wmd-preview");
|
||||
layout = $('body').layout(
|
||||
@ -261,7 +180,7 @@ define([
|
||||
east__minSize : 200
|
||||
})
|
||||
);
|
||||
} else if (core.settings.layoutOrientation == "vertical") {
|
||||
} else if (settings.layoutOrientation == "vertical") {
|
||||
$(".ui-layout-east").remove();
|
||||
$(".ui-layout-south").addClass("well").prop("id", "wmd-preview");
|
||||
layout = $('body').layout(
|
||||
@ -282,7 +201,7 @@ define([
|
||||
layout.allowOverflow('north');
|
||||
});
|
||||
|
||||
extensionManager.onLayoutCreated(layout);
|
||||
extensionMgr.onLayoutCreated(layout);
|
||||
};
|
||||
|
||||
// Create the PageDown editor
|
||||
@ -320,7 +239,7 @@ define([
|
||||
makePreview();
|
||||
};
|
||||
};
|
||||
if(core.settings.lazyRendering === true) {
|
||||
if(settings.lazyRendering === true) {
|
||||
var lastRefresh = 0;
|
||||
previewWrapper = function(makePreview) {
|
||||
//var debouncedMakePreview = _.debounce(makePreview, 500);
|
||||
@ -340,8 +259,8 @@ define([
|
||||
};
|
||||
};
|
||||
}
|
||||
extensionManager.onEditorConfigure(editor);
|
||||
editor.hooks.chain("onPreviewRefresh", extensionManager.onAsyncPreview);
|
||||
extensionMgr.onEditorConfigure(editor);
|
||||
editor.hooks.chain("onPreviewRefresh", extensionMgr.onAsyncPreview);
|
||||
|
||||
// Convert email addresses (not managed by pagedown)
|
||||
converter.hooks.chain("postConversion", function(text) {
|
||||
@ -374,53 +293,29 @@ define([
|
||||
$("#wmd-redo-button").append($("<i>").addClass("icon-share-alt"));
|
||||
};
|
||||
|
||||
// Create an centered popup window
|
||||
core.popupWindow = function(url, title, w, h) {
|
||||
var left = (screen.width / 2) - (w / 2);
|
||||
var top = (screen.height / 2) - (h / 2);
|
||||
return window
|
||||
.open(
|
||||
url,
|
||||
title,
|
||||
'toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=no, resizable=no, copyhistory=no, width='
|
||||
+ w
|
||||
+ ', height='
|
||||
+ h
|
||||
+ ', top='
|
||||
+ top
|
||||
+ ', left='
|
||||
+ left);
|
||||
};
|
||||
|
||||
// Keep a reference to the fileManager
|
||||
core.setFileManager = function(fileManager) {
|
||||
core.fileManager = fileManager;
|
||||
runReadyCallbacks();
|
||||
};
|
||||
|
||||
// onReady event callbacks
|
||||
var readyCallbacks = [];
|
||||
core.onReady = function(callback) {
|
||||
readyCallbacks.push(callback);
|
||||
runReadyCallbacks();
|
||||
};
|
||||
var documentLoaded = false;
|
||||
var ready = false;
|
||||
core.setReady = function() {
|
||||
ready = true;
|
||||
runReadyCallbacks();
|
||||
};
|
||||
function runReadyCallbacks() {
|
||||
if(documentLoaded === true && core.fileManager !== undefined) {
|
||||
if(ready === true) {
|
||||
_.each(readyCallbacks, function(callback) {
|
||||
callback();
|
||||
});
|
||||
readyCallbacks = [];
|
||||
}
|
||||
}
|
||||
$(function() {
|
||||
documentLoaded = true;
|
||||
runReadyCallbacks();
|
||||
});
|
||||
|
||||
core.onReady(extensionManager.onReady);
|
||||
core.onReady(function() {
|
||||
|
||||
|
||||
// Load theme list
|
||||
_.each(THEME_LIST, function(name, value) {
|
||||
$("#input-settings-theme").append($('<option value="' + value + '">' + name + '</option>'));
|
||||
@ -428,7 +323,7 @@ define([
|
||||
|
||||
// listen to online/offline events
|
||||
$(window).on('offline', core.setOffline);
|
||||
$(window).on('online', core.setOnline);
|
||||
$(window).on('online', setOnline);
|
||||
if (navigator.onLine === false) {
|
||||
core.setOffline();
|
||||
}
|
||||
@ -460,10 +355,10 @@ define([
|
||||
|
||||
// Settings loading/saving
|
||||
$(".action-load-settings").click(function() {
|
||||
core.loadSettings();
|
||||
loadSettings();
|
||||
});
|
||||
$(".action-apply-settings").click(function(e) {
|
||||
core.saveSettings(e);
|
||||
saveSettings(e);
|
||||
if(!e.isPropagationStopped()) {
|
||||
window.location.reload();
|
||||
}
|
||||
@ -487,8 +382,8 @@ define([
|
||||
// Editor's textarea
|
||||
$("#wmd-input, #md-section-helper").css({
|
||||
// Apply editor font size
|
||||
"font-size": core.settings.editorFontSize + "px",
|
||||
"line-height": Math.round(core.settings.editorFontSize * (20/14)) + "px"
|
||||
"font-size": settings.editorFontSize + "px",
|
||||
"line-height": Math.round(settings.editorFontSize * (20/14)) + "px"
|
||||
});
|
||||
|
||||
// Manage tab key
|
||||
@ -564,8 +459,8 @@ define([
|
||||
|
||||
// Do periodic tasks
|
||||
intervalId = window.setInterval(function() {
|
||||
updateCurrentTime();
|
||||
core.checkWindowUnique();
|
||||
utils.updateCurrentTime();
|
||||
checkWindowUnique();
|
||||
if(isUserActive() === true || viewerMode === true) {
|
||||
_.each(periodicCallbacks, function(callback) {
|
||||
callback();
|
||||
|
@ -1,4 +1,8 @@
|
||||
define(["jquery", "core", "async-runner"], function($, core, asyncRunner) {
|
||||
define([
|
||||
"jquery",
|
||||
"core",
|
||||
"async-runner"
|
||||
], function($, core, asyncRunner) {
|
||||
|
||||
var PROVIDER_DOWNLOAD = "download";
|
||||
|
||||
|
@ -1,4 +1,10 @@
|
||||
define(["jquery", "core", "async-runner"], function($, core, asyncRunner) {
|
||||
define([
|
||||
"jquery",
|
||||
"underscore",
|
||||
"core",
|
||||
"extension-manager",
|
||||
"async-runner"
|
||||
], function($, _, core, extensionMgr, asyncRunner) {
|
||||
|
||||
var client = undefined;
|
||||
var authenticated = false;
|
||||
@ -50,7 +56,7 @@ define(["jquery", "core", "async-runner"], function($, core, asyncRunner) {
|
||||
var immediate = true;
|
||||
function localAuthenticate() {
|
||||
if (immediate === false) {
|
||||
core.showMessage("Please make sure the Dropbox authorization popup is not blocked by your browser.");
|
||||
extensionMgr.onMessage("Please make sure the Dropbox authorization popup is not blocked by your browser.");
|
||||
// If not immediate we add time for user to enter his credentials
|
||||
task.timeout = ASYNC_TASK_LONG_TIMEOUT;
|
||||
}
|
||||
@ -77,7 +83,6 @@ define(["jquery", "core", "async-runner"], function($, core, asyncRunner) {
|
||||
}
|
||||
|
||||
dropboxHelper.upload = function(path, content, callback) {
|
||||
callback = callback || core.doNothing;
|
||||
var result = undefined;
|
||||
var task = asyncRunner.createTask();
|
||||
connect(task);
|
||||
@ -106,7 +111,6 @@ define(["jquery", "core", "async-runner"], function($, core, asyncRunner) {
|
||||
};
|
||||
|
||||
dropboxHelper.checkChanges = function(lastChangeId, callback) {
|
||||
callback = callback || core.doNothing;
|
||||
var changes = [];
|
||||
var newChangeId = lastChangeId || 0;
|
||||
var task = asyncRunner.createTask();
|
||||
@ -143,7 +147,6 @@ define(["jquery", "core", "async-runner"], function($, core, asyncRunner) {
|
||||
};
|
||||
|
||||
dropboxHelper.downloadMetadata = function(paths, callback) {
|
||||
callback = callback || core.doNothing;
|
||||
var result = [];
|
||||
var task = asyncRunner.createTask();
|
||||
connect(task);
|
||||
@ -177,7 +180,6 @@ define(["jquery", "core", "async-runner"], function($, core, asyncRunner) {
|
||||
};
|
||||
|
||||
dropboxHelper.downloadContent = function(objects, callback) {
|
||||
callback = callback || core.doNothing;
|
||||
var result = [];
|
||||
var task = asyncRunner.createTask();
|
||||
connect(task);
|
||||
@ -289,7 +291,6 @@ define(["jquery", "core", "async-runner"], function($, core, asyncRunner) {
|
||||
}
|
||||
|
||||
dropboxHelper.picker = function(callback) {
|
||||
callback = callback || core.doNothing;
|
||||
var paths = [];
|
||||
var task = asyncRunner.createTask();
|
||||
// Add some time for user to choose his files
|
||||
@ -312,7 +313,7 @@ define(["jquery", "core", "async-runner"], function($, core, asyncRunner) {
|
||||
task.chain();
|
||||
};
|
||||
Dropbox.choose(options);
|
||||
core.showMessage("Please make sure the Dropbox chooser popup is not blocked by your browser.");
|
||||
extensionMgr.onMessage("Please make sure the Dropbox chooser popup is not blocked by your browser.");
|
||||
});
|
||||
task.onSuccess(function() {
|
||||
callback(undefined, paths);
|
||||
|
@ -1,4 +1,10 @@
|
||||
define(["core", "utils", "extension-manager", "dropbox-helper"], function(core, utils, extensionManager, dropboxHelper) {
|
||||
define([
|
||||
"underscore",
|
||||
"utils",
|
||||
"extension-manager",
|
||||
"file-manager",
|
||||
"dropbox-helper"
|
||||
], function(_, utils, extensionMgr, fileMgr, dropboxHelper) {
|
||||
|
||||
var PROVIDER_DROPBOX = "dropbox";
|
||||
|
||||
@ -13,7 +19,7 @@ define(["core", "utils", "extension-manager", "dropbox-helper"], function(core,
|
||||
return undefined;
|
||||
}
|
||||
if(!path.match(/^[^\\<>:"\|?\*]+$/)) {
|
||||
core.showError('"' + path + '" contains invalid characters.');
|
||||
extensionMgr.onError('"' + path + '" contains invalid characters.');
|
||||
return undefined;
|
||||
}
|
||||
if(path.indexOf("/") !== 0) {
|
||||
@ -51,11 +57,11 @@ define(["core", "utils", "extension-manager", "dropbox-helper"], function(core,
|
||||
localStorage[syncAttributes.syncIndex] = utils.serializeAttributes(syncAttributes);
|
||||
var syncLocations = {};
|
||||
syncLocations[syncAttributes.syncIndex] = syncAttributes;
|
||||
var fileDesc = core.fileManager.createFile(file.name, file.content, syncLocations);
|
||||
core.fileManager.selectFile(fileDesc);
|
||||
var fileDesc = fileMgr.createFile(file.name, file.content, syncLocations);
|
||||
fileMgr.selectFile(fileDesc);
|
||||
fileDescList.push(fileDesc);
|
||||
});
|
||||
extensionManager.onSyncImportSuccess(fileDescList, dropboxProvider);
|
||||
extensionMgr.onSyncImportSuccess(fileDescList, dropboxProvider);
|
||||
});
|
||||
});
|
||||
}
|
||||
@ -68,9 +74,9 @@ define(["core", "utils", "extension-manager", "dropbox-helper"], function(core,
|
||||
var importPaths = [];
|
||||
_.each(paths, function(path) {
|
||||
var syncIndex = createSyncIndex(path);
|
||||
var fileDesc = core.fileManager.getFileFromSyncIndex(syncIndex);
|
||||
var fileDesc = fileMgr.getFileFromSyncIndex(syncIndex);
|
||||
if(fileDesc !== undefined) {
|
||||
core.showError('"' + fileDesc.title + '" was already imported');
|
||||
extensionMgr.onError('"' + fileDesc.title + '" was already imported');
|
||||
return;
|
||||
}
|
||||
importPaths.push(path);
|
||||
@ -87,10 +93,10 @@ define(["core", "utils", "extension-manager", "dropbox-helper"], function(core,
|
||||
}
|
||||
// Check that file is not synchronized with an other one
|
||||
var syncIndex = createSyncIndex(path);
|
||||
var fileDesc = core.fileManager.getFileFromSyncIndex(syncIndex);
|
||||
var fileDesc = fileMgr.getFileFromSyncIndex(syncIndex);
|
||||
if(fileDesc !== undefined) {
|
||||
var existingTitle = fileDesc.title;
|
||||
core.showError('File path is already synchronized with "' + existingTitle + '"');
|
||||
extensionMgr.onError('File path is already synchronized with "' + existingTitle + '"');
|
||||
callback(true);
|
||||
return;
|
||||
}
|
||||
@ -143,7 +149,7 @@ define(["core", "utils", "extension-manager", "dropbox-helper"], function(core,
|
||||
var interestingChanges = [];
|
||||
_.each(changes, function(change) {
|
||||
var syncIndex = createSyncIndex(change.path);
|
||||
var syncAttributes = core.fileManager.getSyncAttributes(syncIndex);
|
||||
var syncAttributes = fileMgr.getSyncAttributes(syncIndex);
|
||||
if(syncAttributes === undefined) {
|
||||
return;
|
||||
}
|
||||
@ -164,11 +170,10 @@ define(["core", "utils", "extension-manager", "dropbox-helper"], function(core,
|
||||
callback(error);
|
||||
return;
|
||||
}
|
||||
var updateFileTitles = false;
|
||||
_.each(changes, function(change) {
|
||||
var syncAttributes = change.syncAttributes;
|
||||
var syncIndex = syncAttributes.syncIndex;
|
||||
var fileDesc = core.fileManager.getFileFromSyncIndex(syncIndex);
|
||||
var fileDesc = fileMgr.getFileFromSyncIndex(syncIndex);
|
||||
// No file corresponding (file may have been deleted locally)
|
||||
if(fileDesc === undefined) {
|
||||
return;
|
||||
@ -176,8 +181,8 @@ define(["core", "utils", "extension-manager", "dropbox-helper"], function(core,
|
||||
var localTitle = fileDesc.title;
|
||||
// File deleted
|
||||
if (change.wasRemoved === true) {
|
||||
core.showError('"' + localTitle + '" has been removed from Dropbox.');
|
||||
core.fileManager.removeSync(syncAttributes);
|
||||
extensionMgr.onError('"' + localTitle + '" has been removed from Dropbox.');
|
||||
fileMgr.removeSync(syncAttributes);
|
||||
return;
|
||||
}
|
||||
var localContent = localStorage[fileDesc.fileIndex + ".content"];
|
||||
@ -188,17 +193,16 @@ define(["core", "utils", "extension-manager", "dropbox-helper"], function(core,
|
||||
var fileContentChanged = localContent != file.content;
|
||||
// Conflict detection
|
||||
if (fileContentChanged === true && localContentChanged === true && remoteContentChanged === true) {
|
||||
core.fileManager.createFile(localTitle + " (backup)", localContent);
|
||||
updateFileTitles = true;
|
||||
core.showMessage('Conflict detected on "' + localTitle + '". A backup has been created locally.');
|
||||
var backupFileDesc = fileMgr.createFile(localTitle + " (backup)", localContent);
|
||||
extensionMgr.onTitleChanged(backupFileDesc);
|
||||
extensionMgr.onMessage('Conflict detected on "' + localTitle + '". A backup has been created locally.');
|
||||
}
|
||||
// If file content changed
|
||||
if(fileContentChanged && remoteContentChanged === true) {
|
||||
localStorage[fileDesc.fileIndex + ".content"] = file.content;
|
||||
core.showMessage('"' + localTitle + '" has been updated from Dropbox.');
|
||||
if(core.fileManager.isCurrentFile(fileDesc)) {
|
||||
updateFileTitles = false; // Done by next function
|
||||
core.fileManager.selectFile(); // Refresh editor
|
||||
extensionMgr.onMessage('"' + localTitle + '" has been updated from Dropbox.');
|
||||
if(fileMgr.isCurrentFile(fileDesc)) {
|
||||
fileMgr.selectFile(); // Refresh editor
|
||||
}
|
||||
}
|
||||
// Update syncAttributes
|
||||
@ -206,9 +210,6 @@ define(["core", "utils", "extension-manager", "dropbox-helper"], function(core,
|
||||
syncAttributes.contentCRC = remoteContentCRC;
|
||||
localStorage[syncIndex] = utils.serializeAttributes(syncAttributes);
|
||||
});
|
||||
if(updateFileTitles) {
|
||||
extensionManager.onTitleChanged();
|
||||
}
|
||||
localStorage[PROVIDER_DROPBOX + ".lastChangeId"] = newChangeId;
|
||||
callback();
|
||||
});
|
||||
|
@ -1,29 +1,40 @@
|
||||
define( [
|
||||
"jquery",
|
||||
"utils",
|
||||
"underscore",
|
||||
"bootstrap",
|
||||
"utils",
|
||||
"settings",
|
||||
"extensions/button-publish",
|
||||
"extensions/button-share",
|
||||
"extensions/button-sync",
|
||||
"extensions/document-selector",
|
||||
"extensions/document-title",
|
||||
"extensions/manage-publication",
|
||||
"extensions/manage-synchronization",
|
||||
"extensions/working-indicator",
|
||||
"extensions/notifications",
|
||||
"extensions/markdown-extra",
|
||||
"extensions/toc",
|
||||
"extensions/math-jax",
|
||||
"extensions/scroll-link"
|
||||
], function($, utils) {
|
||||
"extensions/scroll-link",
|
||||
"lib/bootstrap"
|
||||
], function($, _, utils, settings) {
|
||||
|
||||
var extensionManager = {};
|
||||
var extensionMgr = {};
|
||||
|
||||
// Create a list of extensions
|
||||
var extensionList = _.chain(arguments)
|
||||
.map(function(argument) {
|
||||
return _.isObject(argument) && argument.extensionId && argument;
|
||||
}).compact().value();
|
||||
var extensionList = _.chain(
|
||||
arguments
|
||||
).map(function(argument) {
|
||||
return _.isObject(argument) && argument.extensionId && argument;
|
||||
}).compact().value();
|
||||
|
||||
// Return every named callbacks implemented in extensions
|
||||
function getExtensionCallbackList(hookName) {
|
||||
return _.chain(extensionList)
|
||||
.map(function(extension) {
|
||||
return extension.config.enabled && extension[hookName];
|
||||
}).compact().value();
|
||||
return _.chain(
|
||||
extensionList
|
||||
).map(function(extension) {
|
||||
return extension.config.enabled && extension[hookName];
|
||||
}).compact().value();
|
||||
}
|
||||
|
||||
// Return a function that calls every callbacks from extensions
|
||||
@ -38,9 +49,9 @@ define( [
|
||||
};
|
||||
}
|
||||
|
||||
// Add a Hook to the extensionManager
|
||||
// Add a Hook to the extensionMgr
|
||||
function addHook(hookName) {
|
||||
extensionManager[hookName] = createHook(hookName);
|
||||
extensionMgr[hookName] = createHook(hookName);
|
||||
}
|
||||
|
||||
var accordionTmpl = [
|
||||
@ -66,96 +77,97 @@ define( [
|
||||
settingsBloc: extension.settingsBloc
|
||||
})));
|
||||
}
|
||||
|
||||
extensionManager.init = function(extensionSettings) {
|
||||
|
||||
// Set extension config
|
||||
extensionSettings = extensionSettings || {};
|
||||
_.each(extensionList, function(extension) {
|
||||
extension.config = _.extend({}, extension.defaultConfig, extensionSettings[extension.extensionId]);
|
||||
extension.config.enabled = !extension.optional || extension.config.enabled === undefined || extension.config.enabled === true;
|
||||
});
|
||||
|
||||
// Load/Save extension config from/to settings
|
||||
extensionManager["onLoadSettings"] = function() {
|
||||
console.debug("onLoadSettings");
|
||||
_.each(extensionList, function(extension) {
|
||||
utils.setInputChecked("#input-enable-extension-" + extension.extensionId, extension.config.enabled);
|
||||
var onLoadSettingsCallback = extension.onLoadSettings;
|
||||
onLoadSettingsCallback && onLoadSettingsCallback();
|
||||
});
|
||||
};
|
||||
extensionManager["onSaveSettings"] = function(newExtensionSettings, event) {
|
||||
console.debug("onSaveSettings");
|
||||
_.each(extensionList, function(extension) {
|
||||
var newExtensionConfig = extension.defaultConfig || {};
|
||||
newExtensionConfig.enabled = utils.getInputChecked("#input-enable-extension-" + extension.extensionId);
|
||||
var onSaveSettingsCallback = extension.onSaveSettings;
|
||||
onSaveSettingsCallback && onSaveSettingsCallback(newExtensionConfig, event);
|
||||
newExtensionSettings[extension.extensionId] = newExtensionConfig;
|
||||
});
|
||||
};
|
||||
|
||||
addHook("onMessage");
|
||||
addHook("onError");
|
||||
addHook("onOfflineChanged");
|
||||
|
||||
// To store reference to modules that are accessible from extensions
|
||||
addHook("onFileManagerCreated");
|
||||
addHook("onSynchronizerCreated");
|
||||
addHook("onPublisherCreated");
|
||||
|
||||
// Operations on files
|
||||
addHook("onFileSystemLoaded");
|
||||
addHook("onFileCreated");
|
||||
addHook("onFileDeleted");
|
||||
addHook("onFileChanged");
|
||||
addHook("onFileSelected");
|
||||
addHook("onTitleChanged");
|
||||
addHook("onSyncImportSuccess");
|
||||
addHook("onSyncExportSuccess");
|
||||
addHook("onSyncRemoved");
|
||||
addHook("onPublishSuccess");
|
||||
addHook("onNewPublishSuccess");
|
||||
addHook("onPublishRemoved");
|
||||
|
||||
// Operations on Layout
|
||||
addHook("onLayoutConfigure");
|
||||
addHook("onLayoutCreated");
|
||||
|
||||
// Operations on PageDown
|
||||
addHook("onEditorConfigure");
|
||||
|
||||
var onPreviewFinished = createHook("onPreviewFinished");
|
||||
var onAsyncPreviewCallbackList = getExtensionCallbackList("onAsyncPreview");
|
||||
extensionManager["onAsyncPreview"] = function() {
|
||||
console.debug("onAsyncPreview");
|
||||
// Call onPreviewFinished callbacks when all async preview are finished
|
||||
var counter = 0;
|
||||
function tryFinished() {
|
||||
if(counter === onAsyncPreviewCallbackList.length) {
|
||||
onPreviewFinished();
|
||||
}
|
||||
}
|
||||
_.each(onAsyncPreviewCallbackList, function(asyncPreviewCallback) {
|
||||
asyncPreviewCallback(function() {
|
||||
counter++;
|
||||
tryFinished();
|
||||
});
|
||||
});
|
||||
tryFinished();
|
||||
};
|
||||
|
||||
// Call onReady callbacks
|
||||
var onReady = createHook("onReady");
|
||||
extensionManager["onReady"] = function() {
|
||||
|
||||
// Create accordion in settings dialog
|
||||
_.each(extensionList, createSettings);
|
||||
|
||||
onReady();
|
||||
};
|
||||
// Set extension config
|
||||
extensionSettings = settings.extensionSettings || {};
|
||||
_.each(extensionList, function(extension) {
|
||||
extension.config = _.extend({}, extension.defaultConfig, extensionSettings[extension.extensionId]);
|
||||
extension.config.enabled = !extension.optional || extension.config.enabled === undefined || extension.config.enabled === true;
|
||||
});
|
||||
|
||||
// Load/Save extension config from/to settings
|
||||
extensionMgr["onLoadSettings"] = function() {
|
||||
console.debug("onLoadSettings");
|
||||
_.each(extensionList, function(extension) {
|
||||
utils.setInputChecked("#input-enable-extension-" + extension.extensionId, extension.config.enabled);
|
||||
var onLoadSettingsCallback = extension.onLoadSettings;
|
||||
onLoadSettingsCallback && onLoadSettingsCallback();
|
||||
});
|
||||
};
|
||||
extensionMgr["onSaveSettings"] = function(newExtensionSettings, event) {
|
||||
console.debug("onSaveSettings");
|
||||
_.each(extensionList, function(extension) {
|
||||
var newExtensionConfig = extension.defaultConfig || {};
|
||||
newExtensionConfig.enabled = utils.getInputChecked("#input-enable-extension-" + extension.extensionId);
|
||||
var onSaveSettingsCallback = extension.onSaveSettings;
|
||||
onSaveSettingsCallback && onSaveSettingsCallback(newExtensionConfig, event);
|
||||
newExtensionSettings[extension.extensionId] = newExtensionConfig;
|
||||
});
|
||||
};
|
||||
|
||||
return extensionManager;
|
||||
addHook("onReady");
|
||||
addHook("onMessage");
|
||||
addHook("onError");
|
||||
addHook("onOfflineChanged");
|
||||
addHook("onAsyncRunning");
|
||||
|
||||
// To store reference to modules that are accessible from extensions
|
||||
addHook("onFileMgrCreated");
|
||||
addHook("onSynchronizerCreated");
|
||||
addHook("onPublisherCreated");
|
||||
|
||||
// Operations on files
|
||||
addHook("onFileSystemCreated");
|
||||
addHook("onFileCreated");
|
||||
addHook("onFileDeleted");
|
||||
addHook("onFileChanged");
|
||||
addHook("onFileSelected");
|
||||
addHook("onTitleChanged");
|
||||
|
||||
// Sync events
|
||||
addHook("onSyncRunning");
|
||||
addHook("onSyncSuccess");
|
||||
addHook("onSyncImportSuccess");
|
||||
addHook("onSyncExportSuccess");
|
||||
addHook("onSyncRemoved");
|
||||
|
||||
// Publish events
|
||||
addHook("onPublishRunning");
|
||||
addHook("onPublishSuccess");
|
||||
addHook("onNewPublishSuccess");
|
||||
addHook("onPublishRemoved");
|
||||
|
||||
// Operations on Layout
|
||||
addHook("onLayoutConfigure");
|
||||
addHook("onLayoutCreated");
|
||||
|
||||
// Operations on PageDown
|
||||
addHook("onEditorConfigure");
|
||||
|
||||
var onPreviewFinished = createHook("onPreviewFinished");
|
||||
var onAsyncPreviewCallbackList = getExtensionCallbackList("onAsyncPreview");
|
||||
extensionMgr["onAsyncPreview"] = function() {
|
||||
console.debug("onAsyncPreview");
|
||||
// Call onPreviewFinished callbacks when all async preview are finished
|
||||
var counter = 0;
|
||||
function tryFinished() {
|
||||
if(counter === onAsyncPreviewCallbackList.length) {
|
||||
onPreviewFinished();
|
||||
}
|
||||
}
|
||||
_.each(onAsyncPreviewCallbackList, function(asyncPreviewCallback) {
|
||||
asyncPreviewCallback(function() {
|
||||
counter++;
|
||||
tryFinished();
|
||||
});
|
||||
});
|
||||
tryFinished();
|
||||
};
|
||||
|
||||
$(function() {
|
||||
// Create accordion in settings dialog
|
||||
_.each(extensionList, createSettings);
|
||||
});
|
||||
|
||||
return extensionMgr;
|
||||
});
|
58
js/extensions/button-publish.js
Normal file
58
js/extensions/button-publish.js
Normal file
@ -0,0 +1,58 @@
|
||||
define([
|
||||
"jquery",
|
||||
"underscore"
|
||||
], function($, _) {
|
||||
|
||||
var buttonPublish = {
|
||||
extensionId: "buttonPublish",
|
||||
extensionName: 'Button "Publish"',
|
||||
optional: true,
|
||||
settingsBloc: '<p>Adds a "Publish document" button in the navigation bar.</p>'
|
||||
};
|
||||
|
||||
var currentFileDesc = undefined;
|
||||
var publishRunning = false;
|
||||
var hasPublications = false;
|
||||
var isOffline = false;
|
||||
// Enable/disable the button
|
||||
function updateButtonState() {
|
||||
if(publishRunning === true || hasPublications === false || isOffline === true) {
|
||||
$(".action-force-publish").addClass("disabled");
|
||||
}
|
||||
else {
|
||||
$(".action-force-publish").removeClass("disabled");
|
||||
}
|
||||
};
|
||||
|
||||
buttonPublish.onPublishRunning = function(isRunning) {
|
||||
publishRunning = isRunning;
|
||||
updateButtonState();
|
||||
};
|
||||
|
||||
buttonPublish.onOfflineChanged = function(isOfflineParameter) {
|
||||
isOffline = isOfflineParameter;
|
||||
updateButtonState();
|
||||
};
|
||||
|
||||
// Check that current file has publications
|
||||
var checkPublication = function() {
|
||||
if(_.size(currentFileDesc.publishLocations) === 0) {
|
||||
hasPublications = false;
|
||||
}
|
||||
else {
|
||||
hasPublications = true;
|
||||
}
|
||||
updateButtonState();
|
||||
};
|
||||
|
||||
buttonPublish.onFileSelected = function(fileDesc) {
|
||||
currentFileDesc = fileDesc;
|
||||
checkPublication();
|
||||
};
|
||||
|
||||
buttonPublish.onPublishRemoved = checkPublication;
|
||||
buttonPublish.onNewPublishSuccess = checkPublication;
|
||||
|
||||
return buttonPublish;
|
||||
|
||||
});
|
@ -1,12 +1,13 @@
|
||||
define( [ "jquery", "underscore" ], function($) {
|
||||
define([
|
||||
"jquery",
|
||||
"underscore"
|
||||
], function($, _) {
|
||||
|
||||
var sharingButton = {
|
||||
extensionId: "sharingButton",
|
||||
extensionName: "Sharing button",
|
||||
var buttonShare = {
|
||||
extensionId: "buttonShare",
|
||||
extensionName: 'Button "Share"',
|
||||
optional: true,
|
||||
settingsBloc: [
|
||||
'<p>Adds a "Share document" button in the navigation bar.</p>'
|
||||
].join("")
|
||||
settingsBloc: '<p>Adds a "Share document" button in the navigation bar.</p>'
|
||||
};
|
||||
|
||||
var fileDesc = undefined;
|
||||
@ -14,8 +15,7 @@ define( [ "jquery", "underscore" ], function($) {
|
||||
'<div class="input-prepend">',
|
||||
'<a href="<%= link %>" class="add-on" title="Sharing location"><i class="icon-link"></i></a>',
|
||||
'<input class="span2" type="text" value="<%= link %>" readonly />',
|
||||
'</div>'
|
||||
].join("");
|
||||
'</div>'].join("");
|
||||
var refreshDocumentSharing = function(fileDescParameter) {
|
||||
if(fileDescParameter !== undefined && fileDescParameter !== fileDesc) {
|
||||
return;
|
||||
@ -39,14 +39,14 @@ define( [ "jquery", "underscore" ], function($) {
|
||||
});
|
||||
};
|
||||
|
||||
sharingButton.onFileSelected = function(fileDescParameter) {
|
||||
buttonShare.onFileSelected = function(fileDescParameter) {
|
||||
fileDesc = fileDescParameter;
|
||||
refreshDocumentSharing(fileDescParameter);
|
||||
};
|
||||
|
||||
sharingButton.onNewPublishSuccess = refreshDocumentSharing;
|
||||
sharingButton.onPublishRemoved = refreshDocumentSharing;
|
||||
buttonShare.onNewPublishSuccess = refreshDocumentSharing;
|
||||
buttonShare.onPublishRemoved = refreshDocumentSharing;
|
||||
|
||||
return sharingButton;
|
||||
return buttonShare;
|
||||
|
||||
});
|
55
js/extensions/button-sync.js
Normal file
55
js/extensions/button-sync.js
Normal file
@ -0,0 +1,55 @@
|
||||
define([
|
||||
"jquery",
|
||||
"underscore"
|
||||
], function($, _) {
|
||||
|
||||
var buttonSync = {
|
||||
extensionId: "buttonSync",
|
||||
extensionName: 'Button "Synchronize"',
|
||||
optional: true,
|
||||
settingsBloc: '<p>Adds a "Synchronize documents" button in the navigation bar.</p>'
|
||||
};
|
||||
|
||||
var syncRunning = false;
|
||||
var uploadPending = false;
|
||||
var isOffline = false;
|
||||
// Enable/disable the button
|
||||
function updateButtonState() {
|
||||
if(syncRunning === true || uploadPending === false || isOffline) {
|
||||
$(".action-force-sync").addClass("disabled");
|
||||
}
|
||||
else {
|
||||
$(".action-force-sync").removeClass("disabled");
|
||||
}
|
||||
};
|
||||
|
||||
buttonSync.onSyncRunning = function(isRunning) {
|
||||
syncRunning = isRunning;
|
||||
uploadPending = true;
|
||||
updateButtonState();
|
||||
};
|
||||
|
||||
buttonSync.onSyncSuccess = function() {
|
||||
uploadPending = false;
|
||||
updateButtonState();
|
||||
};
|
||||
|
||||
buttonSync.onOfflineChanged = function(isOfflineParameter) {
|
||||
isOffline = isOfflineParameter;
|
||||
updateButtonState();
|
||||
};
|
||||
|
||||
// Check that a file has synchronized locations
|
||||
var checkSynchronization = function(fileDesc) {
|
||||
if(_.size(fileDesc.syncLocations) !== 0) {
|
||||
uploadPending = true;
|
||||
updateButtonState();
|
||||
}
|
||||
};
|
||||
|
||||
buttonSync.onFileChanged = checkSynchronization;
|
||||
buttonSync.onTitleChanged = checkSynchronization;
|
||||
|
||||
return buttonSync;
|
||||
|
||||
});
|
@ -1,64 +1,75 @@
|
||||
define( [ "jquery", "underscore" ], function($) {
|
||||
define([
|
||||
"jquery",
|
||||
"underscore"
|
||||
], function($, _) {
|
||||
|
||||
var documentSelector = {
|
||||
extensionId: "documentSelector",
|
||||
extensionName: "Document selector",
|
||||
settingsBloc: [
|
||||
'<p>Builds the "Open document" dropdown menu.</p>'
|
||||
].join("")
|
||||
settingsBloc: '<p>Builds the "Open document" dropdown menu.</p>'
|
||||
};
|
||||
|
||||
var fileSystemDescriptor = undefined;
|
||||
documentSelector.onFileSystemLoaded = function(fileSystemDescriptorParameter) {
|
||||
fileSystemDescriptor = fileSystemDescriptorParameter;
|
||||
var fileSystem = undefined;
|
||||
documentSelector.onFileSystemCreated = function(fileSystemParameter) {
|
||||
fileSystem = fileSystemParameter;
|
||||
};
|
||||
|
||||
var fileDesc = undefined;
|
||||
var updateSelector = function() {
|
||||
var sortedDescriptor = _.sortBy(fileSystemDescriptor, function(fileDesc) {
|
||||
return fileDesc.title.toLowerCase();
|
||||
});
|
||||
|
||||
var fileMgr = undefined;
|
||||
documentSelector.onFileMgrCreated = function(fileMgrParameter) {
|
||||
fileMgr = fileMgrParameter;
|
||||
};
|
||||
|
||||
var liMap = undefined;
|
||||
var buildSelector = function() {
|
||||
|
||||
function composeTitle(fileDesc) {
|
||||
var result = [];
|
||||
var syncAttributesList = _.values(fileDesc.syncLocations);
|
||||
var publishAttributesList = _.values(fileDesc.publishLocations);
|
||||
var attributesList = syncAttributesList.concat(publishAttributesList);
|
||||
_.chain(attributesList).sortBy(function(attributes) {
|
||||
return attributes.provider;
|
||||
return attributes.provider.providerId;
|
||||
}).each(function(attributes) {
|
||||
result.push('<i class="icon-' + attributes.provider + '"></i>');
|
||||
result.push('<i class="icon-' + attributes.provider.providerId + '"></i>');
|
||||
});
|
||||
result.push(" ");
|
||||
result.push(fileDesc.title);
|
||||
return result.join("");
|
||||
}
|
||||
|
||||
liMap = {};
|
||||
$("#file-selector li:not(.stick)").empty();
|
||||
_.each(sortedDescriptor, function(fileDescToPrint) {
|
||||
var a = $("<a>").html(composeTitle(fileDescToPrint.fileIndex));
|
||||
_.chain(
|
||||
fileSystem
|
||||
).sortBy(function(fileDesc) {
|
||||
return fileDesc.title.toLowerCase();
|
||||
}).each(function(fileDesc) {
|
||||
var a = $('<a href="#">').html(composeTitle(fileDesc)).click(function() {
|
||||
if(liMap[fileDesc.fileIndex].is(".disabled")) {
|
||||
fileMgr.selectFile(fileDesc);
|
||||
}
|
||||
});
|
||||
var li = $("<li>").append(a);
|
||||
if (fileDescToPrint === fileDesc) {
|
||||
li.addClass("disabled");
|
||||
} else {
|
||||
a.prop("href", "#").click(function() {
|
||||
fileManager.selectFile(fileDescToPrint);
|
||||
});
|
||||
}
|
||||
liMap[fileDesc.fileIndex] = li;
|
||||
$("#file-selector").append(li);
|
||||
});
|
||||
};
|
||||
|
||||
documentSelector.onFileSelected = function(fileDescParameter) {
|
||||
fileDesc = fileDescParameter;
|
||||
updateSelector();
|
||||
documentSelector.onFileSelected = function(fileDesc) {
|
||||
if(liMap === undefined) {
|
||||
buildSelector();
|
||||
}
|
||||
$("#file-selector li:not(.stick)").removeClass("disabled");
|
||||
liMap[fileDesc.fileIndex].addClass("disabled");
|
||||
};
|
||||
|
||||
documentSelector.onTitleChanged = updateSelector;
|
||||
documentSelector.onSyncExportSuccess = updateSelector;
|
||||
documentSelector.onSyncRemoved = updateSelector;
|
||||
documentSelector.onNewPublishSuccess = updateSelector;
|
||||
documentSelector.onPublishRemoved = updateSelector;
|
||||
documentSelector.onFileCreated = buildSelector;
|
||||
documentSelector.onFileDeleted = buildSelector;
|
||||
documentSelector.onTitleChanged = buildSelector;
|
||||
documentSelector.onSyncExportSuccess = buildSelector;
|
||||
documentSelector.onSyncRemoved = buildSelector;
|
||||
documentSelector.onNewPublishSuccess = buildSelector;
|
||||
documentSelector.onPublishRemoved = buildSelector;
|
||||
|
||||
return documentSelector;
|
||||
|
||||
|
@ -1,11 +1,12 @@
|
||||
define( [ "jquery", "underscore" ], function($) {
|
||||
define([
|
||||
"jquery",
|
||||
"underscore"
|
||||
], function($, _) {
|
||||
|
||||
var documentTitle = {
|
||||
extensionId: "documentTitle",
|
||||
extensionName: "Document title",
|
||||
settingsBloc: [
|
||||
'<p>Responsible for showing the document title in the navigation bar.</p>'
|
||||
].join("")
|
||||
settingsBloc: '<p>Responsible for showing the document title in the navigation bar.</p>'
|
||||
};
|
||||
|
||||
var layout = undefined;
|
||||
@ -15,7 +16,7 @@ define( [ "jquery", "underscore" ], function($) {
|
||||
|
||||
var fileDesc = undefined;
|
||||
var updateTitle = function(fileDescParameter) {
|
||||
if(fileDescParameter !== undefined && fileDescParameter !== fileDesc) {
|
||||
if(fileDescParameter !== fileDesc) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -25,9 +26,9 @@ define( [ "jquery", "underscore" ], function($) {
|
||||
var publishAttributesList = _.values(fileDesc.publishLocations);
|
||||
var attributesList = syncAttributesList.concat(publishAttributesList);
|
||||
_.chain(attributesList).sortBy(function(attributes) {
|
||||
return attributes.provider;
|
||||
return attributes.provider.providerId;
|
||||
}).each(function(attributes) {
|
||||
result.push('<i class="icon-' + attributes.provider + '"></i>');
|
||||
result.push('<i class="icon-' + attributes.provider.providerId + '"></i>');
|
||||
});
|
||||
result.push(" ");
|
||||
result.push(fileDesc.title);
|
||||
|
@ -1,16 +1,17 @@
|
||||
define( [ "jquery", "underscore" ], function($) {
|
||||
define([
|
||||
"jquery",
|
||||
"underscore"
|
||||
], function($, _) {
|
||||
|
||||
var managePublication = {
|
||||
extensionId: "managePublication",
|
||||
extensionName: "Manage Publication",
|
||||
settingsBloc: [
|
||||
'<p>Populates the "Manage publication" dialog box.</p>'
|
||||
].join("")
|
||||
settingsBloc: '<p>Populates the "Manage publication" dialog box.</p>'
|
||||
};
|
||||
|
||||
var fileManager = undefined;
|
||||
manageSynchronization.onFileManagerCreated = function(fileManagerParameter) {
|
||||
fileManager = fileManagerParameter;
|
||||
var fileMgr = undefined;
|
||||
managePublication.onFileMgrCreated = function(fileMgrParameter) {
|
||||
fileMgr = fileMgrParameter;
|
||||
};
|
||||
|
||||
var fileDesc = undefined;
|
||||
@ -46,7 +47,7 @@ define( [ "jquery", "underscore" ], function($) {
|
||||
publishDesc: publishDesc
|
||||
}));
|
||||
lineElement.append($(removeButtonTemplate).click(function() {
|
||||
fileManager.removePublish(publishAttributes);
|
||||
fileMgr.removePublish(publishAttributes);
|
||||
}));
|
||||
publishList.append(lineElement);
|
||||
});
|
||||
|
@ -1,16 +1,17 @@
|
||||
define( [ "jquery", "underscore" ], function($) {
|
||||
define([
|
||||
"jquery",
|
||||
"underscore"
|
||||
], function($, _) {
|
||||
|
||||
var manageSynchronization = {
|
||||
extensionId: "manageSynchronization",
|
||||
extensionName: "Manage Synchronization",
|
||||
settingsBloc: [
|
||||
'<p>Populates the "Manage synchronization" dialog box.</p>'
|
||||
].join("")
|
||||
settingsBloc: '<p>Populates the "Manage synchronization" dialog box.</p>'
|
||||
};
|
||||
|
||||
var fileManager = undefined;
|
||||
manageSynchronization.onFileManagerCreated = function(fileManagerParameter) {
|
||||
fileManager = fileManagerParameter;
|
||||
var fileMgr = undefined;
|
||||
manageSynchronization.onFileMgrCreated = function(fileMgrParameter) {
|
||||
fileMgr = fileMgrParameter;
|
||||
};
|
||||
|
||||
var fileDesc = undefined;
|
||||
@ -42,7 +43,7 @@ define( [ "jquery", "underscore" ], function($) {
|
||||
syncDesc: syncDesc
|
||||
}));
|
||||
lineElement.append($(removeButtonTemplate).click(function() {
|
||||
fileManager.removeSync(syncAttributes);
|
||||
fileMgr.removeSync(syncAttributes);
|
||||
}));
|
||||
syncList.append(lineElement);
|
||||
});
|
||||
|
@ -1,4 +1,7 @@
|
||||
define( [ "utils", "Markdown.Extra" ], function(utils) {
|
||||
define([
|
||||
"utils",
|
||||
"lib/Markdown.Extra"
|
||||
], function(utils) {
|
||||
|
||||
var markdownExtra = {
|
||||
extensionId: "markdownExtra",
|
||||
|
@ -1,4 +1,6 @@
|
||||
define( [ "MathJax" ], function($) {
|
||||
define([
|
||||
"lib/MathJax"
|
||||
], function() {
|
||||
|
||||
var mathJax = {
|
||||
extensionId: "mathJax",
|
||||
|
@ -1,4 +1,8 @@
|
||||
define( [ "jquery", "jgrowl", "underscore" ], function($) {
|
||||
define([
|
||||
"jquery",
|
||||
"underscore",
|
||||
"jgrowl"
|
||||
], function($, _, jGrowl) {
|
||||
|
||||
var notifications = {
|
||||
extensionId: "notifications",
|
||||
@ -6,15 +10,15 @@ define( [ "jquery", "jgrowl", "underscore" ], function($) {
|
||||
defaultConfig: {
|
||||
showingTime: 5000
|
||||
},
|
||||
settingsBloc: "<p>Shows notification messages in the bottom-right corner of the screen.</p>"
|
||||
settingsBloc: '<p>Shows notification messages in the bottom-right corner of the screen.</p>'
|
||||
};
|
||||
|
||||
notifications.onReady = function() {
|
||||
// jGrowl configuration
|
||||
$.jGrowl.defaults.life = notifications.config.showingTime;
|
||||
$.jGrowl.defaults.closer = false;
|
||||
$.jGrowl.defaults.closeTemplate = '';
|
||||
$.jGrowl.defaults.position = 'bottom-right';
|
||||
jGrowl.defaults.life = notifications.config.showingTime;
|
||||
jGrowl.defaults.closer = false;
|
||||
jGrowl.defaults.closeTemplate = '';
|
||||
jGrowl.defaults.position = 'bottom-right';
|
||||
};
|
||||
|
||||
function showMessage(msg, iconClass, options) {
|
||||
@ -30,15 +34,22 @@ define( [ "jquery", "jgrowl", "underscore" ], function($) {
|
||||
}
|
||||
options = options || {};
|
||||
iconClass = iconClass || "icon-info-sign";
|
||||
$.jGrowl("<i class='icon-white " + iconClass + "'></i> " + _.escape(msg), options);
|
||||
jGrowl("<i class='icon-white " + iconClass + "'></i> " + _.escape(msg), options);
|
||||
}
|
||||
|
||||
notifications.onMessage = function(message) {
|
||||
console.log(message);
|
||||
showMessage(message);
|
||||
};
|
||||
|
||||
notifications.onError = function(error) {
|
||||
showMessage(error, "icon-warning-sign");
|
||||
console.error(error);
|
||||
if(_.isString(error)) {
|
||||
showMessage(error, "icon-warning-sign");
|
||||
}
|
||||
else if(_.isObject(error)) {
|
||||
showMessage(error.message, "icon-warning-sign");
|
||||
}
|
||||
};
|
||||
|
||||
notifications.onOfflineChanged = function(isOffline) {
|
||||
|
@ -1,4 +1,8 @@
|
||||
define( [ "jquery", "underscore" ], function($) {
|
||||
define([
|
||||
"jquery",
|
||||
"underscore",
|
||||
"lib/css_browser_selector"
|
||||
], function($, _) {
|
||||
|
||||
var scrollLink = {
|
||||
extensionId: "scrollLink",
|
||||
@ -7,8 +11,8 @@ define( [ "jquery", "underscore" ], function($) {
|
||||
settingsBloc: [
|
||||
'<p>Binds together editor and preview scrollbars.</p>',
|
||||
'<blockquote class="muted"><b>NOTE:</b> ',
|
||||
'The mapping between Markdown and HTML is based on the position of the title elements (h1, h2, ...) in the page. ',
|
||||
'Therefore, if your document does not contain any title, the mapping will be linear and consequently less accurate.',
|
||||
'The mapping between Markdown and HTML is based on the position of the title elements (h1, h2, ...) in the page. ',
|
||||
'Therefore, if your document does not contain any title, the mapping will be linear and consequently less accurate.',
|
||||
'</bloquote>'
|
||||
].join("")
|
||||
};
|
||||
|
@ -1,25 +1,16 @@
|
||||
define( [ "jquery", "underscore" ], function($) {
|
||||
define([
|
||||
"jquery",
|
||||
"underscore",
|
||||
"utils"
|
||||
], function($, _, utils) {
|
||||
|
||||
var toc = {
|
||||
extensionId: "toc",
|
||||
extensionName: "Table Of Content",
|
||||
optional: true,
|
||||
settingsBloc: [
|
||||
'<p>Generates tables of content using the marker [TOC].</p>'
|
||||
].join("")
|
||||
settingsBloc: '<p>Generates tables of content using the marker [TOC].</p>'
|
||||
};
|
||||
|
||||
// Used to generate an anchor
|
||||
function slugify(text)
|
||||
{
|
||||
return text.toLowerCase()
|
||||
.replace(/\s+/g, '-') // Replace spaces with -
|
||||
.replace(/[^\w\-]+/g, '') // Remove all non-word chars
|
||||
.replace(/\-\-+/g, '-') // Replace multiple - with single -
|
||||
.replace(/^-+/, '') // Trim - from start of text
|
||||
.replace(/-+$/, ''); // Trim - from end of text
|
||||
}
|
||||
|
||||
// TOC element description
|
||||
function TocElement(tagName, anchor, text) {
|
||||
this.tagName = tagName;
|
||||
@ -83,7 +74,7 @@ define( [ "jquery", "underscore" ], function($) {
|
||||
function buildToc() {
|
||||
var anchorList = {};
|
||||
function createAnchor(element) {
|
||||
var id = element.prop("id") || slugify(element.text());
|
||||
var id = element.prop("id") || utils.slugify(element.text());
|
||||
var anchor = id;
|
||||
var index = 0;
|
||||
while(_.has(anchorList, anchor)) {
|
||||
|
24
js/extensions/working-indicator.js
Normal file
24
js/extensions/working-indicator.js
Normal file
@ -0,0 +1,24 @@
|
||||
define([
|
||||
"jquery",
|
||||
"underscore"
|
||||
], function($, _) {
|
||||
|
||||
var workingIndicator = {
|
||||
extensionId: "workingIndicator",
|
||||
extensionName: "Working indicator",
|
||||
settingsBloc: '<p>Displays an animated image when a network operation is running.</p>'
|
||||
};
|
||||
|
||||
workingIndicator.onAsyncRunning = function(isRunning) {
|
||||
if (isRunning === false) {
|
||||
$(".working-indicator").removeClass("show");
|
||||
$("body").removeClass("working");
|
||||
} else {
|
||||
$(".working-indicator").addClass("show");
|
||||
$("body").addClass("working");
|
||||
}
|
||||
};
|
||||
|
||||
return workingIndicator;
|
||||
|
||||
});
|
@ -1,53 +1,30 @@
|
||||
define([
|
||||
"jquery",
|
||||
"underscore",
|
||||
"core",
|
||||
"utils",
|
||||
"settings",
|
||||
"extension-manager",
|
||||
"synchronizer",
|
||||
"publisher",
|
||||
"sharing",
|
||||
"text!../WELCOME.md",
|
||||
"underscore"
|
||||
], function($, core, utils, extensionManager, synchronizer, publisher, sharing, welcomeContent) {
|
||||
"file-system",
|
||||
"lib/text!../WELCOME.md"
|
||||
], function($, _, core, utils, settings, extensionMgr, fileSystem, welcomeContent) {
|
||||
|
||||
var fileManager = {};
|
||||
|
||||
// Load file descriptors from localStorage and store in a map
|
||||
var fileSystemDescriptor = _.chain(localStorage["file.list"].split(";"))
|
||||
.compact()
|
||||
.reduce(function(fileSystemDescriptor, fileIndex) {
|
||||
var title = localStorage[fileIndex + ".title"];
|
||||
var fileDesc = {
|
||||
fileIndex : fileIndex,
|
||||
title : title,
|
||||
syncLocations: {},
|
||||
publishLocations: {}
|
||||
};
|
||||
synchronizer.populateSyncLocations(fileDesc),
|
||||
publisher.populatePublishLocations(fileDesc),
|
||||
fileSystemDescriptor[fileIndex] = fileDesc;
|
||||
return fileSystemDescriptor;
|
||||
}, {})
|
||||
.value();
|
||||
extensionManager.onFileSystemLoaded(fileSystemDescriptor);
|
||||
fileManager.getFileList = function() {
|
||||
return _.values(fileSystemDescriptor);
|
||||
};
|
||||
var fileMgr = {};
|
||||
|
||||
// Defines the current file
|
||||
var currentFile = (function() {
|
||||
var currentFileIndex = localStorage["file.current"];
|
||||
if(currentFileIndex !== undefined) {
|
||||
return fileSystemDescriptor[currentFileIndex];
|
||||
var fileIndex = localStorage["file.current"];
|
||||
if(fileIndex !== undefined) {
|
||||
return fileSystem[fileIndex];
|
||||
}
|
||||
})();
|
||||
fileManager.getCurrentFile = function() {
|
||||
fileMgr.getCurrentFile = function() {
|
||||
return currentFile;
|
||||
};
|
||||
fileManager.isCurrentFile = function(fileDesc) {
|
||||
fileMgr.isCurrentFile = function(fileDesc) {
|
||||
return fileDesc === currentFile;
|
||||
};
|
||||
fileManager.setCurrentFile = function(fileDesc) {
|
||||
fileMgr.setCurrentFile = function(fileDesc) {
|
||||
currentFile = fileDesc;
|
||||
if(fileDesc === undefined) {
|
||||
localStorage.removeItem("file.current");
|
||||
@ -58,24 +35,21 @@ define([
|
||||
};
|
||||
|
||||
// Caution: this function recreate the editor (reset undo operations)
|
||||
fileManager.selectFile = function(fileDesc) {
|
||||
fileMgr.selectFile = function(fileDesc) {
|
||||
var fileSystemSize = _.size(fileSystem);
|
||||
// If no file create one
|
||||
if (_.size(fileSystemDescriptor) === 0) {
|
||||
fileDesc = fileManager.createFile(WELCOME_DOCUMENT_TITLE, welcomeContent);
|
||||
if (_.size(fileSystem) === 0) {
|
||||
fileDesc = fileMgr.createFile(WELCOME_DOCUMENT_TITLE, welcomeContent);
|
||||
}
|
||||
|
||||
if(fileDesc === undefined) {
|
||||
// If no file is selected take the last created
|
||||
fileDesc = fileSystemDescriptor[_.keys(fileSystemDescriptor)[fileSystemDescriptor.length - 1]];
|
||||
fileDesc = fileSystem[_.keys(fileSystem)[fileSystemSize - 1]];
|
||||
}
|
||||
fileManager.setCurrentFile(fileDesc);
|
||||
fileMgr.setCurrentFile(fileDesc);
|
||||
|
||||
// Update the file titles
|
||||
fileManager.updateFileTitles();
|
||||
publisher.notifyPublish();
|
||||
|
||||
// Notify extensions
|
||||
extensionManager.onFileSelected(fileDesc);
|
||||
extensionMgr.onFileSelected(fileDesc);
|
||||
|
||||
// Hide the viewer pencil button
|
||||
if(fileDesc.fileIndex == TEMPORARY_FILE_INDEX) {
|
||||
@ -89,18 +63,18 @@ define([
|
||||
$("#wmd-input").val(localStorage[fileDesc.fileIndex + ".content"]);
|
||||
core.createEditor(function() {
|
||||
// Callback to save content when textarea changes
|
||||
fileManager.saveFile();
|
||||
fileMgr.saveFile();
|
||||
});
|
||||
};
|
||||
|
||||
fileManager.createFile = function(title, content, syncLocations, isTemporary) {
|
||||
content = content !== undefined ? content : core.settings.defaultContent;
|
||||
fileMgr.createFile = function(title, content, syncLocations, isTemporary) {
|
||||
content = content !== undefined ? content : settings.defaultContent;
|
||||
syncLocations = syncLocations || {};
|
||||
if (!title) {
|
||||
// Create a file title
|
||||
title = DEFAULT_FILE_TITLE;
|
||||
var indicator = 2;
|
||||
while(_.some(fileSystemDescriptor, function(fileDesc) {
|
||||
while(_.some(fileSystem, function(fileDesc) {
|
||||
return fileDesc.title == title;
|
||||
})) {
|
||||
title = DEFAULT_FILE_TITLE + indicator++;
|
||||
@ -112,7 +86,7 @@ define([
|
||||
if(!isTemporary) {
|
||||
do {
|
||||
fileIndex = "file." + utils.randomString();
|
||||
} while(_.has(fileSystemDescriptor, fileIndex));
|
||||
} while(_.has(fileSystem, fileIndex));
|
||||
}
|
||||
|
||||
// Create the file in the localStorage
|
||||
@ -137,27 +111,27 @@ define([
|
||||
// Add the index to the file list
|
||||
if(!isTemporary) {
|
||||
localStorage["file.list"] += fileIndex + ";";
|
||||
fileSystemDescriptor[fileIndex] = fileDesc;
|
||||
extensionManager.onFileCreated(fileDesc);
|
||||
fileSystem[fileIndex] = fileDesc;
|
||||
extensionMgr.onFileCreated(fileDesc);
|
||||
}
|
||||
return fileDesc;
|
||||
};
|
||||
|
||||
fileManager.deleteFile = function(fileDesc) {
|
||||
fileDesc = fileDesc || fileManager.getCurrentFile();
|
||||
if(fileManager.isCurrentFile(fileDesc)) {
|
||||
fileMgr.deleteFile = function(fileDesc) {
|
||||
fileDesc = fileDesc || fileMgr.getCurrentFile();
|
||||
if(fileMgr.isCurrentFile(fileDesc)) {
|
||||
// Unset the current fileDesc
|
||||
fileManager.setCurrentFile();
|
||||
fileMgr.setCurrentFile();
|
||||
}
|
||||
|
||||
// Remove synchronized locations
|
||||
_.each(fileDesc.syncLocations, function(syncAttributes) {
|
||||
fileManager.removeSync(syncAttributes, true);
|
||||
fileMgr.removeSync(syncAttributes, true);
|
||||
});
|
||||
|
||||
// Remove publish locations
|
||||
_.each(fileDesc.publishLocations, function(publishAttributes) {
|
||||
fileManager.removePublish(publishAttributes, true);
|
||||
fileMgr.removePublish(publishAttributes, true);
|
||||
});
|
||||
|
||||
// Remove the index from the file list
|
||||
@ -168,30 +142,29 @@ define([
|
||||
localStorage.removeItem(fileIndex + ".content");
|
||||
localStorage.removeItem(fileIndex + ".sync");
|
||||
localStorage.removeItem(fileIndex + ".publish");
|
||||
fileSystemDescriptor.removeItem(fileIndex);
|
||||
extensionManager.onFileDeleted(fileDesc);
|
||||
fileSystem.removeItem(fileIndex);
|
||||
extensionMgr.onFileDeleted(fileDesc);
|
||||
};
|
||||
|
||||
// Save current file in localStorage
|
||||
fileManager.saveFile = function() {
|
||||
fileMgr.saveFile = function() {
|
||||
var content = $("#wmd-input").val();
|
||||
var fileDesc = fileManager.getCurrentFile();
|
||||
var fileDesc = fileMgr.getCurrentFile();
|
||||
localStorage[fileDesc.fileIndex + ".content"] = content;
|
||||
extensionManager.onFileChanged(fileDesc);
|
||||
synchronizer.notifyChange(fileDesc);
|
||||
extensionMgr.onFileChanged(fileDesc);
|
||||
};
|
||||
|
||||
// Add a synchronized location to a file
|
||||
fileManager.addSync = function(fileDesc, syncAttributes) {
|
||||
fileMgr.addSync = function(fileDesc, syncAttributes) {
|
||||
localStorage[fileDesc.fileIndex + ".sync"] += syncAttributes.syncIndex + ";";
|
||||
fileDesc.syncLocations[syncAttributes.syncIndex] = syncAttributes;
|
||||
// addSync is only used for export, not for import
|
||||
extensionManager.onSyncExportSuccess(fileDesc, syncAttributes);
|
||||
extensionMgr.onSyncExportSuccess(fileDesc, syncAttributes);
|
||||
};
|
||||
|
||||
// Remove a synchronized location
|
||||
fileManager.removeSync = function(syncAttributes, skipExtensions) {
|
||||
var fileDesc = fileManager.getFileFromSyncIndex(syncAttributes.syncIndex);
|
||||
fileMgr.removeSync = function(syncAttributes, skipExtensions) {
|
||||
var fileDesc = fileMgr.getFileFromSyncIndex(syncAttributes.syncIndex);
|
||||
if(fileDesc !== undefined) {
|
||||
localStorage[fileDesc.fileIndex + ".sync"] = localStorage[fileDesc.fileIndex + ".sync"].replace(";"
|
||||
+ syncAttributes.syncIndex + ";", ";");
|
||||
@ -200,26 +173,26 @@ define([
|
||||
localStorage.removeItem(syncAttributes.syncIndex);
|
||||
fileDesc.syncLocations.removeItem(syncIndex);
|
||||
if(!skipExtensions) {
|
||||
extensionManager.onSyncRemoved(fileDesc, syncAttributes);
|
||||
extensionMgr.onSyncRemoved(fileDesc, syncAttributes);
|
||||
}
|
||||
};
|
||||
|
||||
// Get the file descriptor associated to a syncIndex
|
||||
fileManager.getFileFromSyncIndex = function(syncIndex) {
|
||||
return _.find(fileSystemDescriptor, function(fileDesc) {
|
||||
fileMgr.getFileFromSyncIndex = function(syncIndex) {
|
||||
return _.find(fileSystem, function(fileDesc) {
|
||||
return _.has(fileDesc.syncLocations, syncIndex);
|
||||
});
|
||||
};
|
||||
|
||||
// Get syncAttributes from syncIndex
|
||||
fileManager.getSyncAttributes = function(syncIndex) {
|
||||
var fileDesc = fileManager.getFileFromSyncIndex(syncIndex);
|
||||
fileMgr.getSyncAttributes = function(syncIndex) {
|
||||
var fileDesc = fileMgr.getFileFromSyncIndex(syncIndex);
|
||||
return fileDesc && fileDesc.syncLocations[syncIndex];
|
||||
};
|
||||
|
||||
// Returns true if provider has locations to synchronize
|
||||
fileManager.hasSync = function(provider) {
|
||||
return _.some(fileSystemDescriptor, function(fileDesc) {
|
||||
fileMgr.hasSync = function(provider) {
|
||||
return _.some(fileSystem, function(fileDesc) {
|
||||
return _.some(fileDesc.syncLocations, function(syncAttributes) {
|
||||
syncAttributes.provider == provider.providerId;
|
||||
});
|
||||
@ -227,33 +200,30 @@ define([
|
||||
};
|
||||
|
||||
// Add a publishIndex (publish location) to a file
|
||||
fileManager.addPublish = function(fileDesc, publishAttributes) {
|
||||
fileMgr.addPublish = function(fileDesc, publishAttributes) {
|
||||
localStorage[fileDesc.fileIndex + ".publish"] += publishAttributes.publishIndex + ";";
|
||||
fileDesc.publishLocations[publishAttributes.publishIndex] = publishAttributes;
|
||||
extensionManager.onNewPublishSuccess(fileDesc, publishAttributes);
|
||||
extensionMgr.onNewPublishSuccess(fileDesc, publishAttributes);
|
||||
};
|
||||
|
||||
// Remove a publishIndex (publish location)
|
||||
fileManager.removePublish = function(publishAttributes, skipExtensions) {
|
||||
var fileDesc = fileManager.getFileFromPublish(publishAttributes.publishIndex);
|
||||
fileMgr.removePublish = function(publishAttributes, skipExtensions) {
|
||||
var fileDesc = fileMgr.getFileFromPublish(publishAttributes.publishIndex);
|
||||
if(fileDesc !== undefined) {
|
||||
localStorage[fileDesc.fileIndex + ".publish"] = localStorage[fileDesc.fileIndex + ".publish"].replace(";"
|
||||
+ publishAttributes.publishIndex + ";", ";");
|
||||
if(fileManager.isCurrentFile(fileDesc)) {
|
||||
publisher.notifyPublish();
|
||||
}
|
||||
}
|
||||
// Remove publish attributes
|
||||
localStorage.removeItem(publishAttributes.publishIndex);
|
||||
fileDesc.publishLocations.removeItem(publishIndex);
|
||||
if(!skipExtensions) {
|
||||
extensionManager.onPublishRemoved(fileDesc, publishAttributes);
|
||||
extensionMgr.onPublishRemoved(fileDesc, publishAttributes);
|
||||
}
|
||||
};
|
||||
|
||||
// Get the file descriptor associated to a publishIndex
|
||||
fileManager.getFileFromPublish = function(publishIndex) {
|
||||
return _.find(fileSystemDescriptor, function(fileDesc) {
|
||||
fileMgr.getFileFromPublish = function(publishIndex) {
|
||||
return _.find(fileSystem, function(fileDesc) {
|
||||
return _.has(fileDesc.publishLocations, publishIndex);
|
||||
});
|
||||
};
|
||||
@ -276,11 +246,12 @@ define([
|
||||
}
|
||||
|
||||
core.onReady(function() {
|
||||
fileManager.selectFile();
|
||||
|
||||
fileMgr.selectFile();
|
||||
|
||||
$(".action-create-file").click(function() {
|
||||
var fileDesc = fileManager.createFile();
|
||||
fileManager.selectFile(fileDesc);
|
||||
var fileDesc = fileMgr.createFile();
|
||||
fileMgr.selectFile(fileDesc);
|
||||
var wmdInput = $("#wmd-input").focus().get(0);
|
||||
if(wmdInput.setSelectionRange) {
|
||||
wmdInput.setSelectionRange(0, 0);
|
||||
@ -288,8 +259,8 @@ define([
|
||||
$("#file-title").click();
|
||||
});
|
||||
$(".action-remove-file").click(function() {
|
||||
fileManager.deleteFile();
|
||||
fileManager.selectFile();
|
||||
fileMgr.deleteFile();
|
||||
fileMgr.selectFile();
|
||||
});
|
||||
$("#file-title").click(function() {
|
||||
if(viewerMode === true) {
|
||||
@ -305,15 +276,13 @@ define([
|
||||
input.hide();
|
||||
$("#file-title").show();
|
||||
var title = $.trim(input.val());
|
||||
var fileDesc = fileManager.getCurrentFile();
|
||||
var fileDesc = fileMgr.getCurrentFile();
|
||||
var fileIndexTitle = fileDesc.fileIndex + ".title";
|
||||
if (title) {
|
||||
if (title != localStorage[fileIndexTitle]) {
|
||||
localStorage[fileIndexTitle] = title;
|
||||
fileDesc.title = title;
|
||||
fileManager.updateFileTitles();
|
||||
synchronizer.notifyChange(fileDesc);
|
||||
extensionManager.onTitleChanged(fileDesc);
|
||||
extensionMgr.onTitleChanged(fileDesc);
|
||||
}
|
||||
}
|
||||
input.val(localStorage[fileIndexTitle]);
|
||||
@ -346,33 +315,17 @@ define([
|
||||
});
|
||||
$(".action-edit-document").click(function() {
|
||||
var content = $("#wmd-input").val();
|
||||
var title = fileManager.getCurrentFile().title;
|
||||
var fileDesc = fileManager.createFile(title, content);
|
||||
fileManager.selectFile(fileDesc);
|
||||
var title = fileMgr.getCurrentFile().title;
|
||||
var fileDesc = fileMgr.createFile(title, content);
|
||||
fileMgr.selectFile(fileDesc);
|
||||
window.location.href = ".";
|
||||
});
|
||||
$(".action-download-md").click(function() {
|
||||
var content = $("#wmd-input").val();
|
||||
var title = fileManager.getCurrentFile().title;
|
||||
core.saveFile(content, title + ".md");
|
||||
});
|
||||
$(".action-download-html").click(function() {
|
||||
var content = $("#wmd-preview").html();
|
||||
var title = fileManager.getCurrentFile().title;
|
||||
core.saveFile(content, title + ".html");
|
||||
});
|
||||
$(".action-download-template").click(function() {
|
||||
var content = publisher.applyTemplate();
|
||||
var title = fileManager.getCurrentFile().title;
|
||||
core.saveFile(content, title + ".txt");
|
||||
});
|
||||
$(".action-welcome-file").click(function() {
|
||||
var fileDesc = fileManager.createFile(WELCOME_DOCUMENT_TITLE, welcomeContent);
|
||||
fileManager.selectFile(fileDesc);
|
||||
var fileDesc = fileMgr.createFile(WELCOME_DOCUMENT_TITLE, welcomeContent);
|
||||
fileMgr.selectFile(fileDesc);
|
||||
});
|
||||
});
|
||||
|
||||
core.setFileManager(fileManager);
|
||||
extensionManager.onFileManagerCreated(fileManager);
|
||||
return fileManager;
|
||||
extensionMgr.onFileMgrCreated(fileMgr);
|
||||
return fileMgr;
|
||||
});
|
||||
|
22
js/file-system.js
Normal file
22
js/file-system.js
Normal file
@ -0,0 +1,22 @@
|
||||
define([
|
||||
"underscore",
|
||||
"extension-manager"
|
||||
], function(_, extensionMgr) {
|
||||
|
||||
var fileSystem = {};
|
||||
|
||||
// Load file descriptors from localStorage
|
||||
_.chain(
|
||||
localStorage["file.list"].split(";")
|
||||
).compact().each(function(fileIndex) {
|
||||
fileSystem[fileIndex] = {
|
||||
fileIndex : fileIndex,
|
||||
title : localStorage[fileIndex + ".title"],
|
||||
syncLocations: {},
|
||||
publishLocations: {}
|
||||
};
|
||||
});
|
||||
extensionMgr.onFileSystemCreated(fileSystem);
|
||||
|
||||
return fileSystem;
|
||||
});
|
@ -1,4 +1,11 @@
|
||||
define(["core", "utils", "extension-manager", "google-helper", "underscore"], function(core, utils, extensionManager, googleHelper) {
|
||||
define([
|
||||
"underscore",
|
||||
"core",
|
||||
"utils",
|
||||
"extension-manager",
|
||||
"file-manager",
|
||||
"google-helper"
|
||||
], function(_, core, utils, extensionMgr, fileMgr, googleHelper) {
|
||||
|
||||
var PROVIDER_GDRIVE = "gdrive";
|
||||
|
||||
@ -39,11 +46,11 @@ define(["core", "utils", "extension-manager", "google-helper", "underscore"], fu
|
||||
localStorage[syncAttributes.syncIndex] = utils.serializeAttributes(syncAttributes);
|
||||
var syncLocations = {};
|
||||
syncLocations[syncAttributes.syncIndex] = syncAttributes;
|
||||
var fileDesc = core.fileManager.createFile(file.title, file.content, syncLocations);
|
||||
core.fileManager.selectFile(fileDesc);
|
||||
var fileDesc = fileMgr.createFile(file.title, file.content, syncLocations);
|
||||
fileMgr.selectFile(fileDesc);
|
||||
fileDescList.push(fileDesc);
|
||||
});
|
||||
extensionManager.onSyncImportSuccess(fileDescList, gdriveProvider);
|
||||
extensionMgr.onSyncImportSuccess(fileDescList, gdriveProvider);
|
||||
});
|
||||
});
|
||||
};
|
||||
@ -56,9 +63,9 @@ define(["core", "utils", "extension-manager", "google-helper", "underscore"], fu
|
||||
var importIds = [];
|
||||
_.each(ids, function(id) {
|
||||
var syncIndex = createSyncIndex(id);
|
||||
var fileDesc = core.fileManager.getFileFromSyncIndex(syncIndex);
|
||||
var fileDesc = fileMgr.getFileFromSyncIndex(syncIndex);
|
||||
if(fileDesc !== undefined) {
|
||||
core.showError('"' + fileDesc.title + '" was already imported');
|
||||
extensionMgr.onError('"' + fileDesc.title + '" was already imported');
|
||||
return;
|
||||
}
|
||||
importIds.push(id);
|
||||
@ -87,9 +94,9 @@ define(["core", "utils", "extension-manager", "google-helper", "underscore"], fu
|
||||
}
|
||||
// Check that file is not synchronized with an other one
|
||||
var syncIndex = createSyncIndex(id);
|
||||
var fileDesc = core.fileManager.getFileFromSyncIndex(syncIndex);
|
||||
var fileDesc = fileMgr.getFileFromSyncIndex(syncIndex);
|
||||
if(fileDesc !== undefined) {
|
||||
core.showError('File ID is already synchronized with "' + fileDesc.title + '"');
|
||||
extensionMgr.onError('File ID is already synchronized with "' + fileDesc.title + '"');
|
||||
callback(true);
|
||||
return;
|
||||
}
|
||||
@ -134,7 +141,7 @@ define(["core", "utils", "extension-manager", "google-helper", "underscore"], fu
|
||||
var interestingChanges = [];
|
||||
_.each(changes, function(change) {
|
||||
var syncIndex = createSyncIndex(change.fileId);
|
||||
var syncAttributes = core.fileManager.getSyncAttributes(syncIndex);
|
||||
var syncAttributes = fileMgr.getSyncAttributes(syncIndex);
|
||||
if(syncAttributes === undefined) {
|
||||
return;
|
||||
}
|
||||
@ -155,11 +162,10 @@ define(["core", "utils", "extension-manager", "google-helper", "underscore"], fu
|
||||
callback(error);
|
||||
return;
|
||||
}
|
||||
var updateFileTitles = false;
|
||||
_.each(changes, function(change) {
|
||||
var syncAttributes = change.syncAttributes;
|
||||
var syncIndex = syncAttributes.syncIndex;
|
||||
var fileDesc = core.fileManager.getFileFromSyncIndex(syncIndex);
|
||||
var fileDesc = fileMgr.getFileFromSyncIndex(syncIndex);
|
||||
// No file corresponding (file may have been deleted locally)
|
||||
if(fileDesc === undefined) {
|
||||
return;
|
||||
@ -167,8 +173,8 @@ define(["core", "utils", "extension-manager", "google-helper", "underscore"], fu
|
||||
var localTitle = fileDesc.title;
|
||||
// File deleted
|
||||
if (change.deleted === true) {
|
||||
core.showError('"' + localTitle + '" has been removed from Google Drive.');
|
||||
core.fileManager.removeSync(syncAttributes);
|
||||
extensionMgr.onError('"' + localTitle + '" has been removed from Google Drive.');
|
||||
fileMgr.removeSync(syncAttributes);
|
||||
return;
|
||||
}
|
||||
var localTitleChanged = syncAttributes.titleCRC != utils.crc32(localTitle);
|
||||
@ -184,24 +190,23 @@ define(["core", "utils", "extension-manager", "google-helper", "underscore"], fu
|
||||
// Conflict detection
|
||||
if ((fileTitleChanged === true && localTitleChanged === true && remoteTitleChanged === true)
|
||||
|| (fileContentChanged === true && localContentChanged === true && remoteContentChanged === true)) {
|
||||
core.fileManager.createFile(localTitle + " (backup)", localContent);
|
||||
updateFileTitles = true;
|
||||
core.showMessage('Conflict detected on "' + localTitle + '". A backup has been created locally.');
|
||||
var backupFileDesc = fileMgr.createFile(localTitle + " (backup)", localContent);
|
||||
extensionMgr.onTitleChanged(backupFileDesc);
|
||||
extensionMgr.onMessage('Conflict detected on "' + localTitle + '". A backup has been created locally.');
|
||||
}
|
||||
// If file title changed
|
||||
if(fileTitleChanged && remoteTitleChanged === true) {
|
||||
localStorage[fileDesc.fileIndex + ".title"] = file.title;
|
||||
fileDesc.title = file.title;
|
||||
updateFileTitles = true;
|
||||
core.showMessage('"' + localTitle + '" has been renamed to "' + file.title + '" on Google Drive.');
|
||||
extensionMgr.onTitleChanged(fileDesc);
|
||||
extensionMgr.onMessage('"' + localTitle + '" has been renamed to "' + file.title + '" on Google Drive.');
|
||||
}
|
||||
// If file content changed
|
||||
if(fileContentChanged && remoteContentChanged === true) {
|
||||
localStorage[fileDesc.fileIndex + ".content"] = file.content;
|
||||
core.showMessage('"' + file.title + '" has been updated from Google Drive.');
|
||||
if(core.fileManager.isCurrentFile(fileDesc)) {
|
||||
updateFileTitles = false; // Done by next function
|
||||
core.fileManager.selectFile(); // Refresh editor
|
||||
extensionMgr.onMessage('"' + file.title + '" has been updated from Google Drive.');
|
||||
if(fileMgr.isCurrentFile(fileDesc)) {
|
||||
fileMgr.selectFile(); // Refresh editor
|
||||
}
|
||||
}
|
||||
// Update syncAttributes
|
||||
@ -210,9 +215,6 @@ define(["core", "utils", "extension-manager", "google-helper", "underscore"], fu
|
||||
syncAttributes.titleCRC = remoteTitleCRC;
|
||||
localStorage[syncIndex] = utils.serializeAttributes(syncAttributes);
|
||||
});
|
||||
if(updateFileTitles) {
|
||||
extensionManager.onTitleChanged();
|
||||
}
|
||||
localStorage[PROVIDER_GDRIVE + ".lastChangeId"] = newChangeId;
|
||||
callback();
|
||||
});
|
||||
@ -264,18 +266,18 @@ define(["core", "utils", "extension-manager", "google-helper", "underscore"], fu
|
||||
localStorage[syncAttributes.syncIndex] = utils.serializeAttributes(syncAttributes);
|
||||
var syncLocations = {};
|
||||
syncLocations[syncAttributes.syncIndex] = syncAttributes;
|
||||
var fileDesc = core.fileManager.createFile(file.title, file.content, syncAttributes);
|
||||
core.fileManager.selectFile(fileDesc);
|
||||
core.showMessage('"' + file.title + '" created successfully on Google Drive.');
|
||||
var fileDesc = fileMgr.createFile(file.title, file.content, syncAttributes);
|
||||
fileMgr.selectFile(fileDesc);
|
||||
extensionMgr.onMessage('"' + file.title + '" created successfully on Google Drive.');
|
||||
});
|
||||
}
|
||||
else if (state.action == "open") {
|
||||
var importIds = [];
|
||||
_.each(state.ids, function(id) {
|
||||
var syncIndex = createSyncIndex(id);
|
||||
var fileDesc = core.fileManager.getFileFromSyncIndex(syncIndex);
|
||||
var fileDesc = fileMgr.getFileFromSyncIndex(syncIndex);
|
||||
if(fileDesc !== undefined) {
|
||||
core.fileManager.selectFile(fileDesc);
|
||||
fileMgr.selectFile(fileDesc);
|
||||
}
|
||||
else {
|
||||
importIds.push(id);
|
||||
|
@ -1,4 +1,7 @@
|
||||
define(["utils", "github-helper"], function(utils, githubHelper) {
|
||||
define([
|
||||
"utils",
|
||||
"github-helper"
|
||||
], function(utils, githubHelper) {
|
||||
|
||||
var PROVIDER_GIST = "gist";
|
||||
|
||||
|
@ -1,4 +1,10 @@
|
||||
define(["jquery", "core", "async-runner"], function($, core, asyncRunner) {
|
||||
define([
|
||||
"jquery",
|
||||
"core",
|
||||
"utils",
|
||||
"extension-manager",
|
||||
"async-runner"
|
||||
], function($, core, utils, extensionMgr, asyncRunner) {
|
||||
|
||||
var connected = undefined;
|
||||
var github = undefined;
|
||||
@ -51,14 +57,14 @@ define(["jquery", "core", "async-runner"], function($, core, asyncRunner) {
|
||||
task.chain();
|
||||
return;
|
||||
}
|
||||
core.showMessage("Please make sure the Github authorization popup is not blocked by your browser.");
|
||||
extensionMgr.onMessage("Please make sure the Github authorization popup is not blocked by your browser.");
|
||||
var errorMsg = "Failed to retrieve a token from GitHub.";
|
||||
// We add time for user to enter his credentials
|
||||
task.timeout = ASYNC_TASK_LONG_TIMEOUT;
|
||||
var code = undefined;
|
||||
function getCode() {
|
||||
localStorage.removeItem("githubCode");
|
||||
authWindow = core.popupWindow(
|
||||
authWindow = utils.popupWindow(
|
||||
'github-oauth-client.html?client_id=' + GITHUB_CLIENT_ID,
|
||||
'stackedit-github-oauth', 960, 600);
|
||||
authWindow.focus();
|
||||
@ -106,7 +112,6 @@ define(["jquery", "core", "async-runner"], function($, core, asyncRunner) {
|
||||
}
|
||||
|
||||
githubHelper.upload = function(reponame, branch, path, content, commitMsg, callback) {
|
||||
callback = callback || core.doNothing;
|
||||
var task = asyncRunner.createTask();
|
||||
connect(task);
|
||||
authenticate(task);
|
||||
@ -145,7 +150,6 @@ define(["jquery", "core", "async-runner"], function($, core, asyncRunner) {
|
||||
};
|
||||
|
||||
githubHelper.uploadGist = function(gistId, filename, isPublic, title, content, callback) {
|
||||
callback = callback || core.doNothing;
|
||||
var task = asyncRunner.createTask();
|
||||
connect(task);
|
||||
authenticate(task);
|
||||
@ -184,7 +188,6 @@ define(["jquery", "core", "async-runner"], function($, core, asyncRunner) {
|
||||
};
|
||||
|
||||
githubHelper.downloadGist = function(gistId, filename, callback) {
|
||||
callback = callback || core.doNothing;
|
||||
var task = asyncRunner.createTask();
|
||||
connect(task);
|
||||
// No need for authentication
|
||||
|
@ -1,4 +1,8 @@
|
||||
define(["core", "utils", "github-helper"], function(core, utils, githubHelper) {
|
||||
define([
|
||||
"utils",
|
||||
"settings",
|
||||
"github-helper"
|
||||
], function(utils, settings, githubHelper) {
|
||||
|
||||
var PROVIDER_GITHUB = "github";
|
||||
|
||||
@ -9,7 +13,7 @@ define(["core", "utils", "github-helper"], function(core, utils, githubHelper) {
|
||||
};
|
||||
|
||||
githubProvider.publish = function(publishAttributes, title, content, callback) {
|
||||
var commitMsg = core.settings.commitMsg;
|
||||
var commitMsg = settings.commitMsg;
|
||||
githubHelper.upload(publishAttributes.repository, publishAttributes.branch,
|
||||
publishAttributes.path, content, commitMsg, callback);
|
||||
};
|
||||
|
@ -1,4 +1,10 @@
|
||||
define(["jquery", "core", "utils", "async-runner"], function($, core, utils, asyncRunner) {
|
||||
define([
|
||||
"jquery",
|
||||
"core",
|
||||
"utils",
|
||||
"extension-manager",
|
||||
"async-runner"
|
||||
], function($, core, utils, extensionMgr, asyncRunner) {
|
||||
|
||||
var connected = false;
|
||||
var authenticated = false;
|
||||
@ -44,7 +50,7 @@ define(["jquery", "core", "utils", "async-runner"], function($, core, utils, asy
|
||||
var immediate = true;
|
||||
function localAuthenticate() {
|
||||
if (immediate === false) {
|
||||
core.showMessage("Please make sure the Google authorization popup is not blocked by your browser.");
|
||||
extensionMgr.onMessage("Please make sure the Google authorization popup is not blocked by your browser.");
|
||||
// If not immediate we add time for user to enter his credentials
|
||||
task.timeout = ASYNC_TASK_LONG_TIMEOUT;
|
||||
}
|
||||
@ -74,7 +80,6 @@ define(["jquery", "core", "utils", "async-runner"], function($, core, utils, asy
|
||||
}
|
||||
|
||||
googleHelper.upload = function(fileId, parentId, title, content, etag, callback) {
|
||||
callback = callback || core.doNothing;
|
||||
var result = undefined;
|
||||
var task = asyncRunner.createTask();
|
||||
connect(task);
|
||||
@ -151,7 +156,6 @@ define(["jquery", "core", "utils", "async-runner"], function($, core, utils, asy
|
||||
};
|
||||
|
||||
googleHelper.checkChanges = function(lastChangeId, callback) {
|
||||
callback = callback || core.doNothing;
|
||||
var changes = [];
|
||||
var newChangeId = lastChangeId || 0;
|
||||
var task = asyncRunner.createTask();
|
||||
@ -202,7 +206,6 @@ define(["jquery", "core", "utils", "async-runner"], function($, core, utils, asy
|
||||
};
|
||||
|
||||
googleHelper.downloadMetadata = function(ids, callback, skipAuth) {
|
||||
callback = callback || core.doNothing;
|
||||
var result = [];
|
||||
var task = asyncRunner.createTask();
|
||||
connect(task);
|
||||
@ -255,7 +258,6 @@ define(["jquery", "core", "utils", "async-runner"], function($, core, utils, asy
|
||||
};
|
||||
|
||||
googleHelper.downloadContent = function(objects, callback, skipAuth) {
|
||||
callback = callback || core.doNothing;
|
||||
var result = [];
|
||||
var task = asyncRunner.createTask();
|
||||
// Add some time for user to choose his files
|
||||
@ -378,7 +380,6 @@ define(["jquery", "core", "utils", "async-runner"], function($, core, utils, asy
|
||||
}
|
||||
|
||||
googleHelper.picker = function(callback) {
|
||||
callback = callback || core.doNothing;
|
||||
var ids = [];
|
||||
var picker = undefined;
|
||||
function hidePicker() {
|
||||
|
0
js/bootstrap.js → js/lib/bootstrap.js
vendored
0
js/bootstrap.js → js/lib/bootstrap.js
vendored
0
js/jquery-ui.js → js/lib/jquery-ui.js
vendored
0
js/jquery-ui.js → js/lib/jquery-ui.js
vendored
0
js/jquery.js → js/lib/jquery.js
vendored
0
js/jquery.js → js/lib/jquery.js
vendored
@ -1,5 +1,5 @@
|
||||
/** vim: et:ts=4:sw=4:sts=4
|
||||
* @license RequireJS 2.1.5 Copyright (c) 2010-2012, The Dojo Foundation All Rights Reserved.
|
||||
* @license RequireJS 2.1.6 Copyright (c) 2010-2012, The Dojo Foundation All Rights Reserved.
|
||||
* Available via the MIT or new BSD license.
|
||||
* see: http://github.com/jrburke/requirejs for details
|
||||
*/
|
||||
@ -12,7 +12,7 @@ var requirejs, require, define;
|
||||
(function (global) {
|
||||
var req, s, head, baseElement, dataMain, src,
|
||||
interactiveScript, currentlyAddingScript, mainScript, subPath,
|
||||
version = '2.1.5',
|
||||
version = '2.1.6',
|
||||
commentRegExp = /(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg,
|
||||
cjsRequireRegExp = /[^.]\s*require\s*\(\s*["']([^'"\s]+)["']\s*\)/g,
|
||||
jsSuffixRegExp = /\.js$/,
|
||||
@ -22,7 +22,7 @@ var requirejs, require, define;
|
||||
hasOwn = op.hasOwnProperty,
|
||||
ap = Array.prototype,
|
||||
apsp = ap.splice,
|
||||
isBrowser = !!(typeof window !== 'undefined' && navigator && document),
|
||||
isBrowser = !!(typeof window !== 'undefined' && navigator && window.document),
|
||||
isWebWorker = !isBrowser && typeof importScripts !== 'undefined',
|
||||
//PS3 indicates loaded and complete, but need to wait for complete
|
||||
//specifically. Sequence is 'loading', 'loaded', execution,
|
||||
@ -134,6 +134,10 @@ var requirejs, require, define;
|
||||
return document.getElementsByTagName('script');
|
||||
}
|
||||
|
||||
function defaultOnError(err) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
//Allow getting a global that expressed in
|
||||
//dot notation, like 'a.b.c'.
|
||||
function getGlobal(value) {
|
||||
@ -500,7 +504,12 @@ var requirejs, require, define;
|
||||
fn(defined[id]);
|
||||
}
|
||||
} else {
|
||||
getModule(depMap).on(name, fn);
|
||||
mod = getModule(depMap);
|
||||
if (mod.error && name === 'error') {
|
||||
fn(mod.error);
|
||||
} else {
|
||||
mod.on(name, fn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -571,7 +580,13 @@ var requirejs, require, define;
|
||||
id: mod.map.id,
|
||||
uri: mod.map.url,
|
||||
config: function () {
|
||||
return (config.config && getOwn(config.config, mod.map.id)) || {};
|
||||
var c,
|
||||
pkg = getOwn(config.pkgs, mod.map.id);
|
||||
// For packages, only support config targeted
|
||||
// at the main module.
|
||||
c = pkg ? getOwn(config.config, mod.map.id + '/' + pkg.main) :
|
||||
getOwn(config.config, mod.map.id);
|
||||
return c || {};
|
||||
},
|
||||
exports: defined[mod.map.id]
|
||||
});
|
||||
@ -840,8 +855,13 @@ var requirejs, require, define;
|
||||
if (this.depCount < 1 && !this.defined) {
|
||||
if (isFunction(factory)) {
|
||||
//If there is an error listener, favor passing
|
||||
//to that instead of throwing an error.
|
||||
if (this.events.error) {
|
||||
//to that instead of throwing an error. However,
|
||||
//only do it for define()'d modules. require
|
||||
//errbacks should not be called for failures in
|
||||
//their callbacks (#699). However if a global
|
||||
//onError is set, use that.
|
||||
if ((this.events.error && this.map.isDefine) ||
|
||||
req.onError !== defaultOnError) {
|
||||
try {
|
||||
exports = context.execCb(id, factory, depExports, exports);
|
||||
} catch (e) {
|
||||
@ -869,8 +889,8 @@ var requirejs, require, define;
|
||||
|
||||
if (err) {
|
||||
err.requireMap = this.map;
|
||||
err.requireModules = [this.map.id];
|
||||
err.requireType = 'define';
|
||||
err.requireModules = this.map.isDefine ? [this.map.id] : null;
|
||||
err.requireType = this.map.isDefine ? 'define' : 'require';
|
||||
return onError((this.error = err));
|
||||
}
|
||||
|
||||
@ -1093,7 +1113,7 @@ var requirejs, require, define;
|
||||
}));
|
||||
|
||||
if (this.errback) {
|
||||
on(depMap, 'error', this.errback);
|
||||
on(depMap, 'error', bind(this, this.errback));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1605,7 +1625,7 @@ var requirejs, require, define;
|
||||
},
|
||||
|
||||
/**
|
||||
* Executes a module callack function. Broken out as a separate function
|
||||
* Executes a module callback function. Broken out as a separate function
|
||||
* solely to allow the build system to sequence the files in the built
|
||||
* layer in the right sequence.
|
||||
*
|
||||
@ -1643,7 +1663,7 @@ var requirejs, require, define;
|
||||
onScriptError: function (evt) {
|
||||
var data = getScriptData(evt);
|
||||
if (!hasPathFallback(data.id)) {
|
||||
return onError(makeError('scripterror', 'Script error', evt, [data.id]));
|
||||
return onError(makeError('scripterror', 'Script error for: ' + data.id, evt, [data.id]));
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -1772,9 +1792,7 @@ var requirejs, require, define;
|
||||
* function. Intercept/override it if you want custom error handling.
|
||||
* @param {Error} err the error object.
|
||||
*/
|
||||
req.onError = function (err) {
|
||||
throw err;
|
||||
};
|
||||
req.onError = defaultOnError;
|
||||
|
||||
/**
|
||||
* Does the request to load a module for the browser case.
|
||||
@ -1906,24 +1924,31 @@ var requirejs, require, define;
|
||||
//baseUrl, if it is not already set.
|
||||
dataMain = script.getAttribute('data-main');
|
||||
if (dataMain) {
|
||||
//Preserve dataMain in case it is a path (i.e. contains '?')
|
||||
mainScript = dataMain;
|
||||
|
||||
//Set final baseUrl if there is not already an explicit one.
|
||||
if (!cfg.baseUrl) {
|
||||
//Pull off the directory of data-main for use as the
|
||||
//baseUrl.
|
||||
src = dataMain.split('/');
|
||||
src = mainScript.split('/');
|
||||
mainScript = src.pop();
|
||||
subPath = src.length ? src.join('/') + '/' : './';
|
||||
|
||||
cfg.baseUrl = subPath;
|
||||
dataMain = mainScript;
|
||||
}
|
||||
|
||||
//Strip off any trailing .js since dataMain is now
|
||||
//Strip off any trailing .js since mainScript is now
|
||||
//like a module name.
|
||||
dataMain = dataMain.replace(jsSuffixRegExp, '');
|
||||
mainScript = mainScript.replace(jsSuffixRegExp, '');
|
||||
|
||||
//If mainScript is still a path, fall back to dataMain
|
||||
if (req.jsExtRegExp.test(mainScript)) {
|
||||
mainScript = dataMain;
|
||||
}
|
||||
|
||||
//Put the data-main script in the files to load.
|
||||
cfg.deps = cfg.deps ? cfg.deps.concat(dataMain) : [dataMain];
|
||||
cfg.deps = cfg.deps ? cfg.deps.concat(mainScript) : [mainScript];
|
||||
|
||||
return true;
|
||||
}
|
||||
@ -1951,12 +1976,13 @@ var requirejs, require, define;
|
||||
//This module may not have dependencies
|
||||
if (!isArray(deps)) {
|
||||
callback = deps;
|
||||
deps = [];
|
||||
deps = null;
|
||||
}
|
||||
|
||||
//If no name, and callback is a function, then figure out if it a
|
||||
//CommonJS thing with dependencies.
|
||||
if (!deps.length && isFunction(callback)) {
|
||||
if (!deps && isFunction(callback)) {
|
||||
deps = [];
|
||||
//Remove comments from the callback string,
|
||||
//look for require calls, and pull them into the dependencies,
|
||||
//but only if there are function args.
|
286
js/main-min.js
vendored
286
js/main-min.js
vendored
File diff suppressed because one or more lines are too long
34
js/main.js
34
js/main.js
@ -2,19 +2,34 @@
|
||||
requirejs.config({
|
||||
waitSeconds: 0,
|
||||
paths: {
|
||||
MathJax: '../lib/MathJax/MathJax.js?config=TeX-AMS_HTML'
|
||||
"jquery": "lib/jquery",
|
||||
"underscore": "lib/underscore",
|
||||
"jgrowl": "lib/jgrowl",
|
||||
"lib/MathJax": '../lib/MathJax/MathJax.js?config=TeX-AMS_HTML'
|
||||
},
|
||||
shim: {
|
||||
'jquery-ui': ['jquery'],
|
||||
'bootstrap': ['jquery'],
|
||||
'jgrowl': ['jquery'],
|
||||
'layout': ['jquery-ui'],
|
||||
'Markdown.Extra': ['Markdown.Converter', 'prettify'],
|
||||
'Markdown.Editor': ['Markdown.Converter']
|
||||
'lib/underscore': {
|
||||
exports: '_'
|
||||
},
|
||||
'lib/jgrowl': {
|
||||
deps: ['lib/jquery'],
|
||||
exports: 'jQuery.jGrowl'
|
||||
},
|
||||
'lib/jquery-ui': ['lib/jquery'],
|
||||
'lib/bootstrap': ['lib/jquery'],
|
||||
'lib/layout': ['lib/jquery-ui'],
|
||||
'lib/Markdown.Extra': ['lib/Markdown.Converter', 'lib/prettify'],
|
||||
'lib/Markdown.Editor': ['lib/Markdown.Converter']
|
||||
}
|
||||
});
|
||||
|
||||
require(["jquery", "file-manager", "synchronizer", "publisher"], function($) {
|
||||
require([
|
||||
"jquery",
|
||||
"core",
|
||||
"synchronizer",
|
||||
"publisher"
|
||||
], function($, core) {
|
||||
|
||||
$(function() {
|
||||
// If browser has detected a new application cache.
|
||||
if (window.applicationCache) {
|
||||
@ -25,5 +40,8 @@ require(["jquery", "file-manager", "synchronizer", "publisher"], function($) {
|
||||
}
|
||||
}, false);
|
||||
}
|
||||
|
||||
core.setReady();
|
||||
});
|
||||
|
||||
});
|
||||
|
124
js/publisher.js
124
js/publisher.js
@ -1,8 +1,12 @@
|
||||
define([
|
||||
"jquery",
|
||||
"underscore",
|
||||
"core",
|
||||
"utils",
|
||||
"settings",
|
||||
"extension-manager",
|
||||
"file-system",
|
||||
"file-manager",
|
||||
"sharing",
|
||||
"blogger-provider",
|
||||
"dropbox-provider",
|
||||
@ -11,59 +15,44 @@ define([
|
||||
"gdrive-provider",
|
||||
"ssh-provider",
|
||||
"tumblr-provider",
|
||||
"wordpress-provider",
|
||||
"underscore"
|
||||
], function($, core, utils, extensionManager, sharing) {
|
||||
"wordpress-provider"
|
||||
], function($, _, core, utils, settings, extensionMgr, fileSystem, fileMgr, sharing) {
|
||||
|
||||
var publisher = {};
|
||||
|
||||
// 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 the current file has publications
|
||||
var hasPublications = false;
|
||||
// Create a map with providerId: providerModule
|
||||
var providerMap = _.chain(
|
||||
arguments
|
||||
).map(function(argument) {
|
||||
return argument && argument.providerId && [argument.providerId, argument];
|
||||
}).compact().object().value();
|
||||
|
||||
// Allows external modules to update hasPublications flag
|
||||
publisher.notifyPublish = function() {
|
||||
var fileDesc = core.fileManager.getCurrentFile();
|
||||
|
||||
// Check that file has publications
|
||||
if(_.size(fileDesc.publishLocations) === 0) {
|
||||
hasPublications = false;
|
||||
}
|
||||
else {
|
||||
hasPublications = true;
|
||||
}
|
||||
publisher.updatePublishButton();
|
||||
};
|
||||
|
||||
// Used to enable/disable the publish button
|
||||
publisher.updatePublishButton = function() {
|
||||
if(publishRunning === true || hasPublications === false || core.isOffline) {
|
||||
$(".action-force-publish").addClass("disabled");
|
||||
}
|
||||
else {
|
||||
$(".action-force-publish").removeClass("disabled");
|
||||
}
|
||||
};
|
||||
// Run updatePublishButton function on online/offline event
|
||||
core.addOfflineListener(publisher.updatePublishButton);
|
||||
// Retrieve publish locations from localStorage
|
||||
_.each(fileSystem, function(fileDesc) {
|
||||
_.chain(
|
||||
localStorage[fileDesc.fileIndex + ".publish"].split(";")
|
||||
).compact().each(function(publishIndex) {
|
||||
var publishAttributes = JSON.parse(localStorage[publishIndex]);
|
||||
// Store publishIndex
|
||||
publishAttributes.publishIndex = publishIndex;
|
||||
// Replace provider ID by provider module in attributes
|
||||
publishAttributes.provider = providerMap[publishAttributes.provider];
|
||||
fileDesc.publishLocations[publishIndex] = publishAttributes;
|
||||
});
|
||||
});
|
||||
|
||||
// Apply template to the current document
|
||||
publisher.applyTemplate = function(publishAttributes) {
|
||||
var fileDesc = core.fileManager.getCurrentFile();
|
||||
var fileDesc = fileMgr.getCurrentFile();
|
||||
try {
|
||||
return _.template(core.settings.template, {
|
||||
return _.template(settings.template, {
|
||||
documentTitle: fileDesc.title,
|
||||
documentMarkdown: $("#wmd-input").val(),
|
||||
documentHTML: $("#wmd-preview").html(),
|
||||
publishAttributes: publishAttributes
|
||||
});
|
||||
} catch(e) {
|
||||
core.showError(e);
|
||||
extensionMgr.onError(e);
|
||||
throw e;
|
||||
}
|
||||
};
|
||||
@ -105,7 +94,7 @@ define([
|
||||
if(error !== undefined) {
|
||||
var errorMsg = error.toString();
|
||||
if(errorMsg.indexOf("|removePublish") !== -1) {
|
||||
core.fileManager.removePublish(publishAttributes);
|
||||
fileMgr.removePublish(publishAttributes);
|
||||
}
|
||||
if(errorMsg.indexOf("|stopPublish") !== -1) {
|
||||
callback(error);
|
||||
@ -124,15 +113,15 @@ define([
|
||||
}
|
||||
|
||||
publishRunning = true;
|
||||
publisher.updatePublishButton();
|
||||
var fileDesc = fileManager.getCurrentFile();
|
||||
extensionMgr.onPublishRunning(true);
|
||||
var fileDesc = fileMgr.getCurrentFile();
|
||||
publishTitle = fileDesc.title;
|
||||
publishAttributesList = _.values(fileDesc.publishLocations);
|
||||
publishLocation(function(errorFlag) {
|
||||
publishRunning = false;
|
||||
publisher.updatePublishButton();
|
||||
extensionMgr.onPublishRunning(false);
|
||||
if(errorFlag === undefined) {
|
||||
extensionManager.onPublishSuccess(fileDesc);
|
||||
extensionMgr.onPublishSuccess(fileDesc);
|
||||
}
|
||||
});
|
||||
};
|
||||
@ -144,8 +133,8 @@ define([
|
||||
publishIndex = "publish." + utils.randomString();
|
||||
} while(_.has(localStorage, publishIndex));
|
||||
publishAttributes.publishIndex = publishIndex;
|
||||
localStorage[publishIndex] = JSON.stringify(publishAttributes);
|
||||
core.fileManager.addPublish(fileDesc, publishAttributes);
|
||||
localStorage[publishIndex] = utils.serializeAttributes(publishAttributes);
|
||||
fileMgr.addPublish(fileDesc, publishAttributes);
|
||||
}
|
||||
|
||||
// Initialize the "New publication" dialog
|
||||
@ -185,7 +174,7 @@ define([
|
||||
}
|
||||
|
||||
// Perform provider's publishing
|
||||
var fileDesc = core.fileManager.getCurrentFile();
|
||||
var fileDesc = fileMgr.getCurrentFile();
|
||||
var title = fileDesc.title;
|
||||
var content = getPublishContent(publishAttributes);
|
||||
provider.publish(publishAttributes, title, content, function(error) {
|
||||
@ -193,8 +182,6 @@ define([
|
||||
publishAttributes.provider = provider.providerId;
|
||||
sharing.createLink(publishAttributes, function() {
|
||||
createPublishIndex(fileDesc, publishAttributes);
|
||||
publisher.notifyPublish();
|
||||
core.fileManager.updateFileTitles();
|
||||
});
|
||||
}
|
||||
});
|
||||
@ -210,16 +197,16 @@ define([
|
||||
|
||||
// Retrieve file's publish locations from localStorage
|
||||
publisher.populatePublishLocations = function(fileDesc) {
|
||||
_.chain(localStorage[fileDesc.fileIndex + ".publish"].split(";"))
|
||||
.compact()
|
||||
.each(function(publishIndex) {
|
||||
var publishAttributes = JSON.parse(localStorage[publishIndex]);
|
||||
// Store publishIndex
|
||||
publishAttributes.publishIndex = publishIndex;
|
||||
// Replace provider ID by provider module in attributes
|
||||
publishAttributes.provider = providerMap[publishAttributes.provider];
|
||||
fileDesc.publishLocations[publishIndex] = publishAttributes;
|
||||
});
|
||||
_.chain(
|
||||
localStorage[fileDesc.fileIndex + ".publish"].split(";")
|
||||
).compact().each(function(publishIndex) {
|
||||
var publishAttributes = JSON.parse(localStorage[publishIndex]);
|
||||
// Store publishIndex
|
||||
publishAttributes.publishIndex = publishIndex;
|
||||
// Replace provider ID by provider module in attributes
|
||||
publishAttributes.provider = providerMap[publishAttributes.provider];
|
||||
fileDesc.publishLocations[publishIndex] = publishAttributes;
|
||||
});
|
||||
};
|
||||
|
||||
core.onReady(function() {
|
||||
@ -249,8 +236,25 @@ define([
|
||||
publisher.publish();
|
||||
}
|
||||
});
|
||||
|
||||
// Save As menu items
|
||||
$(".action-download-md").click(function() {
|
||||
var content = $("#wmd-input").val();
|
||||
var title = fileMgr.getCurrentFile().title;
|
||||
utils.saveAs(content, title + ".md");
|
||||
});
|
||||
$(".action-download-html").click(function() {
|
||||
var content = $("#wmd-preview").html();
|
||||
var title = fileMgr.getCurrentFile().title;
|
||||
utils.saveAs(content, title + ".html");
|
||||
});
|
||||
$(".action-download-template").click(function() {
|
||||
var content = publisher.applyTemplate();
|
||||
var title = fileMgr.getCurrentFile().title;
|
||||
utils.saveAs(content, title + ".txt");
|
||||
});
|
||||
});
|
||||
|
||||
extensionManager.onPublisherCreated(publisher);
|
||||
extensionMgr.onPublisherCreated(publisher);
|
||||
return publisher;
|
||||
});
|
28
js/settings.js
Normal file
28
js/settings.js
Normal file
@ -0,0 +1,28 @@
|
||||
define([
|
||||
"underscore"
|
||||
], function(_) {
|
||||
|
||||
var settings = {
|
||||
layoutOrientation : "horizontal",
|
||||
lazyRendering : true,
|
||||
editorFontSize : 14,
|
||||
defaultContent: "\n\n\n> Written with [StackEdit](http://benweet.github.io/stackedit/).",
|
||||
commitMsg : "Published by http://benweet.github.io/stackedit",
|
||||
template : [
|
||||
'<!DOCTYPE html>\n',
|
||||
'<html>\n',
|
||||
'<head>\n',
|
||||
'<title><%= documentTitle %></title>\n',
|
||||
'</head>\n',
|
||||
'<body><%= documentHTML %></body>\n',
|
||||
'</html>'].join(""),
|
||||
sshProxy : SSH_PROXY_URL,
|
||||
extensionSettings: {}
|
||||
};
|
||||
|
||||
if (_.has(localStorage, "settings")) {
|
||||
_.extend(settings, JSON.parse(localStorage.settings));
|
||||
}
|
||||
|
||||
return settings;
|
||||
});
|
@ -1,11 +1,24 @@
|
||||
define(["jquery", "core", "utils", "async-runner", "download-provider", "gist-provider", "underscore"], function($, core, utils, asyncRunner) {
|
||||
define([
|
||||
"jquery",
|
||||
"underscore",
|
||||
"core",
|
||||
"utils",
|
||||
"extension-manager",
|
||||
"file-system",
|
||||
"file-manager",
|
||||
"async-runner",
|
||||
"download-provider",
|
||||
"gist-provider"
|
||||
], function($, _, core, utils, extensionMgr, fileMgr, asyncRunner) {
|
||||
|
||||
var sharing = {};
|
||||
|
||||
// Create a map with providerId: providerObject
|
||||
var providerMap = _.chain(arguments)
|
||||
.map(function(argument) {
|
||||
return argument && argument.providerId && [argument.providerId, argument];
|
||||
}).compact().object().value();
|
||||
// Create a map with providerId: providerModule
|
||||
var providerMap = _.chain(
|
||||
arguments
|
||||
).map(function(argument) {
|
||||
return argument && argument.providerId && [argument.providerId, argument];
|
||||
}).compact().object().value();
|
||||
|
||||
// Used to populate the "Sharing" dropdown box
|
||||
var lineTemplate = ['<div class="input-prepend">',
|
||||
@ -52,11 +65,12 @@ define(["jquery", "core", "utils", "async-runner", "download-provider", "gist-pr
|
||||
url.push('=');
|
||||
url.push(encodeURIComponent(attributes[attributeName]));
|
||||
});
|
||||
url = url.join("");
|
||||
$.getJSON(
|
||||
"https://api-ssl.bitly.com/v3/shorten",
|
||||
{
|
||||
"access_token": BITLY_ACCESS_TOKEN,
|
||||
"longUrl": url.join("")
|
||||
"longUrl": url
|
||||
},
|
||||
function(response)
|
||||
{
|
||||
@ -64,46 +78,22 @@ define(["jquery", "core", "utils", "async-runner", "download-provider", "gist-pr
|
||||
shortUrl = response.data.url;
|
||||
attributes.sharingLink = shortUrl;
|
||||
}
|
||||
else {
|
||||
extensionMgr.onError("An error occured while creating sharing link.");
|
||||
attributes.sharingLink = url;
|
||||
}
|
||||
task.chain();
|
||||
}
|
||||
);
|
||||
});
|
||||
function onFinish() {
|
||||
if(shortUrl === undefined) {
|
||||
localStorage["missingSharingLink"] = true;
|
||||
}
|
||||
callback(shortUrl);
|
||||
callback();
|
||||
}
|
||||
task.onSuccess(onFinish);
|
||||
task.onError(onFinish);
|
||||
asyncRunner.addTask(task);
|
||||
};
|
||||
|
||||
// Create the possible missing links
|
||||
function checkMissingLinks() {
|
||||
if(core.isOffline === true) {
|
||||
return;
|
||||
}
|
||||
if(!_.has(localStorage, "missingSharingLink")) {
|
||||
return;
|
||||
}
|
||||
localStorage.removeItem("missingSharingLink");
|
||||
var fileDescList = core.fileManager.getFileList();
|
||||
_.each(fileDescList, function(fileDesc) {
|
||||
_.each(fileDescList.publishLocations, function(publishAttributes, publishIndex) {
|
||||
sharing.createLink(publishAttributes, function(shortUrl) {
|
||||
if(shortUrl !== undefined) {
|
||||
localStorage[publishIndex] = utils.serializeAttributes(attributes);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
// Periodically check that links are not missing
|
||||
if(viewerMode === false) {
|
||||
core.addPeriodicCallback(checkMissingLinks);
|
||||
}
|
||||
|
||||
core.onReady(function() {
|
||||
if(viewerMode === false) {
|
||||
return;
|
||||
@ -135,8 +125,8 @@ define(["jquery", "core", "utils", "async-runner", "download-provider", "gist-pr
|
||||
if(error) {
|
||||
return;
|
||||
}
|
||||
var fileDesc = core.fileManager.createFile(title, content, undefined, true);
|
||||
core.fileManager.selectFile(fileDesc);
|
||||
var fileDesc = fileMgr.createFile(title, content, undefined, true);
|
||||
fileMgr.selectFile(fileDesc);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -1,4 +1,8 @@
|
||||
define(["jquery", "core", "async-runner"], function($, core, asyncRunner) {
|
||||
define([
|
||||
"jquery",
|
||||
"core",
|
||||
"async-runner"
|
||||
], function($, core, asyncRunner) {
|
||||
|
||||
var sshHelper = {};
|
||||
|
||||
@ -14,7 +18,6 @@ define(["jquery", "core", "async-runner"], function($, core, asyncRunner) {
|
||||
}
|
||||
|
||||
sshHelper.upload = function(host, port, username, password, path, title, content, callback) {
|
||||
callback = callback || core.doNothing;
|
||||
var task = asyncRunner.createTask();
|
||||
connect(task);
|
||||
task.onRun(function() {
|
||||
|
@ -1,4 +1,7 @@
|
||||
define([ "utils", "ssh-helper" ], function(utils, sshHelper) {
|
||||
define([
|
||||
"utils",
|
||||
"ssh-helper"
|
||||
], function(utils, sshHelper) {
|
||||
|
||||
var PROVIDER_SSH = "ssh";
|
||||
|
||||
|
@ -1,5 +1,7 @@
|
||||
// Setup an empty localStorage or upgrade an existing one
|
||||
define( [ "underscore" ], function() {
|
||||
// Setup an empty localStorage or upgrade an existing one
|
||||
define([
|
||||
"underscore"
|
||||
], function(_) {
|
||||
|
||||
// Create the file system if not exist
|
||||
if (localStorage["file.list"] === undefined) {
|
||||
@ -122,8 +124,6 @@ define( [ "underscore" ], function() {
|
||||
}
|
||||
});
|
||||
});
|
||||
// Force creation of sharing links
|
||||
localStorage["missingSharingLink"] = true;
|
||||
version = "v6";
|
||||
}
|
||||
|
||||
|
@ -1,44 +1,37 @@
|
||||
define([
|
||||
"jquery",
|
||||
"underscore",
|
||||
"core",
|
||||
"utils",
|
||||
"extension-manager",
|
||||
"file-system",
|
||||
"file-manager",
|
||||
"dropbox-provider",
|
||||
"gdrive-provider",
|
||||
"underscore"
|
||||
], function($, core, utils, extensionManager) {
|
||||
"gdrive-provider"
|
||||
], function($, _, core, utils, extensionMgr, fileSystem, fileMgr) {
|
||||
|
||||
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;
|
||||
// Create a map with providerId: providerModule
|
||||
var providerMap = _.chain(
|
||||
arguments
|
||||
).map(function(argument) {
|
||||
return argument && argument.providerId && [argument.providerId, argument];
|
||||
}).compact().object().value();
|
||||
|
||||
// Allows external modules to update uploadPending flag
|
||||
synchronizer.notifyChange = function(fileDesc) {
|
||||
// Check that file has synchronized locations
|
||||
if(_.size(fileDesc.syncLocations) !== 0) {
|
||||
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);
|
||||
// Retrieve sync locations from localStorage
|
||||
_.each(fileSystem, function(fileDesc) {
|
||||
_.chain(
|
||||
localStorage[fileDesc.fileIndex + ".sync"].split(";")
|
||||
).compact().each(function(syncIndex) {
|
||||
var syncAttributes = JSON.parse(localStorage[syncIndex]);
|
||||
// Store syncIndex
|
||||
syncAttributes.syncIndex = syncIndex;
|
||||
// Replace provider ID by provider module in attributes
|
||||
syncAttributes.provider = providerMap[syncAttributes.provider];
|
||||
fileDesc.syncLocations[syncIndex] = syncAttributes;
|
||||
});
|
||||
});
|
||||
|
||||
// Force the synchronization
|
||||
synchronizer.forceSync = function() {
|
||||
@ -73,8 +66,6 @@ define([
|
||||
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);
|
||||
@ -121,7 +112,7 @@ define([
|
||||
if(uploadCycle === true) {
|
||||
// New upload cycle
|
||||
uploadCycle = false;
|
||||
uploadFileList = core.fileManager.getFileList();
|
||||
uploadFileList = fileMgr.getFileList();
|
||||
fileUp(callback);
|
||||
}
|
||||
else {
|
||||
@ -139,7 +130,7 @@ define([
|
||||
var provider = providerList.pop();
|
||||
|
||||
// Check that provider has files to sync
|
||||
if(!core.fileManager.hasSync(provider)) {
|
||||
if(!fileMgr.hasSync(provider)) {
|
||||
providerDown(callback);
|
||||
return;
|
||||
}
|
||||
@ -165,18 +156,18 @@ define([
|
||||
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) {
|
||||
if (syncRunning || lastSync + SYNC_PERIOD > utils.currentTime || core.isOffline) {
|
||||
return;
|
||||
}
|
||||
syncRunning = true;
|
||||
extensionMgr.onSyncRunning(true);
|
||||
uploadCycle = true;
|
||||
lastSync = core.currentTime;
|
||||
synchronizer.updateSyncButton();
|
||||
lastSync = utils.currentTime;
|
||||
|
||||
function isError(error) {
|
||||
if(error !== undefined) {
|
||||
syncRunning = false;
|
||||
synchronizer.updateSyncButton();
|
||||
extensionMgr.onSyncRunning(false);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@ -191,7 +182,8 @@ define([
|
||||
return;
|
||||
}
|
||||
syncRunning = false;
|
||||
uploadPending = false;
|
||||
extensionMgr.onSyncRunning(false);
|
||||
extensionMgr.onSyncSuccess();
|
||||
});
|
||||
});
|
||||
};
|
||||
@ -200,20 +192,6 @@ define([
|
||||
core.addPeriodicCallback(synchronizer.sync);
|
||||
}
|
||||
|
||||
// Retrieve file's sync locations from localStorage
|
||||
publisher.populateSyncLocations = function(fileDesc) {
|
||||
_.chain(localStorage[fileDesc.fileIndex + ".sync"].split(";"))
|
||||
.compact()
|
||||
.each(function(syncIndex) {
|
||||
var syncAttributes = JSON.parse(localStorage[syncIndex]);
|
||||
// Store syncIndex
|
||||
syncAttributes.syncIndex = syncIndex;
|
||||
// Replace provider ID by provider module in attributes
|
||||
syncAttributes.provider = providerMap[syncAttributes.provider];
|
||||
fileDesc.syncLocations[syncIndex] = syncAttributes;
|
||||
});
|
||||
};
|
||||
|
||||
// Initialize the export dialog
|
||||
function initExportDialog(provider) {
|
||||
|
||||
@ -247,14 +225,14 @@ define([
|
||||
$(".action-sync-export-" + provider.providerId).click(function(event) {
|
||||
|
||||
// Perform the provider's export
|
||||
var fileDesc = core.fileManager.getCurrentFile();
|
||||
var fileDesc = fileMgr.getCurrentFile();
|
||||
var title = fileDesc.title;
|
||||
var content = localStorage[fileDesc.fileIndex + ".content"];
|
||||
provider.exportFile(event, title, content, function(error, syncAttributes) {
|
||||
if(error) {
|
||||
return;
|
||||
}
|
||||
core.fileManager.addSync(fileDesc, syncAttributes);
|
||||
fileMgr.addSync(fileDesc, syncAttributes);
|
||||
});
|
||||
|
||||
// Store input values as preferences for next time we open the export dialog
|
||||
@ -266,19 +244,18 @@ define([
|
||||
});
|
||||
// Provider's manual export button
|
||||
$(".action-sync-manual-" + provider.providerId).click(function(event) {
|
||||
var fileDesc = core.fileManager.getCurrentFile();
|
||||
var fileDesc = fileMgr.getCurrentFile();
|
||||
var title = fileDesc.title;
|
||||
var content = localStorage[fileDesc.fileIndex + ".content"];
|
||||
provider.exportManual(event, title, content, function(error, syncAttributes) {
|
||||
if(error) {
|
||||
return;
|
||||
}
|
||||
core.fileManager.addSync(fileDesc, syncAttributes);
|
||||
fileMgr.addSync(fileDesc, syncAttributes);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
synchronizer.updateSyncButton();
|
||||
$(".action-force-sync").click(function() {
|
||||
if(!$(this).hasClass("disabled")) {
|
||||
synchronizer.forceSync();
|
||||
@ -286,6 +263,6 @@ define([
|
||||
});
|
||||
});
|
||||
|
||||
extensionManager.onSynchronizerCreated(synchronizer);
|
||||
extensionMgr.onSynchronizerCreated(synchronizer);
|
||||
return synchronizer;
|
||||
});
|
||||
|
@ -1,4 +1,10 @@
|
||||
define(["jquery", "core", "async-runner"], function($, core, asyncRunner) {
|
||||
define([
|
||||
"jquery",
|
||||
"core",
|
||||
"utils",
|
||||
"extension-manager",
|
||||
"async-runner"
|
||||
], function($, core, utils, extensionMgr, asyncRunner) {
|
||||
|
||||
var oauthParams = undefined;
|
||||
|
||||
@ -30,7 +36,7 @@ define(["jquery", "core", "async-runner"], function($, core, asyncRunner) {
|
||||
task.chain();
|
||||
return;
|
||||
}
|
||||
core.showMessage("Please make sure the Tumblr authorization popup is not blocked by your browser.");
|
||||
extensionMgr.onMessage("Please make sure the Tumblr authorization popup is not blocked by your browser.");
|
||||
var errorMsg = "Failed to retrieve a token from Tumblr.";
|
||||
// We add time for user to enter his credentials
|
||||
task.timeout = ASYNC_TASK_LONG_TIMEOUT;
|
||||
@ -48,7 +54,7 @@ define(["jquery", "core", "async-runner"], function($, core, asyncRunner) {
|
||||
}
|
||||
function getVerifier() {
|
||||
localStorage.removeItem("tumblrVerifier");
|
||||
authWindow = core.popupWindow(
|
||||
authWindow = utils.popupWindow(
|
||||
'tumblr-oauth-client.html?oauth_token=' + oauth_object.oauth_token,
|
||||
'stackedit-tumblr-oauth', 800, 600);
|
||||
authWindow.focus();
|
||||
@ -92,7 +98,6 @@ define(["jquery", "core", "async-runner"], function($, core, asyncRunner) {
|
||||
}
|
||||
|
||||
tumblrHelper.upload = function(blogHostname, postId, tags, format, title, content, callback) {
|
||||
callback = callback || core.doNothing;
|
||||
var task = asyncRunner.createTask();
|
||||
connect(task);
|
||||
authenticate(task);
|
||||
|
@ -1,4 +1,7 @@
|
||||
define(["utils", "tumblr-helper"], function(utils, tumblrHelper) {
|
||||
define([
|
||||
"utils",
|
||||
"tumblr-helper"
|
||||
], function(utils, tumblrHelper) {
|
||||
|
||||
var PROVIDER_TUMBLR = "tumblr";
|
||||
|
||||
|
53
js/utils.js
53
js/utils.js
@ -1,4 +1,8 @@
|
||||
define([ "jquery", "underscore" ], function($) {
|
||||
define([
|
||||
"jquery",
|
||||
"underscore",
|
||||
"lib/FileSaver"
|
||||
], function($, _) {
|
||||
|
||||
var utils = {};
|
||||
|
||||
@ -107,6 +111,16 @@ define([ "jquery", "underscore" ], function($) {
|
||||
return $.trim(str);
|
||||
};
|
||||
|
||||
// Slug function
|
||||
utils.slugify = function(text) {
|
||||
return text.toLowerCase()
|
||||
.replace(/\s+/g, '-') // Replace spaces with -
|
||||
.replace(/[^\w\-]+/g, '') // Remove all non-word chars
|
||||
.replace(/\-\-+/g, '-') // Replace multiple - with single -
|
||||
.replace(/^-+/, '') // Trim - from start of text
|
||||
.replace(/-+$/, ''); // Trim - from end of text
|
||||
};
|
||||
|
||||
// Check an URL
|
||||
utils.checkUrl = function(url, addSlash) {
|
||||
if(!url) {
|
||||
@ -242,11 +256,48 @@ define([ "jquery", "underscore" ], function($) {
|
||||
return crc.toString(16);
|
||||
};
|
||||
|
||||
// Create an centered popup window
|
||||
utils.popupWindow = function(url, title, width, height) {
|
||||
var left = (screen.width / 2) - (width / 2);
|
||||
var top = (screen.height / 2) - (height / 2);
|
||||
return window.open(
|
||||
url,
|
||||
title,
|
||||
'toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=no, resizable=no, copyhistory=no, width='
|
||||
+ width
|
||||
+ ', height='
|
||||
+ height
|
||||
+ ', top='
|
||||
+ top
|
||||
+ ', left='
|
||||
+ left);
|
||||
};
|
||||
|
||||
// Export data on disk
|
||||
utils.saveAs = function(content, filename) {
|
||||
if(saveAs !== undefined) {
|
||||
var blob = new Blob([content], {type: "text/plain;charset=utf-8"});
|
||||
saveAs(blob, filename);
|
||||
}
|
||||
else {
|
||||
var uriContent = "data:application/octet-stream;base64,"
|
||||
+ utils.encodeBase64(content);
|
||||
window.open(uriContent, 'file');
|
||||
}
|
||||
};
|
||||
|
||||
// Generates a random string
|
||||
utils.randomString = function() {
|
||||
return _.random(4294967296).toString(36);
|
||||
};
|
||||
|
||||
// Time shared by others modules
|
||||
utils.updateCurrentTime = function() {
|
||||
utils.currentTime = new Date().getTime();
|
||||
};
|
||||
utils.updateCurrentTime();
|
||||
|
||||
|
||||
// Serialize sync/publish attributes
|
||||
utils.serializeAttributes = function(attributes) {
|
||||
// Don't store sync/publish index
|
||||
|
@ -1,4 +1,10 @@
|
||||
define(["jquery", "core", "async-runner"], function($, core, asyncRunner) {
|
||||
define([
|
||||
"jquery",
|
||||
"core",
|
||||
"utils",
|
||||
"extension-manager",
|
||||
"async-runner"
|
||||
], function($, core, utils, extensionMgr, asyncRunner) {
|
||||
|
||||
var token = undefined;
|
||||
|
||||
@ -25,14 +31,14 @@ define(["jquery", "core", "async-runner"], function($, core, asyncRunner) {
|
||||
task.chain();
|
||||
return;
|
||||
}
|
||||
core.showMessage("Please make sure the Wordpress authorization popup is not blocked by your browser.");
|
||||
extensionMgr.onMessage("Please make sure the Wordpress authorization popup is not blocked by your browser.");
|
||||
var errorMsg = "Failed to retrieve a token from Wordpress.";
|
||||
// We add time for user to enter his credentials
|
||||
task.timeout = ASYNC_TASK_LONG_TIMEOUT;
|
||||
var code = undefined;
|
||||
function getCode() {
|
||||
localStorage.removeItem("wordpressCode");
|
||||
authWindow = core.popupWindow(
|
||||
authWindow = utils.popupWindow(
|
||||
'wordpress-oauth-client.html?client_id=' + WORDPRESS_CLIENT_ID,
|
||||
'stackedit-wordpress-oauth', 960, 600);
|
||||
authWindow.focus();
|
||||
@ -76,7 +82,6 @@ define(["jquery", "core", "async-runner"], function($, core, asyncRunner) {
|
||||
}
|
||||
|
||||
wordpressHelper.upload = function(site, postId, tags, title, content, callback) {
|
||||
callback = callback || core.doNothing;
|
||||
var task = asyncRunner.createTask();
|
||||
connect(task);
|
||||
authenticate(task);
|
||||
|
@ -1,4 +1,7 @@
|
||||
define(["utils", "wordpress-helper"], function(utils, wordpressHelper) {
|
||||
define([
|
||||
"utils",
|
||||
"wordpress-helper"
|
||||
], function(utils, wordpressHelper) {
|
||||
|
||||
var PROVIDER_WORDPRESS = "wordpress";
|
||||
|
||||
|
@ -24,7 +24,7 @@
|
||||
var require = { baseUrl : "js", deps : [ "main" + suffix ] };
|
||||
var viewerMode = true;
|
||||
</script>
|
||||
<script src="js/require.js"></script>
|
||||
<script src="js/lib/require.js"></script>
|
||||
</head>
|
||||
<body class="viewer">
|
||||
<div id="navbar" class="navbar navbar-fixed-top ui-layout-north">
|
||||
|
Loading…
Reference in New Issue
Block a user