New extension pattern

This commit is contained in:
benweet 2013-05-29 20:55:23 +01:00
parent c6c06373da
commit d7304444a1
44 changed files with 5801 additions and 5349 deletions

View File

@ -327,7 +327,6 @@ hr {
div.dropdown-menu {
padding: 5px 20px;
white-space: normal;
}
div.dropdown-menu p,
@ -335,12 +334,17 @@ div.dropdown-menu blockquote {
margin: 10px 0;
}
div.dropdown-menu .stat {
margin-bottom: 10px;
}
div.dropdown-menu i {
margin-right: 0;
}
#link-container {
min-width: 210px;
white-space: normal;
}
#link-container .link-list {
@ -518,6 +522,10 @@ div.dropdown-menu i {
text-align: left;
}
#modal-settings .accordion-inner .form-inline .label-text {
margin: 0 10px;
}
.accordion-toggle {
cursor: help;
}

BIN
doc/img/architecture.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 31 KiB

View File

@ -9,184 +9,185 @@ define([
"extension-manager"
], function(_, core, utils, extensionMgr) {
var asyncRunner = {};
var asyncRunner = {};
var taskQueue = [];
var asyncRunning = false;
var currentTask = undefined;
var currentTaskRunning = false;
var currentTaskStartTime = 0;
var taskQueue = [];
var asyncRunning = false;
var currentTask = undefined;
var currentTaskRunning = false;
var currentTaskStartTime = 0;
asyncRunner.createTask = function() {
var task = {};
task.finished = false;
task.timeout = ASYNC_TASK_DEFAULT_TIMEOUT;
task.retryCounter = 0;
/**
* onRun callbacks are called by chain(). These callbacks have to call
* chain() themselves to chain with next onRun callback or error() to
* throw an exception or retry() to restart the task.
*/
// Run callbacks
task.runCallbacks = [];
task.onRun = function(callback) {
task.runCallbacks.push(callback);
};
/**
* onSuccess callbacks are called when every onRun callbacks have
* succeed.
*/
task.successCallbacks = [];
task.onSuccess = function(callback) {
task.successCallbacks.push(callback);
};
/**
* onError callbacks are called when error() is called in a onRun
* callback.
*/
task.errorCallbacks = [];
task.onError = function(callback) {
task.errorCallbacks.push(callback);
};
/**
* chain() calls the next onRun callback or the onSuccess callbacks when
* finished. The optional callback parameter can be used to pass an onRun
* callback during execution.
*/
task.chain = function(callback) {
if (task.finished === true) {
return;
}
// If first execution
if (task.queue === undefined) {
// Create a copy of the onRun callbacks
task.queue = task.runCallbacks.slice();
}
// If a callback is passed as a parameter
if(callback !== undefined) {
callback();
return;
}
// If all callbacks have been run
if (task.queue.length === 0) {
// Run the onSuccess callbacks
runSafe(task, task.successCallbacks);
return;
}
// Run the next callback
var runCallback = task.queue.shift();
runCallback();
};
/**
* error() calls the onError callbacks passing the error parameter and ends
* the task by throwing an exception.
*/
task.error = function(error) {
if (task.finished === true) {
return;
}
error = error || new Error("Unknown error");
if(error.message) {
extensionMgr.onError(error);
}
runSafe(task, task.errorCallbacks, error);
// Exit the current call stack
throw error;
};
/**
* retry() can be called in an onRun callback to restart the task
*/
task.retry = function(error, maxRetryCounter) {
if (task.finished === true) {
return;
}
maxRetryCounter = maxRetryCounter || 5;
task.queue = undefined;
if (task.retryCounter >= maxRetryCounter) {
task.error(error);
return;
}
// Implement an exponential backoff
var delay = Math.pow(2, task.retryCounter++) * 1000;
currentTaskStartTime = utils.currentTime + delay;
currentTaskRunning = false;
asyncRunner.runTask();
};
return task;
};
asyncRunner.createTask = function() {
var task = {};
task.finished = false;
task.timeout = ASYNC_TASK_DEFAULT_TIMEOUT;
task.retryCounter = 0;
/**
* onRun callbacks are called by chain(). These callbacks have to call
* chain() themselves to chain with next onRun callback or error() to
* throw an exception or retry() to restart the task.
*/
// Run callbacks
task.runCallbacks = [];
task.onRun = function(callback) {
task.runCallbacks.push(callback);
};
/**
* onSuccess callbacks are called when every onRun callbacks have
* succeed.
*/
task.successCallbacks = [];
task.onSuccess = function(callback) {
task.successCallbacks.push(callback);
};
/**
* onError callbacks are called when error() is called in a onRun
* callback.
*/
task.errorCallbacks = [];
task.onError = function(callback) {
task.errorCallbacks.push(callback);
};
/**
* chain() calls the next onRun callback or the onSuccess callbacks when
* finished. The optional callback parameter can be used to pass an
* onRun callback during execution.
*/
task.chain = function(callback) {
if(task.finished === true) {
return;
}
// If first execution
if(task.queue === undefined) {
// Create a copy of the onRun callbacks
task.queue = task.runCallbacks.slice();
}
// If a callback is passed as a parameter
if(callback !== undefined) {
callback();
return;
}
// If all callbacks have been run
if(task.queue.length === 0) {
// Run the onSuccess callbacks
runSafe(task, task.successCallbacks);
return;
}
// Run the next callback
var runCallback = task.queue.shift();
runCallback();
};
/**
* error() calls the onError callbacks passing the error parameter and
* ends the task by throwing an exception.
*/
task.error = function(error) {
if(task.finished === true) {
return;
}
error = error || new Error("Unknown error");
if(error.message) {
extensionMgr.onError(error);
}
runSafe(task, task.errorCallbacks, error);
// Exit the current call stack
throw error;
};
/**
* retry() can be called in an onRun callback to restart the task
*/
task.retry = function(error, maxRetryCounter) {
if(task.finished === true) {
return;
}
maxRetryCounter = maxRetryCounter || 5;
task.queue = undefined;
if(task.retryCounter >= maxRetryCounter) {
task.error(error);
return;
}
// Implement an exponential backoff
var delay = Math.pow(2, task.retryCounter++) * 1000;
currentTaskStartTime = utils.currentTime + delay;
currentTaskRunning = false;
asyncRunner.runTask();
};
return task;
};
// Run the next task in the queue if any and no other running
asyncRunner.runTask = function() {
// Use defer to avoid stack overflow
_.defer(function() {
// Run the next task in the queue if any and no other running
asyncRunner.runTask = function() {
// Use defer to avoid stack overflow
_.defer(function() {
// If there is a task currently running
if (currentTaskRunning === true) {
// If the current task takes too long
if (currentTaskStartTime + currentTask.timeout < utils.currentTime) {
currentTask.error(new Error("A timeout occurred."));
}
return;
}
// If there is a task currently running
if(currentTaskRunning === true) {
// If the current task takes too long
if(currentTaskStartTime + currentTask.timeout < utils.currentTime) {
currentTask.error(new Error("A timeout occurred."));
}
return;
}
if (currentTask === undefined) {
// If no task in the queue
if (taskQueue.length === 0) {
return;
}
if(currentTask === undefined) {
// If no task in the queue
if(taskQueue.length === 0) {
return;
}
// Dequeue an enqueued task
currentTask = taskQueue.shift();
currentTaskStartTime = utils.currentTime;
if(asyncRunning === false) {
asyncRunning = true;
extensionMgr.onAsyncRunning(true);
}
}
// Dequeue an enqueued task
currentTask = taskQueue.shift();
currentTaskStartTime = utils.currentTime;
if(asyncRunning === false) {
asyncRunning = true;
extensionMgr.onAsyncRunning(true);
}
}
// Run the task
if (currentTaskStartTime <= utils.currentTime) {
currentTaskRunning = true;
currentTask.chain();
}
});
};
// Run runTask function periodically
core.addPeriodicCallback(asyncRunner.runTask);
// Run the task
if(currentTaskStartTime <= utils.currentTime) {
currentTaskRunning = true;
currentTask.chain();
}
});
};
// Run runTask function periodically
core.addPeriodicCallback(asyncRunner.runTask);
function runSafe(task, callbacks, param) {
try {
_.each(callbacks, function(callback) {
callback(param);
});
}
finally {
task.finished = true;
if (currentTask === task) {
currentTask = undefined;
currentTaskRunning = false;
}
if (taskQueue.length === 0) {
asyncRunning = false;
extensionMgr.onAsyncRunning(false);
} else {
asyncRunner.runTask();
}
}
}
function runSafe(task, callbacks, param) {
try {
_.each(callbacks, function(callback) {
callback(param);
});
}
finally {
task.finished = true;
if(currentTask === task) {
currentTask = undefined;
currentTaskRunning = false;
}
if(taskQueue.length === 0) {
asyncRunning = false;
extensionMgr.onAsyncRunning(false);
}
else {
asyncRunner.runTask();
}
}
}
// Add a task to the queue
asyncRunner.addTask = function(task) {
taskQueue.push(task);
asyncRunner.runTask();
};
// Add a task to the queue
asyncRunner.addTask = function(task) {
taskQueue.push(task);
asyncRunner.runTask();
};
// Change current task timeout
asyncRunner.setCurrentTaskTimeout = function(timeout) {
if (currentTask !== undefined) {
currentTask.timeout = timeout;
}
};
// Change current task timeout
asyncRunner.setCurrentTaskTimeout = function(timeout) {
if(currentTask !== undefined) {
currentTask.timeout = timeout;
}
};
return asyncRunner;
return asyncRunner;
});

View File

@ -1,59 +1,51 @@
define([
"underscore",
"underscore",
"utils",
"google-helper"
], function(_, utils, googleHelper) {
var PROVIDER_BLOGGER = "blogger";
var PROVIDER_BLOGGER = "blogger";
var bloggerProvider = {
providerId: PROVIDER_BLOGGER,
providerName: "Blogger",
defaultPublishFormat: "html",
publishPreferencesInputIds: ["blogger-url"]
};
var bloggerProvider = {
providerId: PROVIDER_BLOGGER,
providerName: "Blogger",
defaultPublishFormat: "html",
publishPreferencesInputIds: [
"blogger-url"
]
};
bloggerProvider.publish = function(publishAttributes, title, content, callback) {
googleHelper.uploadBlogger(
publishAttributes.blogUrl,
publishAttributes.blogId,
publishAttributes.postId,
publishAttributes.labelList,
title,
content,
function(error, blogId, postId) {
if(error) {
callback(error);
return;
}
publishAttributes.blogId = blogId;
publishAttributes.postId = postId;
callback();
}
);
};
bloggerProvider.publish = function(publishAttributes, title, content, callback) {
googleHelper.uploadBlogger(publishAttributes.blogUrl, publishAttributes.blogId, publishAttributes.postId, publishAttributes.labelList, title, content, function(error, blogId, postId) {
if(error) {
callback(error);
return;
}
publishAttributes.blogId = blogId;
publishAttributes.postId = postId;
callback();
});
};
bloggerProvider.newPublishAttributes = function(event) {
var publishAttributes = {};
var blogUrl = utils.getInputTextValue("#input-publish-blogger-url", event);
if(blogUrl !== undefined) {
publishAttributes.blogUrl = utils.checkUrl(blogUrl);
}
publishAttributes.postId = utils.getInputTextValue("#input-publish-postid");
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();
}
if(event.isPropagationStopped()) {
return undefined;
}
return publishAttributes;
};
bloggerProvider.newPublishAttributes = function(event) {
var publishAttributes = {};
var blogUrl = utils.getInputTextValue("#input-publish-blogger-url", event);
if(blogUrl !== undefined) {
publishAttributes.blogUrl = utils.checkUrl(blogUrl);
}
publishAttributes.postId = utils.getInputTextValue("#input-publish-postid");
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();
}
if(event.isPropagationStopped()) {
return undefined;
}
return publishAttributes;
};
return bloggerProvider;
return bloggerProvider;
});

View File

@ -1,8 +1,10 @@
var MAIN_URL = "http://benweet.github.io/stackedit/";
var GOOGLE_API_KEY = "AIzaSyAeCU8CGcSkn0z9js6iocHuPBX4f_mMWkw";
var GOOGLE_SCOPES = [ "https://www.googleapis.com/auth/drive.install",
"https://www.googleapis.com/auth/drive",
"https://www.googleapis.com/auth/blogger" ];
var GOOGLE_SCOPES = [
"https://www.googleapis.com/auth/drive.install",
"https://www.googleapis.com/auth/drive",
"https://www.googleapis.com/auth/blogger"
];
var GOOGLE_DRIVE_APP_ID = "241271498917";
var DROPBOX_APP_KEY = "lq6mwopab8wskas";
var DROPBOX_APP_SECRET = "851fgnucpezy84t";
@ -25,9 +27,9 @@ var SSH_PROXY_URL = "http://stackedit-ssh-proxy.herokuapp.com/";
// Use by Google's client.js
var delayedFunction = undefined;
function runDelayedFunction() {
if (delayedFunction !== undefined) {
delayedFunction();
}
if(delayedFunction !== undefined) {
delayedFunction();
}
}
// Site dependent
@ -38,15 +40,15 @@ var GATEKEEPER_URL = "http://stackedit-gatekeeper-localhost.herokuapp.com/";
var TUMBLR_PROXY_URL = "http://stackedit-tumblr-proxy-local.herokuapp.com/";
if(location.hostname.indexOf("benweet.github.io") === 0) {
BASE_URL = MAIN_URL;
GOOGLE_CLIENT_ID = '241271498917-jpto9lls9fqnem1e4h6ppds9uob8rpvu.apps.googleusercontent.com';
GITHUB_CLIENT_ID = 'fa0d09514da8377ee32e';
GATEKEEPER_URL = "http://stackedit-gatekeeper.herokuapp.com/";
TUMBLR_PROXY_URL = "http://stackedit-tumblr-proxy.herokuapp.com/";
BASE_URL = MAIN_URL;
GOOGLE_CLIENT_ID = '241271498917-jpto9lls9fqnem1e4h6ppds9uob8rpvu.apps.googleusercontent.com';
GITHUB_CLIENT_ID = 'fa0d09514da8377ee32e';
GATEKEEPER_URL = "http://stackedit-gatekeeper.herokuapp.com/";
TUMBLR_PROXY_URL = "http://stackedit-tumblr-proxy.herokuapp.com/";
}
var THEME_LIST = {
"": "Default",
"blue-gray": "Blue-Gray",
"night": "Night"
"": "Default",
"blue-gray": "Blue-Gray",
"night": "Night"
};

View File

@ -1,6 +1,6 @@
define([
"jquery",
"underscore",
"underscore",
"utils",
"settings",
"extension-manager",
@ -11,460 +11,445 @@ define([
"lib/Markdown.Editor"
], function($, _, utils, settings, extensionMgr) {
var core = {};
var core = {};
// Used for periodic tasks
var intervalId = undefined;
var periodicCallbacks = [];
core.addPeriodicCallback = function(callback) {
periodicCallbacks.push(callback);
};
// Used for periodic tasks
var intervalId = undefined;
var periodicCallbacks = [];
core.addPeriodicCallback = function(callback) {
periodicCallbacks.push(callback);
};
// Used to detect user activity
var userReal = false;
var userActive = false;
var windowUnique = true;
var userLastActivity = 0;
function setUserActive() {
userReal = true;
userActive = true;
userLastActivity = utils.currentTime;
};
function isUserActive() {
if(userActive === true
&& utils.currentTime - userLastActivity > USER_IDLE_THRESHOLD) {
userActive = false;
}
return userActive && windowUnique;
}
// Used to detect user activity
var userReal = false;
var userActive = false;
var windowUnique = true;
var userLastActivity = 0;
function setUserActive() {
userReal = true;
userActive = true;
userLastActivity = utils.currentTime;
}
// Used to only have 1 window of the application in the same browser
var windowId = undefined;
function checkWindowUnique() {
if(userReal === false || windowUnique === false) {
return;
}
if(windowId === undefined) {
windowId = utils.randomString();
localStorage["frontWindowId"] = windowId;
}
var frontWindowId = localStorage["frontWindowId"];
if(frontWindowId != windowId) {
windowUnique = false;
if(intervalId !== undefined) {
clearInterval(intervalId);
}
$(".modal").modal("hide");
$('#modal-non-unique').modal({
backdrop: "static",
keyboard: false
});
}
}
function isUserActive() {
if(userActive === true && utils.currentTime - userLastActivity > USER_IDLE_THRESHOLD) {
userActive = false;
}
return userActive && windowUnique;
}
// Offline management
core.isOffline = false;
var offlineTime = utils.currentTime;
core.setOffline = function() {
offlineTime = utils.currentTime;
if(core.isOffline === false) {
core.isOffline = true;
extensionMgr.onOfflineChanged(true);
}
};
function setOnline() {
if(core.isOffline === true) {
core.isOffline = false;
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 < 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() {
setOnline();
});
}
}
// Used to only have 1 window of the application in the same browser
var windowId = undefined;
function checkWindowUnique() {
if(userReal === false || windowUnique === false) {
return;
}
if(windowId === undefined) {
windowId = utils.randomString();
localStorage["frontWindowId"] = windowId;
}
var frontWindowId = localStorage["frontWindowId"];
if(frontWindowId != windowId) {
windowUnique = false;
if(intervalId !== undefined) {
clearInterval(intervalId);
}
$(".modal").modal("hide");
$('#modal-non-unique').modal({
backdrop: "static",
keyboard: false
});
}
}
// Load settings in settings dialog
function loadSettings() {
// Offline management
core.isOffline = false;
var offlineTime = utils.currentTime;
core.setOffline = function() {
offlineTime = utils.currentTime;
if(core.isOffline === false) {
core.isOffline = true;
extensionMgr.onOfflineChanged(true);
}
};
function setOnline() {
if(core.isOffline === true) {
core.isOffline = false;
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 < 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() {
setOnline();
});
}
}
// Layout orientation
utils.setInputRadio("radio-layout-orientation", settings.layoutOrientation);
// Theme
utils.setInputValue("#input-settings-theme", localStorage.theme);
// Lazy rendering
utils.setInputChecked("#input-settings-lazy-rendering", settings.lazyRendering);
// Editor font size
utils.setInputValue("#input-settings-editor-font-size", settings.editorFontSize);
// Default content
utils.setInputValue("#textarea-settings-default-content", settings.defaultContent);
// Commit message
utils.setInputValue("#input-settings-publish-commit-msg", settings.commitMsg);
// Template
utils.setInputValue("#textarea-settings-publish-template", settings.template);
// SSH proxy
utils.setInputValue("#input-settings-ssh-proxy", settings.sshProxy);
// Load settings in settings dialog
function loadSettings() {
// Load extension settings
extensionMgr.onLoadSettings();
}
// Layout orientation
utils.setInputRadio("radio-layout-orientation", settings.layoutOrientation);
// Theme
utils.setInputValue("#input-settings-theme", localStorage.theme);
// Lazy rendering
utils.setInputChecked("#input-settings-lazy-rendering", settings.lazyRendering);
// Editor font size
utils.setInputValue("#input-settings-editor-font-size", settings.editorFontSize);
// Default content
utils.setInputValue("#textarea-settings-default-content", settings.defaultContent);
// Commit message
utils.setInputValue("#input-settings-publish-commit-msg", settings.commitMsg);
// Template
utils.setInputValue("#textarea-settings-publish-template", settings.template);
// SSH proxy
utils.setInputValue("#input-settings-ssh-proxy", settings.sshProxy);
// Save settings from settings dialog
function saveSettings(event) {
var newSettings = {};
// Load extension settings
extensionMgr.onLoadSettings();
}
// Layout orientation
newSettings.layoutOrientation = utils.getInputRadio("radio-layout-orientation");
// Theme
var theme = utils.getInputValue("#input-settings-theme");
// Lazy Rendering
newSettings.lazyRendering = utils.getInputChecked("#input-settings-lazy-rendering");
// Editor font size
newSettings.editorFontSize = utils.getInputIntValue("#input-settings-editor-font-size", event, 1, 99);
// Default content
newSettings.defaultContent = utils.getInputValue("#textarea-settings-default-content");
// Commit message
newSettings.commitMsg = utils.getInputTextValue("#input-settings-publish-commit-msg", event);
// Template
newSettings.template = utils.getInputTextValue("#textarea-settings-publish-template", event);
// SSH proxy
newSettings.sshProxy = utils.checkUrl(utils.getInputTextValue("#input-settings-ssh-proxy", event), true);
// Save settings from settings dialog
function saveSettings(event) {
var newSettings = {};
// Save extension settings
newSettings.extensionSettings = {};
extensionMgr.onSaveSettings(newSettings.extensionSettings, event);
// Layout orientation
newSettings.layoutOrientation = utils.getInputRadio("radio-layout-orientation");
// Theme
var theme = utils.getInputValue("#input-settings-theme");
// Lazy Rendering
newSettings.lazyRendering = utils.getInputChecked("#input-settings-lazy-rendering");
// Editor font size
newSettings.editorFontSize = utils.getInputIntValue("#input-settings-editor-font-size", event, 1, 99);
// Default content
newSettings.defaultContent = utils.getInputValue("#textarea-settings-default-content");
// Commit message
newSettings.commitMsg = utils.getInputTextValue("#input-settings-publish-commit-msg", event);
// Template
newSettings.template = utils.getInputTextValue("#textarea-settings-publish-template", event);
// SSH proxy
newSettings.sshProxy = utils.checkUrl(utils.getInputTextValue("#input-settings-ssh-proxy", event), true);
if(!event.isPropagationStopped()) {
$.extend(settings, newSettings);
localStorage.settings = JSON.stringify(settings);
localStorage.theme = theme;
}
}
// Save extension settings
newSettings.extensionSettings = {};
extensionMgr.onSaveSettings(newSettings.extensionSettings, event);
// Create the layout
var layout = undefined;
core.createLayout = function() {
if(viewerMode === true) {
return;
}
var layoutGlobalConfig = {
closable : true,
resizable : false,
slidable : false,
livePaneResizing : true,
enableCursorHotkey : false,
spacing_open : 15,
spacing_closed : 15,
togglerLength_open : 90,
togglerLength_closed : 90,
stateManagement__enabled : false,
center__minWidth : 200,
center__minHeight : 200
};
extensionMgr.onLayoutConfigure(layoutGlobalConfig);
if (settings.layoutOrientation == "horizontal") {
$(".ui-layout-south").remove();
$(".ui-layout-east").addClass("well").prop("id", "wmd-preview");
layout = $('body').layout(
$.extend(layoutGlobalConfig, {
east__resizable : true,
east__size : .5,
east__minSize : 200
})
);
} else if (settings.layoutOrientation == "vertical") {
$(".ui-layout-east").remove();
$(".ui-layout-south").addClass("well").prop("id", "wmd-preview");
layout = $('body').layout(
$.extend(layoutGlobalConfig, {
south__resizable : true,
south__size : .5,
south__minSize : 200
})
);
}
$(".ui-layout-toggler-north").addClass("btn").append(
$("<b>").addClass("caret"));
$(".ui-layout-toggler-south").addClass("btn").append(
$("<b>").addClass("caret"));
$(".ui-layout-toggler-east").addClass("btn").append(
$("<b>").addClass("caret"));
$("#navbar").click(function() {
layout.allowOverflow('north');
});
if(!event.isPropagationStopped()) {
$.extend(settings, newSettings);
localStorage.settings = JSON.stringify(settings);
localStorage.theme = theme;
}
}
extensionMgr.onLayoutCreated(layout);
};
// Create the layout
var layout = undefined;
core.createLayout = function() {
if(viewerMode === true) {
return;
}
var layoutGlobalConfig = {
closable: true,
resizable: false,
slidable: false,
livePaneResizing: true,
enableCursorHotkey: false,
spacing_open: 15,
spacing_closed: 15,
togglerLength_open: 90,
togglerLength_closed: 90,
stateManagement__enabled: false,
center__minWidth: 200,
center__minHeight: 200
};
extensionMgr.onLayoutConfigure(layoutGlobalConfig);
if(settings.layoutOrientation == "horizontal") {
$(".ui-layout-south").remove();
$(".ui-layout-east").addClass("well").prop("id", "wmd-preview");
layout = $('body').layout($.extend(layoutGlobalConfig, {
east__resizable: true,
east__size: .5,
east__minSize: 200
}));
}
else if(settings.layoutOrientation == "vertical") {
$(".ui-layout-east").remove();
$(".ui-layout-south").addClass("well").prop("id", "wmd-preview");
layout = $('body').layout($.extend(layoutGlobalConfig, {
south__resizable: true,
south__size: .5,
south__minSize: 200
}));
}
$(".ui-layout-toggler-north").addClass("btn").append($("<b>").addClass("caret"));
$(".ui-layout-toggler-south").addClass("btn").append($("<b>").addClass("caret"));
$(".ui-layout-toggler-east").addClass("btn").append($("<b>").addClass("caret"));
$("#navbar").click(function() {
layout.allowOverflow('north');
});
// Create the PageDown editor
var insertLinkCallback = undefined;
core.createEditor = function(onTextChange) {
var converter = new Markdown.Converter();
var editor = new Markdown.Editor(converter);
// Custom insert link dialog
editor.hooks.set("insertLinkDialog", function (callback) {
insertLinkCallback = callback;
utils.resetModalInputs();
$("#modal-insert-link").modal();
_.defer(function() {
$("#input-insert-link").focus();
});
return true;
});
// Custom insert image dialog
editor.hooks.set("insertImageDialog", function (callback) {
insertLinkCallback = callback;
utils.resetModalInputs();
$("#modal-insert-image").modal();
_.defer(function() {
$("#input-insert-image").focus();
});
return true;
});
extensionMgr.onLayoutCreated(layout);
};
var documentContent = undefined;
function checkDocumentChanges() {
var newDocumentContent = $("#wmd-input").val();
if(documentContent !== undefined && documentContent != newDocumentContent) {
onTextChange();
}
documentContent = newDocumentContent;
}
var previewWrapper = undefined;
if(settings.lazyRendering === true) {
previewWrapper = function(makePreview) {
var debouncedMakePreview = _.debounce(makePreview, 500);
return function() {
if(documentContent === undefined) {
makePreview();
}
else {
debouncedMakePreview();
}
checkDocumentChanges();
};
};
}
else {
previewWrapper = function(makePreview) {
return function() {
checkDocumentChanges();
makePreview();
};
};
}
editor.hooks.chain("onPreviewRefresh", extensionMgr.onAsyncPreview);
extensionMgr.onEditorConfigure(editor);
// Create the PageDown editor
var insertLinkCallback = undefined;
core.createEditor = function(onTextChange) {
var converter = new Markdown.Converter();
var editor = new Markdown.Editor(converter);
// Custom insert link dialog
editor.hooks.set("insertLinkDialog", function(callback) {
insertLinkCallback = callback;
utils.resetModalInputs();
$("#modal-insert-link").modal();
_.defer(function() {
$("#input-insert-link").focus();
});
return true;
});
// Custom insert image dialog
editor.hooks.set("insertImageDialog", function(callback) {
insertLinkCallback = callback;
utils.resetModalInputs();
$("#modal-insert-image").modal();
_.defer(function() {
$("#input-insert-image").focus();
});
return true;
});
$("#wmd-input, #wmd-preview").scrollTop(0);
$("#wmd-button-bar").empty();
editor.run(previewWrapper);
firstChange = false;
var documentContent = undefined;
function checkDocumentChanges() {
var newDocumentContent = $("#wmd-input").val();
if(documentContent !== undefined && documentContent != newDocumentContent) {
onTextChange();
}
documentContent = newDocumentContent;
}
var previewWrapper = undefined;
if(settings.lazyRendering === true) {
previewWrapper = function(makePreview) {
var debouncedMakePreview = _.debounce(makePreview, 500);
return function() {
if(documentContent === undefined) {
makePreview();
}
else {
debouncedMakePreview();
}
checkDocumentChanges();
};
};
}
else {
previewWrapper = function(makePreview) {
return function() {
checkDocumentChanges();
makePreview();
};
};
}
editor.hooks.chain("onPreviewRefresh", extensionMgr.onAsyncPreview);
extensionMgr.onEditorConfigure(editor);
// Hide default buttons
$(".wmd-button-row").addClass("btn-group").find("li:not(.wmd-spacer)")
.addClass("btn").css("left", 0).find("span").hide();
$("#wmd-input, #wmd-preview").scrollTop(0);
$("#wmd-button-bar").empty();
editor.run(previewWrapper);
firstChange = false;
// Add customized buttons
$("#wmd-bold-button").append($("<i>").addClass("icon-bold"));
$("#wmd-italic-button").append($("<i>").addClass("icon-italic"));
$("#wmd-link-button").append($("<i>").addClass("icon-globe"));
$("#wmd-quote-button").append($("<i>").addClass("icon-indent-left"));
$("#wmd-code-button").append($("<i>").addClass("icon-code"));
$("#wmd-image-button").append($("<i>").addClass("icon-picture"));
$("#wmd-olist-button").append($("<i>").addClass("icon-numbered-list"));
$("#wmd-ulist-button").append($("<i>").addClass("icon-list"));
$("#wmd-heading-button").append($("<i>").addClass("icon-text-height"));
$("#wmd-hr-button").append($("<i>").addClass("icon-hr"));
$("#wmd-undo-button").append($("<i>").addClass("icon-undo"));
$("#wmd-redo-button").append($("<i>").addClass("icon-share-alt"));
};
// Hide default buttons
$(".wmd-button-row").addClass("btn-group").find("li:not(.wmd-spacer)").addClass("btn").css("left", 0).find("span").hide();
// onReady event callbacks
var readyCallbacks = [];
core.onReady = function(callback) {
readyCallbacks.push(callback);
runReadyCallbacks();
};
var ready = false;
core.setReady = function() {
ready = true;
runReadyCallbacks();
};
function runReadyCallbacks() {
if(ready === true) {
_.each(readyCallbacks, function(callback) {
callback();
});
readyCallbacks = [];
}
}
// Add customized buttons
$("#wmd-bold-button").append($("<i>").addClass("icon-bold"));
$("#wmd-italic-button").append($("<i>").addClass("icon-italic"));
$("#wmd-link-button").append($("<i>").addClass("icon-globe"));
$("#wmd-quote-button").append($("<i>").addClass("icon-indent-left"));
$("#wmd-code-button").append($("<i>").addClass("icon-code"));
$("#wmd-image-button").append($("<i>").addClass("icon-picture"));
$("#wmd-olist-button").append($("<i>").addClass("icon-numbered-list"));
$("#wmd-ulist-button").append($("<i>").addClass("icon-list"));
$("#wmd-heading-button").append($("<i>").addClass("icon-text-height"));
$("#wmd-hr-button").append($("<i>").addClass("icon-hr"));
$("#wmd-undo-button").append($("<i>").addClass("icon-undo"));
$("#wmd-redo-button").append($("<i>").addClass("icon-share-alt"));
};
core.onReady(extensionMgr.onReady);
core.onReady(function() {
// onReady event callbacks
var readyCallbacks = [];
core.onReady = function(callback) {
readyCallbacks.push(callback);
runReadyCallbacks();
};
var ready = false;
core.setReady = function() {
ready = true;
runReadyCallbacks();
};
function runReadyCallbacks() {
if(ready === true) {
_.each(readyCallbacks, function(callback) {
callback();
});
readyCallbacks = [];
}
}
// Load theme list
_.each(THEME_LIST, function(name, value) {
$("#input-settings-theme").append($('<option value="' + value + '">' + name + '</option>'));
});
core.onReady(extensionMgr.onReady);
core.onReady(function() {
// listen to online/offline events
$(window).on('offline', core.setOffline);
$(window).on('online', setOnline);
if (navigator.onLine === false) {
core.setOffline();
}
// Load theme list
_.each(THEME_LIST, function(name, value) {
$("#input-settings-theme").append($('<option value="' + value + '">' + name + '</option>'));
});
// Detect user activity
$(document).mousemove(setUserActive).keypress(setUserActive);
// listen to online/offline events
$(window).on('offline', core.setOffline);
$(window).on('online', setOnline);
if(navigator.onLine === false) {
core.setOffline();
}
// Avoid dropdown to close when clicking on submenu
$(".dropdown-submenu > a").click(function(e) {
e.stopPropagation();
});
// Detect user activity
$(document).mousemove(setUserActive).keypress(setUserActive);
// Click events on "insert link" and "insert image" dialog buttons
$(".action-insert-link").click(function(e) {
var value = utils.getInputTextValue($("#input-insert-link"), e);
if(value !== undefined) {
insertLinkCallback(value);
}
});
$(".action-insert-image").click(function(e) {
var value = utils.getInputTextValue($("#input-insert-image"), e);
if(value !== undefined) {
insertLinkCallback(value);
}
});
$(".action-close-insert-link").click(function(e) {
insertLinkCallback(null);
});
// Avoid dropdown to close when clicking on submenu
$(".dropdown-submenu > a").click(function(e) {
e.stopPropagation();
});
// Settings loading/saving
$(".action-load-settings").click(function() {
loadSettings();
});
$(".action-apply-settings").click(function(e) {
saveSettings(e);
if(!e.isPropagationStopped()) {
window.location.reload();
}
});
// Click events on "insert link" and "insert image" dialog buttons
$(".action-insert-link").click(function(e) {
var value = utils.getInputTextValue($("#input-insert-link"), e);
if(value !== undefined) {
insertLinkCallback(value);
}
});
$(".action-insert-image").click(function(e) {
var value = utils.getInputTextValue($("#input-insert-image"), e);
if(value !== undefined) {
insertLinkCallback(value);
}
});
$(".action-close-insert-link").click(function(e) {
insertLinkCallback(null);
});
$(".action-default-settings").click(function() {
localStorage.removeItem("settings");
localStorage.removeItem("theme");
window.location.reload();
});
// Settings loading/saving
$(".action-load-settings").click(function() {
loadSettings();
});
$(".action-apply-settings").click(function(e) {
saveSettings(e);
if(!e.isPropagationStopped()) {
window.location.reload();
}
});
$(".action-app-reset").click(function() {
localStorage.clear();
window.location.reload();
});
$(".action-default-settings").click(function() {
localStorage.removeItem("settings");
localStorage.removeItem("theme");
window.location.reload();
});
// UI layout
$("#menu-bar, .ui-layout-center, .ui-layout-east, .ui-layout-south").removeClass("hide");
core.createLayout();
$(".action-app-reset").click(function() {
localStorage.clear();
window.location.reload();
});
// Editor's textarea
$("#wmd-input, #md-section-helper").css({
// Apply editor font size
"font-size": settings.editorFontSize + "px",
"line-height": Math.round(settings.editorFontSize * (20/14)) + "px"
});
// UI layout
$("#menu-bar, .ui-layout-center, .ui-layout-east, .ui-layout-south").removeClass("hide");
core.createLayout();
// Manage tab key
$("#wmd-input").keydown(function(e) {
if(e.keyCode === 9) {
var value = $(this).val();
var start = this.selectionStart;
var end = this.selectionEnd;
// IE8 does not support selection attributes
if(start === undefined || end === undefined) {
return;
}
$(this).val(value.substring(0, start) + "\t" + value.substring(end));
this.selectionStart = this.selectionEnd = start + 1;
e.preventDefault();
}
});
// Editor's textarea
$("#wmd-input, #md-section-helper").css({
// Apply editor font size
"font-size": settings.editorFontSize + "px",
"line-height": Math.round(settings.editorFontSize * (20 / 14)) + "px"
});
// Tooltips
$(".tooltip-scroll-link").tooltip({
html: true,
container: '#modal-settings',
placement: 'right',
title: ['Scroll Link is a feature that binds together editor and preview scrollbars. ',
'It allows you to keep an eye on the preview while scrolling the editor and vice versa. ',
'<br><br>',
'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 efficient.',
].join("")
});
$(".tooltip-lazy-rendering").tooltip({
container: '#modal-settings',
placement: 'right',
title: 'Disable preview rendering while typing in order to offload CPU. Refresh preview after 500 ms of inactivity.'
});
$(".tooltip-default-content").tooltip({
html: true,
container: '#modal-settings',
placement: 'right',
title: 'Thanks for supporting StackEdit by adding a backlink in your documents!'
});
$(".tooltip-template").tooltip({
html: true,
container: '#modal-settings',
placement: 'right',
trigger: 'manual',
title: ['Available variables:<br>',
'<ul><li><b>documentTitle</b>: document title</li>',
'<li><b>documentMarkdown</b>: document in Markdown format</li>',
'<li><b>documentHTML</b>: document in HTML format</li>',
'<li><b>publishAttributes</b>: attributes of the publish location (undefined when using "Save")</li></ul>',
'Examples:<br>',
_.escape('<title><%= documentTitle %></title>'),
'<br>',
_.escape('<div><%- documentHTML %></div>'),
'<br>',
_.escape('<% if(publishAttributes.provider == "github") print(documentMarkdown); %>'),
'<br><br><a target="_blank" href="http://underscorejs.org/#template">More info</a>',
].join("")
}).click(function(e) {
$(this).tooltip('show');
e.stopPropagation();
});
// Manage tab key
$("#wmd-input").keydown(function(e) {
if(e.keyCode === 9) {
var value = $(this).val();
var start = this.selectionStart;
var end = this.selectionEnd;
// IE8 does not support selection attributes
if(start === undefined || end === undefined) {
return;
}
$(this).val(value.substring(0, start) + "\t" + value.substring(end));
this.selectionStart = this.selectionEnd = start + 1;
e.preventDefault();
}
});
$(document).click(function(e) {
$(".tooltip-template").tooltip('hide');
});
// Tooltips
$(".tooltip-lazy-rendering").tooltip({
container: '#modal-settings',
placement: 'right',
title: 'Disable preview rendering while typing in order to offload CPU. Refresh preview after 500 ms of inactivity.'
});
$(".tooltip-default-content").tooltip({
html: true,
container: '#modal-settings',
placement: 'right',
title: 'Thanks for supporting StackEdit by adding a backlink in your documents!'
});
$(".tooltip-template").tooltip({
html: true,
container: '#modal-settings',
placement: 'right',
trigger: 'manual',
title: [
'Available variables:<br>',
'<ul>',
' <li><b>documentTitle</b>: document title</li>',
' <li><b>documentMarkdown</b>: document in Markdown format</li>',
' <li><b>documentHTML</b>: document in HTML format</li>',
' <li><b>publishAttributes</b>: attributes of the publish location (undefined when using "Save")</li>',
'</ul>',
'Examples:<br />',
_.escape('<title><%= documentTitle %></title>'),
'<br />',
_.escape('<div><%- documentHTML %></div>'),
'<br />',
_.escape('<% if(publishAttributes.provider == "github") print(documentMarkdown); %>'),
'<br /><br />',
'<a target="_blank" href="http://underscorejs.org/#template">More info</a>',
].join("")
}).click(function(e) {
$(this).tooltip('show');
e.stopPropagation();
});
// Reset inputs
$(".action-reset-input").click(function() {
utils.resetModalInputs();
});
$(document).click(function(e) {
$(".tooltip-template").tooltip('hide');
});
// Do periodic tasks
intervalId = window.setInterval(function() {
utils.updateCurrentTime();
checkWindowUnique();
if(isUserActive() === true || viewerMode === true) {
_.each(periodicCallbacks, function(callback) {
callback();
});
checkOnline();
}
}, 1000);
});
// Reset inputs
$(".action-reset-input").click(function() {
utils.resetModalInputs();
});
return core;
// Do periodic tasks
intervalId = window.setInterval(function() {
utils.updateCurrentTime();
checkWindowUnique();
if(isUserActive() === true || viewerMode === true) {
_.each(periodicCallbacks, function(callback) {
callback();
});
checkOnline();
}
}, 1000);
});
return core;
});

View File

@ -4,45 +4,47 @@ define([
"async-runner"
], function($, core, asyncRunner) {
var PROVIDER_DOWNLOAD = "download";
var PROVIDER_DOWNLOAD = "download";
var downloadProvider = {
providerId: PROVIDER_DOWNLOAD,
sharingAttributes: ["url"]
};
var downloadProvider = {
providerId: PROVIDER_DOWNLOAD,
sharingAttributes: [
"url"
]
};
downloadProvider.importPublic = function(importParameters, callback) {
var task = asyncRunner.createTask();
var title = undefined;
var content = undefined;
task.onRun(function() {
var url = importParameters.url;
var slashUrl = url.lastIndexOf("/");
if(slashUrl === -1) {
task.error(new Error("Invalid URL parameter."));
return;
}
title = url.substring(slashUrl + 1);
$.ajax({
url : DOWNLOAD_PROXY_URL + "download?url=" + url,
type: "GET",
dataType : "text",
timeout : AJAX_TIMEOUT
}).done(function(result, textStatus, jqXHR) {
content = result;
task.chain();
}).fail(function(jqXHR) {
task.error(new Error("Unable to access URL " + url));
});
});
task.onSuccess(function() {
callback(undefined, title, content);
});
task.onError(function(error) {
callback(error);
});
asyncRunner.addTask(task);
};
downloadProvider.importPublic = function(importParameters, callback) {
var task = asyncRunner.createTask();
var title = undefined;
var content = undefined;
task.onRun(function() {
var url = importParameters.url;
var slashUrl = url.lastIndexOf("/");
if(slashUrl === -1) {
task.error(new Error("Invalid URL parameter."));
return;
}
title = url.substring(slashUrl + 1);
$.ajax({
url: DOWNLOAD_PROXY_URL + "download?url=" + url,
type: "GET",
dataType: "text",
timeout: AJAX_TIMEOUT
}).done(function(result, textStatus, jqXHR) {
content = result;
task.chain();
}).fail(function(jqXHR) {
task.error(new Error("Unable to access URL " + url));
});
});
task.onSuccess(function() {
callback(undefined, title, content);
});
task.onError(function(error) {
callback(error);
});
asyncRunner.addTask(task);
};
return downloadProvider;
return downloadProvider;
});

View File

@ -6,323 +6,330 @@ define([
"async-runner"
], function($, _, core, extensionMgr, asyncRunner) {
var client = undefined;
var authenticated = false;
var client = undefined;
var authenticated = false;
var dropboxHelper = {};
var dropboxHelper = {};
// Try to connect dropbox by downloading client.js
function connect(task) {
task.onRun(function() {
if(core.isOffline === true) {
client = undefined;
task.error(new Error("Operation not available in offline mode.|stopPublish"));
return;
}
if (client !== undefined) {
task.chain();
return;
}
$.ajax({
url : "lib/dropbox.min.js",
dataType : "script", timeout : AJAX_TIMEOUT
}).done(function() {
client = new Dropbox.Client({
key: DROPBOX_APP_KEY,
secret: DROPBOX_APP_SECRET
});
client.authDriver(new Dropbox.Drivers.Popup({
receiverUrl: BASE_URL + "dropbox-oauth-receiver.html",
rememberUser: true
}));
task.chain();
}).fail(function(jqXHR) {
var error = {
status: jqXHR.status,
responseText: jqXHR.statusText
};
handleError(error, task);
});
});
}
// Try to connect dropbox by downloading client.js
function connect(task) {
task.onRun(function() {
if(core.isOffline === true) {
client = undefined;
task.error(new Error("Operation not available in offline mode.|stopPublish"));
return;
}
if(client !== undefined) {
task.chain();
return;
}
$.ajax({
url: "lib/dropbox.min.js",
dataType: "script",
timeout: AJAX_TIMEOUT
}).done(function() {
client = new Dropbox.Client({
key: DROPBOX_APP_KEY,
secret: DROPBOX_APP_SECRET
});
client.authDriver(new Dropbox.Drivers.Popup({
receiverUrl: BASE_URL + "dropbox-oauth-receiver.html",
rememberUser: true
}));
task.chain();
}).fail(function(jqXHR) {
var error = {
status: jqXHR.status,
responseText: jqXHR.statusText
};
handleError(error, task);
});
});
}
// Try to authenticate with Oauth
function authenticate(task) {
task.onRun(function() {
if (authenticated === true) {
task.chain();
return;
}
var immediate = true;
function localAuthenticate() {
if (immediate === false) {
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;
}
client.reset();
client.authenticate({interactive: !immediate}, function(error, client) {
// Success
if (client.authState === Dropbox.Client.DONE) {
authenticated = true;
task.chain();
return;
}
// If immediate did not work retry without immediate flag
if (immediate === true) {
immediate = false;
task.chain(localAuthenticate);
return;
}
// Error
task.error(new Error("Access to Dropbox account is not authorized."));
});
}
task.chain(localAuthenticate);
});
}
// Try to authenticate with Oauth
function authenticate(task) {
task.onRun(function() {
if(authenticated === true) {
task.chain();
return;
}
var immediate = true;
function localAuthenticate() {
if(immediate === false) {
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;
}
client.reset();
client.authenticate({
interactive: !immediate
}, function(error, client) {
// Success
if(client.authState === Dropbox.Client.DONE) {
authenticated = true;
task.chain();
return;
}
// If immediate did not work retry without immediate flag
if(immediate === true) {
immediate = false;
task.chain(localAuthenticate);
return;
}
// Error
task.error(new Error("Access to Dropbox account is not authorized."));
});
}
task.chain(localAuthenticate);
});
}
dropboxHelper.upload = function(path, content, callback) {
var result = undefined;
var task = asyncRunner.createTask();
connect(task);
authenticate(task);
task.onRun(function() {
client.writeFile(path, content, function(error, stat) {
if (!error) {
result = stat;
task.chain();
return;
}
// Handle error
if(error.status === 400) {
error = 'Could not upload document into path "' + path + '".';
}
handleError(error, task);
});
});
task.onSuccess(function() {
callback(undefined, result);
});
task.onError(function(error) {
callback(error);
});
asyncRunner.addTask(task);
};
dropboxHelper.upload = function(path, content, callback) {
var result = undefined;
var task = asyncRunner.createTask();
connect(task);
authenticate(task);
task.onRun(function() {
client.writeFile(path, content, function(error, stat) {
if(!error) {
result = stat;
task.chain();
return;
}
// Handle error
if(error.status === 400) {
error = 'Could not upload document into path "' + path + '".';
}
handleError(error, task);
});
});
task.onSuccess(function() {
callback(undefined, result);
});
task.onError(function(error) {
callback(error);
});
asyncRunner.addTask(task);
};
dropboxHelper.checkChanges = function(lastChangeId, callback) {
var changes = [];
var newChangeId = lastChangeId || 0;
var task = asyncRunner.createTask();
connect(task);
authenticate(task);
task.onRun(function() {
function retrievePageOfChanges() {
client.pullChanges(newChangeId, function(error, pullChanges) {
if (error) {
handleError(error, task);
return;
}
// Retrieve success
newChangeId = pullChanges.cursor();
if(pullChanges.changes !== undefined) {
changes = changes.concat(pullChanges.changes);
}
if (pullChanges.shouldPullAgain) {
task.chain(retrievePageOfChanges);
} else {
task.chain();
}
});
}
task.chain(retrievePageOfChanges);
});
task.onSuccess(function() {
callback(undefined, changes, newChangeId);
});
task.onError(function(error) {
callback(error);
});
asyncRunner.addTask(task);
};
dropboxHelper.checkChanges = function(lastChangeId, callback) {
var changes = [];
var newChangeId = lastChangeId || 0;
var task = asyncRunner.createTask();
connect(task);
authenticate(task);
task.onRun(function() {
function retrievePageOfChanges() {
client.pullChanges(newChangeId, function(error, pullChanges) {
if(error) {
handleError(error, task);
return;
}
// Retrieve success
newChangeId = pullChanges.cursor();
if(pullChanges.changes !== undefined) {
changes = changes.concat(pullChanges.changes);
}
if(pullChanges.shouldPullAgain) {
task.chain(retrievePageOfChanges);
}
else {
task.chain();
}
});
}
task.chain(retrievePageOfChanges);
});
task.onSuccess(function() {
callback(undefined, changes, newChangeId);
});
task.onError(function(error) {
callback(error);
});
asyncRunner.addTask(task);
};
dropboxHelper.downloadMetadata = function(paths, callback) {
var result = [];
var task = asyncRunner.createTask();
connect(task);
authenticate(task);
task.onRun(function() {
function recursiveDownloadMetadata() {
if(paths.length === 0) {
task.chain();
return;
}
var path = paths[0];
client.stat(path, function(error, stat) {
if(stat) {
result.push(stat);
paths.shift();
task.chain(recursiveDownloadMetadata);
return;
}
handleError(error, task);
});
}
task.chain(recursiveDownloadMetadata);
});
task.onSuccess(function() {
callback(undefined, result);
});
task.onError(function(error) {
callback(error);
});
asyncRunner.addTask(task);
};
dropboxHelper.downloadMetadata = function(paths, callback) {
var result = [];
var task = asyncRunner.createTask();
connect(task);
authenticate(task);
task.onRun(function() {
function recursiveDownloadMetadata() {
if(paths.length === 0) {
task.chain();
return;
}
var path = paths[0];
client.stat(path, function(error, stat) {
if(stat) {
result.push(stat);
paths.shift();
task.chain(recursiveDownloadMetadata);
return;
}
handleError(error, task);
});
}
task.chain(recursiveDownloadMetadata);
});
task.onSuccess(function() {
callback(undefined, result);
});
task.onError(function(error) {
callback(error);
});
asyncRunner.addTask(task);
};
dropboxHelper.downloadContent = function(objects, callback) {
var result = [];
var task = asyncRunner.createTask();
connect(task);
authenticate(task);
task.onRun(function() {
function recursiveDownloadContent() {
if(objects.length === 0) {
task.chain();
return;
}
var object = objects[0];
result.push(object);
var file = undefined;
// object may be a file
if(object.isFile === true) {
file = object;
}
// object may be a change
else if(object.wasRemoved !== undefined) {
file = object.stat;
}
if(!file) {
objects.shift();
task.chain(recursiveDownloadContent);
return;
}
client.readFile(file.path, function(error, data) {
if(data) {
file.content = data;
objects.shift();
task.chain(recursiveDownloadContent);
return;
}
handleError(error, task);
});
}
task.chain(recursiveDownloadContent);
});
task.onSuccess(function() {
callback(undefined, result);
});
task.onError(function(error) {
callback(error);
});
asyncRunner.addTask(task);
};
dropboxHelper.downloadContent = function(objects, callback) {
var result = [];
var task = asyncRunner.createTask();
connect(task);
authenticate(task);
task.onRun(function() {
function recursiveDownloadContent() {
if(objects.length === 0) {
task.chain();
return;
}
var object = objects[0];
result.push(object);
var file = undefined;
// object may be a file
if(object.isFile === true) {
file = object;
}
// object may be a change
else if(object.wasRemoved !== undefined) {
file = object.stat;
}
if(!file) {
objects.shift();
task.chain(recursiveDownloadContent);
return;
}
client.readFile(file.path, function(error, data) {
if(data) {
file.content = data;
objects.shift();
task.chain(recursiveDownloadContent);
return;
}
handleError(error, task);
});
}
task.chain(recursiveDownloadContent);
});
task.onSuccess(function() {
callback(undefined, result);
});
task.onError(function(error) {
callback(error);
});
asyncRunner.addTask(task);
};
function handleError(error, task) {
var errorMsg = true;
if (error) {
logger.error(error);
// Try to analyze the error
if (typeof error === "string") {
errorMsg = error;
}
else {
errorMsg = "Dropbox error ("
+ error.status + ": " + error.responseText + ").";
function handleError(error, task) {
var errorMsg = true;
if(error) {
logger.error(error);
// Try to analyze the error
if(typeof error === "string") {
errorMsg = error;
}
else {
errorMsg = "Dropbox error (" + error.status + ": " + error.responseText + ").";
if (error.status === 401 || error.status === 403) {
authenticated = false;
errorMsg = "Access to Dropbox account is not authorized.";
task.retry(new Error(errorMsg), 1);
return;
} else if(error.status === 400 && error.responseText
.indexOf("oauth_nonce") !== -1) {
// A bug I guess...
_.each(_.keys(localStorage), function(key) {
// We have to remove the Oauth cache from the localStorage
if(key.indexOf("dropbox-auth") === 0) {
localStorage.removeItem(key);
}
});
authenticated = false;
task.retry(new Error(errorMsg), 1);
return;
} else if (error.status <= 0) {
client = undefined;
authenticated = false;
core.setOffline();
errorMsg = "|stopPublish";
}
}
}
task.error(new Error(errorMsg));
}
if(error.status === 401 || error.status === 403) {
authenticated = false;
errorMsg = "Access to Dropbox account is not authorized.";
task.retry(new Error(errorMsg), 1);
return;
}
else if(error.status === 400 && error.responseText.indexOf("oauth_nonce") !== -1) {
// A bug I guess...
_.each(_.keys(localStorage), function(key) {
// We have to remove the Oauth cache from the
// localStorage
if(key.indexOf("dropbox-auth") === 0) {
localStorage.removeItem(key);
}
});
authenticated = false;
task.retry(new Error(errorMsg), 1);
return;
}
else if(error.status <= 0) {
client = undefined;
authenticated = false;
core.setOffline();
errorMsg = "|stopPublish";
}
}
}
task.error(new Error(errorMsg));
}
var pickerLoaded = false;
function loadPicker(task) {
task.onRun(function() {
if (pickerLoaded === true) {
task.chain();
return;
}
$.ajax({
url : "https://www.dropbox.com/static/api/1/dropbox.js",
dataType : "script", timeout : AJAX_TIMEOUT
}).done(function() {
pickerLoaded = true;
task.chain();
}).fail(function(jqXHR) {
var error = {
status: jqXHR.status,
responseText: jqXHR.statusText
};
handleError(error, task);
});
});
}
var pickerLoaded = false;
function loadPicker(task) {
task.onRun(function() {
if(pickerLoaded === true) {
task.chain();
return;
}
$.ajax({
url: "https://www.dropbox.com/static/api/1/dropbox.js",
dataType: "script",
timeout: AJAX_TIMEOUT
}).done(function() {
pickerLoaded = true;
task.chain();
}).fail(function(jqXHR) {
var error = {
status: jqXHR.status,
responseText: jqXHR.statusText
};
handleError(error, task);
});
});
}
dropboxHelper.picker = function(callback) {
var paths = [];
var task = asyncRunner.createTask();
// Add some time for user to choose his files
task.timeout = ASYNC_TASK_LONG_TIMEOUT;
connect(task);
loadPicker(task);
task.onRun(function() {
var options = {};
options.multiselect = true;
options.linkType = "direct";
options.success = function(files) {
for(var i=0; i<files.length; i++) {
var path = files[i].link;
path = path.replace(/.*\/view\/[^\/]*/, "");
paths.push(decodeURI(path));
}
task.chain();
dropboxHelper.picker = function(callback) {
var paths = [];
var task = asyncRunner.createTask();
// Add some time for user to choose his files
task.timeout = ASYNC_TASK_LONG_TIMEOUT;
connect(task);
loadPicker(task);
task.onRun(function() {
var options = {};
options.multiselect = true;
options.linkType = "direct";
options.success = function(files) {
for ( var i = 0; i < files.length; i++) {
var path = files[i].link;
path = path.replace(/.*\/view\/[^\/]*/, "");
paths.push(decodeURI(path));
}
task.chain();
};
options.cancel = function() {
task.chain();
task.chain();
};
Dropbox.choose(options);
extensionMgr.onMessage("Please make sure the Dropbox chooser popup is not blocked by your browser.");
});
task.onSuccess(function() {
callback(undefined, paths);
});
task.onError(function(error) {
callback(error);
});
asyncRunner.addTask(task);
};
Dropbox.choose(options);
extensionMgr.onMessage("Please make sure the Dropbox chooser popup is not blocked by your browser.");
});
task.onSuccess(function() {
callback(undefined, paths);
});
task.onError(function(error) {
callback(error);
});
asyncRunner.addTask(task);
};
return dropboxHelper;
return dropboxHelper;
});

View File

@ -6,231 +6,232 @@ define([
"dropbox-helper"
], function(_, utils, extensionMgr, fileMgr, dropboxHelper) {
var PROVIDER_DROPBOX = "dropbox";
var PROVIDER_DROPBOX = "dropbox";
var dropboxProvider = {
providerId: PROVIDER_DROPBOX,
providerName: "Dropbox",
defaultPublishFormat: "template"
};
var dropboxProvider = {
providerId: PROVIDER_DROPBOX,
providerName: "Dropbox",
defaultPublishFormat: "template"
};
function checkPath(path) {
if(path === undefined) {
return undefined;
}
if(!path.match(/^[^\\<>:"\|?\*]+$/)) {
extensionMgr.onError('"' + path + '" contains invalid characters.');
return undefined;
}
if(path.indexOf("/") !== 0) {
return "/" + path;
}
return path;
}
function checkPath(path) {
if(path === undefined) {
return undefined;
}
if(!path.match(/^[^\\<>:"\|?\*]+$/)) {
extensionMgr.onError('"' + path + '" contains invalid characters.');
return undefined;
}
if(path.indexOf("/") !== 0) {
return "/" + path;
}
return path;
}
function createSyncIndex(path) {
return "sync." + PROVIDER_DROPBOX + "." + encodeURIComponent(path.toLowerCase());
}
function createSyncIndex(path) {
return "sync." + PROVIDER_DROPBOX + "." + encodeURIComponent(path.toLowerCase());
}
function createSyncAttributes(path, versionTag, content) {
var syncAttributes = {};
syncAttributes.provider = dropboxProvider;
syncAttributes.path = path;
syncAttributes.version = versionTag;
syncAttributes.contentCRC = utils.crc32(content);
syncAttributes.syncIndex = createSyncIndex(path);
utils.storeAttributes(syncAttributes);
return syncAttributes;
}
function createSyncAttributes(path, versionTag, content) {
var syncAttributes = {};
syncAttributes.provider = dropboxProvider;
syncAttributes.path = path;
syncAttributes.version = versionTag;
syncAttributes.contentCRC = utils.crc32(content);
syncAttributes.syncIndex = createSyncIndex(path);
utils.storeAttributes(syncAttributes);
return syncAttributes;
}
function importFilesFromPaths(paths) {
dropboxHelper.downloadMetadata(paths, function(error, result) {
if(error) {
return;
}
dropboxHelper.downloadContent(result, function(error, result) {
if(error) {
return;
}
var fileDescList = [];
_.each(result, function(file) {
var syncAttributes = createSyncAttributes(file.path, file.versionTag, file.content);
var syncLocations = {};
syncLocations[syncAttributes.syncIndex] = syncAttributes;
var fileDesc = fileMgr.createFile(file.name, file.content, syncLocations);
fileMgr.selectFile(fileDesc);
fileDescList.push(fileDesc);
});
extensionMgr.onSyncImportSuccess(fileDescList, dropboxProvider);
});
});
}
function importFilesFromPaths(paths) {
dropboxHelper.downloadMetadata(paths, function(error, result) {
if(error) {
return;
}
dropboxHelper.downloadContent(result, function(error, result) {
if(error) {
return;
}
var fileDescList = [];
_.each(result, function(file) {
var syncAttributes = createSyncAttributes(file.path, file.versionTag, file.content);
var syncLocations = {};
syncLocations[syncAttributes.syncIndex] = syncAttributes;
var fileDesc = fileMgr.createFile(file.name, file.content, syncLocations);
fileMgr.selectFile(fileDesc);
fileDescList.push(fileDesc);
});
extensionMgr.onSyncImportSuccess(fileDescList, dropboxProvider);
});
});
}
dropboxProvider.importFiles = function() {
dropboxHelper.picker(function(error, paths) {
if(error || paths.length === 0) {
return;
}
var importPaths = [];
_.each(paths, function(path) {
var syncIndex = createSyncIndex(path);
var fileDesc = fileMgr.getFileFromSyncIndex(syncIndex);
if(fileDesc !== undefined) {
extensionMgr.onError('"' + fileDesc.title + '" was already imported');
return;
}
importPaths.push(path);
});
importFilesFromPaths(importPaths);
});
};
dropboxProvider.importFiles = function() {
dropboxHelper.picker(function(error, paths) {
if(error || paths.length === 0) {
return;
}
var importPaths = [];
_.each(paths, function(path) {
var syncIndex = createSyncIndex(path);
var fileDesc = fileMgr.getFileFromSyncIndex(syncIndex);
if(fileDesc !== undefined) {
extensionMgr.onError('"' + fileDesc.title + '" was already imported');
return;
}
importPaths.push(path);
});
importFilesFromPaths(importPaths);
});
};
function exportFileToPath(path, title, content, callback) {
path = checkPath(path);
if(path === undefined) {
callback(true);
return;
}
// Check that file is not synchronized with an other one
var syncIndex = createSyncIndex(path);
var fileDesc = fileMgr.getFileFromSyncIndex(syncIndex);
if(fileDesc !== undefined) {
var existingTitle = fileDesc.title;
extensionMgr.onError('File path is already synchronized with "' + existingTitle + '"');
callback(true);
return;
}
dropboxHelper.upload(path, content, function(error, result) {
if (error) {
callback(error);
return;
}
var syncAttributes = createSyncAttributes(result.path, result.versionTag, content);
callback(undefined, syncAttributes);
});
}
function exportFileToPath(path, title, content, callback) {
path = checkPath(path);
if(path === undefined) {
callback(true);
return;
}
// Check that file is not synchronized with an other one
var syncIndex = createSyncIndex(path);
var fileDesc = fileMgr.getFileFromSyncIndex(syncIndex);
if(fileDesc !== undefined) {
var existingTitle = fileDesc.title;
extensionMgr.onError('File path is already synchronized with "' + existingTitle + '"');
callback(true);
return;
}
dropboxHelper.upload(path, content, function(error, result) {
if(error) {
callback(error);
return;
}
var syncAttributes = createSyncAttributes(result.path, result.versionTag, content);
callback(undefined, syncAttributes);
});
}
dropboxProvider.exportFile = function(event, title, content, callback) {
var path = utils.getInputTextValue("#input-sync-export-dropbox-path", event);
exportFileToPath(path, title, content, callback);
};
dropboxProvider.exportFile = function(event, title, content, callback) {
var path = utils.getInputTextValue("#input-sync-export-dropbox-path", event);
exportFileToPath(path, title, content, callback);
};
dropboxProvider.exportManual = function(event, title, content, callback) {
var path = utils.getInputTextValue("#input-sync-manual-dropbox-path", event);
exportFileToPath(path, title, content, callback);
};
dropboxProvider.exportManual = function(event, title, content, callback) {
var path = utils.getInputTextValue("#input-sync-manual-dropbox-path", event);
exportFileToPath(path, title, content, callback);
};
dropboxProvider.syncUp = function(uploadContent, uploadContentCRC, uploadTitle, uploadTitleCRC, syncAttributes, callback) {
var syncContentCRC = syncAttributes.contentCRC;
// Skip if CRC has not changed
if(uploadContentCRC == syncContentCRC) {
callback(undefined, false);
return;
}
dropboxHelper.upload(syncAttributes.path, uploadContent, function(error, result) {
if(error) {
callback(error, true);
return;
}
syncAttributes.version = result.versionTag;
syncAttributes.contentCRC = uploadContentCRC;
callback(undefined, true);
});
};
dropboxProvider.syncUp = function(uploadContent, uploadContentCRC, uploadTitle, uploadTitleCRC, syncAttributes, callback) {
var syncContentCRC = syncAttributes.contentCRC;
// Skip if CRC has not changed
if(uploadContentCRC == syncContentCRC) {
callback(undefined, false);
return;
}
dropboxHelper.upload(syncAttributes.path, uploadContent, function(error, result) {
if(error) {
callback(error, true);
return;
}
syncAttributes.version = result.versionTag;
syncAttributes.contentCRC = uploadContentCRC;
callback(undefined, true);
});
};
dropboxProvider.syncDown = function(callback) {
var lastChangeId = localStorage[PROVIDER_DROPBOX + ".lastChangeId"];
dropboxHelper.checkChanges(lastChangeId, function(error, changes, newChangeId) {
if (error) {
callback(error);
return;
}
var interestingChanges = [];
_.each(changes, function(change) {
var syncIndex = createSyncIndex(change.path);
var syncAttributes = fileMgr.getSyncAttributes(syncIndex);
if(syncAttributes === undefined) {
return;
}
// Store syncAttributes to avoid 2 times searching
change.syncAttributes = syncAttributes;
// Delete
if(change.wasRemoved === true) {
interestingChanges.push(change);
return;
}
// Modify
if(syncAttributes.version != change.stat.versionTag) {
interestingChanges.push(change);
}
});
dropboxHelper.downloadContent(interestingChanges, function(error, changes) {
if (error) {
callback(error);
return;
}
_.each(changes, function(change) {
var syncAttributes = change.syncAttributes;
var syncIndex = syncAttributes.syncIndex;
var fileDesc = fileMgr.getFileFromSyncIndex(syncIndex);
// No file corresponding (file may have been deleted locally)
if(fileDesc === undefined) {
return;
}
var localTitle = fileDesc.title;
// File deleted
if (change.wasRemoved === true) {
extensionMgr.onError('"' + localTitle + '" has been removed from Dropbox.');
fileMgr.removeSync(syncAttributes);
return;
}
var localContent = fileDesc.getContent();
var localContentChanged = syncAttributes.contentCRC != utils.crc32(localContent);
var file = change.stat;
dropboxProvider.syncDown = function(callback) {
var lastChangeId = localStorage[PROVIDER_DROPBOX + ".lastChangeId"];
dropboxHelper.checkChanges(lastChangeId, function(error, changes, newChangeId) {
if(error) {
callback(error);
return;
}
var interestingChanges = [];
_.each(changes, function(change) {
var syncIndex = createSyncIndex(change.path);
var syncAttributes = fileMgr.getSyncAttributes(syncIndex);
if(syncAttributes === undefined) {
return;
}
// Store syncAttributes to avoid 2 times searching
change.syncAttributes = syncAttributes;
// Delete
if(change.wasRemoved === true) {
interestingChanges.push(change);
return;
}
// Modify
if(syncAttributes.version != change.stat.versionTag) {
interestingChanges.push(change);
}
});
dropboxHelper.downloadContent(interestingChanges, function(error, changes) {
if(error) {
callback(error);
return;
}
_.each(changes, function(change) {
var syncAttributes = change.syncAttributes;
var syncIndex = syncAttributes.syncIndex;
var fileDesc = fileMgr.getFileFromSyncIndex(syncIndex);
// No file corresponding (file may have been deleted
// locally)
if(fileDesc === undefined) {
return;
}
var localTitle = fileDesc.title;
// File deleted
if(change.wasRemoved === true) {
extensionMgr.onError('"' + localTitle + '" has been removed from Dropbox.');
fileMgr.removeSync(syncAttributes);
return;
}
var localContent = fileDesc.content;
var localContentChanged = syncAttributes.contentCRC != utils.crc32(localContent);
var file = change.stat;
var remoteContentCRC = utils.crc32(file.content);
var remoteContentChanged = syncAttributes.contentCRC != remoteContentCRC;
var fileContentChanged = localContent != file.content;
// Conflict detection
if (fileContentChanged === true && localContentChanged === true && remoteContentChanged === true) {
fileMgr.createFile(localTitle + " (backup)", localContent);
extensionMgr.onMessage('Conflict detected on "' + localTitle + '". A backup has been created locally.');
}
// If file content changed
if(fileContentChanged && remoteContentChanged === true) {
fileDesc.setContent(file.content);
extensionMgr.onMessage('"' + localTitle + '" has been updated from Dropbox.');
if(fileMgr.isCurrentFile(fileDesc)) {
fileMgr.selectFile(); // Refresh editor
}
}
// Update syncAttributes
syncAttributes.version = file.versionTag;
syncAttributes.contentCRC = remoteContentCRC;
utils.storeAttributes(syncAttributes);
});
localStorage[PROVIDER_DROPBOX + ".lastChangeId"] = newChangeId;
callback();
});
});
};
var fileContentChanged = localContent != file.content;
// Conflict detection
if(fileContentChanged === true && localContentChanged === true && remoteContentChanged === true) {
fileMgr.createFile(localTitle + " (backup)", localContent);
extensionMgr.onMessage('Conflict detected on "' + localTitle + '". A backup has been created locally.');
}
// If file content changed
if(fileContentChanged && remoteContentChanged === true) {
fileDesc.content = file.content;
extensionMgr.onMessage('"' + localTitle + '" has been updated from Dropbox.');
if(fileMgr.isCurrentFile(fileDesc)) {
fileMgr.selectFile(); // Refresh editor
}
}
// Update syncAttributes
syncAttributes.version = file.versionTag;
syncAttributes.contentCRC = remoteContentCRC;
utils.storeAttributes(syncAttributes);
});
localStorage[PROVIDER_DROPBOX + ".lastChangeId"] = newChangeId;
callback();
});
});
};
dropboxProvider.publish = function(publishAttributes, title, content, callback) {
var path = checkPath(publishAttributes.path);
if(path === undefined) {
callback(true);
return;
}
dropboxHelper.upload(path, content, callback);
};
dropboxProvider.publish = function(publishAttributes, title, content, callback) {
var path = checkPath(publishAttributes.path);
if(path === undefined) {
callback(true);
return;
}
dropboxHelper.upload(path, content, callback);
};
dropboxProvider.newPublishAttributes = function(event) {
var publishAttributes = {};
publishAttributes.path = utils.getInputTextValue("#input-publish-dropbox-path", event);
if(event.isPropagationStopped()) {
return undefined;
}
return publishAttributes;
};
dropboxProvider.newPublishAttributes = function(event) {
var publishAttributes = {};
publishAttributes.path = utils.getInputTextValue("#input-publish-dropbox-path", event);
if(event.isPropagationStopped()) {
return undefined;
}
return publishAttributes;
};
return dropboxProvider;
return dropboxProvider;
});

View File

@ -1,4 +1,4 @@
define( [
define([
"jquery",
"underscore",
"utils",
@ -21,166 +21,161 @@ define( [
"lib/bootstrap"
], function($, _, utils, settings) {
var extensionMgr = {};
var extensionMgr = {};
// Create a list of extensions
var extensionList = _.chain(
arguments
).map(function(argument) {
return _.isObject(argument) && argument.extensionId && argument;
}).compact().value();
// Create a list of extensions
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 every named callbacks implemented in extensions
function getExtensionCallbackList(hookName) {
return _.chain(extensionList).map(function(extension) {
return extension.config.enabled && extension[hookName];
}).compact().value();
}
// Return a function that calls every callbacks from extensions
function createHook(hookName) {
var callbackList = getExtensionCallbackList(hookName);
return function() {
logger.debug(hookName, arguments);
var callbackArguments = arguments;
_.each(callbackList, function(callback) {
callback.apply(null, callbackArguments);
});
};
}
// Return a function that calls every callbacks from extensions
function createHook(hookName) {
var callbackList = getExtensionCallbackList(hookName);
return function() {
logger.debug(hookName, arguments);
var callbackArguments = arguments;
_.each(callbackList, function(callback) {
callback.apply(null, callbackArguments);
});
};
}
// Add a Hook to the extensionMgr
function addHook(hookName) {
extensionMgr[hookName] = createHook(hookName);
}
// Add a Hook to the extensionMgr
function addHook(hookName) {
extensionMgr[hookName] = createHook(hookName);
}
// 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;
});
// 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() {
logger.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) {
logger.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;
});
};
// Load/Save extension config from/to settings
extensionMgr["onLoadSettings"] = function() {
logger.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) {
logger.debug("onSaveSettings");
_.each(extensionList, function(extension) {
var newExtensionConfig = _.extend({}, extension.defaultConfig);
newExtensionConfig.enabled = utils.getInputChecked("#input-enable-extension-" + extension.extensionId);
var onSaveSettingsCallback = extension.onSaveSettings;
onSaveSettingsCallback && onSaveSettingsCallback(newExtensionConfig, event);
newExtensionSettings[extension.extensionId] = newExtensionConfig;
});
};
addHook("onReady");
addHook("onMessage");
addHook("onError");
addHook("onOfflineChanged");
addHook("onAsyncRunning");
addHook("onReady");
addHook("onMessage");
addHook("onError");
addHook("onOfflineChanged");
addHook("onAsyncRunning");
// To access modules that are loaded after extensions
addHook("onFileMgrCreated");
addHook("onSynchronizerCreated");
addHook("onPublisherCreated");
// To access modules that are loaded after extensions
addHook("onFileMgrCreated");
addHook("onSynchronizerCreated");
addHook("onPublisherCreated");
// Operations on files
addHook("onFileCreated");
addHook("onFileDeleted");
addHook("onFileSelected");
addHook("onContentChanged");
addHook("onTitleChanged");
// Operations on files
addHook("onFileCreated");
addHook("onFileDeleted");
addHook("onFileSelected");
addHook("onContentChanged");
addHook("onTitleChanged");
// Sync events
addHook("onSyncRunning");
addHook("onSyncSuccess");
addHook("onSyncImportSuccess");
addHook("onSyncExportSuccess");
addHook("onSyncRemoved");
// Sync events
addHook("onSyncRunning");
addHook("onSyncSuccess");
addHook("onSyncImportSuccess");
addHook("onSyncExportSuccess");
addHook("onSyncRemoved");
// Publish events
addHook("onPublishRunning");
addHook("onPublishSuccess");
addHook("onNewPublishSuccess");
addHook("onPublishRemoved");
// Publish events
addHook("onPublishRunning");
addHook("onPublishSuccess");
addHook("onNewPublishSuccess");
addHook("onPublishRemoved");
// Operations on Layout
addHook("onLayoutConfigure");
addHook("onLayoutCreated");
// Operations on Layout
addHook("onLayoutConfigure");
addHook("onLayoutCreated");
// Operations on PageDown
addHook("onEditorConfigure");
// Operations on PageDown
addHook("onEditorConfigure");
var onPreviewFinished = createHook("onPreviewFinished");
var onAsyncPreviewCallbackList = getExtensionCallbackList("onAsyncPreview");
extensionMgr["onAsyncPreview"] = function() {
logger.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();
};
var onPreviewFinished = createHook("onPreviewFinished");
var onAsyncPreviewCallbackList = getExtensionCallbackList("onAsyncPreview");
extensionMgr["onAsyncPreview"] = function() {
logger.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();
};
var accordionTmpl = [
var accordionTmpl = [
'<div class="accordion-group">',
'<div class="accordion-heading">',
'<label class="checkbox pull-right">',
'<input id="input-enable-extension-<%= extensionId %>" type="checkbox" <% if(!optional) { %> disabled <% } %>> enabled',
'</label>',
'<a id="accordion-toggle-test" data-toggle="collapse" data-parent="#accordion-extensions" class="accordion-toggle" href="#collapse-<%= extensionId %>">',
'<%= extensionName %>',
'</a>',
'</div>',
'<div id="collapse-<%= extensionId %>" class="accordion-body collapse">',
'<div class="accordion-inner"><%= settingsBloc %></div>',
'</div>',
'</div>'].join("");
' <div class="accordion-heading">',
' <label class="checkbox pull-right">',
' <input id="input-enable-extension-<%= extensionId %>" type="checkbox" <% if(!optional) { %> disabled <% } %>> enabled',
' </label>',
' <a id="accordion-toggle-test" data-toggle="collapse" data-parent="#accordion-extensions" class="accordion-toggle" href="#collapse-<%= extensionId %>">',
' <%= extensionName %>',
' </a>',
' </div>',
' <div id="collapse-<%= extensionId %>" class="accordion-body collapse">',
' <div class="accordion-inner"><%= settingsBloc %></div>',
' </div>',
'</div>'
].join("");
function createSettings(extension) {
$("#accordion-extensions").append($(_.template(accordionTmpl, {
extensionId: extension.extensionId,
extensionName: extension.extensionName,
optional: extension.optional,
settingsBloc: extension.settingsBloc
})));
}
function createSettings(extension) {
$("#accordion-extensions").append($(_.template(accordionTmpl, {
extensionId: extension.extensionId,
extensionName: extension.extensionName,
optional: extension.optional,
settingsBloc: extension.settingsBloc
})));
}
$(function() {
// Create accordion in settings dialog
_.chain(
extensionList
).sortBy(function(extension) {
return extension.extensionName.toLowerCase();
}).each(createSettings);
$(function() {
// Create accordion in settings dialog
_.chain(extensionList).sortBy(function(extension) {
return extension.extensionName.toLowerCase();
}).each(createSettings);
// Create extension buttons
logger.debug("onCreateButton");
var onCreateButtonCallbackList = getExtensionCallbackList("onCreateButton");
_.each(onCreateButtonCallbackList, function(callback) {
$("#extension-buttons").append($('<div class="btn-group">').append(callback()));
});
// Create extension buttons
logger.debug("onCreateButton");
var onCreateButtonCallbackList = getExtensionCallbackList("onCreateButton");
_.each(onCreateButtonCallbackList, function(callback) {
$("#extension-buttons").append($('<div class="btn-group">').append(callback()));
});
});
});
return extensionMgr;
return extensionMgr;
});

View File

@ -3,77 +3,78 @@ define([
"underscore"
], function($, _) {
var buttonPublish = {
extensionId: "buttonPublish",
extensionName: 'Button "Publish"',
settingsBloc: '<p>Adds a "Publish document" button in the navigation bar.</p>'
};
var buttonPublish = {
extensionId: "buttonPublish",
extensionName: 'Button "Publish"',
settingsBloc: '<p>Adds a "Publish document" button in the navigation bar.</p>'
};
var button = undefined;
var currentFileDesc = undefined;
var publishRunning = false;
var hasPublications = false;
var isOffline = false;
// Enable/disable the button
function updateButtonState() {
if(button === undefined) {
return;
}
if(publishRunning === true || hasPublications === false || isOffline === true) {
button.addClass("disabled");
}
else {
button.removeClass("disabled");
}
};
var button = undefined;
var currentFileDesc = undefined;
var publishRunning = false;
var hasPublications = false;
var isOffline = false;
// Enable/disable the button
function updateButtonState() {
if(button === undefined) {
return;
}
if(publishRunning === true || hasPublications === false || isOffline === true) {
button.addClass("disabled");
}
else {
button.removeClass("disabled");
}
}
;
var publisher = undefined;
buttonPublish.onPublisherCreated = function(publisherParameter) {
publisher = publisherParameter;
};
var publisher = undefined;
buttonPublish.onPublisherCreated = function(publisherParameter) {
publisher = publisherParameter;
};
buttonPublish.onCreateButton = function() {
button = $([
'<button class="btn" title="Publish this document">',
'<i class="icon-share"></i>',
'</button>'].join("")
).click(function() {
if(!$(this).hasClass("disabled")) {
publisher.publish();
}
});
return button;
};
buttonPublish.onCreateButton = function() {
button = $([
'<button class="btn" title="Publish this document">',
' <i class="icon-share"></i>',
'</button>'
].join("")).click(function() {
if(!$(this).hasClass("disabled")) {
publisher.publish();
}
});
return button;
};
buttonPublish.onPublishRunning = function(isRunning) {
publishRunning = isRunning;
updateButtonState();
};
buttonPublish.onPublishRunning = function(isRunning) {
publishRunning = isRunning;
updateButtonState();
};
buttonPublish.onOfflineChanged = function(isOfflineParameter) {
isOffline = isOfflineParameter;
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();
};
// 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.onFileSelected = function(fileDesc) {
currentFileDesc = fileDesc;
checkPublication();
};
buttonPublish.onPublishRemoved = checkPublication;
buttonPublish.onNewPublishSuccess = checkPublication;
buttonPublish.onPublishRemoved = checkPublication;
buttonPublish.onNewPublishSuccess = checkPublication;
return buttonPublish;
return buttonPublish;
});

View File

@ -3,70 +3,72 @@ define([
"underscore"
], function($, _) {
var buttonShare = {
extensionId: "buttonShare",
extensionName: 'Button "Share"',
var buttonShare = {
extensionId: "buttonShare",
extensionName: 'Button "Share"',
optional: true,
settingsBloc: '<p>Adds a "Share document" button in the navigation bar.</p>'
};
settingsBloc: '<p>Adds a "Share document" button in the navigation bar.</p>'
};
buttonShare.onCreateButton = function() {
return $([
'<button class="btn dropdown-toggle" data-toggle="dropdown" title="Share this document">',
'<i class="icon-link"></i>',
'</button>',
'<div id="link-container" class="dropdown-menu pull-right">',
'<div class="link-list"></div>',
'<p class="no-link">To share this document you need first to ',
'<a href="#" class="action-publish-gist">publish it as a Gist</a>',
' in Markdown format.',
'</p>',
'<blockquote class="muted">',
'<b>NOTE:</b> You can open any URL within StackEdit using ',
'<a href="viewer.html?url=https://raw.github.com/benweet/stackedit/master/README.md"',
'title="Sharing example">viewer.html?url=...</a>',
'</blockquote>',
'</div>'].join("")
);
};
buttonShare.onCreateButton = function() {
return $([
'<button class="btn dropdown-toggle" data-toggle="dropdown" title="Share this document">',
' <i class="icon-link"></i>',
'</button>',
'<div id="link-container" class="dropdown-menu pull-right">',
' <h3 class="muted">Sharing</h3>',
' <div class="link-list"></div>',
' <p class="no-link">To share this document you need first to ',
' <a href="#" class="action-publish-gist">publish it as a Gist</a>',
' in Markdown format.',
' </p>',
' <blockquote class="muted">',
' <b>NOTE:</b> You can open any URL within StackEdit using',
' <a href="viewer.html?url=https://raw.github.com/benweet/stackedit/master/README.md"',
' title="Sharing example">viewer.html?url=...</a>',
' </blockquote>',
'</div>'
].join(""));
};
var fileDesc = undefined;
var lineTemplate = [
var fileDesc = undefined;
var lineTemplate = [
'<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("");
var refreshDocumentSharing = function(fileDescParameter) {
if(fileDescParameter !== undefined && fileDescParameter !== fileDesc) {
return;
}
' <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("");
var refreshDocumentSharing = function(fileDescParameter) {
if(fileDescParameter !== undefined && fileDescParameter !== fileDesc) {
return;
}
var linkList = $("#link-container .link-list").empty();
$("#link-container .no-link").show();
var linkList = $("#link-container .link-list").empty();
$("#link-container .no-link").show();
var attributesList = _.values(fileDesc.publishLocations);
_.each(attributesList, function(attributes) {
if(attributes.sharingLink) {
var lineElement = $(_.template(lineTemplate, {
link: attributes.sharingLink
}));
lineElement.click(function(event) {
event.stopPropagation();
});
linkList.append(lineElement);
$("#link-container .no-link").hide();
}
});
};
var attributesList = _.values(fileDesc.publishLocations);
_.each(attributesList, function(attributes) {
if(attributes.sharingLink) {
var lineElement = $(_.template(lineTemplate, {
link: attributes.sharingLink
}));
lineElement.click(function(event) {
event.stopPropagation();
});
linkList.append(lineElement);
$("#link-container .no-link").hide();
}
});
};
buttonShare.onFileSelected = function(fileDescParameter) {
fileDesc = fileDescParameter;
refreshDocumentSharing(fileDescParameter);
};
buttonShare.onFileSelected = function(fileDescParameter) {
fileDesc = fileDescParameter;
refreshDocumentSharing(fileDescParameter);
};
buttonShare.onNewPublishSuccess = refreshDocumentSharing;
buttonShare.onPublishRemoved = refreshDocumentSharing;
buttonShare.onNewPublishSuccess = refreshDocumentSharing;
buttonShare.onPublishRemoved = refreshDocumentSharing;
return buttonShare;
return buttonShare;
});

View File

@ -1,72 +1,78 @@
define([
"jquery",
"underscore"
], function($, _) {
"underscore",
"utils"
], function($, _, utils) {
var buttonStat = {
extensionId: "buttonStat",
extensionName: 'Button "Statistics"',
var buttonStat = {
extensionId: "buttonStat",
extensionName: 'Button "Statistics"',
optional: true,
settingsBloc: '<p>Adds a "Document statistics" button in the navigation bar.</p>'
};
defaultConfig: {
name1: "Words",
value1: "\\S+",
name2: "Characters",
value2: "\\S",
name3: "Paragraphs",
value3: ".+",
},
settingsBloc: [
'<p>Adds a "Document statistics" button in the navigation bar.</p>',
'<p><div class="form-inline">',
' <label class="label-text" for="input-stat-name1">Title</label>',
' <input id="input-stat-name1" type="text" class="input-small">',
' <label class="label-text" for="input-stat-value1">RegExp</label>',
' <input id="input-stat-value1" type="text" class="span2">',
'</div></p>',
'<p><div class="form-inline">',
' <label class="label-text" for="input-stat-name2">Title</label>',
' <input id="input-stat-name2" type="text" class="input-small">',
' <label class="label-text" for="input-stat-value2">RegExp</label>',
' <input id="input-stat-value2" type="text" class="span2">',
'</div></p>',
'<p><div class="form-inline">',
' <label class="label-text" for="input-stat-name3">Title</label>',
' <input id="input-stat-name3" type="text" class="input-small">',
' <label class="label-text" for="input-stat-value3">RegExp</label>',
' <input id="input-stat-value3" type="text" class="span2">',
'</div></p>'].join("")
};
buttonStat.onCreateButton = function() {
return $([
'<button class="btn dropdown-toggle" data-toggle="dropdown" title="Document statistics">',
'<i class="icon-stat"></i>',
'</button>',
'<div id="statistics-container" class="dropdown-menu pull-right">',
'<div class="link-list"></div>',
'<p class="no-link">To share this document you need first to <a',
'href="#" class="action-publish-gist">publish it as a Gist</a> in',
'Markdown format.',
'</p>',
'<blockquote class="muted">',
'<b>NOTE:</b> You can open any URL within StackEdit using <a',
'href="viewer.html?url=https://raw.github.com/benweet/stackedit/master/README.md"',
'title="Sharing example">viewer.html?url=...</a>',
'</blockquote>',
'</div>'].join("")
);
};
buttonStat.onLoadSettings = function() {
_.each(buttonStat.defaultConfig, function(value, key) {
utils.setInputValue("#input-stat-" + key, buttonStat.config[key]);
});
};
var fileDesc = undefined;
var lineTemplate = [
'<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("");
var refreshDocumentSharing = function(fileDescParameter) {
if(fileDescParameter !== undefined && fileDescParameter !== fileDesc) {
return;
}
buttonStat.onSaveSettings = function(newConfig, event) {
_.each(buttonStat.defaultConfig, function(value, key) {
newConfig[key] = utils.getInputTextValue("#input-stat-" + key, event);
});
};
var linkList = $("#link-container .link-list").empty();
$("#link-container .no-link").show();
buttonStat.onCreateButton = function() {
return $([
'<button class="btn dropdown-toggle" data-toggle="dropdown" title="Document statistics">',
' <i class="icon-stat"></i>',
'</button>',
'<div id="statistics-container" class="dropdown-menu pull-right">',
' <h3 class="muted">Statistics</h3>',
' <div class="stat">',
' <div>' + buttonStat.config.name1 + ': <span id="span-stat-value1"></span></div>',
' <div>' + buttonStat.config.name2 + ': <span id="span-stat-value2"></span></div>',
' <div>' + buttonStat.config.name3 + ': <span id="span-stat-value3"></span></div>',
' </div>',
'</div>'
].join(""));
};
var attributesList = _.values(fileDesc.publishLocations);
_.each(attributesList, function(attributes) {
if(attributes.sharingLink) {
var lineElement = $(_.template(lineTemplate, {
link: attributes.sharingLink
}));
lineElement.click(function(event) {
event.stopPropagation();
});
linkList.append(lineElement);
$("#link-container .no-link").hide();
}
});
};
buttonStat.onPreviewFinished = function() {
var text = $("#wmd-preview").text();
$("#span-stat-value1").text(text.match(new RegExp(buttonStat.config.value1, "g")).length);
$("#span-stat-value2").text(text.match(new RegExp(buttonStat.config.value2, "g")).length);
$("#span-stat-value3").text(text.match(new RegExp(buttonStat.config.value3, "g")).length);
};
buttonStat.onFileSelected = function(fileDescParameter) {
fileDesc = fileDescParameter;
refreshDocumentSharing(fileDescParameter);
};
buttonStat.onNewPublishSuccess = refreshDocumentSharing;
buttonStat.onPublishRemoved = refreshDocumentSharing;
return buttonStat;
return buttonStat;
});

View File

@ -3,76 +3,76 @@ define([
"underscore"
], function($, _) {
var buttonSync = {
extensionId: "buttonSync",
extensionName: 'Button "Synchronize"',
settingsBloc: '<p>Adds a "Synchronize documents" button in the navigation bar.</p>'
};
var buttonSync = {
extensionId: "buttonSync",
extensionName: 'Button "Synchronize"',
settingsBloc: '<p>Adds a "Synchronize documents" button in the navigation bar.</p>'
};
var button = undefined;
var syncRunning = false;
var uploadPending = false;
var isOffline = false;
// Enable/disable the button
var updateButtonState = function() {
if(button === undefined) {
return;
}
if(syncRunning === true || uploadPending === false || isOffline) {
button.addClass("disabled");
}
else {
button.removeClass("disabled");
}
};
var button = undefined;
var syncRunning = false;
var uploadPending = false;
var isOffline = false;
// Enable/disable the button
var updateButtonState = function() {
if(button === undefined) {
return;
}
if(syncRunning === true || uploadPending === false || isOffline) {
button.addClass("disabled");
}
else {
button.removeClass("disabled");
}
};
var synchronizer = undefined;
buttonSync.onSynchronizerCreated = function(synchronizerParameter) {
synchronizer = synchronizerParameter;
};
var synchronizer = undefined;
buttonSync.onSynchronizerCreated = function(synchronizerParameter) {
synchronizer = synchronizerParameter;
};
buttonSync.onCreateButton = function() {
button = $([
'<button class="btn" title="Synchronize all documents">',
'<i class="icon-refresh"></i>',
'</button>'].join("")
).click(function() {
if(!$(this).hasClass("disabled")) {
synchronizer.forceSync();
}
});
return button;
};
buttonSync.onCreateButton = function() {
button = $([
'<button class="btn" title="Synchronize all documents">',
' <i class="icon-refresh"></i>',
'</button>'
].join("")).click(function() {
if(!$(this).hasClass("disabled")) {
synchronizer.forceSync();
}
});
return button;
};
buttonSync.onReady = updateButtonState;
buttonSync.onReady = updateButtonState;
buttonSync.onSyncRunning = function(isRunning) {
syncRunning = isRunning;
uploadPending = true;
updateButtonState();
};
buttonSync.onSyncRunning = function(isRunning) {
syncRunning = isRunning;
uploadPending = true;
updateButtonState();
};
buttonSync.onSyncSuccess = function() {
uploadPending = false;
updateButtonState();
};
buttonSync.onSyncSuccess = function() {
uploadPending = false;
updateButtonState();
};
buttonSync.onOfflineChanged = function(isOfflineParameter) {
isOffline = isOfflineParameter;
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();
}
};
// Check that a file has synchronized locations
var checkSynchronization = function(fileDesc) {
if(_.size(fileDesc.syncLocations) !== 0) {
uploadPending = true;
updateButtonState();
}
};
buttonSync.onContentChanged = checkSynchronization;
buttonSync.onTitleChanged = checkSynchronization;
buttonSync.onContentChanged = checkSynchronization;
buttonSync.onTitleChanged = checkSynchronization;
return buttonSync;
return buttonSync;
});

View File

@ -4,69 +4,98 @@ define([
"file-system"
], function($, _, fileSystem) {
var documentSelector = {
extensionId: "documentSelector",
extensionName: "Document selector",
settingsBloc: '<p>Builds the "Open document" dropdown menu.</p>'
};
var documentSelector = {
extensionId: "documentSelector",
extensionName: "Document selector",
settingsBloc: '<p>Builds the "Open document" dropdown menu.</p>'
};
var fileMgr = undefined;
documentSelector.onFileMgrCreated = function(fileMgrParameter) {
fileMgr = fileMgrParameter;
};
var fileMgr = undefined;
documentSelector.onFileMgrCreated = function(fileMgrParameter) {
fileMgr = fileMgrParameter;
};
var liMap = undefined;
var buildSelector = function() {
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.providerId;
}).each(function(attributes) {
result.push('<i class="icon-' + attributes.provider.providerId + '"></i>');
});
result.push(" ");
result.push(fileDesc.title);
return result.join("");
}
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.providerId;
}).each(function(attributes) {
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();
_.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);
liMap[fileDesc.fileIndex] = li;
$("#file-selector").append(li);
});
};
liMap = {};
$("#file-selector li:not(.stick)").empty();
_.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);
liMap[fileDesc.fileIndex] = li;
$("#file-selector").append(li);
});
};
documentSelector.onFileSelected = function(fileDesc) {
if(liMap === undefined) {
buildSelector();
}
$("#file-selector li:not(.stick)").removeClass("disabled");
liMap[fileDesc.fileIndex].addClass("disabled");
};
documentSelector.onFileSelected = function(fileDesc) {
if(liMap === undefined) {
buildSelector();
}
$("#file-selector li:not(.stick)").removeClass("disabled");
liMap[fileDesc.fileIndex].addClass("disabled");
};
documentSelector.onFileCreated = buildSelector;
documentSelector.onFileDeleted = buildSelector;
documentSelector.onTitleChanged = buildSelector;
documentSelector.onSyncExportSuccess = buildSelector;
documentSelector.onSyncRemoved = buildSelector;
documentSelector.onNewPublishSuccess = buildSelector;
documentSelector.onPublishRemoved = buildSelector;
documentSelector.onFileCreated = buildSelector;
documentSelector.onFileDeleted = buildSelector;
documentSelector.onTitleChanged = buildSelector;
documentSelector.onSyncExportSuccess = buildSelector;
documentSelector.onSyncRemoved = buildSelector;
documentSelector.onNewPublishSuccess = buildSelector;
documentSelector.onPublishRemoved = buildSelector;
return documentSelector;
// Filter for search input in file selector
function filterFileSelector(filter) {
var liList = $("#file-selector li:not(.stick)");
liList.show();
if(filter) {
var words = filter.toLowerCase().split(/\s+/);
liList.each(function() {
var fileTitle = $(this).text().toLowerCase();
if(_.some(words, function(word) {
return fileTitle.indexOf(word) === -1;
})) {
$(this).hide();
}
});
}
}
documentSelector.onReady = function() {
$(".action-open-file").click(function() {
filterFileSelector();
_.defer(function() {
$("#file-search").val("").focus();
});
});
$("#file-search").keyup(function() {
filterFileSelector($(this).val());
}).click(function(event) {
event.stopPropagation();
});
};
return documentSelector;
});

View File

@ -3,61 +3,61 @@ define([
"underscore"
], function($, _) {
var documentTitle = {
extensionId: "documentTitle",
extensionName: "Document title",
settingsBloc: '<p>Responsible for showing the document title in the navigation bar.</p>'
};
var documentTitle = {
extensionId: "documentTitle",
extensionName: "Document title",
settingsBloc: '<p>Responsible for showing the document title in the navigation bar.</p>'
};
var layout = undefined;
documentTitle.onLayoutCreated = function(layoutParameter) {
layout = layoutParameter;
};
var layout = undefined;
documentTitle.onLayoutCreated = function(layoutParameter) {
layout = layoutParameter;
};
var fileDesc = undefined;
var updateTitle = function(fileDescParameter) {
if(fileDescParameter !== fileDesc) {
return;
}
var fileDesc = undefined;
var updateTitle = function(fileDescParameter) {
if(fileDescParameter !== fileDesc) {
return;
}
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.providerId;
}).each(function(attributes) {
result.push('<i class="icon-' + attributes.provider.providerId + '"></i>');
});
result.push(" ");
result.push(fileDesc.title);
return result.join("");
}
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.providerId;
}).each(function(attributes) {
result.push('<i class="icon-' + attributes.provider.providerId + '"></i>');
});
result.push(" ");
result.push(fileDesc.title);
return result.join("");
}
var title = fileDesc.title;
document.title = "StackEdit - " + title;
$("#file-title").html(composeTitle(fileDesc));
$(".file-title").text(title);
$("#file-title-input").val(title);
var title = fileDesc.title;
document.title = "StackEdit - " + title;
$("#file-title").html(composeTitle(fileDesc));
$(".file-title").text(title);
$("#file-title-input").val(title);
if(layout !== undefined) {
// Use defer to make sure UI has been updated
_.defer(layout.resizeAll);
}
};
if(layout !== undefined) {
// Use defer to make sure UI has been updated
_.defer(layout.resizeAll);
}
};
documentTitle.onFileSelected = function(fileDescParameter) {
fileDesc = fileDescParameter;
updateTitle(fileDescParameter);
};
documentTitle.onFileSelected = function(fileDescParameter) {
fileDesc = fileDescParameter;
updateTitle(fileDescParameter);
};
documentTitle.onTitleChanged = updateTitle;
documentTitle.onSyncExportSuccess = updateTitle;
documentTitle.onSyncRemoved = updateTitle;
documentTitle.onNewPublishSuccess = updateTitle;
documentTitle.onPublishRemoved = updateTitle;
documentTitle.onTitleChanged = updateTitle;
documentTitle.onSyncExportSuccess = updateTitle;
documentTitle.onSyncRemoved = updateTitle;
documentTitle.onNewPublishSuccess = updateTitle;
documentTitle.onPublishRemoved = updateTitle;
return documentTitle;
return documentTitle;
});

View File

@ -1,20 +1,19 @@
define(function() {
var emailConverter = {
extensionId: "emailConverter",
extensionName: "Email Converter",
var emailConverter = {
extensionId: "emailConverter",
extensionName: "Email Converter",
optional: true,
settingsBloc: '<p>Converts email adresses in the form &lt;email@example.com&gt; into a clickable links.</p>'
};
settingsBloc: '<p>Converts email adresses in the form &lt;email@example.com&gt; into a clickable links.</p>'
};
emailConverter.onEditorConfigure = function(editor) {
editor.getConverter().hooks.chain("postConversion", function(text) {
return text.replace(/<(mailto\:)?([^\s>]+@[^\s>]+\.\S+?)>/g, function(match, mailto, email) {
return '<a href="mailto:' + email + '">' + email + '</a>';
});
});
};
emailConverter.onEditorConfigure = function(editor) {
editor.getConverter().hooks.chain("postConversion", function(text) {
return text.replace(/<(mailto\:)?([^\s>]+@[^\s>]+\.\S+?)>/g, function(match, mailto, email) {
return '<a href="mailto:' + email + '">' + email + '</a>';
});
});
};
return emailConverter;
return emailConverter;
});

View File

@ -3,64 +3,66 @@ define([
"underscore"
], function($, _) {
var managePublication = {
extensionId: "managePublication",
extensionName: "Manage publication",
settingsBloc: '<p>Populates the "Manage publication" dialog box.</p>'
};
var managePublication = {
extensionId: "managePublication",
extensionName: "Manage publication",
settingsBloc: '<p>Populates the "Manage publication" dialog box.</p>'
};
var fileMgr = undefined;
managePublication.onFileMgrCreated = function(fileMgrParameter) {
fileMgr = fileMgrParameter;
};
var fileMgr = undefined;
managePublication.onFileMgrCreated = function(fileMgrParameter) {
fileMgr = fileMgrParameter;
};
var fileDesc = undefined;
var lineTemplate = [
var fileDesc = undefined;
var lineTemplate = [
'<div class="input-prepend input-append">',
'<span class="add-on" title="<%= provider.providerName %>">',
'<i class="icon-<%= provider.providerId %>"></i>',
'</span>',
'<input class="span5" type="text" value="<%= publishDesc %>" disabled />',
'</div>'].join("");
var removeButtonTemplate = '<a class="btn" title="Remove this location"><i class="icon-trash"></i></a>';
var refreshDialog = function(fileDescParameter) {
if(fileDescParameter !== undefined && fileDescParameter !== fileDesc) {
return;
}
' <span class="add-on" title="<%= provider.providerName %>">',
' <i class="icon-<%= provider.providerId %>"></i>',
' </span>',
' <input class="span5" type="text" value="<%= publishDesc %>" disabled />',
'</div>'
].join("");
var removeButtonTemplate = '<a class="btn" title="Remove this location"><i class="icon-trash"></i></a>';
var refreshDialog = function(fileDescParameter) {
if(fileDescParameter !== undefined && fileDescParameter !== fileDesc) {
return;
}
var publishAttributesList = _.values(fileDesc.publishLocations);
$(".msg-no-publish, .msg-publish-list").addClass("hide");
var publishList = $("#manage-publish-list").empty();
if (publishAttributesList.length > 0) {
$(".msg-publish-list").removeClass("hide");
} else {
$(".msg-no-publish").removeClass("hide");
}
_.each(publishAttributesList, function(publishAttributes) {
formattedAttributes = _.omit(publishAttributes, "provider", "publishIndex", "sharingLink");
if(formattedAttributes.password) {
formattedAttributes.password = "********";
}
var publishDesc = JSON.stringify(formattedAttributes).replace(/{|}|"/g, "").replace(/,/g, ", ");
var lineElement = $(_.template(lineTemplate, {
provider: publishAttributes.provider,
publishDesc: publishDesc
}));
lineElement.append($(removeButtonTemplate).click(function() {
fileMgr.removePublish(publishAttributes);
}));
publishList.append(lineElement);
});
};
var publishAttributesList = _.values(fileDesc.publishLocations);
$(".msg-no-publish, .msg-publish-list").addClass("hide");
var publishList = $("#manage-publish-list").empty();
if(publishAttributesList.length > 0) {
$(".msg-publish-list").removeClass("hide");
}
else {
$(".msg-no-publish").removeClass("hide");
}
_.each(publishAttributesList, function(publishAttributes) {
formattedAttributes = _.omit(publishAttributes, "provider", "publishIndex", "sharingLink");
if(formattedAttributes.password) {
formattedAttributes.password = "********";
}
var publishDesc = JSON.stringify(formattedAttributes).replace(/{|}|"/g, "").replace(/,/g, ", ");
var lineElement = $(_.template(lineTemplate, {
provider: publishAttributes.provider,
publishDesc: publishDesc
}));
lineElement.append($(removeButtonTemplate).click(function() {
fileMgr.removePublish(publishAttributes);
}));
publishList.append(lineElement);
});
};
managePublication.onFileSelected = function(fileDescParameter) {
fileDesc = fileDescParameter;
refreshDialog(fileDescParameter);
};
managePublication.onFileSelected = function(fileDescParameter) {
fileDesc = fileDescParameter;
refreshDialog(fileDescParameter);
};
managePublication.onNewPublishSuccess = refreshDialog;
managePublication.onPublishRemoved = refreshDialog;
managePublication.onNewPublishSuccess = refreshDialog;
managePublication.onPublishRemoved = refreshDialog;
return managePublication;
return managePublication;
});

View File

@ -3,60 +3,62 @@ define([
"underscore"
], function($, _) {
var manageSynchronization = {
extensionId: "manageSynchronization",
extensionName: "Manage synchronization",
settingsBloc: '<p>Populates the "Manage synchronization" dialog box.</p>'
};
var manageSynchronization = {
extensionId: "manageSynchronization",
extensionName: "Manage synchronization",
settingsBloc: '<p>Populates the "Manage synchronization" dialog box.</p>'
};
var fileMgr = undefined;
manageSynchronization.onFileMgrCreated = function(fileMgrParameter) {
fileMgr = fileMgrParameter;
};
var fileMgr = undefined;
manageSynchronization.onFileMgrCreated = function(fileMgrParameter) {
fileMgr = fileMgrParameter;
};
var fileDesc = undefined;
var lineTemplate = [
var fileDesc = undefined;
var lineTemplate = [
'<div class="input-prepend input-append">',
'<span class="add-on" title="<%= provider.providerName %>">',
'<i class="icon-<%= provider.providerId %>"></i>',
'</span>',
'<input class="span5" type="text" value="<%= syncDesc %>" disabled />',
'</div>'].join("");
var removeButtonTemplate = '<a class="btn" title="Remove this location"><i class="icon-trash"></i></a>';
var refreshDialog = function(fileDescParameter) {
if(fileDescParameter !== undefined && fileDescParameter !== fileDesc) {
return;
}
' <span class="add-on" title="<%= provider.providerName %>">',
' <i class="icon-<%= provider.providerId %>"></i>',
' </span>',
' <input class="span5" type="text" value="<%= syncDesc %>" disabled />',
'</div>'
].join("");
var removeButtonTemplate = '<a class="btn" title="Remove this location"><i class="icon-trash"></i></a>';
var refreshDialog = function(fileDescParameter) {
if(fileDescParameter !== undefined && fileDescParameter !== fileDesc) {
return;
}
var syncAttributesList = _.values(fileDesc.syncLocations);
$(".msg-no-sync, .msg-sync-list").addClass("hide");
var syncList = $("#manage-sync-list").empty();
if (syncAttributesList.length > 0) {
$(".msg-sync-list").removeClass("hide");
} else {
$(".msg-no-sync").removeClass("hide");
}
_.each(syncAttributesList, function(syncAttributes) {
var syncDesc = syncAttributes.id || syncAttributes.path;
var lineElement = $(_.template(lineTemplate, {
provider: syncAttributes.provider,
syncDesc: syncDesc
}));
lineElement.append($(removeButtonTemplate).click(function() {
fileMgr.removeSync(syncAttributes);
}));
syncList.append(lineElement);
});
};
var syncAttributesList = _.values(fileDesc.syncLocations);
$(".msg-no-sync, .msg-sync-list").addClass("hide");
var syncList = $("#manage-sync-list").empty();
if(syncAttributesList.length > 0) {
$(".msg-sync-list").removeClass("hide");
}
else {
$(".msg-no-sync").removeClass("hide");
}
_.each(syncAttributesList, function(syncAttributes) {
var syncDesc = syncAttributes.id || syncAttributes.path;
var lineElement = $(_.template(lineTemplate, {
provider: syncAttributes.provider,
syncDesc: syncDesc
}));
lineElement.append($(removeButtonTemplate).click(function() {
fileMgr.removeSync(syncAttributes);
}));
syncList.append(lineElement);
});
};
manageSynchronization.onFileSelected = function(fileDescParameter) {
fileDesc = fileDescParameter;
refreshDialog(fileDescParameter);
};
manageSynchronization.onFileSelected = function(fileDescParameter) {
fileDesc = fileDescParameter;
refreshDialog(fileDescParameter);
};
manageSynchronization.onSyncExportSuccess = refreshDialog;
manageSynchronization.onSyncRemoved = refreshDialog;
manageSynchronization.onSyncExportSuccess = refreshDialog;
manageSynchronization.onSyncRemoved = refreshDialog;
return manageSynchronization;
return manageSynchronization;
});

View File

@ -8,38 +8,38 @@ define([
extensionName: "Markdown Extra",
optional: true,
defaultConfig: {
prettify: true
},
prettify: true
},
settingsBloc: [
'<p>Adds extra features to the original Markdown syntax.</p>',
'<div class="form-horizontal">',
'<div class="control-group">',
'<label class="control-label" for="input-markdownextra-prettify">Prettify syntax highlighting</label>',
'<div class="controls">',
'<input type="checkbox" id="input-markdownextra-prettify">',
'</div>',
'</div>',
'</div>'
].join("")
'<p>Adds extra features to the original Markdown syntax.</p>',
'<div class="form-horizontal">',
' <div class="control-group">',
' <label class="control-label" for="input-markdownextra-prettify">Prettify syntax highlighting</label>',
' <div class="controls">',
' <input type="checkbox" id="input-markdownextra-prettify">',
' </div>',
' </div>',
'</div>'
].join("")
};
markdownExtra.onLoadSettings = function() {
utils.setInputChecked("#input-markdownextra-prettify", markdownExtra.config.prettify);
utils.setInputChecked("#input-markdownextra-prettify", markdownExtra.config.prettify);
};
markdownExtra.onSaveSettings = function(newConfig, event) {
newConfig.prettify = utils.getInputChecked("#input-markdownextra-prettify");
newConfig.prettify = utils.getInputChecked("#input-markdownextra-prettify");
};
markdownExtra.onEditorConfigure = function(editor) {
var converter = editor.getConverter();
var options = {};
if(markdownExtra.config.prettify === true) {
options.highlighter = "prettify";
editor.hooks.chain("onPreviewRefresh", prettyPrint);
}
Markdown.Extra.init(converter, options);
};
var converter = editor.getConverter();
var options = {};
if(markdownExtra.config.prettify === true) {
options.highlighter = "prettify";
editor.hooks.chain("onPreviewRefresh", prettyPrint);
}
Markdown.Extra.init(converter, options);
};
return markdownExtra;
});

View File

@ -5,116 +5,116 @@ define([
"jgrowl"
], function($, _, utils, jGrowl) {
var notifications = {
extensionId: "notifications",
extensionName: "Notifications",
defaultConfig: {
timeout: 5000
},
var notifications = {
extensionId: "notifications",
extensionName: "Notifications",
defaultConfig: {
timeout: 5000
},
settingsBloc: [
'<p>Shows notification messages in the bottom-right corner of the screen.</p>',
'<div class="form-horizontal">',
'<div class="control-group">',
'<label class="control-label" for="input-notifications-timeout">Timeout</label>',
'<div class="controls">',
'<input type="text" id="input-notifications-timeout" class="input-mini">',
'<span class="help-inline">ms</span>',
'</div>',
'</div>',
'</div>'
].join("")
};
'<p>Shows notification messages in the bottom-right corner of the screen.</p>',
'<div class="form-horizontal">',
' <div class="control-group">',
' <label class="control-label" for="input-notifications-timeout">Timeout</label>',
' <div class="controls">',
' <input type="text" id="input-notifications-timeout" class="input-mini">',
' <span class="help-inline">ms</span>',
' </div>',
' </div>',
'</div>'
].join("")
};
notifications.onLoadSettings = function() {
utils.setInputValue("#input-notifications-timeout", notifications.config.timeout);
notifications.onLoadSettings = function() {
utils.setInputValue("#input-notifications-timeout", notifications.config.timeout);
};
notifications.onSaveSettings = function(newConfig, event) {
newConfig.timeout = utils.getInputIntValue("#input-notifications-timeout", event, 1, 60000);
newConfig.timeout = utils.getInputIntValue("#input-notifications-timeout", event, 1, 60000);
};
notifications.onReady = function() {
// jGrowl configuration
jGrowl.defaults.life = notifications.config.timeout;
jGrowl.defaults.closer = false;
jGrowl.defaults.closeTemplate = '';
jGrowl.defaults.position = 'bottom-right';
};
notifications.onReady = function() {
// jGrowl configuration
jGrowl.defaults.life = notifications.config.timeout;
jGrowl.defaults.closer = false;
jGrowl.defaults.closeTemplate = '';
jGrowl.defaults.position = 'bottom-right';
};
function showMessage(msg, iconClass, options) {
if(!msg) {
return;
}
var endOfMsg = msg.indexOf("|");
if(endOfMsg !== -1) {
msg = msg.substring(0, endOfMsg);
if(!msg) {
return;
}
}
options = options || {};
iconClass = iconClass || "icon-info-sign";
jGrowl("<i class='icon-white " + iconClass + "'></i> " + _.escape(msg), options);
}
function showMessage(msg, iconClass, options) {
if(!msg) {
return;
}
var endOfMsg = msg.indexOf("|");
if(endOfMsg !== -1) {
msg = msg.substring(0, endOfMsg);
if(!msg) {
return;
}
}
options = options || {};
iconClass = iconClass || "icon-info-sign";
jGrowl("<i class='icon-white " + iconClass + "'></i> " + _.escape(msg), options);
}
notifications.onMessage = function(message) {
logger.log(message);
showMessage(message);
};
notifications.onMessage = function(message) {
logger.log(message);
showMessage(message);
};
notifications.onError = function(error) {
logger.error(error);
if(_.isString(error)) {
showMessage(error, "icon-warning-sign");
}
else if(_.isObject(error)) {
showMessage(error.message, "icon-warning-sign");
}
};
notifications.onError = function(error) {
logger.error(error);
if(_.isString(error)) {
showMessage(error, "icon-warning-sign");
}
else if(_.isObject(error)) {
showMessage(error.message, "icon-warning-sign");
}
};
notifications.onOfflineChanged = function(isOffline) {
if(isOffline === true) {
showMessage("You are offline.", "icon-exclamation-sign msg-offline", {
sticky : true,
close : function() {
showMessage("You are back online!", "icon-signal");
}
});
} else {
$(".msg-offline").parents(".jGrowl-notification").trigger(
'jGrowl.beforeClose');
}
};
notifications.onOfflineChanged = function(isOffline) {
if(isOffline === true) {
showMessage("You are offline.", "icon-exclamation-sign msg-offline", {
sticky: true,
close: function() {
showMessage("You are back online!", "icon-signal");
}
});
}
else {
$(".msg-offline").parents(".jGrowl-notification").trigger('jGrowl.beforeClose');
}
};
notifications.onSyncImportSuccess = function(fileDescList, provider) {
if(!fileDescList) {
return;
}
var titles = _.map(fileDescList, function(fileDesc) {
return fileDesc.title;
}).join(", ");
showMessage(titles + ' imported successfully from ' + provider.providerName + '.');
};
notifications.onSyncImportSuccess = function(fileDescList, provider) {
if(!fileDescList) {
return;
}
var titles = _.map(fileDescList, function(fileDesc) {
return fileDesc.title;
}).join(", ");
showMessage(titles + ' imported successfully from ' + provider.providerName + '.');
};
notifications.onSyncExportSuccess = function(fileDesc, syncAttributes) {
showMessage('"' + fileDesc.title + '" will now be synchronized on ' + syncAttributes.provider.providerName + '.');
};
notifications.onSyncExportSuccess = function(fileDesc, syncAttributes) {
showMessage('"' + fileDesc.title + '" will now be synchronized on ' + syncAttributes.provider.providerName + '.');
};
notifications.onSyncRemoved = function(fileDesc, syncAttributes) {
showMessage(syncAttributes.provider.providerName + " synchronized location has been removed.");
};
notifications.onSyncRemoved = function(fileDesc, syncAttributes) {
showMessage(syncAttributes.provider.providerName + " synchronized location has been removed.");
};
notifications.onPublishSuccess = function(fileDesc) {
showMessage('"' + fileDesc.title + '" successfully published.');
};
notifications.onPublishSuccess = function(fileDesc) {
showMessage('"' + fileDesc.title + '" successfully published.');
};
notifications.onNewPublishSuccess = function(fileDesc, publishIndex, publishAttributes) {
showMessage('"' + fileDesc.title + '" is now published on ' + publishAttributes.provider.providerName + '.');
};
notifications.onNewPublishSuccess = function(fileDesc, publishIndex, publishAttributes) {
showMessage('"' + fileDesc.title + '" is now published on ' + publishAttributes.provider.providerName + '.');
};
notifications.onPublishRemoved = function(fileDesc, publishAttributes) {
showMessage(publishAttributes.provider.providerName + " publish location has been removed.");
};
notifications.onPublishRemoved = function(fileDesc, publishAttributes) {
showMessage(publishAttributes.provider.providerName + " publish location has been removed.");
};
return notifications;
return notifications;
});

View File

@ -1,199 +1,202 @@
define([
"jquery",
"underscore",
"lib/css_browser_selector"
"lib/css_browser_selector"
], function($, _) {
var scrollLink = {
extensionId: "scrollLink",
extensionName: "Scroll Link",
var scrollLink = {
extensionId: "scrollLink",
extensionName: "Scroll Link",
optional: true,
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.',
'</bloquote>'
].join("")
};
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.',
'</bloquote>'
].join("")
};
var mdSectionList = [];
var htmlSectionList = [];
function pxToFloat(px) {
return parseFloat(px.substring(0, px.length-2));
}
var buildSections = _.debounce(function() {
var mdSectionList = [];
var htmlSectionList = [];
function pxToFloat(px) {
return parseFloat(px.substring(0, px.length - 2));
}
var buildSections = _.debounce(function() {
// Try to find Markdown sections by looking for titles
var editorElt = $("#wmd-input");
mdSectionList = [];
// This textarea is used to measure sections height
var textareaElt = $("#md-section-helper");
// It has to be the same width than wmd-input
textareaElt.width(editorElt.width());
// Consider wmd-input top padding
var padding = pxToFloat(editorElt.css('padding-top'));
var offset = 0, mdSectionOffset = 0;
function addMdSection(sectionText) {
var sectionHeight = padding;
if(sectionText !== undefined) {
textareaElt.val(sectionText);
sectionHeight += textareaElt.prop('scrollHeight');
}
var newSectionOffset = mdSectionOffset + sectionHeight;
mdSectionList.push({
startOffset: mdSectionOffset,
endOffset: newSectionOffset,
height: sectionHeight
});
mdSectionOffset = newSectionOffset;
padding = 0;
}
// Create MD sections by finding title patterns (excluding gfm blocs)
var text = editorElt.val() + "\n\n";
text.replace(/^```.*\n[\s\S]*?\n```|(^.+[ \t]*\n=+[ \t]*\n+|^.+[ \t]*\n-+[ \t]*\n+|^\#{1,6}[ \t]*.+?[ \t]*\#*\n+)/gm,
function(match, title, matchOffset) {
if(title) {
// We just found a title which means end of the previous section
// Exclude last \n of the section
var sectionText = undefined;
if(matchOffset > offset) {
sectionText = text.substring(offset, matchOffset-1);
}
addMdSection(sectionText);
offset = matchOffset;
}
return "";
}
);
// Last section
// Consider wmd-input bottom padding and exclude \n\n previously added
padding += pxToFloat(editorElt.css('padding-bottom'));
addMdSection(text.substring(offset, text.length-2));
// Try to find Markdown sections by looking for titles
var editorElt = $("#wmd-input");
mdSectionList = [];
// This textarea is used to measure sections height
var textareaElt = $("#md-section-helper");
// It has to be the same width than wmd-input
textareaElt.width(editorElt.width());
// Consider wmd-input top padding
var padding = pxToFloat(editorElt.css('padding-top'));
var offset = 0, mdSectionOffset = 0;
function addMdSection(sectionText) {
var sectionHeight = padding;
if(sectionText !== undefined) {
textareaElt.val(sectionText);
sectionHeight += textareaElt.prop('scrollHeight');
}
var newSectionOffset = mdSectionOffset + sectionHeight;
mdSectionList.push({
startOffset: mdSectionOffset,
endOffset: newSectionOffset,
height: sectionHeight
});
mdSectionOffset = newSectionOffset;
padding = 0;
}
// Create MD sections by finding title patterns (excluding gfm blocs)
var text = editorElt.val() + "\n\n";
text.replace(/^```.*\n[\s\S]*?\n```|(^.+[ \t]*\n=+[ \t]*\n+|^.+[ \t]*\n-+[ \t]*\n+|^\#{1,6}[ \t]*.+?[ \t]*\#*\n+)/gm, function(match, title, matchOffset) {
if(title) {
// We just found a title which means end of the previous section
// Exclude last \n of the section
var sectionText = undefined;
if(matchOffset > offset) {
sectionText = text.substring(offset, matchOffset - 1);
}
addMdSection(sectionText);
offset = matchOffset;
}
return "";
});
// Last section
// Consider wmd-input bottom padding and exclude \n\n previously added
padding += pxToFloat(editorElt.css('padding-bottom'));
addMdSection(text.substring(offset, text.length - 2));
// Try to find corresponding sections in the preview
var previewElt = $("#wmd-preview");
htmlSectionList = [];
var htmlSectionOffset = 0;
var previewScrollTop = previewElt.scrollTop();
// Each title element is a section separator
previewElt.children("h1,h2,h3,h4,h5,h6").each(function() {
// Consider div scroll position and header element top margin
var newSectionOffset = $(this).position().top + previewScrollTop + pxToFloat($(this).css('margin-top'));
htmlSectionList.push({
startOffset: htmlSectionOffset,
endOffset: newSectionOffset,
height: newSectionOffset - htmlSectionOffset
});
htmlSectionOffset = newSectionOffset;
});
// Last section
var scrollHeight = previewElt.prop('scrollHeight');
htmlSectionList.push({
startOffset: htmlSectionOffset,
endOffset: scrollHeight,
height: scrollHeight - htmlSectionOffset
});
// Try to find corresponding sections in the preview
var previewElt = $("#wmd-preview");
htmlSectionList = [];
var htmlSectionOffset = 0;
var previewScrollTop = previewElt.scrollTop();
// Each title element is a section separator
previewElt.children("h1,h2,h3,h4,h5,h6").each(function() {
// Consider div scroll position and header element top margin
var newSectionOffset = $(this).position().top + previewScrollTop + pxToFloat($(this).css('margin-top'));
htmlSectionList.push({
startOffset: htmlSectionOffset,
endOffset: newSectionOffset,
height: newSectionOffset - htmlSectionOffset
});
htmlSectionOffset = newSectionOffset;
});
// Last section
var scrollHeight = previewElt.prop('scrollHeight');
htmlSectionList.push({
startOffset: htmlSectionOffset,
endOffset: scrollHeight,
height: scrollHeight - htmlSectionOffset
});
// apply Scroll Link
lastEditorScrollTop = -9;
skipScrollLink = false;
isScrollPreview = false;
runScrollLink();
}, 500);
// apply Scroll Link
lastEditorScrollTop = -9;
skipScrollLink = false;
isScrollPreview = false;
runScrollLink();
}, 500);
// -9 is less than -5
var lastEditorScrollTop = -9;
var lastPreviewScrollTop = -9;
var skipScrollLink = false;
var isScrollPreview = false;
var runScrollLink = _.debounce(function() {
if(skipScrollLink === true || mdSectionList.length === 0 || mdSectionList.length !== htmlSectionList.length) {
return;
}
var editorElt = $("#wmd-input");
var editorScrollTop = editorElt.scrollTop();
var previewElt = $("#wmd-preview");
var previewScrollTop = previewElt.scrollTop();
function animate(srcScrollTop, srcSectionList, destElt, destSectionList, lastDestScrollTop, callback) {
// Find the section corresponding to the offset
var sectionIndex = undefined;
var srcSection = _.find(srcSectionList, function(section, index) {
sectionIndex = index;
return srcScrollTop < section.endOffset;
});
if(srcSection === undefined) {
// Something wrong in the algorithm...
return -9;
}
var posInSection = (srcScrollTop - srcSection.startOffset) / srcSection.height;
var destSection = destSectionList[sectionIndex];
var destScrollTop = destSection.startOffset + destSection.height * posInSection;
destScrollTop = _.min([destScrollTop, destElt.prop('scrollHeight') - destElt.outerHeight()]);
if(Math.abs(destScrollTop - lastDestScrollTop) < 5) {
// Skip the animation in case it's not necessary
return;
}
destElt.animate({scrollTop: destScrollTop}, 600, function() {
callback(destScrollTop);
});
}
// Perform the animation if diff > 5px
if(isScrollPreview === false && Math.abs(editorScrollTop - lastEditorScrollTop) > 5) {
// Animate the preview
lastEditorScrollTop = editorScrollTop;
animate(editorScrollTop, mdSectionList, previewElt, htmlSectionList, lastPreviewScrollTop, function(destScrollTop) {
lastPreviewScrollTop = destScrollTop;
});
}
else if(Math.abs(previewScrollTop - lastPreviewScrollTop) > 5) {
// Animate the editor
lastPreviewScrollTop = previewScrollTop;
animate(previewScrollTop, htmlSectionList, editorElt, mdSectionList, lastEditorScrollTop, function(destScrollTop) {
lastEditorScrollTop = destScrollTop;
});
}
}, 600);
// -9 is less than -5
var lastEditorScrollTop = -9;
var lastPreviewScrollTop = -9;
var skipScrollLink = false;
var isScrollPreview = false;
var runScrollLink = _.debounce(function() {
if(skipScrollLink === true || mdSectionList.length === 0 || mdSectionList.length !== htmlSectionList.length) {
return;
}
var editorElt = $("#wmd-input");
var editorScrollTop = editorElt.scrollTop();
var previewElt = $("#wmd-preview");
var previewScrollTop = previewElt.scrollTop();
function animate(srcScrollTop, srcSectionList, destElt, destSectionList, lastDestScrollTop, callback) {
// Find the section corresponding to the offset
var sectionIndex = undefined;
var srcSection = _.find(srcSectionList, function(section, index) {
sectionIndex = index;
return srcScrollTop < section.endOffset;
});
if(srcSection === undefined) {
// Something wrong in the algorithm...
return -9;
}
var posInSection = (srcScrollTop - srcSection.startOffset) / srcSection.height;
var destSection = destSectionList[sectionIndex];
var destScrollTop = destSection.startOffset + destSection.height * posInSection;
destScrollTop = _.min([
destScrollTop,
destElt.prop('scrollHeight') - destElt.outerHeight()
]);
if(Math.abs(destScrollTop - lastDestScrollTop) < 5) {
// Skip the animation in case it's not necessary
return;
}
destElt.animate({
scrollTop: destScrollTop
}, 600, function() {
callback(destScrollTop);
});
}
// Perform the animation if diff > 5px
if(isScrollPreview === false && Math.abs(editorScrollTop - lastEditorScrollTop) > 5) {
// Animate the preview
lastEditorScrollTop = editorScrollTop;
animate(editorScrollTop, mdSectionList, previewElt, htmlSectionList, lastPreviewScrollTop, function(destScrollTop) {
lastPreviewScrollTop = destScrollTop;
});
}
else if(Math.abs(previewScrollTop - lastPreviewScrollTop) > 5) {
// Animate the editor
lastPreviewScrollTop = previewScrollTop;
animate(previewScrollTop, htmlSectionList, editorElt, mdSectionList, lastEditorScrollTop, function(destScrollTop) {
lastEditorScrollTop = destScrollTop;
});
}
}, 600);
scrollLink.onLayoutConfigure = function(layoutConfig) {
layoutConfig.onresize = buildSections;
};
scrollLink.onLayoutConfigure = function(layoutConfig) {
layoutConfig.onresize = buildSections;
};
scrollLink.onLayoutCreated = function() {
$("#wmd-preview").scroll(function() {
isScrollPreview = true;
runScrollLink();
});
$("#wmd-input").scroll(function() {
isScrollPreview = false;
runScrollLink();
});
};
scrollLink.onLayoutCreated = function() {
$("#wmd-preview").scroll(function() {
isScrollPreview = true;
runScrollLink();
});
$("#wmd-input").scroll(function() {
isScrollPreview = false;
runScrollLink();
});
};
scrollLink.onEditorConfigure = function(editor) {
skipScrollLink = true;
lastPreviewScrollTop = 0;
editor.hooks.chain("onPreviewRefresh", function() {
skipScrollLink = true;
});
};
scrollLink.onEditorConfigure = function(editor) {
skipScrollLink = true;
lastPreviewScrollTop = 0;
editor.hooks.chain("onPreviewRefresh", function() {
skipScrollLink = true;
});
};
scrollLink.onPreviewFinished = function() {
// MathJax may have change the scrolling position. Restore it.
if(lastPreviewScrollTop >= 0) {
$("#wmd-preview").scrollTop(lastPreviewScrollTop);
}
_.defer(function() {
// Modify scroll position of the preview not the editor
lastEditorScrollTop = -9;
buildSections();
// Preview may change if images are loading
$("#wmd-preview img").load(function() {
lastEditorScrollTop = -9;
buildSections();
});
});
};
scrollLink.onPreviewFinished = function() {
// MathJax may have change the scrolling position. Restore it.
if(lastPreviewScrollTop >= 0) {
$("#wmd-preview").scrollTop(lastPreviewScrollTop);
}
_.defer(function() {
// Modify scroll position of the preview not the editor
lastEditorScrollTop = -9;
buildSections();
// Preview may change if images are loading
$("#wmd-preview img").load(function() {
lastEditorScrollTop = -9;
buildSections();
});
});
};
return scrollLink;
return scrollLink;
});

View File

@ -4,115 +4,105 @@ define([
"utils"
], function($, _, utils) {
var toc = {
extensionId: "toc",
extensionName: "Table of content",
var toc = {
extensionId: "toc",
extensionName: "Table of content",
optional: true,
settingsBloc: '<p>Generates a table of content when a [TOC] marker is found.</p>'
};
settingsBloc: '<p>Generates a table of content when a [TOC] marker is found.</p>'
};
// TOC element description
function TocElement(tagName, anchor, text) {
this.tagName = tagName;
this.anchor = anchor;
this.text = text;
this.children = [];
}
TocElement.prototype.childrenToString = function() {
if(this.children.length === 0) {
return "";
}
var result = "<ul>";
_.each(this.children, function(child) {
result += child.toString();
});
result += "</ul>";
return result;
};
TocElement.prototype.toString = function() {
var result = "<li>";
if(this.anchor && this.text) {
result += '<a href="#' + this.anchor + '">' + this.text + '</a>';
}
result += this.childrenToString() + "</li>";
return result;
};
// TOC element description
function TocElement(tagName, anchor, text) {
this.tagName = tagName;
this.anchor = anchor;
this.text = text;
this.children = [];
}
TocElement.prototype.childrenToString = function() {
if(this.children.length === 0) {
return "";
}
var result = "<ul>";
_.each(this.children, function(child) {
result += child.toString();
});
result += "</ul>";
return result;
};
TocElement.prototype.toString = function() {
var result = "<li>";
if(this.anchor && this.text) {
result += '<a href="#' + this.anchor + '">' + this.text + '</a>';
}
result += this.childrenToString() + "</li>";
return result;
};
// Transform flat list of TocElement into a tree
function groupTags(array, level) {
level = level || 1;
var tagName = "H" + level;
var result = [];
// Transform flat list of TocElement into a tree
function groupTags(array, level) {
level = level || 1;
var tagName = "H" + level;
var result = [];
var currentElement = undefined;
function pushCurrentElement() {
if(currentElement !== undefined) {
if(currentElement.children.length > 0) {
currentElement.children = groupTags(currentElement.children, level + 1);
}
result.push(currentElement);
}
}
var currentElement = undefined;
function pushCurrentElement() {
if(currentElement !== undefined) {
if(currentElement.children.length > 0) {
currentElement.children = groupTags(currentElement.children, level + 1);
}
result.push(currentElement);
}
}
_.each(array, function(element, index) {
if(element.tagName != tagName) {
if(currentElement === undefined) {
currentElement = new TocElement();
}
currentElement.children.push(element);
}
else {
pushCurrentElement();
currentElement = element;
}
});
pushCurrentElement();
return result;
}
_.each(array, function(element, index) {
if(element.tagName != tagName) {
if(currentElement === undefined) {
currentElement = new TocElement();
}
currentElement.children.push(element);
}
else {
pushCurrentElement();
currentElement = element;
}
});
pushCurrentElement();
return result;
}
// Build the TOC
function buildToc() {
var anchorList = {};
function createAnchor(element) {
var id = element.prop("id") || utils.slugify(element.text());
var anchor = id;
var index = 0;
while(_.has(anchorList, anchor)) {
anchor = id + "-" + (++index);
}
anchorList[anchor] = true;
// Update the id of the element
element.prop("id", anchor);
return anchor;
}
// Build the TOC
function buildToc() {
var anchorList = {};
function createAnchor(element) {
var id = element.prop("id") || utils.slugify(element.text());
var anchor = id;
var index = 0;
while (_.has(anchorList, anchor)) {
anchor = id + "-" + (++index);
}
anchorList[anchor] = true;
// Update the id of the element
element.prop("id", anchor);
return anchor;
}
var elementList = [];
$("#wmd-preview > h1," +
"#wmd-preview > h2," +
"#wmd-preview > h3," +
"#wmd-preview > h4," +
"#wmd-preview > h5," +
"#wmd-preview > h6").each(function() {
elementList.push(new TocElement(
$(this).prop("tagName"),
createAnchor($(this)),
$(this).text()
));
});
elementList = groupTags(elementList);
return '<div class="toc"><ul>' + elementList.toString() + '</ul></div>';
}
var elementList = [];
$("#wmd-preview > h1," + "#wmd-preview > h2," + "#wmd-preview > h3," + "#wmd-preview > h4," + "#wmd-preview > h5," + "#wmd-preview > h6").each(function() {
elementList.push(new TocElement($(this).prop("tagName"), createAnchor($(this)), $(this).text()));
});
elementList = groupTags(elementList);
return '<div class="toc"><ul>' + elementList.toString() + '</ul></div>';
}
toc.onEditorConfigure = function(editor) {
// Run TOC generation when conversion is finished directly on HTML
editor.hooks.chain("onPreviewRefresh", function() {
var toc = buildToc();
var html = $("#wmd-preview").html();
html = html.replace(/<p>\[TOC\]<\/p>/g, toc);
$("#wmd-preview").html(html);
});
};
toc.onEditorConfigure = function(editor) {
// Run TOC generation when conversion is finished directly on HTML
editor.hooks.chain("onPreviewRefresh", function() {
var toc = buildToc();
var html = $("#wmd-preview").html();
html = html.replace(/<p>\[TOC\]<\/p>/g, toc);
$("#wmd-preview").html(html);
});
};
return toc;
return toc;
});

View File

@ -1,24 +1,25 @@
define([
"jquery",
"underscore"
"jquery",
"underscore"
], function($, _) {
var workingIndicator = {
extensionId: "workingIndicator",
extensionName: "Working indicator",
settingsBloc: '<p>Displays an animated image when a network operation is running.</p>'
};
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");
}
};
workingIndicator.onAsyncRunning = function(isRunning) {
if(isRunning === false) {
$(".working-indicator").removeClass("show");
$("body").removeClass("working");
}
else {
$(".working-indicator").addClass("show");
$("body").addClass("working");
}
};
return workingIndicator;
return workingIndicator;
});

View File

@ -9,346 +9,315 @@ define([
"lib/text!../WELCOME.md"
], function($, _, core, utils, settings, extensionMgr, fileSystem, welcomeContent) {
var fileMgr = {};
var fileMgr = {};
// Defines a file descriptor in the file system (fileDesc objects)
function FileDescriptor(fileIndex, title, syncLocations, publishLocations) {
this.fileIndex = fileIndex;
this.title = title;
this.syncLocations = syncLocations || {};
this.publishLocations = publishLocations || {};
}
FileDescriptor.prototype.getContent = function() {
return localStorage[this.fileIndex + ".content"];
};
FileDescriptor.prototype.setContent = function(content) {
localStorage[this.fileIndex + ".content"] = content;
extensionMgr.onContentChanged(this);
};
FileDescriptor.prototype.setTitle = function(title) {
this.title = title;
localStorage[this.fileIndex + ".title"] = title;
extensionMgr.onTitleChanged(this);
};
// Defines a file descriptor in the file system (fileDesc objects)
function FileDescriptor(fileIndex, title, syncLocations, publishLocations) {
this.fileIndex = fileIndex;
this._title = title;
this.__defineGetter__("title", function() {
return this._title;
});
this.__defineSetter__("title", function(title) {
this._title = title;
localStorage[this.fileIndex + ".title"] = title;
extensionMgr.onTitleChanged(this);
});
this.__defineGetter__("content", function() {
return localStorage[this.fileIndex + ".content"];
});
this.__defineSetter__("content", function(content) {
localStorage[this.fileIndex + ".content"] = content;
extensionMgr.onContentChanged(this);
});
this.syncLocations = syncLocations || {};
this.publishLocations = publishLocations || {};
}
// Load file descriptors from localStorage
_.chain(
localStorage["file.list"].split(";")
).compact().each(function(fileIndex) {
fileSystem[fileIndex] = new FileDescriptor(fileIndex, localStorage[fileIndex + ".title"]);
});
// Load file descriptors from localStorage
_.chain(localStorage["file.list"].split(";")).compact().each(function(fileIndex) {
fileSystem[fileIndex] = new FileDescriptor(fileIndex, localStorage[fileIndex + ".title"]);
});
// Defines the current file
var currentFile = undefined;
fileMgr.getCurrentFile = function() {
return currentFile;
};
fileMgr.isCurrentFile = function(fileDesc) {
return fileDesc === currentFile;
};
fileMgr.setCurrentFile = function(fileDesc) {
currentFile = fileDesc;
if(fileDesc === undefined) {
localStorage.removeItem("file.current");
}
else if(fileDesc.fileIndex != TEMPORARY_FILE_INDEX) {
localStorage["file.current"] = fileDesc.fileIndex;
}
};
// Defines the current file
var currentFile = undefined;
fileMgr.getCurrentFile = function() {
return currentFile;
};
fileMgr.isCurrentFile = function(fileDesc) {
return fileDesc === currentFile;
};
fileMgr.setCurrentFile = function(fileDesc) {
currentFile = fileDesc;
if(fileDesc === undefined) {
localStorage.removeItem("file.current");
}
else if(fileDesc.fileIndex != TEMPORARY_FILE_INDEX) {
localStorage["file.current"] = fileDesc.fileIndex;
}
};
// Caution: this function recreates the editor (reset undo operations)
fileMgr.selectFile = function(fileDesc) {
fileDesc = fileDesc || fileMgr.getCurrentFile();
// Caution: this function recreates the editor (reset undo operations)
fileMgr.selectFile = function(fileDesc) {
fileDesc = fileDesc || fileMgr.getCurrentFile();
if(fileDesc === undefined) {
var fileSystemSize = _.size(fileSystem);
// If fileSystem empty create one file
if (fileSystemSize === 0) {
fileDesc = fileMgr.createFile(WELCOME_DOCUMENT_TITLE, welcomeContent);
}
else {
var fileIndex = localStorage["file.current"];
// If no file is selected take the last created
if(fileIndex === undefined) {
fileIndex = _.keys(fileSystem)[fileSystemSize - 1];
}
fileDesc = fileSystem[fileIndex];
}
}
if(fileDesc === undefined) {
var fileSystemSize = _.size(fileSystem);
// If fileSystem empty create one file
if(fileSystemSize === 0) {
fileDesc = fileMgr.createFile(WELCOME_DOCUMENT_TITLE, welcomeContent);
}
else {
var fileIndex = localStorage["file.current"];
// If no file is selected take the last created
if(fileIndex === undefined) {
fileIndex = _.keys(fileSystem)[fileSystemSize - 1];
}
fileDesc = fileSystem[fileIndex];
}
}
if(fileMgr.isCurrentFile(fileDesc) === false) {
fileMgr.setCurrentFile(fileDesc);
if(fileMgr.isCurrentFile(fileDesc) === false) {
fileMgr.setCurrentFile(fileDesc);
// Notify extensions
extensionMgr.onFileSelected(fileDesc);
// Notify extensions
extensionMgr.onFileSelected(fileDesc);
// Hide the viewer pencil button
if(fileDesc.fileIndex == TEMPORARY_FILE_INDEX) {
$(".action-edit-document").removeClass("hide");
}
else {
$(".action-edit-document").addClass("hide");
}
}
// Hide the viewer pencil button
if(fileDesc.fileIndex == TEMPORARY_FILE_INDEX) {
$(".action-edit-document").removeClass("hide");
}
else {
$(".action-edit-document").addClass("hide");
}
}
// Recreate the editor
$("#wmd-input").val(fileDesc.getContent());
core.createEditor(function() {
// Callback to save content when textarea changes
fileMgr.saveFile();
});
};
// Recreate the editor
$("#wmd-input").val(fileDesc.content);
core.createEditor(function() {
// Callback to save content when textarea changes
fileMgr.saveFile();
});
};
fileMgr.createFile = function(title, content, syncLocations, isTemporary) {
content = content !== undefined ? content : settings.defaultContent;
if (!title) {
// Create a file title
title = DEFAULT_FILE_TITLE;
var indicator = 2;
while(_.some(fileSystem, function(fileDesc) {
return fileDesc.title == title;
})) {
title = DEFAULT_FILE_TITLE + indicator++;
}
}
fileMgr.createFile = function(title, content, syncLocations, isTemporary) {
content = content !== undefined ? content : settings.defaultContent;
if(!title) {
// Create a file title
title = DEFAULT_FILE_TITLE;
var indicator = 2;
while (_.some(fileSystem, function(fileDesc) {
return fileDesc.title == title;
})) {
title = DEFAULT_FILE_TITLE + indicator++;
}
}
// Generate a unique fileIndex
var fileIndex = TEMPORARY_FILE_INDEX;
if(!isTemporary) {
do {
fileIndex = "file." + utils.randomString();
} while(_.has(fileSystem, fileIndex));
}
// Generate a unique fileIndex
var fileIndex = TEMPORARY_FILE_INDEX;
if(!isTemporary) {
do {
fileIndex = "file." + utils.randomString();
} while (_.has(fileSystem, fileIndex));
}
// syncIndex associations
syncLocations = syncLocations || {};
var sync = _.reduce(syncLocations, function(sync, syncAttributes, syncIndex) {
return sync + syncIndex + ";";
}, ";");
// syncIndex associations
syncLocations = syncLocations || {};
var sync = _.reduce(syncLocations, function(sync, syncAttributes, syncIndex) {
return sync + syncIndex + ";";
}, ";");
localStorage[fileIndex + ".title"] = title;
localStorage[fileIndex + ".content"] = content;
localStorage[fileIndex + ".sync"] = sync;
localStorage[fileIndex + ".publish"] = ";";
localStorage[fileIndex + ".title"] = title;
localStorage[fileIndex + ".content"] = content;
localStorage[fileIndex + ".sync"] = sync;
localStorage[fileIndex + ".publish"] = ";";
// Create the file descriptor
var fileDesc = new FileDescriptor(fileIndex, title, syncLocations);
// Create the file descriptor
var fileDesc = new FileDescriptor(fileIndex, title, syncLocations);
// Add the index to the file list
if(!isTemporary) {
localStorage["file.list"] += fileIndex + ";";
fileSystem[fileIndex] = fileDesc;
extensionMgr.onFileCreated(fileDesc);
}
return fileDesc;
};
// Add the index to the file list
if(!isTemporary) {
localStorage["file.list"] += fileIndex + ";";
fileSystem[fileIndex] = fileDesc;
extensionMgr.onFileCreated(fileDesc);
}
return fileDesc;
};
fileMgr.deleteFile = function(fileDesc) {
fileDesc = fileDesc || fileMgr.getCurrentFile();
if(fileMgr.isCurrentFile(fileDesc) === true) {
// Unset the current fileDesc
fileMgr.setCurrentFile();
// Refresh the editor with an other file
fileMgr.selectFile();
}
fileMgr.deleteFile = function(fileDesc) {
fileDesc = fileDesc || fileMgr.getCurrentFile();
if(fileMgr.isCurrentFile(fileDesc) === true) {
// Unset the current fileDesc
fileMgr.setCurrentFile();
// Refresh the editor with an other file
fileMgr.selectFile();
}
// Remove synchronized locations
_.each(fileDesc.syncLocations, function(syncAttributes) {
fileMgr.removeSync(syncAttributes, true);
});
// Remove synchronized locations
_.each(fileDesc.syncLocations, function(syncAttributes) {
fileMgr.removeSync(syncAttributes, true);
});
// Remove publish locations
_.each(fileDesc.publishLocations, function(publishAttributes) {
fileMgr.removePublish(publishAttributes, true);
});
// Remove publish locations
_.each(fileDesc.publishLocations, function(publishAttributes) {
fileMgr.removePublish(publishAttributes, true);
});
// Remove the index from the file list
var fileIndex = fileDesc.fileIndex;
localStorage["file.list"] = localStorage["file.list"].replace(";"
+ fileIndex + ";", ";");
// Remove the index from the file list
var fileIndex = fileDesc.fileIndex;
localStorage["file.list"] = localStorage["file.list"].replace(";" + fileIndex + ";", ";");
localStorage.removeItem(fileIndex + ".title");
localStorage.removeItem(fileIndex + ".content");
localStorage.removeItem(fileIndex + ".sync");
localStorage.removeItem(fileIndex + ".publish");
localStorage.removeItem(fileIndex + ".title");
localStorage.removeItem(fileIndex + ".content");
localStorage.removeItem(fileIndex + ".sync");
localStorage.removeItem(fileIndex + ".publish");
fileSystem.removeItem(fileIndex);
extensionMgr.onFileDeleted(fileDesc);
};
fileSystem.removeItem(fileIndex);
extensionMgr.onFileDeleted(fileDesc);
};
// Save current file in localStorage
fileMgr.saveFile = function() {
var content = $("#wmd-input").val();
var fileDesc = fileMgr.getCurrentFile();
fileDesc.setContent(content);
};
// Save current file in localStorage
fileMgr.saveFile = function() {
var fileDesc = fileMgr.getCurrentFile();
fileDesc.content = $("#wmd-input").val();
};
// Add a synchronized location to a file
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
extensionMgr.onSyncExportSuccess(fileDesc, syncAttributes);
};
// Add a synchronized location to a file
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
extensionMgr.onSyncExportSuccess(fileDesc, syncAttributes);
};
// Remove a synchronized location
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 + ";", ";");
}
// Remove sync attributes
localStorage.removeItem(syncAttributes.syncIndex);
fileDesc.syncLocations.removeItem(syncAttributes.syncIndex);
if(!skipExtensions) {
extensionMgr.onSyncRemoved(fileDesc, syncAttributes);
}
};
// Remove a synchronized location
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 + ";", ";");
}
// Remove sync attributes
localStorage.removeItem(syncAttributes.syncIndex);
fileDesc.syncLocations.removeItem(syncAttributes.syncIndex);
if(!skipExtensions) {
extensionMgr.onSyncRemoved(fileDesc, syncAttributes);
}
};
// Get the file descriptor associated to a syncIndex
fileMgr.getFileFromSyncIndex = function(syncIndex) {
return _.find(fileSystem, function(fileDesc) {
return _.has(fileDesc.syncLocations, syncIndex);
});
};
// Get the file descriptor associated to a syncIndex
fileMgr.getFileFromSyncIndex = function(syncIndex) {
return _.find(fileSystem, function(fileDesc) {
return _.has(fileDesc.syncLocations, syncIndex);
});
};
// Get syncAttributes from syncIndex
fileMgr.getSyncAttributes = function(syncIndex) {
var fileDesc = fileMgr.getFileFromSyncIndex(syncIndex);
return fileDesc && fileDesc.syncLocations[syncIndex];
};
// Get syncAttributes from syncIndex
fileMgr.getSyncAttributes = function(syncIndex) {
var fileDesc = fileMgr.getFileFromSyncIndex(syncIndex);
return fileDesc && fileDesc.syncLocations[syncIndex];
};
// Returns true if provider has locations to synchronize
fileMgr.hasSync = function(provider) {
return _.some(fileSystem, function(fileDesc) {
return _.some(fileDesc.syncLocations, function(syncAttributes) {
return syncAttributes.provider === provider;
});
});
};
// Returns true if provider has locations to synchronize
fileMgr.hasSync = function(provider) {
return _.some(fileSystem, function(fileDesc) {
return _.some(fileDesc.syncLocations, function(syncAttributes) {
return syncAttributes.provider === provider;
});
});
};
// Add a publishIndex (publish location) to a file
fileMgr.addPublish = function(fileDesc, publishAttributes) {
localStorage[fileDesc.fileIndex + ".publish"] += publishAttributes.publishIndex + ";";
fileDesc.publishLocations[publishAttributes.publishIndex] = publishAttributes;
extensionMgr.onNewPublishSuccess(fileDesc, publishAttributes);
};
// Add a publishIndex (publish location) to a file
fileMgr.addPublish = function(fileDesc, publishAttributes) {
localStorage[fileDesc.fileIndex + ".publish"] += publishAttributes.publishIndex + ";";
fileDesc.publishLocations[publishAttributes.publishIndex] = publishAttributes;
extensionMgr.onNewPublishSuccess(fileDesc, publishAttributes);
};
// Remove a publishIndex (publish location)
fileMgr.removePublish = function(publishAttributes, skipExtensions) {
var fileDesc = fileMgr.getFileFromPublishIndex(publishAttributes.publishIndex);
if(fileDesc !== undefined) {
localStorage[fileDesc.fileIndex + ".publish"] = localStorage[fileDesc.fileIndex + ".publish"].replace(";"
+ publishAttributes.publishIndex + ";", ";");
}
// Remove publish attributes
localStorage.removeItem(publishAttributes.publishIndex);
fileDesc.publishLocations.removeItem(publishAttributes.publishIndex);
if(!skipExtensions) {
extensionMgr.onPublishRemoved(fileDesc, publishAttributes);
}
};
// Remove a publishIndex (publish location)
fileMgr.removePublish = function(publishAttributes, skipExtensions) {
var fileDesc = fileMgr.getFileFromPublishIndex(publishAttributes.publishIndex);
if(fileDesc !== undefined) {
localStorage[fileDesc.fileIndex + ".publish"] = localStorage[fileDesc.fileIndex + ".publish"].replace(";" + publishAttributes.publishIndex + ";", ";");
}
// Remove publish attributes
localStorage.removeItem(publishAttributes.publishIndex);
fileDesc.publishLocations.removeItem(publishAttributes.publishIndex);
if(!skipExtensions) {
extensionMgr.onPublishRemoved(fileDesc, publishAttributes);
}
};
// Get the file descriptor associated to a publishIndex
fileMgr.getFileFromPublishIndex = function(publishIndex) {
return _.find(fileSystem, function(fileDesc) {
return _.has(fileDesc.publishLocations, publishIndex);
});
};
// Get the file descriptor associated to a publishIndex
fileMgr.getFileFromPublishIndex = function(publishIndex) {
return _.find(fileSystem, function(fileDesc) {
return _.has(fileDesc.publishLocations, publishIndex);
});
};
// Filter for search input in file selector
function filterFileSelector(filter) {
var liList = $("#file-selector li:not(.stick)");
liList.show();
if(filter) {
var words = filter.toLowerCase().split(/\s+/);
liList.each(function() {
var fileTitle = $(this).text().toLowerCase();
if(_.some(words, function(word) {
return fileTitle.indexOf(word) === -1;
})) {
$(this).hide();
}
});
}
}
core.onReady(function() {
core.onReady(function() {
fileMgr.selectFile();
fileMgr.selectFile();
$(".action-create-file").click(function() {
var fileDesc = fileMgr.createFile();
fileMgr.selectFile(fileDesc);
var wmdInput = $("#wmd-input").focus().get(0);
if(wmdInput.setSelectionRange) {
wmdInput.setSelectionRange(0, 0);
}
$("#file-title").click();
});
$(".action-remove-file").click(function() {
fileMgr.deleteFile();
});
$("#file-title").click(function() {
if(viewerMode === true) {
return;
}
$(this).hide();
var fileTitleInput = $("#file-title-input").show();
_.defer(function() {
fileTitleInput.focus().get(0).select();
});
});
function applyTitle(input) {
input.hide();
$("#file-title").show();
var title = $.trim(input.val());
var fileDesc = fileMgr.getCurrentFile();
if(title && title != fileDesc.title) {
fileDesc.title = title;
}
input.val(fileDesc.title);
$("#wmd-input").focus();
}
$("#file-title-input").blur(function() {
applyTitle($(this));
}).keyup(function(e) {
if(e.keyCode == 13) {
applyTitle($(this));
}
if(e.keyCode == 27) {
$(this).val("");
applyTitle($(this));
}
});
$(".action-open-stackedit").click(function() {
window.location.href = ".";
});
$(".action-edit-document").click(function() {
var content = $("#wmd-input").val();
var title = fileMgr.getCurrentFile().title;
var fileDesc = fileMgr.createFile(title, content);
fileMgr.selectFile(fileDesc);
window.location.href = ".";
});
$(".action-welcome-file").click(function() {
var fileDesc = fileMgr.createFile(WELCOME_DOCUMENT_TITLE, welcomeContent);
fileMgr.selectFile(fileDesc);
});
});
$(".action-create-file").click(function() {
var fileDesc = fileMgr.createFile();
fileMgr.selectFile(fileDesc);
var wmdInput = $("#wmd-input").focus().get(0);
if(wmdInput.setSelectionRange) {
wmdInput.setSelectionRange(0, 0);
}
$("#file-title").click();
});
$(".action-remove-file").click(function() {
fileMgr.deleteFile();
});
$("#file-title").click(function() {
if(viewerMode === true) {
return;
}
$(this).hide();
var fileTitleInput = $("#file-title-input").show();
_.defer(function() {
fileTitleInput.focus().get(0).select();
});
});
function applyTitle(input) {
input.hide();
$("#file-title").show();
var title = $.trim(input.val());
var fileDesc = fileMgr.getCurrentFile();
if (title && title != fileDesc.title) {
fileDesc.setTitle(title);
}
input.val(fileDesc.title);
$("#wmd-input").focus();
}
$("#file-title-input").blur(function() {
applyTitle($(this));
}).keyup(function(e) {
if (e.keyCode == 13) {
applyTitle($(this));
}
if (e.keyCode == 27) {
$(this).val("");
applyTitle($(this));
}
});
$(".action-open-file").click(function() {
filterFileSelector();
_.defer(function() {
$("#file-search").val("").focus();
});
});
$("#file-search").keyup(function() {
filterFileSelector($(this).val());
}).click(function(event) {
event.stopPropagation();
});
$(".action-open-stackedit").click(function() {
window.location.href = ".";
});
$(".action-edit-document").click(function() {
var content = $("#wmd-input").val();
var title = fileMgr.getCurrentFile().title;
var fileDesc = fileMgr.createFile(title, content);
fileMgr.selectFile(fileDesc);
window.location.href = ".";
});
$(".action-welcome-file").click(function() {
var fileDesc = fileMgr.createFile(WELCOME_DOCUMENT_TITLE, welcomeContent);
fileMgr.selectFile(fileDesc);
});
});
extensionMgr.onFileMgrCreated(fileMgr);
return fileMgr;
extensionMgr.onFileMgrCreated(fileMgr);
return fileMgr;
});

View File

@ -7,279 +7,274 @@ define([
"google-helper"
], function(_, core, utils, extensionMgr, fileMgr, googleHelper) {
var PROVIDER_GDRIVE = "gdrive";
var PROVIDER_GDRIVE = "gdrive";
var gdriveProvider = {
providerId: PROVIDER_GDRIVE,
providerName: "Google Drive",
defaultPublishFormat: "template",
exportPreferencesInputIds: ["gdrive-parentid"]
};
var gdriveProvider = {
providerId: PROVIDER_GDRIVE,
providerName: "Google Drive",
defaultPublishFormat: "template",
exportPreferencesInputIds: [
"gdrive-parentid"
]
};
function createSyncIndex(id) {
return "sync." + PROVIDER_GDRIVE + "." + id;
}
function createSyncIndex(id) {
return "sync." + PROVIDER_GDRIVE + "." + id;
}
function createSyncAttributes(id, etag, content, title) {
var syncAttributes = {};
syncAttributes.provider = gdriveProvider;
syncAttributes.id = id;
syncAttributes.etag = etag;
syncAttributes.contentCRC = utils.crc32(content);
syncAttributes.titleCRC = utils.crc32(title);
syncAttributes.syncIndex = createSyncIndex(id);
utils.storeAttributes(syncAttributes);
return syncAttributes;
}
function createSyncAttributes(id, etag, content, title) {
var syncAttributes = {};
syncAttributes.provider = gdriveProvider;
syncAttributes.id = id;
syncAttributes.etag = etag;
syncAttributes.contentCRC = utils.crc32(content);
syncAttributes.titleCRC = utils.crc32(title);
syncAttributes.syncIndex = createSyncIndex(id);
utils.storeAttributes(syncAttributes);
return syncAttributes;
}
function importFilesFromIds(ids) {
googleHelper.downloadMetadata(ids, function(error, result) {
if(error) {
return;
}
googleHelper.downloadContent(result, function(error, result) {
if(error) {
return;
}
var fileDescList = [];
_.each(result, function(file) {
var syncAttributes = createSyncAttributes(file.id, file.etag, file.content, file.title);
var syncLocations = {};
syncLocations[syncAttributes.syncIndex] = syncAttributes;
var fileDesc = fileMgr.createFile(file.title, file.content, syncLocations);
fileMgr.selectFile(fileDesc);
fileDescList.push(fileDesc);
});
extensionMgr.onSyncImportSuccess(fileDescList, gdriveProvider);
});
});
};
function importFilesFromIds(ids) {
googleHelper.downloadMetadata(ids, function(error, result) {
if(error) {
return;
}
googleHelper.downloadContent(result, function(error, result) {
if(error) {
return;
}
var fileDescList = [];
_.each(result, function(file) {
var syncAttributes = createSyncAttributes(file.id, file.etag, file.content, file.title);
var syncLocations = {};
syncLocations[syncAttributes.syncIndex] = syncAttributes;
var fileDesc = fileMgr.createFile(file.title, file.content, syncLocations);
fileMgr.selectFile(fileDesc);
fileDescList.push(fileDesc);
});
extensionMgr.onSyncImportSuccess(fileDescList, gdriveProvider);
});
});
}
;
gdriveProvider.importFiles = function() {
googleHelper.picker(function(error, ids) {
if(error || ids.length === 0) {
return;
}
var importIds = [];
_.each(ids, function(id) {
var syncIndex = createSyncIndex(id);
var fileDesc = fileMgr.getFileFromSyncIndex(syncIndex);
if(fileDesc !== undefined) {
extensionMgr.onError('"' + fileDesc.title + '" was already imported');
return;
}
importIds.push(id);
});
importFilesFromIds(importIds);
});
};
gdriveProvider.importFiles = function() {
googleHelper.picker(function(error, ids) {
if(error || ids.length === 0) {
return;
}
var importIds = [];
_.each(ids, function(id) {
var syncIndex = createSyncIndex(id);
var fileDesc = fileMgr.getFileFromSyncIndex(syncIndex);
if(fileDesc !== undefined) {
extensionMgr.onError('"' + fileDesc.title + '" was already imported');
return;
}
importIds.push(id);
});
importFilesFromIds(importIds);
});
};
gdriveProvider.exportFile = function(event, title, content, callback) {
var parentId = utils.getInputTextValue("#input-sync-export-gdrive-parentid");
googleHelper.upload(undefined, parentId, title, content, undefined, function(error, result) {
if (error) {
callback(error);
return;
}
var syncAttributes = createSyncAttributes(result.id, result.etag, content, title);
callback(undefined, syncAttributes);
});
};
gdriveProvider.exportFile = function(event, title, content, callback) {
var parentId = utils.getInputTextValue("#input-sync-export-gdrive-parentid");
googleHelper.upload(undefined, parentId, title, content, undefined, function(error, result) {
if(error) {
callback(error);
return;
}
var syncAttributes = createSyncAttributes(result.id, result.etag, content, title);
callback(undefined, syncAttributes);
});
};
gdriveProvider.exportManual = function(event, title, content, callback) {
var id = utils.getInputTextValue("#input-sync-manual-gdrive-id", event);
if(!id) {
return;
}
// Check that file is not synchronized with another an existing document
var syncIndex = createSyncIndex(id);
var fileDesc = fileMgr.getFileFromSyncIndex(syncIndex);
if(fileDesc !== undefined) {
extensionMgr.onError('File ID is already synchronized with "' + fileDesc.title + '"');
callback(true);
return;
}
googleHelper.upload(id, undefined, title, content, undefined, function(error, result) {
if (error) {
callback(error);
return;
}
var syncAttributes = createSyncAttributes(result.id, result.etag, content, title);
callback(undefined, syncAttributes);
});
};
gdriveProvider.exportManual = function(event, title, content, callback) {
var id = utils.getInputTextValue("#input-sync-manual-gdrive-id", event);
if(!id) {
return;
}
// Check that file is not synchronized with another an existing document
var syncIndex = createSyncIndex(id);
var fileDesc = fileMgr.getFileFromSyncIndex(syncIndex);
if(fileDesc !== undefined) {
extensionMgr.onError('File ID is already synchronized with "' + fileDesc.title + '"');
callback(true);
return;
}
googleHelper.upload(id, undefined, title, content, undefined, function(error, result) {
if(error) {
callback(error);
return;
}
var syncAttributes = createSyncAttributes(result.id, result.etag, content, title);
callback(undefined, syncAttributes);
});
};
gdriveProvider.syncUp = function(uploadContent, uploadContentCRC, uploadTitle, uploadTitleCRC, syncAttributes, callback) {
var syncContentCRC = syncAttributes.contentCRC;
var syncTitleCRC = syncAttributes.titleCRC;
// Skip if CRC has not changed
if(uploadContentCRC == syncContentCRC && uploadTitleCRC == syncTitleCRC) {
callback(undefined, false);
return;
}
googleHelper.upload(syncAttributes.id, undefined, uploadTitle, uploadContent, syncAttributes.etag, function(error, result) {
if(error) {
callback(error, true);
return;
}
syncAttributes.etag = result.etag;
syncAttributes.contentCRC = uploadContentCRC;
syncAttributes.titleCRC = uploadTitleCRC;
callback(undefined, true);
});
};
gdriveProvider.syncUp = function(uploadContent, uploadContentCRC, uploadTitle, uploadTitleCRC, syncAttributes, callback) {
var syncContentCRC = syncAttributes.contentCRC;
var syncTitleCRC = syncAttributes.titleCRC;
// Skip if CRC has not changed
if(uploadContentCRC == syncContentCRC && uploadTitleCRC == syncTitleCRC) {
callback(undefined, false);
return;
}
googleHelper.upload(syncAttributes.id, undefined, uploadTitle, uploadContent, syncAttributes.etag, function(error, result) {
if(error) {
callback(error, true);
return;
}
syncAttributes.etag = result.etag;
syncAttributes.contentCRC = uploadContentCRC;
syncAttributes.titleCRC = uploadTitleCRC;
callback(undefined, true);
});
};
gdriveProvider.syncDown = function(callback) {
var lastChangeId = parseInt(localStorage[PROVIDER_GDRIVE + ".lastChangeId"]);
googleHelper.checkChanges(lastChangeId, function(error, changes, newChangeId) {
if (error) {
callback(error);
return;
}
var interestingChanges = [];
_.each(changes, function(change) {
var syncIndex = createSyncIndex(change.fileId);
var syncAttributes = fileMgr.getSyncAttributes(syncIndex);
if(syncAttributes === undefined) {
return;
}
// Store syncAttributes to avoid 2 times searching
change.syncAttributes = syncAttributes;
// Delete
if(change.deleted === true) {
interestingChanges.push(change);
return;
}
// Modify
if(syncAttributes.etag != change.file.etag) {
interestingChanges.push(change);
}
});
googleHelper.downloadContent(interestingChanges, function(error, changes) {
if (error) {
callback(error);
return;
}
_.each(changes, function(change) {
var syncAttributes = change.syncAttributes;
var syncIndex = syncAttributes.syncIndex;
var fileDesc = fileMgr.getFileFromSyncIndex(syncIndex);
// No file corresponding (file may have been deleted locally)
if(fileDesc === undefined) {
return;
}
var localTitle = fileDesc.title;
// File deleted
if (change.deleted === true) {
extensionMgr.onError('"' + localTitle + '" has been removed from Google Drive.');
fileMgr.removeSync(syncAttributes);
return;
}
var localTitleChanged = syncAttributes.titleCRC != utils.crc32(localTitle);
var localContent = fileDesc.getContent();
var localContentChanged = syncAttributes.contentCRC != utils.crc32(localContent);
var file = change.file;
gdriveProvider.syncDown = function(callback) {
var lastChangeId = parseInt(localStorage[PROVIDER_GDRIVE + ".lastChangeId"]);
googleHelper.checkChanges(lastChangeId, function(error, changes, newChangeId) {
if(error) {
callback(error);
return;
}
var interestingChanges = [];
_.each(changes, function(change) {
var syncIndex = createSyncIndex(change.fileId);
var syncAttributes = fileMgr.getSyncAttributes(syncIndex);
if(syncAttributes === undefined) {
return;
}
// Store syncAttributes to avoid 2 times searching
change.syncAttributes = syncAttributes;
// Delete
if(change.deleted === true) {
interestingChanges.push(change);
return;
}
// Modify
if(syncAttributes.etag != change.file.etag) {
interestingChanges.push(change);
}
});
googleHelper.downloadContent(interestingChanges, function(error, changes) {
if(error) {
callback(error);
return;
}
_.each(changes, function(change) {
var syncAttributes = change.syncAttributes;
var syncIndex = syncAttributes.syncIndex;
var fileDesc = fileMgr.getFileFromSyncIndex(syncIndex);
// No file corresponding (file may have been deleted
// locally)
if(fileDesc === undefined) {
return;
}
var localTitle = fileDesc.title;
// File deleted
if(change.deleted === true) {
extensionMgr.onError('"' + localTitle + '" has been removed from Google Drive.');
fileMgr.removeSync(syncAttributes);
return;
}
var localTitleChanged = syncAttributes.titleCRC != utils.crc32(localTitle);
var localContent = fileDesc.content;
var localContentChanged = syncAttributes.contentCRC != utils.crc32(localContent);
var file = change.file;
var remoteTitleCRC = utils.crc32(file.title);
var remoteTitleChanged = syncAttributes.titleCRC != remoteTitleCRC;
var fileTitleChanged = localTitle != file.title;
var remoteContentCRC = utils.crc32(file.content);
var remoteContentChanged = syncAttributes.contentCRC != remoteContentCRC;
var fileContentChanged = localContent != file.content;
// Conflict detection
if ((fileTitleChanged === true && localTitleChanged === true && remoteTitleChanged === true)
|| (fileContentChanged === true && localContentChanged === true && remoteContentChanged === true)) {
fileMgr.createFile(localTitle + " (backup)", localContent);
extensionMgr.onMessage('Conflict detected on "' + localTitle + '". A backup has been created locally.');
}
// If file title changed
if(fileTitleChanged && remoteTitleChanged === true) {
fileDesc.setTitle(file.title);
extensionMgr.onMessage('"' + localTitle + '" has been renamed to "' + file.title + '" on Google Drive.');
}
// If file content changed
if(fileContentChanged && remoteContentChanged === true) {
fileDesc.setContent(file.content);
extensionMgr.onMessage('"' + file.title + '" has been updated from Google Drive.');
if(fileMgr.isCurrentFile(fileDesc)) {
fileMgr.selectFile(); // Refresh editor
}
}
// Update syncAttributes
syncAttributes.etag = file.etag;
syncAttributes.contentCRC = remoteContentCRC;
syncAttributes.titleCRC = remoteTitleCRC;
utils.storeAttributes(syncAttributes);
});
localStorage[PROVIDER_GDRIVE + ".lastChangeId"] = newChangeId;
callback();
});
});
};
var fileContentChanged = localContent != file.content;
// Conflict detection
if((fileTitleChanged === true && localTitleChanged === true && remoteTitleChanged === true) || (fileContentChanged === true && localContentChanged === true && remoteContentChanged === true)) {
fileMgr.createFile(localTitle + " (backup)", localContent);
extensionMgr.onMessage('Conflict detected on "' + localTitle + '". A backup has been created locally.');
}
// If file title changed
if(fileTitleChanged && remoteTitleChanged === true) {
fileDesc.title = file.title;
extensionMgr.onMessage('"' + localTitle + '" has been renamed to "' + file.title + '" on Google Drive.');
}
// If file content changed
if(fileContentChanged && remoteContentChanged === true) {
fileDesc.content = file.content;
extensionMgr.onMessage('"' + file.title + '" has been updated from Google Drive.');
if(fileMgr.isCurrentFile(fileDesc)) {
fileMgr.selectFile(); // Refresh editor
}
}
// Update syncAttributes
syncAttributes.etag = file.etag;
syncAttributes.contentCRC = remoteContentCRC;
syncAttributes.titleCRC = remoteTitleCRC;
utils.storeAttributes(syncAttributes);
});
localStorage[PROVIDER_GDRIVE + ".lastChangeId"] = newChangeId;
callback();
});
});
};
gdriveProvider.publish = function(publishAttributes, title, content, callback) {
googleHelper.upload(
publishAttributes.id,
undefined,
publishAttributes.fileName || title,
content,
undefined,
function(error, result) {
if(error) {
callback(error);
return;
}
publishAttributes.id = result.id;
callback();
}
);
};
gdriveProvider.publish = function(publishAttributes, title, content, callback) {
googleHelper.upload(publishAttributes.id, undefined, publishAttributes.fileName || title, content, undefined, function(error, result) {
if(error) {
callback(error);
return;
}
publishAttributes.id = result.id;
callback();
});
};
gdriveProvider.newPublishAttributes = function(event) {
var publishAttributes = {};
publishAttributes.id = utils.getInputTextValue("#input-publish-gdrive-fileid");
publishAttributes.fileName = utils.getInputTextValue("#input-publish-gdrive-filename");
if(event.isPropagationStopped()) {
return undefined;
}
return publishAttributes;
};
gdriveProvider.newPublishAttributes = function(event) {
var publishAttributes = {};
publishAttributes.id = utils.getInputTextValue("#input-publish-gdrive-fileid");
publishAttributes.fileName = utils.getInputTextValue("#input-publish-gdrive-filename");
if(event.isPropagationStopped()) {
return undefined;
}
return publishAttributes;
};
core.onReady(function() {
var state = localStorage[PROVIDER_GDRIVE + ".state"];
if(state === undefined) {
return;
}
localStorage.removeItem(PROVIDER_GDRIVE + ".state");
state = JSON.parse(state);
if (state.action == "create") {
googleHelper.upload(undefined, state.folderId, GDRIVE_DEFAULT_FILE_TITLE,
"", undefined, function(error, file) {
if(error) {
return;
}
var syncAttributes = createSyncAttributes(file.id, file.etag, file.content, file.title);
var syncLocations = {};
syncLocations[syncAttributes.syncIndex] = syncAttributes;
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 = fileMgr.getFileFromSyncIndex(syncIndex);
if(fileDesc !== undefined) {
fileMgr.selectFile(fileDesc);
}
else {
importIds.push(id);
}
});
importFilesFromIds(importIds);
}
});
core.onReady(function() {
var state = localStorage[PROVIDER_GDRIVE + ".state"];
if(state === undefined) {
return;
}
localStorage.removeItem(PROVIDER_GDRIVE + ".state");
state = JSON.parse(state);
if(state.action == "create") {
googleHelper.upload(undefined, state.folderId, GDRIVE_DEFAULT_FILE_TITLE, "", undefined, function(error, file) {
if(error) {
return;
}
var syncAttributes = createSyncAttributes(file.id, file.etag, file.content, file.title);
var syncLocations = {};
syncLocations[syncAttributes.syncIndex] = syncAttributes;
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 = fileMgr.getFileFromSyncIndex(syncIndex);
if(fileDesc !== undefined) {
fileMgr.selectFile(fileDesc);
}
else {
importIds.push(id);
}
});
importFilesFromIds(importIds);
}
});
return gdriveProvider;
return gdriveProvider;
});

View File

@ -3,41 +3,42 @@ define([
"github-helper"
], function(utils, githubHelper) {
var PROVIDER_GIST = "gist";
var PROVIDER_GIST = "gist";
var gistProvider = {
providerId: PROVIDER_GIST,
providerName: "Gist",
sharingAttributes: ["gistId", "filename"]
};
var gistProvider = {
providerId: PROVIDER_GIST,
providerName: "Gist",
sharingAttributes: [
"gistId",
"filename"
]
};
gistProvider.publish = function(publishAttributes, title, content, callback) {
githubHelper.uploadGist(publishAttributes.gistId, publishAttributes.filename, publishAttributes.isPublic,
title, content, function(error, gistId) {
if(error) {
callback(error);
return;
}
publishAttributes.gistId = gistId;
callback();
}
);
};
gistProvider.publish = function(publishAttributes, title, content, callback) {
githubHelper.uploadGist(publishAttributes.gistId, publishAttributes.filename, publishAttributes.isPublic, title, content, function(error, gistId) {
if(error) {
callback(error);
return;
}
publishAttributes.gistId = gistId;
callback();
});
};
gistProvider.newPublishAttributes = function(event) {
var publishAttributes = {};
publishAttributes.gistId = utils.getInputTextValue("#input-publish-gist-id");
publishAttributes.filename = utils.getInputTextValue("#input-publish-filename", event);
publishAttributes.isPublic = utils.getInputChecked("#input-publish-gist-public");
if(event.isPropagationStopped()) {
return undefined;
}
return publishAttributes;
};
gistProvider.newPublishAttributes = function(event) {
var publishAttributes = {};
publishAttributes.gistId = utils.getInputTextValue("#input-publish-gist-id");
publishAttributes.filename = utils.getInputTextValue("#input-publish-filename", event);
publishAttributes.isPublic = utils.getInputChecked("#input-publish-gist-public");
if(event.isPropagationStopped()) {
return undefined;
}
return publishAttributes;
};
gistProvider.importPublic = function(importParameters, callback) {
githubHelper.downloadGist(importParameters.gistId, importParameters.filename, callback);
};
gistProvider.importPublic = function(importParameters, callback) {
githubHelper.downloadGist(importParameters.gistId, importParameters.filename, callback);
};
return gistProvider;
return gistProvider;
});

View File

@ -6,247 +6,249 @@ define([
"async-runner"
], function($, core, utils, extensionMgr, asyncRunner) {
var connected = undefined;
var github = undefined;
var connected = undefined;
var github = undefined;
var githubHelper = {};
var githubHelper = {};
// Try to connect github by downloading js file
function connect(task) {
task.onRun(function() {
if(core.isOffline === true) {
connected = false;
task.error(new Error("Operation not available in offline mode.|stopPublish"));
return;
}
if (connected === true) {
task.chain();
return;
}
$.ajax({
url : "lib/github.js",
dataType : "script", timeout : AJAX_TIMEOUT
}).done(function() {
connected = true;
task.chain();
}).fail(function(jqXHR) {
var error = {
error: jqXHR.status,
message: jqXHR.statusText
};
handleError(error, task);
});
});
}
// Try to connect github by downloading js file
function connect(task) {
task.onRun(function() {
if(core.isOffline === true) {
connected = false;
task.error(new Error("Operation not available in offline mode.|stopPublish"));
return;
}
if(connected === true) {
task.chain();
return;
}
$.ajax({
url: "lib/github.js",
dataType: "script",
timeout: AJAX_TIMEOUT
}).done(function() {
connected = true;
task.chain();
}).fail(function(jqXHR) {
var error = {
error: jqXHR.status,
message: jqXHR.statusText
};
handleError(error, task);
});
});
}
// Try to authenticate with Oauth
function authenticate(task) {
var authWindow = undefined;
var intervalId = undefined;
task.onRun(function() {
if (github !== undefined) {
task.chain();
return;
}
var token = localStorage["githubToken"];
if(token !== undefined) {
github = new Github({
token: token,
auth: "oauth"
});
task.chain();
return;
}
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 = utils.popupWindow(
'github-oauth-client.html?client_id=' + GITHUB_CLIENT_ID,
'stackedit-github-oauth', 960, 600);
authWindow.focus();
intervalId = setInterval(function() {
if(authWindow.closed === true) {
clearInterval(intervalId);
authWindow = undefined;
intervalId = undefined;
code = localStorage["githubCode"];
if(code === undefined) {
task.error(new Error(errorMsg));
return;
}
localStorage.removeItem("githubCode");
task.chain(getToken);
}
}, 500);
}
function getToken() {
$.getJSON(GATEKEEPER_URL + "authenticate/" + code, function(data) {
if(data.token !== undefined) {
token = data.token;
localStorage["githubToken"] = token;
github = new Github({
token: token,
auth: "oauth"
});
task.chain();
}
else {
task.error(new Error(errorMsg));
}
});
}
task.chain(getCode);
});
task.onError(function() {
if(intervalId !== undefined) {
clearInterval(intervalId);
}
if(authWindow !== undefined) {
authWindow.close();
}
});
}
// Try to authenticate with Oauth
function authenticate(task) {
var authWindow = undefined;
var intervalId = undefined;
task.onRun(function() {
if(github !== undefined) {
task.chain();
return;
}
var token = localStorage["githubToken"];
if(token !== undefined) {
github = new Github({
token: token,
auth: "oauth"
});
task.chain();
return;
}
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 = utils.popupWindow('github-oauth-client.html?client_id=' + GITHUB_CLIENT_ID, 'stackedit-github-oauth', 960, 600);
authWindow.focus();
intervalId = setInterval(function() {
if(authWindow.closed === true) {
clearInterval(intervalId);
authWindow = undefined;
intervalId = undefined;
code = localStorage["githubCode"];
if(code === undefined) {
task.error(new Error(errorMsg));
return;
}
localStorage.removeItem("githubCode");
task.chain(getToken);
}
}, 500);
}
function getToken() {
$.getJSON(GATEKEEPER_URL + "authenticate/" + code, function(data) {
if(data.token !== undefined) {
token = data.token;
localStorage["githubToken"] = token;
github = new Github({
token: token,
auth: "oauth"
});
task.chain();
}
else {
task.error(new Error(errorMsg));
}
});
}
task.chain(getCode);
});
task.onError(function() {
if(intervalId !== undefined) {
clearInterval(intervalId);
}
if(authWindow !== undefined) {
authWindow.close();
}
});
}
githubHelper.upload = function(reponame, branch, path, content, commitMsg, callback) {
var task = asyncRunner.createTask();
connect(task);
authenticate(task);
task.onRun(function() {
var userLogin = undefined;
function getUserLogin() {
var user = github.getUser();
user.show(undefined, function(err, result) {
if(err) {
handleError(err, task);
return;
}
userLogin = result.login;
task.chain(write);
});
}
function write() {
var repo = github.getRepo(userLogin, reponame);
repo.write(branch, path, content, commitMsg, function(err) {
if(err) {
handleError(err, task);
return;
}
task.chain();
});
}
task.chain(getUserLogin);
});
task.onSuccess(function() {
callback();
});
task.onError(function(error) {
callback(error);
});
asyncRunner.addTask(task);
};
githubHelper.upload = function(reponame, branch, path, content, commitMsg, callback) {
var task = asyncRunner.createTask();
connect(task);
authenticate(task);
task.onRun(function() {
var userLogin = undefined;
function getUserLogin() {
var user = github.getUser();
user.show(undefined, function(err, result) {
if(err) {
handleError(err, task);
return;
}
userLogin = result.login;
task.chain(write);
});
}
function write() {
var repo = github.getRepo(userLogin, reponame);
repo.write(branch, path, content, commitMsg, function(err) {
if(err) {
handleError(err, task);
return;
}
task.chain();
});
}
task.chain(getUserLogin);
});
task.onSuccess(function() {
callback();
});
task.onError(function(error) {
callback(error);
});
asyncRunner.addTask(task);
};
githubHelper.uploadGist = function(gistId, filename, isPublic, title, content, callback) {
var task = asyncRunner.createTask();
connect(task);
authenticate(task);
task.onRun(function() {
var gist = github.getGist(gistId);
var files = {};
files[filename] = {content: content};
githubFunction = gist.update;
if(gistId === undefined) {
githubFunction = gist.create;
}
githubFunction({
description: title,
"public": isPublic,
files: files
}, function(err, gist) {
if(err) {
// Handle error
if(err.error === 404 && gistId !== undefined) {
err = 'Gist ' + gistId + ' not found on GitHub.|removePublish';
}
handleError(err, task);
return;
}
gistId = gist.id;
task.chain();
});
});
task.onSuccess(function() {
callback(undefined, gistId);
});
task.onError(function(error) {
callback(error);
});
asyncRunner.addTask(task);
};
githubHelper.uploadGist = function(gistId, filename, isPublic, title, content, callback) {
var task = asyncRunner.createTask();
connect(task);
authenticate(task);
task.onRun(function() {
var gist = github.getGist(gistId);
var files = {};
files[filename] = {
content: content
};
githubFunction = gist.update;
if(gistId === undefined) {
githubFunction = gist.create;
}
githubFunction({
description: title,
"public": isPublic,
files: files
}, function(err, gist) {
if(err) {
// Handle error
if(err.error === 404 && gistId !== undefined) {
err = 'Gist ' + gistId + ' not found on GitHub.|removePublish';
}
handleError(err, task);
return;
}
gistId = gist.id;
task.chain();
});
});
task.onSuccess(function() {
callback(undefined, gistId);
});
task.onError(function(error) {
callback(error);
});
asyncRunner.addTask(task);
};
githubHelper.downloadGist = function(gistId, filename, callback) {
var task = asyncRunner.createTask();
connect(task);
// No need for authentication
var title = undefined;
var content = undefined;
task.onRun(function() {
var github = new Github({});
var gist = github.getGist(gistId);
gist.read(function(err, gist) {
if(err) {
// Handle error
task.error(new Error('Error trying to access Gist ' + gistId + '.'));
return;
}
title = gist.description;
var file = gist.files[filename];
if(file === undefined) {
task.error(new Error('Gist ' + gistId + ' does not contain "' + filename + '".'));
return;
}
content = file.content;
task.chain();
});
});
task.onSuccess(function() {
callback(undefined, title, content);
});
task.onError(function(error) {
callback(error);
});
asyncRunner.addTask(task);
};
githubHelper.downloadGist = function(gistId, filename, callback) {
var task = asyncRunner.createTask();
connect(task);
// No need for authentication
var title = undefined;
var content = undefined;
task.onRun(function() {
var github = new Github({});
var gist = github.getGist(gistId);
gist.read(function(err, gist) {
if(err) {
// Handle error
task.error(new Error('Error trying to access Gist ' + gistId + '.'));
return;
}
title = gist.description;
var file = gist.files[filename];
if(file === undefined) {
task.error(new Error('Gist ' + gistId + ' does not contain "' + filename + '".'));
return;
}
content = file.content;
task.chain();
});
});
task.onSuccess(function() {
callback(undefined, title, content);
});
task.onError(function(error) {
callback(error);
});
asyncRunner.addTask(task);
};
function handleError(error, task) {
var errorMsg = undefined;
if (error) {
logger.error(error);
// Try to analyze the error
if (typeof error === "string") {
errorMsg = error;
}
else {
errorMsg = "Could not publish on GitHub.";
if (error.error === 401 || error.error === 403) {
github = undefined;
localStorage.removeItem("githubToken");
errorMsg = "Access to GitHub account is not authorized.";
task.retry(new Error(errorMsg), 1);
return;
} else if (error.error <= 0) {
connected = false;
github = undefined;
core.setOffline();
errorMsg = "|stopPublish";
}
}
}
task.error(new Error(errorMsg));
}
function handleError(error, task) {
var errorMsg = undefined;
if(error) {
logger.error(error);
// Try to analyze the error
if(typeof error === "string") {
errorMsg = error;
}
else {
errorMsg = "Could not publish on GitHub.";
if(error.error === 401 || error.error === 403) {
github = undefined;
localStorage.removeItem("githubToken");
errorMsg = "Access to GitHub account is not authorized.";
task.retry(new Error(errorMsg), 1);
return;
}
else if(error.error <= 0) {
connected = false;
github = undefined;
core.setOffline();
errorMsg = "|stopPublish";
}
}
}
task.error(new Error(errorMsg));
}
return githubHelper;
return githubHelper;
});

View File

@ -4,30 +4,32 @@ define([
"github-helper"
], function(utils, settings, githubHelper) {
var PROVIDER_GITHUB = "github";
var PROVIDER_GITHUB = "github";
var githubProvider = {
providerId: PROVIDER_GITHUB,
providerName: "GitHub",
publishPreferencesInputIds: ["github-reponame", "github-branch"]
};
var githubProvider = {
providerId: PROVIDER_GITHUB,
providerName: "GitHub",
publishPreferencesInputIds: [
"github-reponame",
"github-branch"
]
};
githubProvider.publish = function(publishAttributes, title, content, callback) {
var commitMsg = settings.commitMsg;
githubHelper.upload(publishAttributes.repository, publishAttributes.branch,
publishAttributes.path, content, commitMsg, callback);
};
githubProvider.publish = function(publishAttributes, title, content, callback) {
var commitMsg = settings.commitMsg;
githubHelper.upload(publishAttributes.repository, publishAttributes.branch, publishAttributes.path, content, commitMsg, callback);
};
githubProvider.newPublishAttributes = function(event) {
var publishAttributes = {};
publishAttributes.repository = utils.getInputTextValue("#input-publish-github-reponame", event);
publishAttributes.branch = utils.getInputTextValue("#input-publish-github-branch", event);
publishAttributes.path = utils.getInputTextValue("#input-publish-file-path", event);
if(event.isPropagationStopped()) {
return undefined;
}
return publishAttributes;
};
githubProvider.newPublishAttributes = function(event) {
var publishAttributes = {};
publishAttributes.repository = utils.getInputTextValue("#input-publish-github-reponame", event);
publishAttributes.branch = utils.getInputTextValue("#input-publish-github-branch", event);
publishAttributes.path = utils.getInputTextValue("#input-publish-file-path", event);
if(event.isPropagationStopped()) {
return undefined;
}
return publishAttributes;
};
return githubProvider;
return githubProvider;
});

File diff suppressed because it is too large Load Diff

View File

@ -18,236 +18,229 @@ define([
"wordpress-provider"
], function($, _, core, utils, settings, extensionMgr, fileSystem, fileMgr, sharing) {
var publisher = {};
var publisher = {};
// Create a map with providerId: providerModule
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();
// 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;
});
});
// 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 = fileMgr.getCurrentFile();
try {
return _.template(settings.template, {
documentTitle: fileDesc.title,
documentMarkdown: $("#wmd-input").val(),
documentHTML: $("#wmd-preview").html(),
publishAttributes: publishAttributes
});
} catch(e) {
extensionMgr.onError(e);
throw e;
}
};
// Apply template to the current document
publisher.applyTemplate = function(publishAttributes) {
var fileDesc = fileMgr.getCurrentFile();
try {
return _.template(settings.template, {
documentTitle: fileDesc.title,
documentMarkdown: $("#wmd-input").val(),
documentHTML: $("#wmd-preview").html(),
publishAttributes: publishAttributes
});
}
catch (e) {
extensionMgr.onError(e);
throw e;
}
};
// Used to get content to publish
function getPublishContent(publishAttributes) {
if(publishAttributes.format === undefined) {
publishAttributes.format = $("input:radio[name=radio-publish-format]:checked").prop("value");
}
if(publishAttributes.format == "markdown") {
return $("#wmd-input").val();
}
else if(publishAttributes.format == "html") {
return $("#wmd-preview").html();
}
else {
return publisher.applyTemplate(publishAttributes);
}
}
// Used to get content to publish
function getPublishContent(publishAttributes) {
if(publishAttributes.format === undefined) {
publishAttributes.format = $("input:radio[name=radio-publish-format]:checked").prop("value");
}
if(publishAttributes.format == "markdown") {
return $("#wmd-input").val();
}
else if(publishAttributes.format == "html") {
return $("#wmd-preview").html();
}
else {
return publisher.applyTemplate(publishAttributes);
}
}
// Recursive function to publish a file on multiple locations
var publishAttributesList = [];
var publishTitle = undefined;
function publishLocation(callback, errorFlag) {
// Recursive function to publish a file on multiple locations
var publishAttributesList = [];
var publishTitle = undefined;
function publishLocation(callback, errorFlag) {
// No more publish location for this document
if (publishAttributesList.length === 0) {
callback(errorFlag);
return;
}
// No more publish location for this document
if(publishAttributesList.length === 0) {
callback(errorFlag);
return;
}
// Dequeue a synchronized location
var publishAttributes = publishAttributesList.pop();
var content = getPublishContent(publishAttributes);
// Dequeue a synchronized location
var publishAttributes = publishAttributesList.pop();
var content = getPublishContent(publishAttributes);
// Call the provider
publishAttributes.provider.publish(publishAttributes, publishTitle, content, function(error) {
if(error !== undefined) {
var errorMsg = error.toString();
if(errorMsg.indexOf("|removePublish") !== -1) {
fileMgr.removePublish(publishAttributes);
}
if(errorMsg.indexOf("|stopPublish") !== -1) {
callback(error);
return;
}
}
publishLocation(callback, errorFlag || error );
});
}
// Call the provider
publishAttributes.provider.publish(publishAttributes, publishTitle, content, function(error) {
if(error !== undefined) {
var errorMsg = error.toString();
if(errorMsg.indexOf("|removePublish") !== -1) {
fileMgr.removePublish(publishAttributes);
}
if(errorMsg.indexOf("|stopPublish") !== -1) {
callback(error);
return;
}
}
publishLocation(callback, errorFlag || error);
});
}
var publishRunning = false;
publisher.publish = function() {
// If publish is running or offline
if(publishRunning === true || core.isOffline) {
return;
}
var publishRunning = false;
publisher.publish = function() {
// If publish is running or offline
if(publishRunning === true || core.isOffline) {
return;
}
publishRunning = true;
extensionMgr.onPublishRunning(true);
var fileDesc = fileMgr.getCurrentFile();
publishTitle = fileDesc.title;
publishAttributesList = _.values(fileDesc.publishLocations);
publishLocation(function(errorFlag) {
publishRunning = false;
extensionMgr.onPublishRunning(false);
if(errorFlag === undefined) {
extensionMgr.onPublishSuccess(fileDesc);
}
});
};
publishRunning = true;
extensionMgr.onPublishRunning(true);
var fileDesc = fileMgr.getCurrentFile();
publishTitle = fileDesc.title;
publishAttributesList = _.values(fileDesc.publishLocations);
publishLocation(function(errorFlag) {
publishRunning = false;
extensionMgr.onPublishRunning(false);
if(errorFlag === undefined) {
extensionMgr.onPublishSuccess(fileDesc);
}
});
};
// Generate a publishIndex associated to a file and store publishAttributes
function createPublishIndex(fileDesc, publishAttributes) {
var publishIndex = undefined;
do {
publishIndex = "publish." + utils.randomString();
} while(_.has(localStorage, publishIndex));
publishAttributes.publishIndex = publishIndex;
utils.storeAttributes(publishAttributes);
fileMgr.addPublish(fileDesc, publishAttributes);
}
// Generate a publishIndex associated to a file and store publishAttributes
function createPublishIndex(fileDesc, publishAttributes) {
var publishIndex = undefined;
do {
publishIndex = "publish." + utils.randomString();
} while (_.has(localStorage, publishIndex));
publishAttributes.publishIndex = publishIndex;
utils.storeAttributes(publishAttributes);
fileMgr.addPublish(fileDesc, publishAttributes);
}
// Initialize the "New publication" dialog
var newLocationProvider = undefined;
function initNewLocation(provider) {
var defaultPublishFormat = provider.defaultPublishFormat || "markdown";
newLocationProvider = provider;
$(".publish-provider-name").text(provider.providerName);
// Initialize the "New publication" dialog
var newLocationProvider = undefined;
function initNewLocation(provider) {
var defaultPublishFormat = provider.defaultPublishFormat || "markdown";
newLocationProvider = provider;
$(".publish-provider-name").text(provider.providerName);
// Show/hide controls depending on provider
$('div[class*=" modal-publish-"]').hide().filter(".modal-publish-" + provider.providerId).show();
// Show/hide controls depending on provider
$('div[class*=" modal-publish-"]').hide().filter(".modal-publish-" + provider.providerId).show();
// Reset fields
utils.resetModalInputs();
$("input:radio[name=radio-publish-format][value=" + defaultPublishFormat + "]").prop("checked", true);
// Reset fields
utils.resetModalInputs();
$("input:radio[name=radio-publish-format][value=" + defaultPublishFormat + "]").prop("checked", true);
// Load preferences
var serializedPreferences = localStorage[provider.providerId + ".publishPreferences"];
if(serializedPreferences) {
var publishPreferences = JSON.parse(serializedPreferences);
_.each(provider.publishPreferencesInputIds, function(inputId) {
utils.setInputValue("#input-publish-" + inputId, publishPreferences[inputId]);
});
utils.setInputRadio("radio-publish-format", publishPreferences.format);
}
// Load preferences
var serializedPreferences = localStorage[provider.providerId + ".publishPreferences"];
if(serializedPreferences) {
var publishPreferences = JSON.parse(serializedPreferences);
_.each(provider.publishPreferencesInputIds, function(inputId) {
utils.setInputValue("#input-publish-" + inputId, publishPreferences[inputId]);
});
utils.setInputRadio("radio-publish-format", publishPreferences.format);
}
// Open dialog box
$("#modal-publish").modal();
}
// Open dialog box
$("#modal-publish").modal();
}
// Add a new publish location to a local document
function performNewLocation(event) {
var provider = newLocationProvider;
var publishAttributes = provider.newPublishAttributes(event);
if(publishAttributes === undefined) {
return;
}
// Add a new publish location to a local document
function performNewLocation(event) {
var provider = newLocationProvider;
var publishAttributes = provider.newPublishAttributes(event);
if(publishAttributes === undefined) {
return;
}
// Perform provider's publishing
var fileDesc = fileMgr.getCurrentFile();
var title = fileDesc.title;
var content = getPublishContent(publishAttributes);
provider.publish(publishAttributes, title, content, function(error) {
if(error === undefined) {
publishAttributes.provider = provider.providerId;
sharing.createLink(publishAttributes, function() {
createPublishIndex(fileDesc, publishAttributes);
});
}
});
// Perform provider's publishing
var fileDesc = fileMgr.getCurrentFile();
var title = fileDesc.title;
var content = getPublishContent(publishAttributes);
provider.publish(publishAttributes, title, content, function(error) {
if(error === undefined) {
publishAttributes.provider = provider.providerId;
sharing.createLink(publishAttributes, function() {
createPublishIndex(fileDesc, publishAttributes);
});
}
});
// Store input values as preferences for next time we open the publish dialog
var publishPreferences = {};
_.each(provider.publishPreferencesInputIds, function(inputId) {
publishPreferences[inputId] = $("#input-publish-" + inputId).val();
});
publishPreferences.format = publishAttributes.format;
localStorage[provider.providerId + ".publishPreferences"] = JSON.stringify(publishPreferences);
}
// Store input values as preferences for next time we open the publish
// dialog
var publishPreferences = {};
_.each(provider.publishPreferencesInputIds, function(inputId) {
publishPreferences[inputId] = $("#input-publish-" + inputId).val();
});
publishPreferences.format = publishAttributes.format;
localStorage[provider.providerId + ".publishPreferences"] = JSON.stringify(publishPreferences);
}
// 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;
});
};
// 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;
});
};
core.onReady(function() {
// Add every provider
var publishMenu = $("#publish-menu");
_.each(providerMap, function(provider) {
// Provider's publish button
publishMenu.append(
$("<li>").append(
$('<a href="#"><i class="icon-' + provider.providerId + '"></i> ' + provider.providerName + '</a>')
.click(function() {
initNewLocation(provider);
}
)
)
);
// Action links (if any)
$(".action-publish-" + provider.providerId).click(function() {
initNewLocation(provider);
});
});
core.onReady(function() {
// Add every provider
var publishMenu = $("#publish-menu");
_.each(providerMap, function(provider) {
// Provider's publish button
publishMenu.append($("<li>").append($('<a href="#"><i class="icon-' + provider.providerId + '"></i> ' + provider.providerName + '</a>').click(function() {
initNewLocation(provider);
})));
// Action links (if any)
$(".action-publish-" + provider.providerId).click(function() {
initNewLocation(provider);
});
});
$(".action-process-publish").click(performNewLocation);
$(".action-process-publish").click(performNewLocation);
// 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");
});
});
// 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");
});
});
extensionMgr.onPublisherCreated(publisher);
return publisher;
extensionMgr.onPublisherCreated(publisher);
return publisher;
});

View File

@ -3,27 +3,28 @@ define([
"config"
], 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 : [
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: {}
};
'<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));
}
if(_.has(localStorage, "settings")) {
_.extend(settings, JSON.parse(localStorage.settings));
}
return settings;
return settings;
});

View File

@ -11,124 +11,127 @@ define([
"gist-provider"
], function($, _, core, utils, extensionMgr, fileMgr, asyncRunner) {
var sharing = {};
var sharing = {};
// Create a map with providerId: providerModule
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">',
'<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("");
sharing.refreshDocumentSharing = function(attributesList) {
var linkList = $("#link-container .link-list").empty();
$("#link-container .no-link").show();
_.each(attributesList, function(attributes) {
if(attributes.sharingLink) {
var lineElement = $(_.template(lineTemplate, {
link: attributes.sharingLink
}));
lineElement.click(function(event) {
event.stopPropagation();
});
linkList.append(lineElement);
$("#link-container .no-link").hide();
}
});
};
// Used to populate the "Sharing" dropdown box
var lineTemplate = [
'<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("");
sharing.refreshDocumentSharing = function(attributesList) {
var linkList = $("#link-container .link-list").empty();
$("#link-container .no-link").show();
_.each(attributesList, function(attributes) {
if(attributes.sharingLink) {
var lineElement = $(_.template(lineTemplate, {
link: attributes.sharingLink
}));
lineElement.click(function(event) {
event.stopPropagation();
});
linkList.append(lineElement);
$("#link-container .no-link").hide();
}
});
};
sharing.createLink = function(attributes, callback) {
var provider = providerMap[attributes.provider];
// Don't create link if link already exists or provider is not compatible for sharing
if(attributes.sharingLink !== undefined || provider === undefined
// Or document is not published in markdown format
|| attributes.format != "markdown") {
callback();
return;
}
var task = asyncRunner.createTask();
var shortUrl = undefined;
task.onRun(function() {
if(core.isOffline === true) {
task.chain();
return;
}
var url = [MAIN_URL, 'viewer.html?provider=', attributes.provider];
_.each(provider.sharingAttributes, function(attributeName) {
url.push('&');
url.push(attributeName);
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
},
function(response)
{
if(response.data) {
shortUrl = response.data.url;
attributes.sharingLink = shortUrl;
}
else {
extensionMgr.onError("An error occured while creating sharing link.");
attributes.sharingLink = url;
}
task.chain();
}
);
});
function onFinish() {
callback();
}
task.onSuccess(onFinish);
task.onError(onFinish);
asyncRunner.addTask(task);
};
sharing.createLink = function(attributes, callback) {
var provider = providerMap[attributes.provider];
// Don't create link if link already exists or provider is not
// compatible for sharing
if(attributes.sharingLink !== undefined || provider === undefined
// Or document is not published in markdown format
|| attributes.format != "markdown") {
callback();
return;
}
var task = asyncRunner.createTask();
var shortUrl = undefined;
task.onRun(function() {
if(core.isOffline === true) {
task.chain();
return;
}
var url = [
MAIN_URL,
'viewer.html?provider=',
attributes.provider
];
_.each(provider.sharingAttributes, function(attributeName) {
url.push('&');
url.push(attributeName);
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
}, function(response) {
if(response.data) {
shortUrl = response.data.url;
attributes.sharingLink = shortUrl;
}
else {
extensionMgr.onError("An error occured while creating sharing link.");
attributes.sharingLink = url;
}
task.chain();
});
});
function onFinish() {
callback();
}
task.onSuccess(onFinish);
task.onError(onFinish);
asyncRunner.addTask(task);
};
core.onReady(function() {
if(viewerMode === false) {
return;
}
// Check parameters to see if we have to download a shared document
var providerId = utils.getURLParameter("provider");
if(providerId === undefined) {
providerId = "download";
}
var provider = providerMap[providerId];
if(provider === undefined) {
return;
}
var importParameters = {};
_.each(provider.sharingAttributes, function(attributeName) {
var parameter = utils.getURLParameter(attributeName);
if(!parameter) {
importParameters = undefined;
return;
}
importParameters[attributeName] = parameter;
});
if(importParameters === undefined) {
return;
}
$("#wmd-preview, #file-title").hide();
provider.importPublic(importParameters, function(error, title, content) {
$("#wmd-preview, #file-title").show();
if(error) {
return;
}
var fileDesc = fileMgr.createFile(title, content, undefined, true);
fileMgr.selectFile(fileDesc);
});
});
core.onReady(function() {
if(viewerMode === false) {
return;
}
// Check parameters to see if we have to download a shared document
var providerId = utils.getURLParameter("provider");
if(providerId === undefined) {
providerId = "download";
}
var provider = providerMap[providerId];
if(provider === undefined) {
return;
}
var importParameters = {};
_.each(provider.sharingAttributes, function(attributeName) {
var parameter = utils.getURLParameter(attributeName);
if(!parameter) {
importParameters = undefined;
return;
}
importParameters[attributeName] = parameter;
});
if(importParameters === undefined) {
return;
}
$("#wmd-preview, #file-title").hide();
provider.importPublic(importParameters, function(error, title, content) {
$("#wmd-preview, #file-title").show();
if(error) {
return;
}
var fileDesc = fileMgr.createFile(title, content, undefined, true);
fileMgr.selectFile(fileDesc);
});
});
return sharing;
return sharing;
});

View File

@ -4,80 +4,80 @@ define([
"async-runner"
], function($, core, asyncRunner) {
var sshHelper = {};
var sshHelper = {};
// Only used to check the offline status
function connect(task) {
task.onRun(function() {
if(core.isOffline === true) {
task.error(new Error("Operation not available in offline mode.|stopPublish"));
return;
}
task.chain();
});
}
// Only used to check the offline status
function connect(task) {
task.onRun(function() {
if(core.isOffline === true) {
task.error(new Error("Operation not available in offline mode.|stopPublish"));
return;
}
task.chain();
});
}
sshHelper.upload = function(host, port, username, password, path, title, content, callback) {
var task = asyncRunner.createTask();
connect(task);
task.onRun(function() {
var url = SSH_PROXY_URL + "upload";
var data = {
host: host,
port: port,
username: username,
password: password,
path: path,
title: title,
content: content
};
$.ajax({
url : url,
data: data,
type: "POST",
dataType : "json",
timeout : AJAX_TIMEOUT
}).done(function(response, textStatus, jqXHR) {
if(response.error === undefined) {
task.chain();
return;
}
handleError(response.error, task);
}).fail(function(jqXHR) {
var error = {
code: jqXHR.status,
message: jqXHR.statusText
};
handleError(error, task);
});
});
task.onSuccess(function() {
callback();
});
task.onError(function(error) {
callback(error);
});
asyncRunner.addTask(task);
};
sshHelper.upload = function(host, port, username, password, path, title, content, callback) {
var task = asyncRunner.createTask();
connect(task);
task.onRun(function() {
var url = SSH_PROXY_URL + "upload";
var data = {
host: host,
port: port,
username: username,
password: password,
path: path,
title: title,
content: content
};
$.ajax({
url: url,
data: data,
type: "POST",
dataType: "json",
timeout: AJAX_TIMEOUT
}).done(function(response, textStatus, jqXHR) {
if(response.error === undefined) {
task.chain();
return;
}
handleError(response.error, task);
}).fail(function(jqXHR) {
var error = {
code: jqXHR.status,
message: jqXHR.statusText
};
handleError(error, task);
});
});
task.onSuccess(function() {
callback();
});
task.onError(function(error) {
callback(error);
});
asyncRunner.addTask(task);
};
function handleError(error, task) {
var errorMsg = undefined;
if (error) {
logger.error(error);
// Try to analyze the error
if (typeof error === "string") {
errorMsg = "SSH error: " + error + ".";
}
else {
errorMsg = "Could not publish on SSH server.";
if (error.code <= 0) {
core.setOffline();
errorMsg = "|stopPublish";
}
}
}
task.error(new Error(errorMsg));
}
function handleError(error, task) {
var errorMsg = undefined;
if(error) {
logger.error(error);
// Try to analyze the error
if(typeof error === "string") {
errorMsg = "SSH error: " + error + ".";
}
else {
errorMsg = "Could not publish on SSH server.";
if(error.code <= 0) {
core.setOffline();
errorMsg = "|stopPublish";
}
}
}
task.error(new Error(errorMsg));
}
return sshHelper;
return sshHelper;
});

View File

@ -3,46 +3,35 @@ define([
"ssh-helper"
], function(utils, sshHelper) {
var PROVIDER_SSH = "ssh";
var PROVIDER_SSH = "ssh";
var sshProvider = {
providerId : PROVIDER_SSH,
providerName : "SSH server",
publishPreferencesInputIds: ["ssh-host", "ssh-port", "ssh-username", "ssh-password"]
};
var sshProvider = {
providerId: PROVIDER_SSH,
providerName: "SSH server",
publishPreferencesInputIds: [
"ssh-host",
"ssh-port",
"ssh-username",
"ssh-password"
]
};
sshProvider.publish = function(publishAttributes, title, content, callback) {
sshHelper.upload(
publishAttributes.host,
publishAttributes.port,
publishAttributes.username,
publishAttributes.password,
publishAttributes.path,
title,
content,
callback);
};
sshProvider.publish = function(publishAttributes, title, content, callback) {
sshHelper.upload(publishAttributes.host, publishAttributes.port, publishAttributes.username, publishAttributes.password, publishAttributes.path, title, content, callback);
};
sshProvider.newPublishAttributes = function(event) {
var publishAttributes = {};
publishAttributes.host = utils
.getInputTextValue(
"#input-publish-ssh-host",
event,
/^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$/);
publishAttributes.port = utils.getInputIntValue(
"#input-publish-ssh-port", undefined, 0);
publishAttributes.username = utils.getInputTextValue(
"#input-publish-ssh-username", event);
publishAttributes.password = utils.getInputTextValue(
"#input-publish-ssh-password", event);
publishAttributes.path = utils.getInputTextValue(
"#input-publish-file-path", event);
if (event.isPropagationStopped()) {
return undefined;
}
return publishAttributes;
};
sshProvider.newPublishAttributes = function(event) {
var publishAttributes = {};
publishAttributes.host = utils.getInputTextValue("#input-publish-ssh-host", event, /^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$/);
publishAttributes.port = utils.getInputIntValue("#input-publish-ssh-port", undefined, 0);
publishAttributes.username = utils.getInputTextValue("#input-publish-ssh-username", event);
publishAttributes.password = utils.getInputTextValue("#input-publish-ssh-password", event);
publishAttributes.path = utils.getInputTextValue("#input-publish-file-path", event);
if(event.isPropagationStopped()) {
return undefined;
}
return publishAttributes;
};
return sshProvider;
return sshProvider;
});

View File

@ -1,131 +1,128 @@
// Setup an empty localStorage or upgrade an existing one
define([
"underscore"
"underscore"
], function(_) {
// Create the file system if not exist
if (localStorage["file.list"] === undefined) {
localStorage["file.list"] = ";";
}
var fileIndexList = _.compact(localStorage["file.list"].split(";"));
// Create the file system if not exist
if(localStorage["file.list"] === undefined) {
localStorage["file.list"] = ";";
}
var fileIndexList = _.compact(localStorage["file.list"].split(";"));
// localStorage versioning
var version = localStorage["version"];
// localStorage versioning
var version = localStorage["version"];
// Upgrade from v0 to v1
if(version === undefined) {
// Upgrade from v0 to v1
if(version === undefined) {
// Not used anymore
localStorage.removeItem("sync.queue");
localStorage.removeItem("sync.current");
localStorage.removeItem("file.counter");
// Not used anymore
localStorage.removeItem("sync.queue");
localStorage.removeItem("sync.current");
localStorage.removeItem("file.counter");
_.each(fileIndexList, function(fileIndex) {
localStorage[fileIndex + ".publish"] = ";";
var syncIndexList = _.compact(localStorage[fileIndex + ".sync"].split(";"));
_.each(syncIndexList, function(syncIndex) {
localStorage[syncIndex + ".contentCRC"] = "0";
// We store title CRC only for Google Drive synchronization
if(localStorage[syncIndex + ".etag"] !== undefined) {
localStorage[syncIndex + ".titleCRC"] = "0";
}
});
});
version = "v1";
}
_.each(fileIndexList, function(fileIndex) {
localStorage[fileIndex + ".publish"] = ";";
var syncIndexList = _.compact(localStorage[fileIndex + ".sync"].split(";"));
_.each(syncIndexList, function(syncIndex) {
localStorage[syncIndex + ".contentCRC"] = "0";
// We store title CRC only for Google Drive synchronization
if(localStorage[syncIndex + ".etag"] !== undefined) {
localStorage[syncIndex + ".titleCRC"] = "0";
}
});
});
version = "v1";
}
// Upgrade from v1 to v2
if(version == "v1") {
var gdriveLastChangeId = localStorage["sync.gdrive.lastChangeId"];
if(gdriveLastChangeId) {
localStorage["gdrive.lastChangeId"] = gdriveLastChangeId;
localStorage.removeItem("sync.gdrive.lastChangeId");
}
var dropboxLastChangeId = localStorage["sync.dropbox.lastChangeId"];
if(dropboxLastChangeId) {
localStorage["dropbox.lastChangeId"] = dropboxLastChangeId;
localStorage.removeItem("sync.dropbox.lastChangeId");
}
// Upgrade from v1 to v2
if(version == "v1") {
var gdriveLastChangeId = localStorage["sync.gdrive.lastChangeId"];
if(gdriveLastChangeId) {
localStorage["gdrive.lastChangeId"] = gdriveLastChangeId;
localStorage.removeItem("sync.gdrive.lastChangeId");
}
var dropboxLastChangeId = localStorage["sync.dropbox.lastChangeId"];
if(dropboxLastChangeId) {
localStorage["dropbox.lastChangeId"] = dropboxLastChangeId;
localStorage.removeItem("sync.dropbox.lastChangeId");
}
var PROVIDER_GDRIVE = "gdrive";
var PROVIDER_DROPBOX = "dropbox";
var SYNC_PROVIDER_GDRIVE = "sync." + PROVIDER_GDRIVE + ".";
var SYNC_PROVIDER_DROPBOX = "sync." + PROVIDER_DROPBOX + ".";
_.each(fileIndexList, function(fileIndex) {
var syncIndexList = _.compact(localStorage[fileIndex + ".sync"].split(";"));
_.each(syncIndexList, function(syncIndex) {
var syncAttributes = {};
if (syncIndex.indexOf(SYNC_PROVIDER_GDRIVE) === 0) {
syncAttributes.provider = PROVIDER_GDRIVE;
syncAttributes.id = syncIndex.substring(SYNC_PROVIDER_GDRIVE.length);
syncAttributes.etag = localStorage[syncIndex + ".etag"];
syncAttributes.contentCRC = localStorage[syncIndex + ".contentCRC"];
syncAttributes.titleCRC = localStorage[syncIndex + ".titleCRC"];
}
else if (syncIndex.indexOf(SYNC_PROVIDER_DROPBOX) === 0) {
syncAttributes.provider = PROVIDER_DROPBOX;
syncAttributes.path = decodeURIComponent(syncIndex.substring(SYNC_PROVIDER_DROPBOX.length));
syncAttributes.version = localStorage[syncIndex + ".version"];
syncAttributes.contentCRC = localStorage[syncIndex + ".contentCRC"];
}
localStorage[syncIndex] = JSON.stringify(syncAttributes);
localStorage.removeItem(syncIndex + ".etag");
localStorage.removeItem(syncIndex + ".version");
localStorage.removeItem(syncIndex + ".contentCRC");
localStorage.removeItem(syncIndex + ".titleCRC");
});
});
version = "v2";
}
var PROVIDER_GDRIVE = "gdrive";
var PROVIDER_DROPBOX = "dropbox";
var SYNC_PROVIDER_GDRIVE = "sync." + PROVIDER_GDRIVE + ".";
var SYNC_PROVIDER_DROPBOX = "sync." + PROVIDER_DROPBOX + ".";
_.each(fileIndexList, function(fileIndex) {
var syncIndexList = _.compact(localStorage[fileIndex + ".sync"].split(";"));
_.each(syncIndexList, function(syncIndex) {
var syncAttributes = {};
if(syncIndex.indexOf(SYNC_PROVIDER_GDRIVE) === 0) {
syncAttributes.provider = PROVIDER_GDRIVE;
syncAttributes.id = syncIndex.substring(SYNC_PROVIDER_GDRIVE.length);
syncAttributes.etag = localStorage[syncIndex + ".etag"];
syncAttributes.contentCRC = localStorage[syncIndex + ".contentCRC"];
syncAttributes.titleCRC = localStorage[syncIndex + ".titleCRC"];
}
else if(syncIndex.indexOf(SYNC_PROVIDER_DROPBOX) === 0) {
syncAttributes.provider = PROVIDER_DROPBOX;
syncAttributes.path = decodeURIComponent(syncIndex.substring(SYNC_PROVIDER_DROPBOX.length));
syncAttributes.version = localStorage[syncIndex + ".version"];
syncAttributes.contentCRC = localStorage[syncIndex + ".contentCRC"];
}
localStorage[syncIndex] = JSON.stringify(syncAttributes);
localStorage.removeItem(syncIndex + ".etag");
localStorage.removeItem(syncIndex + ".version");
localStorage.removeItem(syncIndex + ".contentCRC");
localStorage.removeItem(syncIndex + ".titleCRC");
});
});
version = "v2";
}
// Upgrade from v2 to v3
if(version == "v2") {
_.each(fileIndexList, function(fileIndex) {
if(!_.has(localStorage, fileIndex + ".sync")) {
localStorage.removeItem(fileIndex + ".title");
localStorage.removeItem(fileIndex + ".publish");
localStorage.removeItem(fileIndex + ".content");
localStorage["file.list"] = localStorage["file.list"].replace(";"
+ fileIndex + ";", ";");
}
});
version = "v3";
}
// Upgrade from v2 to v3
if(version == "v2") {
_.each(fileIndexList, function(fileIndex) {
if(!_.has(localStorage, fileIndex + ".sync")) {
localStorage.removeItem(fileIndex + ".title");
localStorage.removeItem(fileIndex + ".publish");
localStorage.removeItem(fileIndex + ".content");
localStorage["file.list"] = localStorage["file.list"].replace(";" + fileIndex + ";", ";");
}
});
version = "v3";
}
// Upgrade from v3 to v4
if(version == "v3") {
var currentFileIndex = localStorage["file.current"];
if(currentFileIndex !== undefined &&
localStorage["file.list"].indexOf(";" + currentFileIndex + ";") === -1)
{
localStorage.removeItem("file.current");
}
version = "v4";
}
// Upgrade from v3 to v4
if(version == "v3") {
var currentFileIndex = localStorage["file.current"];
if(currentFileIndex !== undefined && localStorage["file.list"].indexOf(";" + currentFileIndex + ";") === -1) {
localStorage.removeItem("file.current");
}
version = "v4";
}
// Upgrade from v4 to v5
if(version == "v4") {
// Recreate GitHub token
localStorage.removeItem("githubToken");
version = "v5";
}
// Upgrade from v4 to v5
if(version == "v4") {
// Recreate GitHub token
localStorage.removeItem("githubToken");
version = "v5";
}
// Upgrade from v5 to v6
if(version == "v5") {
_.each(fileIndexList, function(fileIndex) {
var publishIndexList = _.compact(localStorage[fileIndex + ".publish"].split(";"));
_.each(publishIndexList, function(publishIndex) {
var publishAttributes = JSON.parse(localStorage[publishIndex]);
if(publishAttributes.provider == "gdrive") {
// Change fileId to Id to be consistent with syncAttributes
publishAttributes.id = publishAttributes.fileId;
publishAttributes.fileId = undefined;
localStorage[publishIndex] = JSON.stringify(publishAttributes);
}
});
});
version = "v6";
}
// Upgrade from v5 to v6
if(version == "v5") {
_.each(fileIndexList, function(fileIndex) {
var publishIndexList = _.compact(localStorage[fileIndex + ".publish"].split(";"));
_.each(publishIndexList, function(publishIndex) {
var publishAttributes = JSON.parse(localStorage[publishIndex]);
if(publishAttributes.provider == "gdrive") {
// Change fileId to Id to be consistent with syncAttributes
publishAttributes.id = publishAttributes.fileId;
publishAttributes.fileId = undefined;
localStorage[publishIndex] = JSON.stringify(publishAttributes);
}
});
});
version = "v6";
}
localStorage["version"] = version;
localStorage["version"] = version;
});

View File

@ -10,249 +10,243 @@ define([
"gdrive-provider"
], function($, _, core, utils, extensionMgr, fileSystem, fileMgr) {
var synchronizer = {};
var synchronizer = {};
// Create a map with providerId: providerModule
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();
// 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;
});
});
// 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() {
lastSync = 0;
synchronizer.sync();
};
// Force the synchronization
synchronizer.forceSync = function() {
lastSync = 0;
synchronizer.sync();
};
// Recursive function to upload a single file on multiple locations
var uploadSyncAttributesList = [];
var uploadContent = undefined;
var uploadContentCRC = undefined;
var uploadTitle = undefined;
var uploadTitleCRC = undefined;
function locationUp(callback) {
// Recursive function to upload a single file on multiple locations
var uploadSyncAttributesList = [];
var uploadContent = undefined;
var uploadContentCRC = undefined;
var uploadTitle = undefined;
var uploadTitleCRC = undefined;
function locationUp(callback) {
// No more synchronized location for this document
if (uploadSyncAttributesList.length === 0) {
fileUp(callback);
return;
}
// No more synchronized location for this document
if(uploadSyncAttributesList.length === 0) {
fileUp(callback);
return;
}
// Dequeue a synchronized location
var syncAttributes = uploadSyncAttributesList.pop();
// Use the specified provider to perform the upload
syncAttributes.provider.syncUp(
uploadContent,
uploadContentCRC,
uploadTitle,
uploadTitleCRC,
syncAttributes,
function(error, uploadFlag) {
if(uploadFlag === true) {
// If uploadFlag is true, request another upload cycle
uploadCycle = true;
}
if(error) {
callback(error);
return;
}
if(uploadFlag) {
// Update syncAttributes in localStorage
utils.storeAttributes(syncAttributes);
}
locationUp(callback);
}
);
}
// Dequeue a synchronized location
var syncAttributes = uploadSyncAttributesList.pop();
// Use the specified provider to perform the upload
syncAttributes.provider.syncUp(uploadContent, uploadContentCRC, uploadTitle, uploadTitleCRC, syncAttributes, function(error, uploadFlag) {
if(uploadFlag === true) {
// If uploadFlag is true, request another upload cycle
uploadCycle = true;
}
if(error) {
callback(error);
return;
}
if(uploadFlag) {
// Update syncAttributes in localStorage
utils.storeAttributes(syncAttributes);
}
locationUp(callback);
});
}
// Recursive function to upload multiple files
var uploadFileList = [];
function fileUp(callback) {
// Recursive function to upload multiple files
var uploadFileList = [];
function fileUp(callback) {
// No more fileDesc to synchronize
if (uploadFileList.length === 0) {
syncUp(callback);
return;
}
// No more fileDesc to synchronize
if(uploadFileList.length === 0) {
syncUp(callback);
return;
}
// Dequeue a fileDesc to synchronize
var fileDesc = uploadFileList.pop();
uploadSyncAttributesList = _.values(fileDesc.syncLocations);
if(uploadSyncAttributesList.length === 0) {
fileUp(callback);
return;
}
// Dequeue a fileDesc to synchronize
var fileDesc = uploadFileList.pop();
uploadSyncAttributesList = _.values(fileDesc.syncLocations);
if(uploadSyncAttributesList.length === 0) {
fileUp(callback);
return;
}
// Get document title/content
uploadContent = fileDesc.getContent();
uploadContentCRC = utils.crc32(uploadContent);
uploadTitle = fileDesc.title;
uploadTitleCRC = utils.crc32(uploadTitle);
locationUp(callback);
}
// Get document title/content
uploadContent = fileDesc.content;
uploadContentCRC = utils.crc32(uploadContent);
uploadTitle = fileDesc.title;
uploadTitleCRC = utils.crc32(uploadTitle);
locationUp(callback);
}
// Entry point for up synchronization (upload changes)
var uploadCycle = false;
function syncUp(callback) {
if(uploadCycle === true) {
// New upload cycle
uploadCycle = false;
uploadFileList = _.values(fileSystem);
fileUp(callback);
}
else {
callback();
}
}
// Entry point for up synchronization (upload changes)
var uploadCycle = false;
function syncUp(callback) {
if(uploadCycle === true) {
// New upload cycle
uploadCycle = false;
uploadFileList = _.values(fileSystem);
fileUp(callback);
}
else {
callback();
}
}
// Recursive function to download changes from multiple providers
var providerList = [];
function providerDown(callback) {
if(providerList.length === 0) {
callback();
return;
}
var provider = providerList.pop();
// Recursive function to download changes from multiple providers
var providerList = [];
function providerDown(callback) {
if(providerList.length === 0) {
callback();
return;
}
var provider = providerList.pop();
// Check that provider has files to sync
if(!fileMgr.hasSync(provider)) {
providerDown(callback);
return;
}
// Check that provider has files to sync
if(!fileMgr.hasSync(provider)) {
providerDown(callback);
return;
}
// Perform provider's syncDown
provider.syncDown(function(error) {
if(error) {
callback(error);
return;
}
providerDown(callback);
});
}
// Perform provider's syncDown
provider.syncDown(function(error) {
if(error) {
callback(error);
return;
}
providerDown(callback);
});
}
// Entry point for down synchronization (download changes)
function syncDown(callback) {
providerList = _.values(providerMap);
providerDown(callback);
};
// Entry point for down synchronization (download changes)
function syncDown(callback) {
providerList = _.values(providerMap);
providerDown(callback);
}
;
// Main entry point for synchronization
var syncRunning = false;
var lastSync = 0;
synchronizer.sync = function() {
// If sync is already running or timeout is not reached or offline
if (syncRunning || lastSync + SYNC_PERIOD > utils.currentTime || core.isOffline) {
return;
}
syncRunning = true;
extensionMgr.onSyncRunning(true);
uploadCycle = true;
lastSync = utils.currentTime;
// Main entry point for synchronization
var syncRunning = false;
var lastSync = 0;
synchronizer.sync = function() {
// If sync is already running or timeout is not reached or offline
if(syncRunning || lastSync + SYNC_PERIOD > utils.currentTime || core.isOffline) {
return;
}
syncRunning = true;
extensionMgr.onSyncRunning(true);
uploadCycle = true;
lastSync = utils.currentTime;
function isError(error) {
if(error !== undefined) {
syncRunning = false;
extensionMgr.onSyncRunning(false);
return true;
}
return false;
}
function isError(error) {
if(error !== undefined) {
syncRunning = false;
extensionMgr.onSyncRunning(false);
return true;
}
return false;
}
syncDown(function(error) {
if(isError(error)) {
return;
}
syncUp(function(error) {
if(isError(error)) {
return;
}
syncRunning = false;
extensionMgr.onSyncRunning(false);
extensionMgr.onSyncSuccess();
});
});
};
// Run sync function periodically
if(viewerMode === false) {
core.addPeriodicCallback(synchronizer.sync);
}
syncDown(function(error) {
if(isError(error)) {
return;
}
syncUp(function(error) {
if(isError(error)) {
return;
}
syncRunning = false;
extensionMgr.onSyncRunning(false);
extensionMgr.onSyncSuccess();
});
});
};
// Run sync function periodically
if(viewerMode === false) {
core.addPeriodicCallback(synchronizer.sync);
}
// Initialize the export dialog
function initExportDialog(provider) {
// Initialize the export dialog
function initExportDialog(provider) {
// Reset fields
utils.resetModalInputs();
// Reset fields
utils.resetModalInputs();
// Load preferences
var serializedPreferences = localStorage[provider.providerId + ".exportPreferences"];
if(serializedPreferences) {
var exportPreferences = JSON.parse(serializedPreferences);
_.each(provider.exportPreferencesInputIds, function(inputId) {
utils.setInputValue("#input-sync-export-" + inputId, exportPreferences[inputId]);
});
}
// Load preferences
var serializedPreferences = localStorage[provider.providerId + ".exportPreferences"];
if(serializedPreferences) {
var exportPreferences = JSON.parse(serializedPreferences);
_.each(provider.exportPreferencesInputIds, function(inputId) {
utils.setInputValue("#input-sync-export-" + inputId, exportPreferences[inputId]);
});
}
// Open dialog box
$("#modal-upload-" + provider.providerId).modal();
}
// Open dialog box
$("#modal-upload-" + provider.providerId).modal();
}
core.onReady(function() {
// Init each provider
_.each(providerMap, function(provider) {
// Provider's import button
$(".action-sync-import-" + provider.providerId).click(function(event) {
provider.importFiles(event);
});
// Provider's export action
$(".action-sync-export-dialog-" + provider.providerId).click(function() {
initExportDialog(provider);
});
$(".action-sync-export-" + provider.providerId).click(function(event) {
core.onReady(function() {
// Init each provider
_.each(providerMap, function(provider) {
// Provider's import button
$(".action-sync-import-" + provider.providerId).click(function(event) {
provider.importFiles(event);
});
// Provider's export action
$(".action-sync-export-dialog-" + provider.providerId).click(function() {
initExportDialog(provider);
});
$(".action-sync-export-" + provider.providerId).click(function(event) {
// Perform the provider's export
var fileDesc = fileMgr.getCurrentFile();
provider.exportFile(event, fileDesc.title, fileDesc.getContent(), function(error, syncAttributes) {
if(error) {
return;
}
fileMgr.addSync(fileDesc, syncAttributes);
});
// Perform the provider's export
var fileDesc = fileMgr.getCurrentFile();
provider.exportFile(event, fileDesc.title, fileDesc.content, function(error, syncAttributes) {
if(error) {
return;
}
fileMgr.addSync(fileDesc, syncAttributes);
});
// Store input values as preferences for next time we open the export dialog
var exportPreferences = {};
_.each(provider.exportPreferencesInputIds, function(inputId) {
exportPreferences[inputId] = $("#input-sync-export-" + inputId).val();
});
localStorage[provider.providerId + ".exportPreferences"] = JSON.stringify(exportPreferences);
});
// Provider's manual export button
$(".action-sync-manual-" + provider.providerId).click(function(event) {
var fileDesc = fileMgr.getCurrentFile();
provider.exportManual(event, fileDesc.title, fileDesc.getContent(), function(error, syncAttributes) {
if(error) {
return;
}
fileMgr.addSync(fileDesc, syncAttributes);
});
});
});
});
// Store input values as preferences for next time we open the
// export dialog
var exportPreferences = {};
_.each(provider.exportPreferencesInputIds, function(inputId) {
exportPreferences[inputId] = $("#input-sync-export-" + inputId).val();
});
localStorage[provider.providerId + ".exportPreferences"] = JSON.stringify(exportPreferences);
});
// Provider's manual export button
$(".action-sync-manual-" + provider.providerId).click(function(event) {
var fileDesc = fileMgr.getCurrentFile();
provider.exportManual(event, fileDesc.title, fileDesc.content, function(error, syncAttributes) {
if(error) {
return;
}
fileMgr.addSync(fileDesc, syncAttributes);
});
});
});
});
extensionMgr.onSynchronizerCreated(synchronizer);
return synchronizer;
extensionMgr.onSynchronizerCreated(synchronizer);
return synchronizer;
});

View File

@ -6,164 +6,163 @@ define([
"async-runner"
], function($, core, utils, extensionMgr, asyncRunner) {
var oauthParams = undefined;
var oauthParams = undefined;
var tumblrHelper = {};
var tumblrHelper = {};
// Only used to check the offline status
function connect(task) {
task.onRun(function() {
if(core.isOffline === true) {
task.error(new Error("Operation not available in offline mode.|stopPublish"));
return;
}
task.chain();
});
}
// Only used to check the offline status
function connect(task) {
task.onRun(function() {
if(core.isOffline === true) {
task.error(new Error("Operation not available in offline mode.|stopPublish"));
return;
}
task.chain();
});
}
// Try to authenticate with OAuth
function authenticate(task) {
var authWindow = undefined;
var intervalId = undefined;
task.onRun(function() {
if (oauthParams !== undefined) {
task.chain();
return;
}
var serializedOauthParams = localStorage["tumblrOauthParams"];
if(serializedOauthParams !== undefined) {
oauthParams = JSON.parse(serializedOauthParams);
task.chain();
return;
}
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;
var oauth_object = undefined;
function getOauthToken() {
$.getJSON(TUMBLR_PROXY_URL + "request_token", function(data) {
if(data.oauth_token !== undefined) {
oauth_object = data;
task.chain(getVerifier);
}
else {
task.error(new Error(errorMsg));
}
});
}
function getVerifier() {
localStorage.removeItem("tumblrVerifier");
authWindow = utils.popupWindow(
'tumblr-oauth-client.html?oauth_token=' + oauth_object.oauth_token,
'stackedit-tumblr-oauth', 800, 600);
authWindow.focus();
intervalId = setInterval(function() {
if(authWindow.closed === true) {
clearInterval(intervalId);
authWindow = undefined;
intervalId = undefined;
oauth_object.oauth_verifier = localStorage["tumblrVerifier"];
if(oauth_object.oauth_verifier === undefined) {
task.error(new Error(errorMsg));
return;
}
localStorage.removeItem("tumblrVerifier");
task.chain(getAccessToken);
}
}, 500);
}
function getAccessToken() {
$.getJSON(TUMBLR_PROXY_URL + "access_token", oauth_object, function(data) {
if(data.access_token !== undefined && data.access_token_secret !== undefined) {
localStorage["tumblrOauthParams"] = JSON.stringify(data);
oauthParams = data;
task.chain();
}
else {
task.error(new Error(errorMsg));
}
});
}
task.chain(getOauthToken);
});
task.onError(function() {
if(intervalId !== undefined) {
clearInterval(intervalId);
}
if(authWindow !== undefined) {
authWindow.close();
}
});
}
// Try to authenticate with OAuth
function authenticate(task) {
var authWindow = undefined;
var intervalId = undefined;
task.onRun(function() {
if(oauthParams !== undefined) {
task.chain();
return;
}
var serializedOauthParams = localStorage["tumblrOauthParams"];
if(serializedOauthParams !== undefined) {
oauthParams = JSON.parse(serializedOauthParams);
task.chain();
return;
}
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;
var oauth_object = undefined;
function getOauthToken() {
$.getJSON(TUMBLR_PROXY_URL + "request_token", function(data) {
if(data.oauth_token !== undefined) {
oauth_object = data;
task.chain(getVerifier);
}
else {
task.error(new Error(errorMsg));
}
});
}
function getVerifier() {
localStorage.removeItem("tumblrVerifier");
authWindow = utils.popupWindow('tumblr-oauth-client.html?oauth_token=' + oauth_object.oauth_token, 'stackedit-tumblr-oauth', 800, 600);
authWindow.focus();
intervalId = setInterval(function() {
if(authWindow.closed === true) {
clearInterval(intervalId);
authWindow = undefined;
intervalId = undefined;
oauth_object.oauth_verifier = localStorage["tumblrVerifier"];
if(oauth_object.oauth_verifier === undefined) {
task.error(new Error(errorMsg));
return;
}
localStorage.removeItem("tumblrVerifier");
task.chain(getAccessToken);
}
}, 500);
}
function getAccessToken() {
$.getJSON(TUMBLR_PROXY_URL + "access_token", oauth_object, function(data) {
if(data.access_token !== undefined && data.access_token_secret !== undefined) {
localStorage["tumblrOauthParams"] = JSON.stringify(data);
oauthParams = data;
task.chain();
}
else {
task.error(new Error(errorMsg));
}
});
}
task.chain(getOauthToken);
});
task.onError(function() {
if(intervalId !== undefined) {
clearInterval(intervalId);
}
if(authWindow !== undefined) {
authWindow.close();
}
});
}
tumblrHelper.upload = function(blogHostname, postId, tags, format, title, content, callback) {
var task = asyncRunner.createTask();
connect(task);
authenticate(task);
task.onRun(function() {
var data = $.extend({
blog_hostname: blogHostname,
post_id: postId,
tags: tags,
format: format,
title: title,
content: content
}, oauthParams);
$.ajax({
url : TUMBLR_PROXY_URL + "post",
data: data,
type: "POST",
dataType : "json",
timeout : AJAX_TIMEOUT
}).done(function(post, textStatus, jqXHR) {
postId = post.id;
task.chain();
}).fail(function(jqXHR) {
var error = {
code: jqXHR.status,
message: jqXHR.statusText
};
// Handle error
if(error.code === 404 && postId !== undefined) {
error = 'Post ' + postId + ' not found on Tumblr.|removePublish';
}
handleError(error, task);
});
});
task.onSuccess(function() {
callback(undefined, postId);
});
task.onError(function(error) {
callback(error);
});
asyncRunner.addTask(task);
};
tumblrHelper.upload = function(blogHostname, postId, tags, format, title, content, callback) {
var task = asyncRunner.createTask();
connect(task);
authenticate(task);
task.onRun(function() {
var data = $.extend({
blog_hostname: blogHostname,
post_id: postId,
tags: tags,
format: format,
title: title,
content: content
}, oauthParams);
$.ajax({
url: TUMBLR_PROXY_URL + "post",
data: data,
type: "POST",
dataType: "json",
timeout: AJAX_TIMEOUT
}).done(function(post, textStatus, jqXHR) {
postId = post.id;
task.chain();
}).fail(function(jqXHR) {
var error = {
code: jqXHR.status,
message: jqXHR.statusText
};
// Handle error
if(error.code === 404 && postId !== undefined) {
error = 'Post ' + postId + ' not found on Tumblr.|removePublish';
}
handleError(error, task);
});
});
task.onSuccess(function() {
callback(undefined, postId);
});
task.onError(function(error) {
callback(error);
});
asyncRunner.addTask(task);
};
function handleError(error, task) {
var errorMsg = undefined;
if (error) {
logger.error(error);
// Try to analyze the error
if (typeof error === "string") {
errorMsg = error;
}
else {
errorMsg = "Could not publish on Tumblr.";
if (error.code === 401 || error.code === 403) {
oauthParams = undefined;
localStorage.removeItem("tumblrOauthParams");
errorMsg = "Access to Tumblr account is not authorized.";
task.retry(new Error(errorMsg), 1);
return;
} else if (error.code <= 0) {
core.setOffline();
errorMsg = "|stopPublish";
}
}
}
task.error(new Error(errorMsg));
}
function handleError(error, task) {
var errorMsg = undefined;
if(error) {
logger.error(error);
// Try to analyze the error
if(typeof error === "string") {
errorMsg = error;
}
else {
errorMsg = "Could not publish on Tumblr.";
if(error.code === 401 || error.code === 403) {
oauthParams = undefined;
localStorage.removeItem("tumblrOauthParams");
errorMsg = "Access to Tumblr account is not authorized.";
task.retry(new Error(errorMsg), 1);
return;
}
else if(error.code <= 0) {
core.setOffline();
errorMsg = "|stopPublish";
}
}
}
task.error(new Error(errorMsg));
}
return tumblrHelper;
return tumblrHelper;
});

View File

@ -3,47 +3,37 @@ define([
"tumblr-helper"
], function(utils, tumblrHelper) {
var PROVIDER_TUMBLR = "tumblr";
var PROVIDER_TUMBLR = "tumblr";
var tumblrProvider = {
providerId: PROVIDER_TUMBLR,
providerName: "Tumblr",
publishPreferencesInputIds: ["tumblr-hostname"]
};
var tumblrProvider = {
providerId: PROVIDER_TUMBLR,
providerName: "Tumblr",
publishPreferencesInputIds: [
"tumblr-hostname"
]
};
tumblrProvider.publish = function(publishAttributes, title, content, callback) {
tumblrHelper.upload(
publishAttributes.blogHostname,
publishAttributes.postId,
publishAttributes.tags,
publishAttributes.format == "markdown" ? "markdown" : "html",
title,
content,
function(error, postId) {
if(error) {
callback(error);
return;
}
publishAttributes.postId = postId;
callback();
}
);
};
tumblrProvider.publish = function(publishAttributes, title, content, callback) {
tumblrHelper.upload(publishAttributes.blogHostname, publishAttributes.postId, publishAttributes.tags, publishAttributes.format == "markdown" ? "markdown" : "html", title, content, function(error, postId) {
if(error) {
callback(error);
return;
}
publishAttributes.postId = postId;
callback();
});
};
tumblrProvider.newPublishAttributes = function(event) {
var publishAttributes = {};
publishAttributes.blogHostname = utils
.getInputTextValue(
"#input-publish-tumblr-hostname",
event,
/^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$/);
publishAttributes.postId = utils.getInputTextValue("#input-publish-postid");
publishAttributes.tags = utils.getInputTextValue("#input-publish-tags");
if(event.isPropagationStopped()) {
return undefined;
}
return publishAttributes;
};
tumblrProvider.newPublishAttributes = function(event) {
var publishAttributes = {};
publishAttributes.blogHostname = utils.getInputTextValue("#input-publish-tumblr-hostname", event, /^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$/);
publishAttributes.postId = utils.getInputTextValue("#input-publish-postid");
publishAttributes.tags = utils.getInputTextValue("#input-publish-tags");
if(event.isPropagationStopped()) {
return undefined;
}
return publishAttributes;
};
return tumblrProvider;
return tumblrProvider;
});

View File

@ -4,310 +4,517 @@ define([
"lib/FileSaver"
], function($, _) {
var utils = {};
var utils = {};
// Return a parameter from the URL
utils.getURLParameter = function(name) {
var regex = new RegExp(name + "=(.+?)(&|$)");
try {
return decodeURIComponent(regex.exec(location.search)[1]);
} catch (e) {
return undefined;
}
};
// Return a parameter from the URL
utils.getURLParameter = function(name) {
var regex = new RegExp(name + "=(.+?)(&|$)");
try {
return decodeURIComponent(regex.exec(location.search)[1]);
}
catch (e) {
return undefined;
}
};
// Transform a selector into a jQuery object
function jqElt(element) {
if(_.isString(element)) {
return $(element);
}
return element;
}
// Transform a selector into a jQuery object
function jqElt(element) {
if(_.isString(element)) {
return $(element);
}
return element;
}
// For input control
function inputError(element, event) {
if(event !== undefined) {
element.stop(true, true).addClass("error").delay(1000).switchClass("error");
event.stopPropagation();
}
}
// For input control
function inputError(element, event) {
if(event !== undefined) {
element.stop(true, true).addClass("error").delay(1000).switchClass("error");
event.stopPropagation();
}
}
// Return input value
utils.getInputValue = function(element) {
element = jqElt(element);
return element.val();
};
// Return input value
utils.getInputValue = function(element) {
element = jqElt(element);
return element.val();
};
// Set input value
utils.setInputValue = function(element, value) {
element = jqElt(element);
element.val(value);
};
// Set input value
utils.setInputValue = function(element, value) {
element = jqElt(element);
element.val(value);
};
// Return input text value
utils.getInputTextValue = function(element, event, validationRegex) {
element = jqElt(element);
var value = element.val();
if (value === undefined) {
inputError(element, event);
return undefined;
}
// trim
value = utils.trim(value);
if((value.length === 0)
|| (validationRegex !== undefined && !value.match(validationRegex))) {
inputError(element, event);
return undefined;
}
return value;
};
// Return input text value
utils.getInputTextValue = function(element, event, validationRegex) {
element = jqElt(element);
var value = element.val();
if(value === undefined) {
inputError(element, event);
return undefined;
}
// trim
value = utils.trim(value);
if((value.length === 0) || (validationRegex !== undefined && !value.match(validationRegex))) {
inputError(element, event);
return undefined;
}
return value;
};
// Return input integer value
utils.getInputIntValue = function(element, event, min, max) {
element = jqElt(element);
var value = utils.getInputTextValue(element, event);
if(value === undefined) {
return undefined;
}
value = parseInt(value);
if((value === NaN)
|| (min !== undefined && value < min)
|| (max !== undefined && value > max)) {
inputError(element, event);
return undefined;
}
return value;
};
// Return input integer value
utils.getInputIntValue = function(element, event, min, max) {
element = jqElt(element);
var value = utils.getInputTextValue(element, event);
if(value === undefined) {
return undefined;
}
value = parseInt(value);
if((value === NaN) || (min !== undefined && value < min) || (max !== undefined && value > max)) {
inputError(element, event);
return undefined;
}
return value;
};
// Return checkbox boolean value
utils.getInputChecked = function(element) {
element = jqElt(element);
return element.prop("checked");
};
// Return checkbox boolean value
utils.getInputChecked = function(element) {
element = jqElt(element);
return element.prop("checked");
};
// Set checkbox state
utils.setInputChecked = function(element, checked) {
element = jqElt(element);
element.prop("checked", checked);
};
// Set checkbox state
utils.setInputChecked = function(element, checked) {
element = jqElt(element);
element.prop("checked", checked);
};
// Get radio button value
utils.getInputRadio = function(name) {
return $("input:radio[name=" + name + "]:checked").prop("value");
};
// Get radio button value
utils.getInputRadio = function(name) {
return $("input:radio[name=" + name + "]:checked").prop("value");
};
// Set radio button value
utils.setInputRadio = function(name, value) {
$("input:radio[name=" + name + "][value=" + value + "]").prop("checked", true);
};
// Set radio button value
utils.setInputRadio = function(name, value) {
$("input:radio[name=" + name + "][value=" + value + "]").prop("checked", true);
};
// Reset input control in all modals
utils.resetModalInputs = function() {
$(".modal input[type=text]:not([disabled]), .modal input[type=password]").val("");
};
// Reset input control in all modals
utils.resetModalInputs = function() {
$(".modal input[type=text]:not([disabled]), .modal input[type=password]").val("");
};
// Basic trim function
utils.trim = function(str) {
return $.trim(str);
};
// Basic trim function
utils.trim = function(str) {
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
};
// 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) {
return url;
}
if(url.indexOf("http") !== 0) {
url = "http://" + url;
}
if(addSlash && url.indexOf("/", url.length - 1) === -1) {
url += "/";
}
return url;
};
// Check an URL
utils.checkUrl = function(url, addSlash) {
if(!url) {
return url;
}
if(url.indexOf("http") !== 0) {
url = "http://" + url;
}
if(addSlash && url.indexOf("/", url.length - 1) === -1) {
url += "/";
}
return url;
};
// Base64 conversion
utils.encodeBase64 = function(str) {
if (str.length === 0) {
return "";
}
// 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
].join(""));
};
// UTF-8 to byte array
var bytes = [], offset = 0, length, char;
// 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');
}
};
str = encodeURI(str);
length = str.length;
// Generates a random string
utils.randomString = function() {
return _.random(4294967296).toString(36);
};
while (offset < length) {
char = str[offset];
offset += 1;
// Time shared by others modules
utils.updateCurrentTime = function() {
utils.currentTime = new Date().getTime();
};
utils.updateCurrentTime();
if ('%' !== char) {
bytes.push(char.charCodeAt(0));
} else {
char = str[offset] + str[offset + 1];
bytes.push(parseInt(char, 16));
offset += 2;
}
}
// Serialize sync/publish attributes and store it in the fileStorage
utils.storeAttributes = function(attributes) {
var storeIndex = attributes.syncIndex || attributes.publishIndex;
// Don't store sync/publish index
attributes = _.omit(attributes, "syncIndex", "publishIndex");
// Store providerId instead of provider
attributes.provider = attributes.provider.providerId;
localStorage[storeIndex] = JSON.stringify(attributes);
};
// byte array to base64
var padchar = '=';
var alpha = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
// Base64 conversion
utils.encodeBase64 = function(str) {
if(str.length === 0) {
return "";
}
var i, b10;
var x = [];
// UTF-8 to byte array
var bytes = [], offset = 0, length, char;
var imax = bytes.length - bytes.length % 3;
str = encodeURI(str);
length = str.length;
for (i = 0; i < imax; i += 3) {
b10 = (bytes[i] << 16) | (bytes[i+1] << 8) | bytes[i+2];
x.push(alpha.charAt(b10 >> 18));
x.push(alpha.charAt((b10 >> 12) & 0x3F));
x.push(alpha.charAt((b10 >> 6) & 0x3f));
x.push(alpha.charAt(b10 & 0x3f));
}
switch (bytes.length - imax) {
case 1:
b10 = bytes[i] << 16;
x.push(alpha.charAt(b10 >> 18) + alpha.charAt((b10 >> 12) & 0x3F) +
padchar + padchar);
break;
case 2:
b10 = (bytes[i] << 16) | (bytes[i+1] << 8);
x.push(alpha.charAt(b10 >> 18) + alpha.charAt((b10 >> 12) & 0x3F) +
alpha.charAt((b10 >> 6) & 0x3f) + padchar);
break;
}
return x.join('');
};
while (offset < length) {
char = str[offset];
offset += 1;
// CRC32 algorithm
var mHash = [ 0, 1996959894, 3993919788, 2567524794, 124634137,
1886057615, 3915621685, 2657392035, 249268274, 2044508324,
3772115230, 2547177864, 162941995, 2125561021, 3887607047,
2428444049, 498536548, 1789927666, 4089016648, 2227061214,
450548861, 1843258603, 4107580753, 2211677639, 325883990,
1684777152, 4251122042, 2321926636, 335633487, 1661365465,
4195302755, 2366115317, 997073096, 1281953886, 3579855332,
2724688242, 1006888145, 1258607687, 3524101629, 2768942443,
901097722, 1119000684, 3686517206, 2898065728, 853044451,
1172266101, 3705015759, 2882616665, 651767980, 1373503546,
3369554304, 3218104598, 565507253, 1454621731, 3485111705,
3099436303, 671266974, 1594198024, 3322730930, 2970347812,
795835527, 1483230225, 3244367275, 3060149565, 1994146192,
31158534, 2563907772, 4023717930, 1907459465, 112637215,
2680153253, 3904427059, 2013776290, 251722036, 2517215374,
3775830040, 2137656763, 141376813, 2439277719, 3865271297,
1802195444, 476864866, 2238001368, 4066508878, 1812370925,
453092731, 2181625025, 4111451223, 1706088902, 314042704,
2344532202, 4240017532, 1658658271, 366619977, 2362670323,
4224994405, 1303535960, 984961486, 2747007092, 3569037538,
1256170817, 1037604311, 2765210733, 3554079995, 1131014506,
879679996, 2909243462, 3663771856, 1141124467, 855842277,
2852801631, 3708648649, 1342533948, 654459306, 3188396048,
3373015174, 1466479909, 544179635, 3110523913, 3462522015,
1591671054, 702138776, 2966460450, 3352799412, 1504918807,
783551873, 3082640443, 3233442989, 3988292384, 2596254646,
62317068, 1957810842, 3939845945, 2647816111, 81470997, 1943803523,
3814918930, 2489596804, 225274430, 2053790376, 3826175755,
2466906013, 167816743, 2097651377, 4027552580, 2265490386,
503444072, 1762050814, 4150417245, 2154129355, 426522225,
1852507879, 4275313526, 2312317920, 282753626, 1742555852,
4189708143, 2394877945, 397917763, 1622183637, 3604390888,
2714866558, 953729732, 1340076626, 3518719985, 2797360999,
1068828381, 1219638859, 3624741850, 2936675148, 906185462,
1090812512, 3747672003, 2825379669, 829329135, 1181335161,
3412177804, 3160834842, 628085408, 1382605366, 3423369109,
3138078467, 570562233, 1426400815, 3317316542, 2998733608,
733239954, 1555261956, 3268935591, 3050360625, 752459403,
1541320221, 2607071920, 3965973030, 1969922972, 40735498,
2617837225, 3943577151, 1913087877, 83908371, 2512341634,
3803740692, 2075208622, 213261112, 2463272603, 3855990285,
2094854071, 198958881, 2262029012, 4057260610, 1759359992,
534414190, 2176718541, 4139329115, 1873836001, 414664567,
2282248934, 4279200368, 1711684554, 285281116, 2405801727,
4167216745, 1634467795, 376229701, 2685067896, 3608007406,
1308918612, 956543938, 2808555105, 3495958263, 1231636301,
1047427035, 2932959818, 3654703836, 1088359270, 936918000,
2847714899, 3736837829, 1202900863, 817233897, 3183342108,
3401237130, 1404277552, 615818150, 3134207493, 3453421203,
1423857449, 601450431, 3009837614, 3294710456, 1567103746,
711928724, 3020668471, 3272380065, 1510334235, 755167117 ];
utils.crc32 = function(str) {
var n = 0, crc = -1;
for ( var i = 0; i < str.length; i++) {
n = (crc ^ str.charCodeAt(i)) & 0xFF;
crc = (crc >>> 8) ^ mHash[n];
}
crc = crc ^ (-1);
if (crc < 0) {
crc = 0xFFFFFFFF + crc + 1;
}
return crc.toString(16);
};
if('%' !== char) {
bytes.push(char.charCodeAt(0));
}
else {
char = str[offset] + str[offset + 1];
bytes.push(parseInt(char, 16));
offset += 2;
}
}
// 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);
};
// byte array to base64
var padchar = '=';
var alpha = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
// 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');
}
};
var i, b10;
var x = [];
// Generates a random string
utils.randomString = function() {
return _.random(4294967296).toString(36);
};
var imax = bytes.length - bytes.length % 3;
// Time shared by others modules
utils.updateCurrentTime = function() {
utils.currentTime = new Date().getTime();
};
utils.updateCurrentTime();
for (i = 0; i < imax; i += 3) {
b10 = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2];
x.push(alpha.charAt(b10 >> 18));
x.push(alpha.charAt((b10 >> 12) & 0x3F));
x.push(alpha.charAt((b10 >> 6) & 0x3f));
x.push(alpha.charAt(b10 & 0x3f));
}
switch (bytes.length - imax) {
case 1:
b10 = bytes[i] << 16;
x.push(alpha.charAt(b10 >> 18) + alpha.charAt((b10 >> 12) & 0x3F) + padchar + padchar);
break;
case 2:
b10 = (bytes[i] << 16) | (bytes[i + 1] << 8);
x.push(alpha.charAt(b10 >> 18) + alpha.charAt((b10 >> 12) & 0x3F) + alpha.charAt((b10 >> 6) & 0x3f) + padchar);
break;
}
return x.join('');
};
// CRC32 algorithm
var mHash = [
0,
1996959894,
3993919788,
2567524794,
124634137,
1886057615,
3915621685,
2657392035,
249268274,
2044508324,
3772115230,
2547177864,
162941995,
2125561021,
3887607047,
2428444049,
498536548,
1789927666,
4089016648,
2227061214,
450548861,
1843258603,
4107580753,
2211677639,
325883990,
1684777152,
4251122042,
2321926636,
335633487,
1661365465,
4195302755,
2366115317,
997073096,
1281953886,
3579855332,
2724688242,
1006888145,
1258607687,
3524101629,
2768942443,
901097722,
1119000684,
3686517206,
2898065728,
853044451,
1172266101,
3705015759,
2882616665,
651767980,
1373503546,
3369554304,
3218104598,
565507253,
1454621731,
3485111705,
3099436303,
671266974,
1594198024,
3322730930,
2970347812,
795835527,
1483230225,
3244367275,
3060149565,
1994146192,
31158534,
2563907772,
4023717930,
1907459465,
112637215,
2680153253,
3904427059,
2013776290,
251722036,
2517215374,
3775830040,
2137656763,
141376813,
2439277719,
3865271297,
1802195444,
476864866,
2238001368,
4066508878,
1812370925,
453092731,
2181625025,
4111451223,
1706088902,
314042704,
2344532202,
4240017532,
1658658271,
366619977,
2362670323,
4224994405,
1303535960,
984961486,
2747007092,
3569037538,
1256170817,
1037604311,
2765210733,
3554079995,
1131014506,
879679996,
2909243462,
3663771856,
1141124467,
855842277,
2852801631,
3708648649,
1342533948,
654459306,
3188396048,
3373015174,
1466479909,
544179635,
3110523913,
3462522015,
1591671054,
702138776,
2966460450,
3352799412,
1504918807,
783551873,
3082640443,
3233442989,
3988292384,
2596254646,
62317068,
1957810842,
3939845945,
2647816111,
81470997,
1943803523,
3814918930,
2489596804,
225274430,
2053790376,
3826175755,
2466906013,
167816743,
2097651377,
4027552580,
2265490386,
503444072,
1762050814,
4150417245,
2154129355,
426522225,
1852507879,
4275313526,
2312317920,
282753626,
1742555852,
4189708143,
2394877945,
397917763,
1622183637,
3604390888,
2714866558,
953729732,
1340076626,
3518719985,
2797360999,
1068828381,
1219638859,
3624741850,
2936675148,
906185462,
1090812512,
3747672003,
2825379669,
829329135,
1181335161,
3412177804,
3160834842,
628085408,
1382605366,
3423369109,
3138078467,
570562233,
1426400815,
3317316542,
2998733608,
733239954,
1555261956,
3268935591,
3050360625,
752459403,
1541320221,
2607071920,
3965973030,
1969922972,
40735498,
2617837225,
3943577151,
1913087877,
83908371,
2512341634,
3803740692,
2075208622,
213261112,
2463272603,
3855990285,
2094854071,
198958881,
2262029012,
4057260610,
1759359992,
534414190,
2176718541,
4139329115,
1873836001,
414664567,
2282248934,
4279200368,
1711684554,
285281116,
2405801727,
4167216745,
1634467795,
376229701,
2685067896,
3608007406,
1308918612,
956543938,
2808555105,
3495958263,
1231636301,
1047427035,
2932959818,
3654703836,
1088359270,
936918000,
2847714899,
3736837829,
1202900863,
817233897,
3183342108,
3401237130,
1404277552,
615818150,
3134207493,
3453421203,
1423857449,
601450431,
3009837614,
3294710456,
1567103746,
711928724,
3020668471,
3272380065,
1510334235,
755167117
];
utils.crc32 = function(str) {
var n = 0, crc = -1;
for ( var i = 0; i < str.length; i++) {
n = (crc ^ str.charCodeAt(i)) & 0xFF;
crc = (crc >>> 8) ^ mHash[n];
}
crc = crc ^ (-1);
if(crc < 0) {
crc = 0xFFFFFFFF + crc + 1;
}
return crc.toString(16);
};
// Serialize sync/publish attributes and store it in the fileStorage
utils.storeAttributes = function(attributes) {
var storeIndex = attributes.syncIndex || attributes.publishIndex;
// Don't store sync/publish index
attributes = _.omit(attributes, "syncIndex", "publishIndex");
// Store providerId instead of provider
attributes.provider = attributes.provider.providerId;
localStorage[storeIndex] = JSON.stringify(attributes);
};
return utils;
return utils;
});

View File

@ -6,161 +6,160 @@ define([
"async-runner"
], function($, core, utils, extensionMgr, asyncRunner) {
var token = undefined;
var token = undefined;
var wordpressHelper = {};
var wordpressHelper = {};
// Only used to check the offline status
function connect(task) {
task.onRun(function() {
if(core.isOffline === true) {
task.error(new Error("Operation not available in offline mode.|stopPublish"));
return;
}
task.chain();
});
}
// Only used to check the offline status
function connect(task) {
task.onRun(function() {
if(core.isOffline === true) {
task.error(new Error("Operation not available in offline mode.|stopPublish"));
return;
}
task.chain();
});
}
// Try to authenticate with OAuth
function authenticate(task) {
var authWindow = undefined;
var intervalId = undefined;
task.onRun(function() {
token = localStorage["wordpressToken"];
if(token !== undefined) {
task.chain();
return;
}
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 = utils.popupWindow(
'wordpress-oauth-client.html?client_id=' + WORDPRESS_CLIENT_ID,
'stackedit-wordpress-oauth', 960, 600);
authWindow.focus();
intervalId = setInterval(function() {
if(authWindow.closed === true) {
clearInterval(intervalId);
authWindow = undefined;
intervalId = undefined;
code = localStorage["wordpressCode"];
if(code === undefined) {
task.error(new Error(errorMsg));
return;
}
localStorage.removeItem("wordpressCode");
task.chain(getToken);
}
}, 500);
}
function getToken() {
$.getJSON(WORDPRESS_PROXY_URL + "authenticate/" + code, function(data) {
if(data.token !== undefined) {
token = data.token;
localStorage["wordpressToken"] = token;
task.chain();
}
else {
task.error(new Error(errorMsg));
}
});
}
task.chain(getCode);
});
task.onError(function() {
if(intervalId !== undefined) {
clearInterval(intervalId);
}
if(authWindow !== undefined) {
authWindow.close();
}
});
}
// Try to authenticate with OAuth
function authenticate(task) {
var authWindow = undefined;
var intervalId = undefined;
task.onRun(function() {
token = localStorage["wordpressToken"];
if(token !== undefined) {
task.chain();
return;
}
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 = utils.popupWindow('wordpress-oauth-client.html?client_id=' + WORDPRESS_CLIENT_ID, 'stackedit-wordpress-oauth', 960, 600);
authWindow.focus();
intervalId = setInterval(function() {
if(authWindow.closed === true) {
clearInterval(intervalId);
authWindow = undefined;
intervalId = undefined;
code = localStorage["wordpressCode"];
if(code === undefined) {
task.error(new Error(errorMsg));
return;
}
localStorage.removeItem("wordpressCode");
task.chain(getToken);
}
}, 500);
}
function getToken() {
$.getJSON(WORDPRESS_PROXY_URL + "authenticate/" + code, function(data) {
if(data.token !== undefined) {
token = data.token;
localStorage["wordpressToken"] = token;
task.chain();
}
else {
task.error(new Error(errorMsg));
}
});
}
task.chain(getCode);
});
task.onError(function() {
if(intervalId !== undefined) {
clearInterval(intervalId);
}
if(authWindow !== undefined) {
authWindow.close();
}
});
}
wordpressHelper.upload = function(site, postId, tags, title, content, callback) {
var task = asyncRunner.createTask();
connect(task);
authenticate(task);
task.onRun(function() {
var url = WORDPRESS_PROXY_URL + "post";
var data = {
token: token,
site: site,
postId: postId,
tags: tags,
title: title,
content: content
};
$.ajax({
url : url,
data: data,
type: "POST",
dataType : "json",
timeout : AJAX_TIMEOUT
}).done(function(response, textStatus, jqXHR) {
if(response.body.ID) {
postId = response.body.ID;
task.chain();
return;
}
var error = {
code: response.code,
message: response.body.error
};
// Handle error
if(error.code === 404) {
if(error.message == "unknown_blog") {
error = 'Site "' + site + '" not found on WordPress.|removePublish';
}
else if(error.message == "unknown_post"){
error = 'Post ' + postId + ' not found on WordPress.|removePublish';
}
}
handleError(error, task);
}).fail(function(jqXHR) {
var error = {
code: jqXHR.status,
message: jqXHR.statusText
};
handleError(error, task);
});
});
task.onSuccess(function() {
callback(undefined, postId);
});
task.onError(function(error) {
callback(error);
});
asyncRunner.addTask(task);
};
wordpressHelper.upload = function(site, postId, tags, title, content, callback) {
var task = asyncRunner.createTask();
connect(task);
authenticate(task);
task.onRun(function() {
var url = WORDPRESS_PROXY_URL + "post";
var data = {
token: token,
site: site,
postId: postId,
tags: tags,
title: title,
content: content
};
$.ajax({
url: url,
data: data,
type: "POST",
dataType: "json",
timeout: AJAX_TIMEOUT
}).done(function(response, textStatus, jqXHR) {
if(response.body.ID) {
postId = response.body.ID;
task.chain();
return;
}
var error = {
code: response.code,
message: response.body.error
};
// Handle error
if(error.code === 404) {
if(error.message == "unknown_blog") {
error = 'Site "' + site + '" not found on WordPress.|removePublish';
}
else if(error.message == "unknown_post") {
error = 'Post ' + postId + ' not found on WordPress.|removePublish';
}
}
handleError(error, task);
}).fail(function(jqXHR) {
var error = {
code: jqXHR.status,
message: jqXHR.statusText
};
handleError(error, task);
});
});
task.onSuccess(function() {
callback(undefined, postId);
});
task.onError(function(error) {
callback(error);
});
asyncRunner.addTask(task);
};
function handleError(error, task) {
var errorMsg = undefined;
if (error) {
logger.error(error);
// Try to analyze the error
if (typeof error === "string") {
errorMsg = error;
}
else {
errorMsg = "Could not publish on WordPress.";
if ((error.code === 400 && error.message == "invalid_token") || error.code === 401 || error.code === 403) {
localStorage.removeItem("wordpressToken");
errorMsg = "Access to WordPress account is not authorized.";
task.retry(new Error(errorMsg), 1);
return;
} else if (error.code <= 0) {
core.setOffline();
errorMsg = "|stopPublish";
}
}
}
task.error(new Error(errorMsg));
}
function handleError(error, task) {
var errorMsg = undefined;
if(error) {
logger.error(error);
// Try to analyze the error
if(typeof error === "string") {
errorMsg = error;
}
else {
errorMsg = "Could not publish on WordPress.";
if((error.code === 400 && error.message == "invalid_token") || error.code === 401 || error.code === 403) {
localStorage.removeItem("wordpressToken");
errorMsg = "Access to WordPress account is not authorized.";
task.retry(new Error(errorMsg), 1);
return;
}
else if(error.code <= 0) {
core.setOffline();
errorMsg = "|stopPublish";
}
}
}
task.error(new Error(errorMsg));
}
return wordpressHelper;
return wordpressHelper;
});

View File

@ -3,47 +3,38 @@ define([
"wordpress-helper"
], function(utils, wordpressHelper) {
var PROVIDER_WORDPRESS = "wordpress";
var PROVIDER_WORDPRESS = "wordpress";
var wordpressProvider = {
providerId: PROVIDER_WORDPRESS,
providerName: "WordPress",
defaultPublishFormat: "html",
publishPreferencesInputIds: ["wordpress-site"]
};
var wordpressProvider = {
providerId: PROVIDER_WORDPRESS,
providerName: "WordPress",
defaultPublishFormat: "html",
publishPreferencesInputIds: [
"wordpress-site"
]
};
wordpressProvider.publish = function(publishAttributes, title, content, callback) {
wordpressHelper.upload(
publishAttributes.site,
publishAttributes.postId,
publishAttributes.tags,
title,
content,
function(error, postId) {
if(error) {
callback(error);
return;
}
publishAttributes.postId = postId;
callback();
}
);
};
wordpressProvider.publish = function(publishAttributes, title, content, callback) {
wordpressHelper.upload(publishAttributes.site, publishAttributes.postId, publishAttributes.tags, title, content, function(error, postId) {
if(error) {
callback(error);
return;
}
publishAttributes.postId = postId;
callback();
});
};
wordpressProvider.newPublishAttributes = function(event) {
var publishAttributes = {};
publishAttributes.site = utils
.getInputTextValue(
"#input-publish-wordpress-site",
event,
/^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$/);
publishAttributes.postId = utils.getInputTextValue("#input-publish-postid");
publishAttributes.tags = utils.getInputTextValue("#input-publish-tags");
if(event.isPropagationStopped()) {
return undefined;
}
return publishAttributes;
};
wordpressProvider.newPublishAttributes = function(event) {
var publishAttributes = {};
publishAttributes.site = utils.getInputTextValue("#input-publish-wordpress-site", event, /^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$/);
publishAttributes.postId = utils.getInputTextValue("#input-publish-postid");
publishAttributes.tags = utils.getInputTextValue("#input-publish-tags");
if(event.isPropagationStopped()) {
return undefined;
}
return publishAttributes;
};
return wordpressProvider;
return wordpressProvider;
});

View File

@ -0,0 +1,267 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<profiles version="11">
<profile kind="CodeFormatterProfile" name="JsFormatter" version="11">
<setting id="org.eclipse.wst.jsdt.core.formatter.indent_body_declarations_compare_to_annotation_declaration_header" value="true"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_comma_in_enum_constant_arguments" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_brace_in_constructor_declaration" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.brace_position_for_block_in_case" value="end_of_line"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_colon_in_case" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.indent_empty_lines" value="false"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.alignment_for_compact_if" value="16"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.brace_position_for_annotation_type_declaration" value="end_of_line"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_new_line_after_annotation" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.indent_breaks_compare_to_cases" value="true"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_assignment_operator" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_closing_paren_in_annotation" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_parameters" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_new_line_after_opening_brace_in_array_initializer" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_opening_bracket_in_array_reference" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.comment.format_header" value="false"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_closing_paren_in_method_invocation" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.never_indent_line_comments_on_first_column" value="false"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.comment.format_block_comments" value="true"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_colon_in_object_initializer" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_paren_in_enum_constant" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_colon_in_labeled_statement" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.blank_lines_between_type_declarations" value="0"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_opening_paren_in_method_declaration" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_comma_in_enum_declarations" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_opening_brace_in_array_initializer" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.number_of_blank_lines_at_beginning_of_method_body" value="0"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.brace_position_for_method_declaration" value="end_of_line"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_opening_paren_in_parenthesized_expression" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.alignment_for_assignment" value="16"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_comma_in_parameterized_type_reference" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_semicolon_in_for" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_closing_paren_in_synchronized" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.tabulation.size" value="4"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.format_guardian_clause_on_one_line" value="false"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_opening_bracket_in_array_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_closing_brace_in_block" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_colon_in_default" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_new_line_before_else_in_if_statement" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_opening_paren_in_for" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.keep_empty_array_initializer_on_one_line" value="false"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_comma_in_enum_constant_arguments" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.indent_switchstatements_compare_to_cases" value="true"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.alignment_for_arguments_in_explicit_constructor_call" value="16"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.align_type_members_on_columns" value="false"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_paren_in_catch" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_comma_in_type_arguments" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_paren_in_annotation" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.comment.clear_blank_lines_in_block_comment" value="false"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.alignment_for_arguments_in_allocation_expression" value="16"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_bracket_in_array_type_reference" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_binary_operator" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_comma_in_allocation_expression" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_paren_in_while" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_comma_in_array_initializer" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_paren_in_parenthesized_expression" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_closing_paren_in_switch" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.alignment_for_arguments_in_method_invocation" value="16"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_closing_paren_in_cast" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.continuation_indentation_for_objlit_initializer" value="1"/>
<setting id="org.eclipse.wst.jsdt.core.compiler.compliance" value="1.5"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_new_line_before_closing_brace_in_objlit_initializer" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.comment.format_source_code" value="true"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.indent_switchstatements_compare_to_switch" value="false"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_closing_paren_in_for" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.wrap_before_binary_operator" value="true"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_bracket_in_array_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_ellipsis" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_at_in_annotation" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_closing_paren_in_parenthesized_expression" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_between_empty_parens_in_annotation_type_member_declaration" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_prefix_operator" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.continuation_indentation_for_array_initializer" value="1"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_paren_in_annotation_type_member_declaration" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_colon_in_for" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_paren_in_for" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_closing_bracket_in_array_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_closing_paren_in_while" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_comma_in_constructor_declaration_throws" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.brace_position_for_enum_constant" value="end_of_line"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_comma_in_method_invocation_arguments" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_parameters" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_brace_in_annotation_type_declaration" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_comma_in_array_initializer" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_between_brackets_in_array_type_reference" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_and_in_type_parameter" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_opening_paren_in_constructor_declaration" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.alignment_for_expressions_in_array_initializer" value="49"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_comma_in_multiple_field_declarations" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.blank_lines_after_package" value="1"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_new_line_after_comma_in_objlit_initializer" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_opening_paren_in_annotation" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_new_line_in_empty_enum_constant" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_new_line_before_catch_in_try_statement" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.never_indent_block_comments_on_first_column" value="false"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_closing_paren_in_method_declaration" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_paren_in_switch" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_colon_in_for" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_new_line_before_while_in_do_statement" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.comment.format_javadoc_comments" value="true"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_comma_in_type_parameters" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_comma_in_for_inits" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_comma_in_superinterfaces" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_brace_in_enum_declaration" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.indentation.size" value="4"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_new_line_in_empty_annotation_declaration" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_between_empty_brackets_in_array_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_new_line_in_empty_enum_declaration" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_new_line_in_empty_type_declaration" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_new_line_in_empty_anonymous_type_declaration" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_comma_in_explicitconstructorcall_arguments" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.brace_position_for_objlit_initializer" value="end_of_line"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_between_empty_parens_in_method_declaration" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_comma_in_allocation_expression" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.compiler.source" value="1.5"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.use_tabs_only_for_leading_indentations" value="false"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_at_in_annotation_type_declaration" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_new_line_at_end_of_file_if_missing" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_colon_in_conditional" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_brace_in_enum_constant" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_comma_in_method_declaration_throws" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_brace_in_method_declaration" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_opening_paren_in_catch" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_binary_operator" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.number_of_empty_lines_to_preserve" value="1"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_bracket_in_array_reference" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_opening_paren_in_synchronized" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.brace_position_for_switch" value="end_of_line"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_colon_in_conditional" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_comma_in_explicitconstructorcall_arguments" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.alignment_for_parameters_in_constructor_declaration" value="16"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.alignment_for_throws_clause_in_constructor_declaration" value="16"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_brace_in_type_declaration" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_comma_in_multiple_local_declarations" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_parenthesized_expression_in_return" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_semicolon" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_paren_in_method_invocation" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_closing_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_prefix_operator" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_comma_in_multiple_field_declarations" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_closing_paren_in_catch" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.continuation_indentation" value="1"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_paren_in_if" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.alignment_for_conditional_expression" value="16"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.alignment_for_arguments_in_enum_constant" value="16"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.comment.indent_parameter_description" value="true"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_paren_in_constructor_declaration" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_opening_angle_bracket_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.blank_lines_after_imports" value="1"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_unary_operator" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.indent_body_declarations_compare_to_enum_declaration_header" value="true"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.alignment_for_superinterfaces_in_type_declaration" value="16"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_comma_in_constructor_declaration_parameters" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_opening_paren_in_switch" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_comma_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_brace_in_anonymous_type_declaration" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.comment.indent_root_tags" value="true"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.blank_lines_before_package" value="0"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_paren_in_synchronized" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_semicolon_in_for" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_opening_paren_in_cast" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_closing_brace_in_array_initializer" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.blank_lines_before_member_type" value="1"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_comma_in_method_invocation_arguments" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.indent_body_declarations_compare_to_type_header" value="true"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_between_empty_braces_in_array_initializer" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_postfix_operator" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.alignment_for_enum_constants" value="0"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_at_in_annotation_type_declaration" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_comma_in_type_parameters" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_parameters" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.blank_lines_before_imports" value="1"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.keep_imple_if_on_one_line" value="false"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_opening_paren_in_while" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_closing_paren_in_constructor_declaration" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.alignment_for_binary_expression" value="16"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_closing_angle_bracket_in_type_arguments" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_new_line_before_closing_brace_in_array_initializer" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.indent_body_declarations_compare_to_enum_constant_header" value="true"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_closing_paren_in_cast" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.lineSplit" value="999"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_closing_paren_in_enum_constant" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.comment.format_html" value="true"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_postfix_operator" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_parenthesized_expression_in_throw" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.alignment_for_throws_clause_in_method_declaration" value="16"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.comment.insert_new_line_before_root_tags" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.keep_then_statement_on_same_line" value="false"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_between_empty_parens_in_constructor_declaration" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.blank_lines_before_method" value="1"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.alignment_for_superclass_in_type_declaration" value="16"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_paren_in_method_declaration" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_new_line_in_empty_method_body" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_question_in_conditional" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_comma_in_method_declaration_parameters" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.compiler.codegen.targetPlatform" value="1.5"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.alignment_for_arguments_in_qualified_allocation_expression" value="16"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_comma_in_constructor_declaration_parameters" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_comma_in_for_increments" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.keep_empty_objlit_initializer_on_one_line" value="false"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_question_in_conditional" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_closing_angle_bracket_in_type_parameters" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_between_empty_parens_in_method_invocation" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_colon_in_labeled_statement" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_opening_angle_bracket_in_parameterized_type_reference" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.indent_statements_compare_to_block" value="true"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.brace_position_for_constructor_declaration" value="end_of_line"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.brace_position_for_enum_declaration" value="end_of_line"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.comment.format_line_comments" value="true"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_opening_paren_in_method_invocation" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_comma_in_method_declaration_parameters" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_between_empty_parens_in_enum_constant" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_comma_in_multiple_local_declarations" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_new_line_in_empty_block" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.alignment_for_superinterfaces_in_enum_declaration" value="16"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_comma_in_parameterized_type_reference" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_brace_in_switch" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_brace_in_block" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_comma_in_superinterfaces" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.brace_position_for_array_initializer" value="end_of_line"/>
<setting id="org.eclipse.wst.jsdt.core.compiler.problem.assertIdentifier" value="error"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_comma_in_constructor_declaration_throws" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_colon_in_assert" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.comment.insert_new_line_for_parameter" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.brace_position_for_type_declaration" value="end_of_line"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_colon_in_object_initializer" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.comment.line_length" value="80"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_comma_in_method_declaration_throws" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.alignment_for_parameters_in_method_declaration" value="16"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.brace_position_for_block" value="end_of_line"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.alignment_for_multiple_fields" value="16"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_colon_in_case" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_comma_in_enum_declarations" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.indent_statements_compare_to_body" value="true"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.blank_lines_before_new_chunk" value="1"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.tabulation.char" value="space"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.put_empty_statement_on_new_line" value="true"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_opening_paren_in_enum_constant" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_opening_paren_in_if" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_unary_operator" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.keep_else_statement_on_same_line" value="false"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_comma_in_for_increments" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.alignment_for_selector_in_method_invocation" value="16"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.blank_lines_between_import_groups" value="1"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_assignment_operator" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_comma_in_annotation" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.blank_lines_before_field" value="0"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.comment.clear_blank_lines_in_javadoc_comment" value="false"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_new_line_after_opening_brace_in_objlit_initializer" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_new_line_before_finally_in_try_statement" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.compact_else_if" value="true"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_comma_in_annotation" value="insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_ellipsis" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_comma_in_for_inits" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_opening_angle_bracket_in_type_arguments" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.blank_lines_before_first_class_body_declaration" value="0"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.brace_position_for_anonymous_type_declaration" value="end_of_line"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_closing_paren_in_if" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_before_closing_bracket_in_array_reference" value="do not insert"/>
<setting id="org.eclipse.wst.jsdt.core.formatter.insert_space_after_colon_in_assert" value="insert"/>
</profile>
</profiles>