New extension pattern
This commit is contained in:
parent
c6c06373da
commit
d7304444a1
@ -327,7 +327,6 @@ hr {
|
|||||||
|
|
||||||
div.dropdown-menu {
|
div.dropdown-menu {
|
||||||
padding: 5px 20px;
|
padding: 5px 20px;
|
||||||
white-space: normal;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
div.dropdown-menu p,
|
div.dropdown-menu p,
|
||||||
@ -335,12 +334,17 @@ div.dropdown-menu blockquote {
|
|||||||
margin: 10px 0;
|
margin: 10px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
div.dropdown-menu .stat {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
div.dropdown-menu i {
|
div.dropdown-menu i {
|
||||||
margin-right: 0;
|
margin-right: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#link-container {
|
#link-container {
|
||||||
min-width: 210px;
|
min-width: 210px;
|
||||||
|
white-space: normal;
|
||||||
}
|
}
|
||||||
|
|
||||||
#link-container .link-list {
|
#link-container .link-list {
|
||||||
@ -518,6 +522,10 @@ div.dropdown-menu i {
|
|||||||
text-align: left;
|
text-align: left;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#modal-settings .accordion-inner .form-inline .label-text {
|
||||||
|
margin: 0 10px;
|
||||||
|
}
|
||||||
|
|
||||||
.accordion-toggle {
|
.accordion-toggle {
|
||||||
cursor: help;
|
cursor: help;
|
||||||
}
|
}
|
||||||
|
BIN
doc/img/architecture.png
Normal file
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 |
@ -9,184 +9,185 @@ define([
|
|||||||
"extension-manager"
|
"extension-manager"
|
||||||
], function(_, core, utils, extensionMgr) {
|
], function(_, core, utils, extensionMgr) {
|
||||||
|
|
||||||
var asyncRunner = {};
|
var asyncRunner = {};
|
||||||
|
|
||||||
var taskQueue = [];
|
var taskQueue = [];
|
||||||
var asyncRunning = false;
|
var asyncRunning = false;
|
||||||
var currentTask = undefined;
|
var currentTask = undefined;
|
||||||
var currentTaskRunning = false;
|
var currentTaskRunning = false;
|
||||||
var currentTaskStartTime = 0;
|
var currentTaskStartTime = 0;
|
||||||
|
|
||||||
asyncRunner.createTask = function() {
|
asyncRunner.createTask = function() {
|
||||||
var task = {};
|
var task = {};
|
||||||
task.finished = false;
|
task.finished = false;
|
||||||
task.timeout = ASYNC_TASK_DEFAULT_TIMEOUT;
|
task.timeout = ASYNC_TASK_DEFAULT_TIMEOUT;
|
||||||
task.retryCounter = 0;
|
task.retryCounter = 0;
|
||||||
/**
|
/**
|
||||||
* onRun callbacks are called by chain(). These callbacks have to call
|
* onRun callbacks are called by chain(). These callbacks have to call
|
||||||
* chain() themselves to chain with next onRun callback or error() to
|
* chain() themselves to chain with next onRun callback or error() to
|
||||||
* throw an exception or retry() to restart the task.
|
* throw an exception or retry() to restart the task.
|
||||||
*/
|
*/
|
||||||
// Run callbacks
|
// Run callbacks
|
||||||
task.runCallbacks = [];
|
task.runCallbacks = [];
|
||||||
task.onRun = function(callback) {
|
task.onRun = function(callback) {
|
||||||
task.runCallbacks.push(callback);
|
task.runCallbacks.push(callback);
|
||||||
};
|
};
|
||||||
/**
|
/**
|
||||||
* onSuccess callbacks are called when every onRun callbacks have
|
* onSuccess callbacks are called when every onRun callbacks have
|
||||||
* succeed.
|
* succeed.
|
||||||
*/
|
*/
|
||||||
task.successCallbacks = [];
|
task.successCallbacks = [];
|
||||||
task.onSuccess = function(callback) {
|
task.onSuccess = function(callback) {
|
||||||
task.successCallbacks.push(callback);
|
task.successCallbacks.push(callback);
|
||||||
};
|
};
|
||||||
/**
|
/**
|
||||||
* onError callbacks are called when error() is called in a onRun
|
* onError callbacks are called when error() is called in a onRun
|
||||||
* callback.
|
* callback.
|
||||||
*/
|
*/
|
||||||
task.errorCallbacks = [];
|
task.errorCallbacks = [];
|
||||||
task.onError = function(callback) {
|
task.onError = function(callback) {
|
||||||
task.errorCallbacks.push(callback);
|
task.errorCallbacks.push(callback);
|
||||||
};
|
};
|
||||||
/**
|
/**
|
||||||
* chain() calls the next onRun callback or the onSuccess callbacks when
|
* chain() calls the next onRun callback or the onSuccess callbacks when
|
||||||
* finished. The optional callback parameter can be used to pass an onRun
|
* finished. The optional callback parameter can be used to pass an
|
||||||
* callback during execution.
|
* onRun callback during execution.
|
||||||
*/
|
*/
|
||||||
task.chain = function(callback) {
|
task.chain = function(callback) {
|
||||||
if (task.finished === true) {
|
if(task.finished === true) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// If first execution
|
// If first execution
|
||||||
if (task.queue === undefined) {
|
if(task.queue === undefined) {
|
||||||
// Create a copy of the onRun callbacks
|
// Create a copy of the onRun callbacks
|
||||||
task.queue = task.runCallbacks.slice();
|
task.queue = task.runCallbacks.slice();
|
||||||
}
|
}
|
||||||
// If a callback is passed as a parameter
|
// If a callback is passed as a parameter
|
||||||
if(callback !== undefined) {
|
if(callback !== undefined) {
|
||||||
callback();
|
callback();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// If all callbacks have been run
|
// If all callbacks have been run
|
||||||
if (task.queue.length === 0) {
|
if(task.queue.length === 0) {
|
||||||
// Run the onSuccess callbacks
|
// Run the onSuccess callbacks
|
||||||
runSafe(task, task.successCallbacks);
|
runSafe(task, task.successCallbacks);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Run the next callback
|
// Run the next callback
|
||||||
var runCallback = task.queue.shift();
|
var runCallback = task.queue.shift();
|
||||||
runCallback();
|
runCallback();
|
||||||
};
|
};
|
||||||
/**
|
/**
|
||||||
* error() calls the onError callbacks passing the error parameter and ends
|
* error() calls the onError callbacks passing the error parameter and
|
||||||
* the task by throwing an exception.
|
* ends the task by throwing an exception.
|
||||||
*/
|
*/
|
||||||
task.error = function(error) {
|
task.error = function(error) {
|
||||||
if (task.finished === true) {
|
if(task.finished === true) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
error = error || new Error("Unknown error");
|
error = error || new Error("Unknown error");
|
||||||
if(error.message) {
|
if(error.message) {
|
||||||
extensionMgr.onError(error);
|
extensionMgr.onError(error);
|
||||||
}
|
}
|
||||||
runSafe(task, task.errorCallbacks, error);
|
runSafe(task, task.errorCallbacks, error);
|
||||||
// Exit the current call stack
|
// Exit the current call stack
|
||||||
throw error;
|
throw error;
|
||||||
};
|
};
|
||||||
/**
|
/**
|
||||||
* retry() can be called in an onRun callback to restart the task
|
* retry() can be called in an onRun callback to restart the task
|
||||||
*/
|
*/
|
||||||
task.retry = function(error, maxRetryCounter) {
|
task.retry = function(error, maxRetryCounter) {
|
||||||
if (task.finished === true) {
|
if(task.finished === true) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
maxRetryCounter = maxRetryCounter || 5;
|
maxRetryCounter = maxRetryCounter || 5;
|
||||||
task.queue = undefined;
|
task.queue = undefined;
|
||||||
if (task.retryCounter >= maxRetryCounter) {
|
if(task.retryCounter >= maxRetryCounter) {
|
||||||
task.error(error);
|
task.error(error);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Implement an exponential backoff
|
// Implement an exponential backoff
|
||||||
var delay = Math.pow(2, task.retryCounter++) * 1000;
|
var delay = Math.pow(2, task.retryCounter++) * 1000;
|
||||||
currentTaskStartTime = utils.currentTime + delay;
|
currentTaskStartTime = utils.currentTime + delay;
|
||||||
currentTaskRunning = false;
|
currentTaskRunning = false;
|
||||||
asyncRunner.runTask();
|
asyncRunner.runTask();
|
||||||
};
|
};
|
||||||
return task;
|
return task;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Run the next task in the queue if any and no other running
|
// Run the next task in the queue if any and no other running
|
||||||
asyncRunner.runTask = function() {
|
asyncRunner.runTask = function() {
|
||||||
// Use defer to avoid stack overflow
|
// Use defer to avoid stack overflow
|
||||||
_.defer(function() {
|
_.defer(function() {
|
||||||
|
|
||||||
// If there is a task currently running
|
// If there is a task currently running
|
||||||
if (currentTaskRunning === true) {
|
if(currentTaskRunning === true) {
|
||||||
// If the current task takes too long
|
// If the current task takes too long
|
||||||
if (currentTaskStartTime + currentTask.timeout < utils.currentTime) {
|
if(currentTaskStartTime + currentTask.timeout < utils.currentTime) {
|
||||||
currentTask.error(new Error("A timeout occurred."));
|
currentTask.error(new Error("A timeout occurred."));
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (currentTask === undefined) {
|
if(currentTask === undefined) {
|
||||||
// If no task in the queue
|
// If no task in the queue
|
||||||
if (taskQueue.length === 0) {
|
if(taskQueue.length === 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Dequeue an enqueued task
|
// Dequeue an enqueued task
|
||||||
currentTask = taskQueue.shift();
|
currentTask = taskQueue.shift();
|
||||||
currentTaskStartTime = utils.currentTime;
|
currentTaskStartTime = utils.currentTime;
|
||||||
if(asyncRunning === false) {
|
if(asyncRunning === false) {
|
||||||
asyncRunning = true;
|
asyncRunning = true;
|
||||||
extensionMgr.onAsyncRunning(true);
|
extensionMgr.onAsyncRunning(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run the task
|
// Run the task
|
||||||
if (currentTaskStartTime <= utils.currentTime) {
|
if(currentTaskStartTime <= utils.currentTime) {
|
||||||
currentTaskRunning = true;
|
currentTaskRunning = true;
|
||||||
currentTask.chain();
|
currentTask.chain();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
// Run runTask function periodically
|
// Run runTask function periodically
|
||||||
core.addPeriodicCallback(asyncRunner.runTask);
|
core.addPeriodicCallback(asyncRunner.runTask);
|
||||||
|
|
||||||
function runSafe(task, callbacks, param) {
|
function runSafe(task, callbacks, param) {
|
||||||
try {
|
try {
|
||||||
_.each(callbacks, function(callback) {
|
_.each(callbacks, function(callback) {
|
||||||
callback(param);
|
callback(param);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
task.finished = true;
|
task.finished = true;
|
||||||
if (currentTask === task) {
|
if(currentTask === task) {
|
||||||
currentTask = undefined;
|
currentTask = undefined;
|
||||||
currentTaskRunning = false;
|
currentTaskRunning = false;
|
||||||
}
|
}
|
||||||
if (taskQueue.length === 0) {
|
if(taskQueue.length === 0) {
|
||||||
asyncRunning = false;
|
asyncRunning = false;
|
||||||
extensionMgr.onAsyncRunning(false);
|
extensionMgr.onAsyncRunning(false);
|
||||||
} else {
|
}
|
||||||
asyncRunner.runTask();
|
else {
|
||||||
}
|
asyncRunner.runTask();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Add a task to the queue
|
// Add a task to the queue
|
||||||
asyncRunner.addTask = function(task) {
|
asyncRunner.addTask = function(task) {
|
||||||
taskQueue.push(task);
|
taskQueue.push(task);
|
||||||
asyncRunner.runTask();
|
asyncRunner.runTask();
|
||||||
};
|
};
|
||||||
|
|
||||||
// Change current task timeout
|
// Change current task timeout
|
||||||
asyncRunner.setCurrentTaskTimeout = function(timeout) {
|
asyncRunner.setCurrentTaskTimeout = function(timeout) {
|
||||||
if (currentTask !== undefined) {
|
if(currentTask !== undefined) {
|
||||||
currentTask.timeout = timeout;
|
currentTask.timeout = timeout;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return asyncRunner;
|
return asyncRunner;
|
||||||
});
|
});
|
||||||
|
@ -1,59 +1,51 @@
|
|||||||
define([
|
define([
|
||||||
"underscore",
|
"underscore",
|
||||||
"utils",
|
"utils",
|
||||||
"google-helper"
|
"google-helper"
|
||||||
], function(_, utils, googleHelper) {
|
], function(_, utils, googleHelper) {
|
||||||
|
|
||||||
var PROVIDER_BLOGGER = "blogger";
|
|
||||||
|
|
||||||
var bloggerProvider = {
|
var PROVIDER_BLOGGER = "blogger";
|
||||||
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.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;
|
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.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;
|
||||||
});
|
});
|
32
js/config.js
32
js/config.js
@ -1,8 +1,10 @@
|
|||||||
var MAIN_URL = "http://benweet.github.io/stackedit/";
|
var MAIN_URL = "http://benweet.github.io/stackedit/";
|
||||||
var GOOGLE_API_KEY = "AIzaSyAeCU8CGcSkn0z9js6iocHuPBX4f_mMWkw";
|
var GOOGLE_API_KEY = "AIzaSyAeCU8CGcSkn0z9js6iocHuPBX4f_mMWkw";
|
||||||
var GOOGLE_SCOPES = [ "https://www.googleapis.com/auth/drive.install",
|
var GOOGLE_SCOPES = [
|
||||||
"https://www.googleapis.com/auth/drive",
|
"https://www.googleapis.com/auth/drive.install",
|
||||||
"https://www.googleapis.com/auth/blogger" ];
|
"https://www.googleapis.com/auth/drive",
|
||||||
|
"https://www.googleapis.com/auth/blogger"
|
||||||
|
];
|
||||||
var GOOGLE_DRIVE_APP_ID = "241271498917";
|
var GOOGLE_DRIVE_APP_ID = "241271498917";
|
||||||
var DROPBOX_APP_KEY = "lq6mwopab8wskas";
|
var DROPBOX_APP_KEY = "lq6mwopab8wskas";
|
||||||
var DROPBOX_APP_SECRET = "851fgnucpezy84t";
|
var DROPBOX_APP_SECRET = "851fgnucpezy84t";
|
||||||
@ -14,7 +16,7 @@ var AJAX_TIMEOUT = 30000;
|
|||||||
var ASYNC_TASK_DEFAULT_TIMEOUT = 60000;
|
var ASYNC_TASK_DEFAULT_TIMEOUT = 60000;
|
||||||
var ASYNC_TASK_LONG_TIMEOUT = 120000;
|
var ASYNC_TASK_LONG_TIMEOUT = 120000;
|
||||||
var SYNC_PERIOD = 180000;
|
var SYNC_PERIOD = 180000;
|
||||||
var USER_IDLE_THRESHOLD = 300000;
|
var USER_IDLE_THRESHOLD = 300000;
|
||||||
var TEMPORARY_FILE_INDEX = "file.tempIndex";
|
var TEMPORARY_FILE_INDEX = "file.tempIndex";
|
||||||
var WELCOME_DOCUMENT_TITLE = "Welcome document";
|
var WELCOME_DOCUMENT_TITLE = "Welcome document";
|
||||||
var DOWNLOAD_PROXY_URL = "http://stackedit-download-proxy.herokuapp.com/";
|
var DOWNLOAD_PROXY_URL = "http://stackedit-download-proxy.herokuapp.com/";
|
||||||
@ -25,9 +27,9 @@ var SSH_PROXY_URL = "http://stackedit-ssh-proxy.herokuapp.com/";
|
|||||||
// Use by Google's client.js
|
// Use by Google's client.js
|
||||||
var delayedFunction = undefined;
|
var delayedFunction = undefined;
|
||||||
function runDelayedFunction() {
|
function runDelayedFunction() {
|
||||||
if (delayedFunction !== undefined) {
|
if(delayedFunction !== undefined) {
|
||||||
delayedFunction();
|
delayedFunction();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Site dependent
|
// 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/";
|
var TUMBLR_PROXY_URL = "http://stackedit-tumblr-proxy-local.herokuapp.com/";
|
||||||
|
|
||||||
if(location.hostname.indexOf("benweet.github.io") === 0) {
|
if(location.hostname.indexOf("benweet.github.io") === 0) {
|
||||||
BASE_URL = MAIN_URL;
|
BASE_URL = MAIN_URL;
|
||||||
GOOGLE_CLIENT_ID = '241271498917-jpto9lls9fqnem1e4h6ppds9uob8rpvu.apps.googleusercontent.com';
|
GOOGLE_CLIENT_ID = '241271498917-jpto9lls9fqnem1e4h6ppds9uob8rpvu.apps.googleusercontent.com';
|
||||||
GITHUB_CLIENT_ID = 'fa0d09514da8377ee32e';
|
GITHUB_CLIENT_ID = 'fa0d09514da8377ee32e';
|
||||||
GATEKEEPER_URL = "http://stackedit-gatekeeper.herokuapp.com/";
|
GATEKEEPER_URL = "http://stackedit-gatekeeper.herokuapp.com/";
|
||||||
TUMBLR_PROXY_URL = "http://stackedit-tumblr-proxy.herokuapp.com/";
|
TUMBLR_PROXY_URL = "http://stackedit-tumblr-proxy.herokuapp.com/";
|
||||||
}
|
}
|
||||||
|
|
||||||
var THEME_LIST = {
|
var THEME_LIST = {
|
||||||
"": "Default",
|
"": "Default",
|
||||||
"blue-gray": "Blue-Gray",
|
"blue-gray": "Blue-Gray",
|
||||||
"night": "Night"
|
"night": "Night"
|
||||||
};
|
};
|
||||||
|
885
js/core.js
885
js/core.js
@ -1,6 +1,6 @@
|
|||||||
define([
|
define([
|
||||||
"jquery",
|
"jquery",
|
||||||
"underscore",
|
"underscore",
|
||||||
"utils",
|
"utils",
|
||||||
"settings",
|
"settings",
|
||||||
"extension-manager",
|
"extension-manager",
|
||||||
@ -10,461 +10,446 @@ define([
|
|||||||
"lib/layout",
|
"lib/layout",
|
||||||
"lib/Markdown.Editor"
|
"lib/Markdown.Editor"
|
||||||
], function($, _, utils, settings, extensionMgr) {
|
], function($, _, utils, settings, extensionMgr) {
|
||||||
|
|
||||||
var core = {};
|
|
||||||
|
|
||||||
// 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 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
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load settings in settings dialog
|
|
||||||
function loadSettings() {
|
|
||||||
|
|
||||||
// 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 extension settings
|
|
||||||
extensionMgr.onLoadSettings();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save settings from settings dialog
|
var core = {};
|
||||||
function saveSettings(event) {
|
|
||||||
var newSettings = {};
|
|
||||||
|
|
||||||
// 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 extension settings
|
|
||||||
newSettings.extensionSettings = {};
|
|
||||||
extensionMgr.onSaveSettings(newSettings.extensionSettings, event);
|
|
||||||
|
|
||||||
if(!event.isPropagationStopped()) {
|
|
||||||
$.extend(settings, newSettings);
|
|
||||||
localStorage.settings = JSON.stringify(settings);
|
|
||||||
localStorage.theme = theme;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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');
|
|
||||||
});
|
|
||||||
|
|
||||||
extensionMgr.onLayoutCreated(layout);
|
|
||||||
};
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
});
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
$("#wmd-input, #wmd-preview").scrollTop(0);
|
|
||||||
$("#wmd-button-bar").empty();
|
|
||||||
editor.run(previewWrapper);
|
|
||||||
firstChange = false;
|
|
||||||
|
|
||||||
// Hide default buttons
|
// Used for periodic tasks
|
||||||
$(".wmd-button-row").addClass("btn-group").find("li:not(.wmd-spacer)")
|
var intervalId = undefined;
|
||||||
.addClass("btn").css("left", 0).find("span").hide();
|
var periodicCallbacks = [];
|
||||||
|
core.addPeriodicCallback = function(callback) {
|
||||||
// Add customized buttons
|
periodicCallbacks.push(callback);
|
||||||
$("#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"));
|
|
||||||
};
|
|
||||||
|
|
||||||
// onReady event callbacks
|
// Used to detect user activity
|
||||||
var readyCallbacks = [];
|
var userReal = false;
|
||||||
core.onReady = function(callback) {
|
var userActive = false;
|
||||||
readyCallbacks.push(callback);
|
var windowUnique = true;
|
||||||
runReadyCallbacks();
|
var userLastActivity = 0;
|
||||||
};
|
function setUserActive() {
|
||||||
var ready = false;
|
userReal = true;
|
||||||
core.setReady = function() {
|
userActive = true;
|
||||||
ready = true;
|
userLastActivity = utils.currentTime;
|
||||||
runReadyCallbacks();
|
}
|
||||||
};
|
|
||||||
function runReadyCallbacks() {
|
|
||||||
if(ready === true) {
|
|
||||||
_.each(readyCallbacks, function(callback) {
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
readyCallbacks = [];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
core.onReady(extensionMgr.onReady);
|
|
||||||
core.onReady(function() {
|
|
||||||
|
|
||||||
// Load theme list
|
|
||||||
_.each(THEME_LIST, function(name, value) {
|
|
||||||
$("#input-settings-theme").append($('<option value="' + value + '">' + name + '</option>'));
|
|
||||||
});
|
|
||||||
|
|
||||||
// listen to online/offline events
|
|
||||||
$(window).on('offline', core.setOffline);
|
|
||||||
$(window).on('online', setOnline);
|
|
||||||
if (navigator.onLine === false) {
|
|
||||||
core.setOffline();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Detect user activity
|
|
||||||
$(document).mousemove(setUserActive).keypress(setUserActive);
|
|
||||||
|
|
||||||
// Avoid dropdown to close when clicking on submenu
|
|
||||||
$(".dropdown-submenu > a").click(function(e) {
|
|
||||||
e.stopPropagation();
|
|
||||||
});
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Settings loading/saving
|
function isUserActive() {
|
||||||
$(".action-load-settings").click(function() {
|
if(userActive === true && utils.currentTime - userLastActivity > USER_IDLE_THRESHOLD) {
|
||||||
loadSettings();
|
userActive = false;
|
||||||
});
|
}
|
||||||
$(".action-apply-settings").click(function(e) {
|
return userActive && windowUnique;
|
||||||
saveSettings(e);
|
}
|
||||||
if(!e.isPropagationStopped()) {
|
|
||||||
window.location.reload();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$(".action-default-settings").click(function() {
|
|
||||||
localStorage.removeItem("settings");
|
|
||||||
localStorage.removeItem("theme");
|
|
||||||
window.location.reload();
|
|
||||||
});
|
|
||||||
|
|
||||||
$(".action-app-reset").click(function() {
|
|
||||||
localStorage.clear();
|
|
||||||
window.location.reload();
|
|
||||||
});
|
|
||||||
|
|
||||||
// UI layout
|
|
||||||
$("#menu-bar, .ui-layout-center, .ui-layout-east, .ui-layout-south").removeClass("hide");
|
|
||||||
core.createLayout();
|
|
||||||
|
|
||||||
// Editor's textarea
|
// Used to only have 1 window of the application in the same browser
|
||||||
$("#wmd-input, #md-section-helper").css({
|
var windowId = undefined;
|
||||||
// Apply editor font size
|
function checkWindowUnique() {
|
||||||
"font-size": settings.editorFontSize + "px",
|
if(userReal === false || windowUnique === false) {
|
||||||
"line-height": Math.round(settings.editorFontSize * (20/14)) + "px"
|
return;
|
||||||
});
|
}
|
||||||
|
if(windowId === undefined) {
|
||||||
// Manage tab key
|
windowId = utils.randomString();
|
||||||
$("#wmd-input").keydown(function(e) {
|
localStorage["frontWindowId"] = windowId;
|
||||||
if(e.keyCode === 9) {
|
}
|
||||||
var value = $(this).val();
|
var frontWindowId = localStorage["frontWindowId"];
|
||||||
var start = this.selectionStart;
|
if(frontWindowId != windowId) {
|
||||||
var end = this.selectionEnd;
|
windowUnique = false;
|
||||||
// IE8 does not support selection attributes
|
if(intervalId !== undefined) {
|
||||||
if(start === undefined || end === undefined) {
|
clearInterval(intervalId);
|
||||||
return;
|
}
|
||||||
}
|
$(".modal").modal("hide");
|
||||||
$(this).val(value.substring(0, start) + "\t" + value.substring(end));
|
$('#modal-non-unique').modal({
|
||||||
this.selectionStart = this.selectionEnd = start + 1;
|
backdrop: "static",
|
||||||
e.preventDefault();
|
keyboard: false
|
||||||
}
|
});
|
||||||
});
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Tooltips
|
// Offline management
|
||||||
$(".tooltip-scroll-link").tooltip({
|
core.isOffline = false;
|
||||||
html: true,
|
var offlineTime = utils.currentTime;
|
||||||
container: '#modal-settings',
|
core.setOffline = function() {
|
||||||
placement: 'right',
|
offlineTime = utils.currentTime;
|
||||||
title: ['Scroll Link is a feature that binds together editor and preview scrollbars. ',
|
if(core.isOffline === false) {
|
||||||
'It allows you to keep an eye on the preview while scrolling the editor and vice versa. ',
|
core.isOffline = true;
|
||||||
'<br><br>',
|
extensionMgr.onOfflineChanged(true);
|
||||||
'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("")
|
function setOnline() {
|
||||||
});
|
if(core.isOffline === true) {
|
||||||
$(".tooltip-lazy-rendering").tooltip({
|
core.isOffline = false;
|
||||||
container: '#modal-settings',
|
extensionMgr.onOfflineChanged(false);
|
||||||
placement: 'right',
|
}
|
||||||
title: 'Disable preview rendering while typing in order to offload CPU. Refresh preview after 500 ms of inactivity.'
|
}
|
||||||
});
|
function checkOnline() {
|
||||||
$(".tooltip-default-content").tooltip({
|
// Try to reconnect if we are offline but we have some network
|
||||||
html: true,
|
if(core.isOffline === true && navigator.onLine === true && offlineTime + CHECK_ONLINE_PERIOD < utils.currentTime) {
|
||||||
container: '#modal-settings',
|
offlineTime = utils.currentTime;
|
||||||
placement: 'right',
|
// Try to download anything to test the connection
|
||||||
title: 'Thanks for supporting StackEdit by adding a backlink in your documents!'
|
$.ajax({
|
||||||
});
|
url: "//www.google.com/jsapi",
|
||||||
$(".tooltip-template").tooltip({
|
timeout: AJAX_TIMEOUT,
|
||||||
html: true,
|
dataType: "script"
|
||||||
container: '#modal-settings',
|
}).done(function() {
|
||||||
placement: 'right',
|
setOnline();
|
||||||
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();
|
|
||||||
});
|
|
||||||
|
|
||||||
$(document).click(function(e) {
|
|
||||||
$(".tooltip-template").tooltip('hide');
|
|
||||||
});
|
|
||||||
|
|
||||||
// Reset inputs
|
// Load settings in settings dialog
|
||||||
$(".action-reset-input").click(function() {
|
function loadSettings() {
|
||||||
utils.resetModalInputs();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Do periodic tasks
|
|
||||||
intervalId = window.setInterval(function() {
|
|
||||||
utils.updateCurrentTime();
|
|
||||||
checkWindowUnique();
|
|
||||||
if(isUserActive() === true || viewerMode === true) {
|
|
||||||
_.each(periodicCallbacks, function(callback) {
|
|
||||||
callback();
|
|
||||||
});
|
|
||||||
checkOnline();
|
|
||||||
}
|
|
||||||
}, 1000);
|
|
||||||
});
|
|
||||||
|
|
||||||
return core;
|
// 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 extension settings
|
||||||
|
extensionMgr.onLoadSettings();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save settings from settings dialog
|
||||||
|
function saveSettings(event) {
|
||||||
|
var newSettings = {};
|
||||||
|
|
||||||
|
// 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 extension settings
|
||||||
|
newSettings.extensionSettings = {};
|
||||||
|
extensionMgr.onSaveSettings(newSettings.extensionSettings, event);
|
||||||
|
|
||||||
|
if(!event.isPropagationStopped()) {
|
||||||
|
$.extend(settings, newSettings);
|
||||||
|
localStorage.settings = JSON.stringify(settings);
|
||||||
|
localStorage.theme = theme;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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');
|
||||||
|
});
|
||||||
|
|
||||||
|
extensionMgr.onLayoutCreated(layout);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
});
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
$("#wmd-input, #wmd-preview").scrollTop(0);
|
||||||
|
$("#wmd-button-bar").empty();
|
||||||
|
editor.run(previewWrapper);
|
||||||
|
firstChange = false;
|
||||||
|
|
||||||
|
// Hide default buttons
|
||||||
|
$(".wmd-button-row").addClass("btn-group").find("li:not(.wmd-spacer)").addClass("btn").css("left", 0).find("span").hide();
|
||||||
|
|
||||||
|
// 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"));
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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 = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
core.onReady(extensionMgr.onReady);
|
||||||
|
core.onReady(function() {
|
||||||
|
|
||||||
|
// Load theme list
|
||||||
|
_.each(THEME_LIST, function(name, value) {
|
||||||
|
$("#input-settings-theme").append($('<option value="' + value + '">' + name + '</option>'));
|
||||||
|
});
|
||||||
|
|
||||||
|
// listen to online/offline events
|
||||||
|
$(window).on('offline', core.setOffline);
|
||||||
|
$(window).on('online', setOnline);
|
||||||
|
if(navigator.onLine === false) {
|
||||||
|
core.setOffline();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detect user activity
|
||||||
|
$(document).mousemove(setUserActive).keypress(setUserActive);
|
||||||
|
|
||||||
|
// Avoid dropdown to close when clicking on submenu
|
||||||
|
$(".dropdown-submenu > a").click(function(e) {
|
||||||
|
e.stopPropagation();
|
||||||
|
});
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Settings loading/saving
|
||||||
|
$(".action-load-settings").click(function() {
|
||||||
|
loadSettings();
|
||||||
|
});
|
||||||
|
$(".action-apply-settings").click(function(e) {
|
||||||
|
saveSettings(e);
|
||||||
|
if(!e.isPropagationStopped()) {
|
||||||
|
window.location.reload();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$(".action-default-settings").click(function() {
|
||||||
|
localStorage.removeItem("settings");
|
||||||
|
localStorage.removeItem("theme");
|
||||||
|
window.location.reload();
|
||||||
|
});
|
||||||
|
|
||||||
|
$(".action-app-reset").click(function() {
|
||||||
|
localStorage.clear();
|
||||||
|
window.location.reload();
|
||||||
|
});
|
||||||
|
|
||||||
|
// UI layout
|
||||||
|
$("#menu-bar, .ui-layout-center, .ui-layout-east, .ui-layout-south").removeClass("hide");
|
||||||
|
core.createLayout();
|
||||||
|
|
||||||
|
// 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"
|
||||||
|
});
|
||||||
|
|
||||||
|
// 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();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 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();
|
||||||
|
});
|
||||||
|
|
||||||
|
$(document).click(function(e) {
|
||||||
|
$(".tooltip-template").tooltip('hide');
|
||||||
|
});
|
||||||
|
|
||||||
|
// Reset inputs
|
||||||
|
$(".action-reset-input").click(function() {
|
||||||
|
utils.resetModalInputs();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Do periodic tasks
|
||||||
|
intervalId = window.setInterval(function() {
|
||||||
|
utils.updateCurrentTime();
|
||||||
|
checkWindowUnique();
|
||||||
|
if(isUserActive() === true || viewerMode === true) {
|
||||||
|
_.each(periodicCallbacks, function(callback) {
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
checkOnline();
|
||||||
|
}
|
||||||
|
}, 1000);
|
||||||
|
});
|
||||||
|
|
||||||
|
return core;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -3,46 +3,48 @@ define([
|
|||||||
"core",
|
"core",
|
||||||
"async-runner"
|
"async-runner"
|
||||||
], function($, core, asyncRunner) {
|
], function($, core, asyncRunner) {
|
||||||
|
|
||||||
var PROVIDER_DOWNLOAD = "download";
|
|
||||||
|
|
||||||
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);
|
|
||||||
};
|
|
||||||
|
|
||||||
return downloadProvider;
|
var PROVIDER_DOWNLOAD = "download";
|
||||||
|
|
||||||
|
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);
|
||||||
|
};
|
||||||
|
|
||||||
|
return downloadProvider;
|
||||||
});
|
});
|
@ -6,323 +6,330 @@ define([
|
|||||||
"async-runner"
|
"async-runner"
|
||||||
], function($, _, core, extensionMgr, asyncRunner) {
|
], function($, _, core, extensionMgr, asyncRunner) {
|
||||||
|
|
||||||
var client = undefined;
|
var client = undefined;
|
||||||
var authenticated = false;
|
var authenticated = false;
|
||||||
|
|
||||||
var dropboxHelper = {};
|
var dropboxHelper = {};
|
||||||
|
|
||||||
// Try to connect dropbox by downloading client.js
|
// Try to connect dropbox by downloading client.js
|
||||||
function connect(task) {
|
function connect(task) {
|
||||||
task.onRun(function() {
|
task.onRun(function() {
|
||||||
if(core.isOffline === true) {
|
if(core.isOffline === true) {
|
||||||
client = undefined;
|
client = undefined;
|
||||||
task.error(new Error("Operation not available in offline mode.|stopPublish"));
|
task.error(new Error("Operation not available in offline mode.|stopPublish"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (client !== undefined) {
|
if(client !== undefined) {
|
||||||
task.chain();
|
task.chain();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url : "lib/dropbox.min.js",
|
url: "lib/dropbox.min.js",
|
||||||
dataType : "script", timeout : AJAX_TIMEOUT
|
dataType: "script",
|
||||||
}).done(function() {
|
timeout: AJAX_TIMEOUT
|
||||||
client = new Dropbox.Client({
|
}).done(function() {
|
||||||
key: DROPBOX_APP_KEY,
|
client = new Dropbox.Client({
|
||||||
secret: DROPBOX_APP_SECRET
|
key: DROPBOX_APP_KEY,
|
||||||
});
|
secret: DROPBOX_APP_SECRET
|
||||||
client.authDriver(new Dropbox.Drivers.Popup({
|
});
|
||||||
receiverUrl: BASE_URL + "dropbox-oauth-receiver.html",
|
client.authDriver(new Dropbox.Drivers.Popup({
|
||||||
rememberUser: true
|
receiverUrl: BASE_URL + "dropbox-oauth-receiver.html",
|
||||||
}));
|
rememberUser: true
|
||||||
task.chain();
|
}));
|
||||||
}).fail(function(jqXHR) {
|
task.chain();
|
||||||
var error = {
|
}).fail(function(jqXHR) {
|
||||||
status: jqXHR.status,
|
var error = {
|
||||||
responseText: jqXHR.statusText
|
status: jqXHR.status,
|
||||||
};
|
responseText: jqXHR.statusText
|
||||||
handleError(error, task);
|
};
|
||||||
});
|
handleError(error, task);
|
||||||
});
|
});
|
||||||
}
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Try to authenticate with Oauth
|
// Try to authenticate with Oauth
|
||||||
function authenticate(task) {
|
function authenticate(task) {
|
||||||
task.onRun(function() {
|
task.onRun(function() {
|
||||||
if (authenticated === true) {
|
if(authenticated === true) {
|
||||||
task.chain();
|
task.chain();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var immediate = true;
|
var immediate = true;
|
||||||
function localAuthenticate() {
|
function localAuthenticate() {
|
||||||
if (immediate === false) {
|
if(immediate === false) {
|
||||||
extensionMgr.onMessage("Please make sure the Dropbox authorization popup is not blocked by your browser.");
|
extensionMgr.onMessage("Please make sure the Dropbox authorization popup is not blocked by your browser.");
|
||||||
// If not immediate we add time for user to enter his credentials
|
// If not immediate we add time for user to enter his
|
||||||
task.timeout = ASYNC_TASK_LONG_TIMEOUT;
|
// credentials
|
||||||
}
|
task.timeout = ASYNC_TASK_LONG_TIMEOUT;
|
||||||
client.reset();
|
}
|
||||||
client.authenticate({interactive: !immediate}, function(error, client) {
|
client.reset();
|
||||||
// Success
|
client.authenticate({
|
||||||
if (client.authState === Dropbox.Client.DONE) {
|
interactive: !immediate
|
||||||
authenticated = true;
|
}, function(error, client) {
|
||||||
task.chain();
|
// Success
|
||||||
return;
|
if(client.authState === Dropbox.Client.DONE) {
|
||||||
}
|
authenticated = true;
|
||||||
// If immediate did not work retry without immediate flag
|
task.chain();
|
||||||
if (immediate === true) {
|
return;
|
||||||
immediate = false;
|
}
|
||||||
task.chain(localAuthenticate);
|
// If immediate did not work retry without immediate flag
|
||||||
return;
|
if(immediate === true) {
|
||||||
}
|
immediate = false;
|
||||||
// Error
|
task.chain(localAuthenticate);
|
||||||
task.error(new Error("Access to Dropbox account is not authorized."));
|
return;
|
||||||
});
|
}
|
||||||
}
|
// Error
|
||||||
task.chain(localAuthenticate);
|
task.error(new Error("Access to Dropbox account is not authorized."));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
task.chain(localAuthenticate);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
dropboxHelper.upload = function(path, content, callback) {
|
dropboxHelper.upload = function(path, content, callback) {
|
||||||
var result = undefined;
|
var result = undefined;
|
||||||
var task = asyncRunner.createTask();
|
var task = asyncRunner.createTask();
|
||||||
connect(task);
|
connect(task);
|
||||||
authenticate(task);
|
authenticate(task);
|
||||||
task.onRun(function() {
|
task.onRun(function() {
|
||||||
client.writeFile(path, content, function(error, stat) {
|
client.writeFile(path, content, function(error, stat) {
|
||||||
if (!error) {
|
if(!error) {
|
||||||
result = stat;
|
result = stat;
|
||||||
task.chain();
|
task.chain();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Handle error
|
// Handle error
|
||||||
if(error.status === 400) {
|
if(error.status === 400) {
|
||||||
error = 'Could not upload document into path "' + path + '".';
|
error = 'Could not upload document into path "' + path + '".';
|
||||||
}
|
}
|
||||||
handleError(error, task);
|
handleError(error, task);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
task.onSuccess(function() {
|
task.onSuccess(function() {
|
||||||
callback(undefined, result);
|
callback(undefined, result);
|
||||||
});
|
});
|
||||||
task.onError(function(error) {
|
task.onError(function(error) {
|
||||||
callback(error);
|
callback(error);
|
||||||
});
|
});
|
||||||
asyncRunner.addTask(task);
|
asyncRunner.addTask(task);
|
||||||
};
|
};
|
||||||
|
|
||||||
dropboxHelper.checkChanges = function(lastChangeId, callback) {
|
dropboxHelper.checkChanges = function(lastChangeId, callback) {
|
||||||
var changes = [];
|
var changes = [];
|
||||||
var newChangeId = lastChangeId || 0;
|
var newChangeId = lastChangeId || 0;
|
||||||
var task = asyncRunner.createTask();
|
var task = asyncRunner.createTask();
|
||||||
connect(task);
|
connect(task);
|
||||||
authenticate(task);
|
authenticate(task);
|
||||||
task.onRun(function() {
|
task.onRun(function() {
|
||||||
function retrievePageOfChanges() {
|
function retrievePageOfChanges() {
|
||||||
client.pullChanges(newChangeId, function(error, pullChanges) {
|
client.pullChanges(newChangeId, function(error, pullChanges) {
|
||||||
if (error) {
|
if(error) {
|
||||||
handleError(error, task);
|
handleError(error, task);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Retrieve success
|
// Retrieve success
|
||||||
newChangeId = pullChanges.cursor();
|
newChangeId = pullChanges.cursor();
|
||||||
if(pullChanges.changes !== undefined) {
|
if(pullChanges.changes !== undefined) {
|
||||||
changes = changes.concat(pullChanges.changes);
|
changes = changes.concat(pullChanges.changes);
|
||||||
}
|
}
|
||||||
if (pullChanges.shouldPullAgain) {
|
if(pullChanges.shouldPullAgain) {
|
||||||
task.chain(retrievePageOfChanges);
|
task.chain(retrievePageOfChanges);
|
||||||
} else {
|
}
|
||||||
task.chain();
|
else {
|
||||||
}
|
task.chain();
|
||||||
});
|
}
|
||||||
}
|
});
|
||||||
task.chain(retrievePageOfChanges);
|
}
|
||||||
});
|
task.chain(retrievePageOfChanges);
|
||||||
task.onSuccess(function() {
|
});
|
||||||
callback(undefined, changes, newChangeId);
|
task.onSuccess(function() {
|
||||||
});
|
callback(undefined, changes, newChangeId);
|
||||||
task.onError(function(error) {
|
});
|
||||||
callback(error);
|
task.onError(function(error) {
|
||||||
});
|
callback(error);
|
||||||
asyncRunner.addTask(task);
|
});
|
||||||
};
|
asyncRunner.addTask(task);
|
||||||
|
};
|
||||||
|
|
||||||
dropboxHelper.downloadMetadata = function(paths, callback) {
|
dropboxHelper.downloadMetadata = function(paths, callback) {
|
||||||
var result = [];
|
var result = [];
|
||||||
var task = asyncRunner.createTask();
|
var task = asyncRunner.createTask();
|
||||||
connect(task);
|
connect(task);
|
||||||
authenticate(task);
|
authenticate(task);
|
||||||
task.onRun(function() {
|
task.onRun(function() {
|
||||||
function recursiveDownloadMetadata() {
|
function recursiveDownloadMetadata() {
|
||||||
if(paths.length === 0) {
|
if(paths.length === 0) {
|
||||||
task.chain();
|
task.chain();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var path = paths[0];
|
var path = paths[0];
|
||||||
client.stat(path, function(error, stat) {
|
client.stat(path, function(error, stat) {
|
||||||
if(stat) {
|
if(stat) {
|
||||||
result.push(stat);
|
result.push(stat);
|
||||||
paths.shift();
|
paths.shift();
|
||||||
task.chain(recursiveDownloadMetadata);
|
task.chain(recursiveDownloadMetadata);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
handleError(error, task);
|
handleError(error, task);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
task.chain(recursiveDownloadMetadata);
|
task.chain(recursiveDownloadMetadata);
|
||||||
});
|
});
|
||||||
task.onSuccess(function() {
|
task.onSuccess(function() {
|
||||||
callback(undefined, result);
|
callback(undefined, result);
|
||||||
});
|
});
|
||||||
task.onError(function(error) {
|
task.onError(function(error) {
|
||||||
callback(error);
|
callback(error);
|
||||||
});
|
});
|
||||||
asyncRunner.addTask(task);
|
asyncRunner.addTask(task);
|
||||||
};
|
};
|
||||||
|
|
||||||
dropboxHelper.downloadContent = function(objects, callback) {
|
dropboxHelper.downloadContent = function(objects, callback) {
|
||||||
var result = [];
|
var result = [];
|
||||||
var task = asyncRunner.createTask();
|
var task = asyncRunner.createTask();
|
||||||
connect(task);
|
connect(task);
|
||||||
authenticate(task);
|
authenticate(task);
|
||||||
task.onRun(function() {
|
task.onRun(function() {
|
||||||
function recursiveDownloadContent() {
|
function recursiveDownloadContent() {
|
||||||
if(objects.length === 0) {
|
if(objects.length === 0) {
|
||||||
task.chain();
|
task.chain();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var object = objects[0];
|
var object = objects[0];
|
||||||
result.push(object);
|
result.push(object);
|
||||||
var file = undefined;
|
var file = undefined;
|
||||||
// object may be a file
|
// object may be a file
|
||||||
if(object.isFile === true) {
|
if(object.isFile === true) {
|
||||||
file = object;
|
file = object;
|
||||||
}
|
}
|
||||||
// object may be a change
|
// object may be a change
|
||||||
else if(object.wasRemoved !== undefined) {
|
else if(object.wasRemoved !== undefined) {
|
||||||
file = object.stat;
|
file = object.stat;
|
||||||
}
|
}
|
||||||
if(!file) {
|
if(!file) {
|
||||||
objects.shift();
|
objects.shift();
|
||||||
task.chain(recursiveDownloadContent);
|
task.chain(recursiveDownloadContent);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
client.readFile(file.path, function(error, data) {
|
client.readFile(file.path, function(error, data) {
|
||||||
if(data) {
|
if(data) {
|
||||||
file.content = data;
|
file.content = data;
|
||||||
objects.shift();
|
objects.shift();
|
||||||
task.chain(recursiveDownloadContent);
|
task.chain(recursiveDownloadContent);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
handleError(error, task);
|
handleError(error, task);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
task.chain(recursiveDownloadContent);
|
task.chain(recursiveDownloadContent);
|
||||||
});
|
});
|
||||||
task.onSuccess(function() {
|
task.onSuccess(function() {
|
||||||
callback(undefined, result);
|
callback(undefined, result);
|
||||||
});
|
});
|
||||||
task.onError(function(error) {
|
task.onError(function(error) {
|
||||||
callback(error);
|
callback(error);
|
||||||
});
|
});
|
||||||
asyncRunner.addTask(task);
|
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 + ").";
|
|
||||||
|
|
||||||
if (error.status === 401 || error.status === 403) {
|
function handleError(error, task) {
|
||||||
authenticated = false;
|
var errorMsg = true;
|
||||||
errorMsg = "Access to Dropbox account is not authorized.";
|
if(error) {
|
||||||
task.retry(new Error(errorMsg), 1);
|
logger.error(error);
|
||||||
return;
|
// Try to analyze the error
|
||||||
} else if(error.status === 400 && error.responseText
|
if(typeof error === "string") {
|
||||||
.indexOf("oauth_nonce") !== -1) {
|
errorMsg = error;
|
||||||
// A bug I guess...
|
}
|
||||||
_.each(_.keys(localStorage), function(key) {
|
else {
|
||||||
// We have to remove the Oauth cache from the localStorage
|
errorMsg = "Dropbox error (" + error.status + ": " + error.responseText + ").";
|
||||||
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;
|
if(error.status === 401 || error.status === 403) {
|
||||||
function loadPicker(task) {
|
authenticated = false;
|
||||||
task.onRun(function() {
|
errorMsg = "Access to Dropbox account is not authorized.";
|
||||||
if (pickerLoaded === true) {
|
task.retry(new Error(errorMsg), 1);
|
||||||
task.chain();
|
return;
|
||||||
return;
|
}
|
||||||
}
|
else if(error.status === 400 && error.responseText.indexOf("oauth_nonce") !== -1) {
|
||||||
$.ajax({
|
// A bug I guess...
|
||||||
url : "https://www.dropbox.com/static/api/1/dropbox.js",
|
_.each(_.keys(localStorage), function(key) {
|
||||||
dataType : "script", timeout : AJAX_TIMEOUT
|
// We have to remove the Oauth cache from the
|
||||||
}).done(function() {
|
// localStorage
|
||||||
pickerLoaded = true;
|
if(key.indexOf("dropbox-auth") === 0) {
|
||||||
task.chain();
|
localStorage.removeItem(key);
|
||||||
}).fail(function(jqXHR) {
|
}
|
||||||
var error = {
|
});
|
||||||
status: jqXHR.status,
|
authenticated = false;
|
||||||
responseText: jqXHR.statusText
|
task.retry(new Error(errorMsg), 1);
|
||||||
};
|
return;
|
||||||
handleError(error, task);
|
}
|
||||||
});
|
else if(error.status <= 0) {
|
||||||
});
|
client = undefined;
|
||||||
}
|
authenticated = false;
|
||||||
|
core.setOffline();
|
||||||
dropboxHelper.picker = function(callback) {
|
errorMsg = "|stopPublish";
|
||||||
var paths = [];
|
}
|
||||||
var task = asyncRunner.createTask();
|
}
|
||||||
// Add some time for user to choose his files
|
}
|
||||||
task.timeout = ASYNC_TASK_LONG_TIMEOUT;
|
task.error(new Error(errorMsg));
|
||||||
connect(task);
|
}
|
||||||
loadPicker(task);
|
|
||||||
task.onRun(function() {
|
var pickerLoaded = false;
|
||||||
var options = {};
|
function loadPicker(task) {
|
||||||
options.multiselect = true;
|
task.onRun(function() {
|
||||||
options.linkType = "direct";
|
if(pickerLoaded === true) {
|
||||||
options.success = function(files) {
|
task.chain();
|
||||||
for(var i=0; i<files.length; i++) {
|
return;
|
||||||
var path = files[i].link;
|
}
|
||||||
path = path.replace(/.*\/view\/[^\/]*/, "");
|
$.ajax({
|
||||||
paths.push(decodeURI(path));
|
url: "https://www.dropbox.com/static/api/1/dropbox.js",
|
||||||
}
|
dataType: "script",
|
||||||
task.chain();
|
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();
|
||||||
};
|
};
|
||||||
options.cancel = function() {
|
options.cancel = function() {
|
||||||
task.chain();
|
task.chain();
|
||||||
};
|
};
|
||||||
Dropbox.choose(options);
|
Dropbox.choose(options);
|
||||||
extensionMgr.onMessage("Please make sure the Dropbox chooser popup is not blocked by your browser.");
|
extensionMgr.onMessage("Please make sure the Dropbox chooser popup is not blocked by your browser.");
|
||||||
});
|
});
|
||||||
task.onSuccess(function() {
|
task.onSuccess(function() {
|
||||||
callback(undefined, paths);
|
callback(undefined, paths);
|
||||||
});
|
});
|
||||||
task.onError(function(error) {
|
task.onError(function(error) {
|
||||||
callback(error);
|
callback(error);
|
||||||
});
|
});
|
||||||
asyncRunner.addTask(task);
|
asyncRunner.addTask(task);
|
||||||
};
|
};
|
||||||
|
|
||||||
return dropboxHelper;
|
return dropboxHelper;
|
||||||
});
|
});
|
||||||
|
@ -5,232 +5,233 @@ define([
|
|||||||
"file-manager",
|
"file-manager",
|
||||||
"dropbox-helper"
|
"dropbox-helper"
|
||||||
], function(_, utils, extensionMgr, fileMgr, dropboxHelper) {
|
], function(_, utils, extensionMgr, fileMgr, dropboxHelper) {
|
||||||
|
|
||||||
var PROVIDER_DROPBOX = "dropbox";
|
|
||||||
|
|
||||||
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 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 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() {
|
var PROVIDER_DROPBOX = "dropbox";
|
||||||
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);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
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 dropboxProvider = {
|
||||||
var path = utils.getInputTextValue("#input-sync-manual-dropbox-path", event);
|
providerId: PROVIDER_DROPBOX,
|
||||||
exportFileToPath(path, title, content, callback);
|
providerName: "Dropbox",
|
||||||
};
|
defaultPublishFormat: "template"
|
||||||
|
};
|
||||||
dropboxProvider.syncUp = function(uploadContent, uploadContentCRC, uploadTitle, uploadTitleCRC, syncAttributes, callback) {
|
|
||||||
var syncContentCRC = syncAttributes.contentCRC;
|
function checkPath(path) {
|
||||||
// Skip if CRC has not changed
|
if(path === undefined) {
|
||||||
if(uploadContentCRC == syncContentCRC) {
|
return undefined;
|
||||||
callback(undefined, false);
|
}
|
||||||
return;
|
if(!path.match(/^[^\\<>:"\|?\*]+$/)) {
|
||||||
}
|
extensionMgr.onError('"' + path + '" contains invalid characters.');
|
||||||
dropboxHelper.upload(syncAttributes.path, uploadContent, function(error, result) {
|
return undefined;
|
||||||
if(error) {
|
}
|
||||||
callback(error, true);
|
if(path.indexOf("/") !== 0) {
|
||||||
return;
|
return "/" + path;
|
||||||
}
|
}
|
||||||
syncAttributes.version = result.versionTag;
|
return path;
|
||||||
syncAttributes.contentCRC = uploadContentCRC;
|
}
|
||||||
callback(undefined, true);
|
|
||||||
});
|
function createSyncIndex(path) {
|
||||||
};
|
return "sync." + PROVIDER_DROPBOX + "." + encodeURIComponent(path.toLowerCase());
|
||||||
|
}
|
||||||
dropboxProvider.syncDown = function(callback) {
|
|
||||||
var lastChangeId = localStorage[PROVIDER_DROPBOX + ".lastChangeId"];
|
function createSyncAttributes(path, versionTag, content) {
|
||||||
dropboxHelper.checkChanges(lastChangeId, function(error, changes, newChangeId) {
|
var syncAttributes = {};
|
||||||
if (error) {
|
syncAttributes.provider = dropboxProvider;
|
||||||
callback(error);
|
syncAttributes.path = path;
|
||||||
return;
|
syncAttributes.version = versionTag;
|
||||||
}
|
syncAttributes.contentCRC = utils.crc32(content);
|
||||||
var interestingChanges = [];
|
syncAttributes.syncIndex = createSyncIndex(path);
|
||||||
_.each(changes, function(change) {
|
utils.storeAttributes(syncAttributes);
|
||||||
var syncIndex = createSyncIndex(change.path);
|
return syncAttributes;
|
||||||
var syncAttributes = fileMgr.getSyncAttributes(syncIndex);
|
}
|
||||||
if(syncAttributes === undefined) {
|
|
||||||
return;
|
function importFilesFromPaths(paths) {
|
||||||
}
|
dropboxHelper.downloadMetadata(paths, function(error, result) {
|
||||||
// Store syncAttributes to avoid 2 times searching
|
if(error) {
|
||||||
change.syncAttributes = syncAttributes;
|
return;
|
||||||
// Delete
|
}
|
||||||
if(change.wasRemoved === true) {
|
dropboxHelper.downloadContent(result, function(error, result) {
|
||||||
interestingChanges.push(change);
|
if(error) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Modify
|
var fileDescList = [];
|
||||||
if(syncAttributes.version != change.stat.versionTag) {
|
_.each(result, function(file) {
|
||||||
interestingChanges.push(change);
|
var syncAttributes = createSyncAttributes(file.path, file.versionTag, file.content);
|
||||||
}
|
var syncLocations = {};
|
||||||
});
|
syncLocations[syncAttributes.syncIndex] = syncAttributes;
|
||||||
dropboxHelper.downloadContent(interestingChanges, function(error, changes) {
|
var fileDesc = fileMgr.createFile(file.name, file.content, syncLocations);
|
||||||
if (error) {
|
fileMgr.selectFile(fileDesc);
|
||||||
callback(error);
|
fileDescList.push(fileDesc);
|
||||||
return;
|
});
|
||||||
}
|
extensionMgr.onSyncImportSuccess(fileDescList, dropboxProvider);
|
||||||
_.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)
|
dropboxProvider.importFiles = function() {
|
||||||
if(fileDesc === undefined) {
|
dropboxHelper.picker(function(error, paths) {
|
||||||
return;
|
if(error || paths.length === 0) {
|
||||||
}
|
return;
|
||||||
var localTitle = fileDesc.title;
|
}
|
||||||
// File deleted
|
var importPaths = [];
|
||||||
if (change.wasRemoved === true) {
|
_.each(paths, function(path) {
|
||||||
extensionMgr.onError('"' + localTitle + '" has been removed from Dropbox.');
|
var syncIndex = createSyncIndex(path);
|
||||||
fileMgr.removeSync(syncAttributes);
|
var fileDesc = fileMgr.getFileFromSyncIndex(syncIndex);
|
||||||
return;
|
if(fileDesc !== undefined) {
|
||||||
}
|
extensionMgr.onError('"' + fileDesc.title + '" was already imported');
|
||||||
var localContent = fileDesc.getContent();
|
return;
|
||||||
var localContentChanged = syncAttributes.contentCRC != utils.crc32(localContent);
|
}
|
||||||
var file = change.stat;
|
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);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
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.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.content;
|
||||||
|
var localContentChanged = syncAttributes.contentCRC != utils.crc32(localContent);
|
||||||
|
var file = change.stat;
|
||||||
var remoteContentCRC = utils.crc32(file.content);
|
var remoteContentCRC = utils.crc32(file.content);
|
||||||
var remoteContentChanged = syncAttributes.contentCRC != remoteContentCRC;
|
var remoteContentChanged = syncAttributes.contentCRC != remoteContentCRC;
|
||||||
var fileContentChanged = localContent != file.content;
|
var fileContentChanged = localContent != file.content;
|
||||||
// Conflict detection
|
// Conflict detection
|
||||||
if (fileContentChanged === true && localContentChanged === true && remoteContentChanged === true) {
|
if(fileContentChanged === true && localContentChanged === true && remoteContentChanged === true) {
|
||||||
fileMgr.createFile(localTitle + " (backup)", localContent);
|
fileMgr.createFile(localTitle + " (backup)", localContent);
|
||||||
extensionMgr.onMessage('Conflict detected on "' + localTitle + '". A backup has been created locally.');
|
extensionMgr.onMessage('Conflict detected on "' + localTitle + '". A backup has been created locally.');
|
||||||
}
|
}
|
||||||
// If file content changed
|
// If file content changed
|
||||||
if(fileContentChanged && remoteContentChanged === true) {
|
if(fileContentChanged && remoteContentChanged === true) {
|
||||||
fileDesc.setContent(file.content);
|
fileDesc.content = file.content;
|
||||||
extensionMgr.onMessage('"' + localTitle + '" has been updated from Dropbox.');
|
extensionMgr.onMessage('"' + localTitle + '" has been updated from Dropbox.');
|
||||||
if(fileMgr.isCurrentFile(fileDesc)) {
|
if(fileMgr.isCurrentFile(fileDesc)) {
|
||||||
fileMgr.selectFile(); // Refresh editor
|
fileMgr.selectFile(); // Refresh editor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Update syncAttributes
|
// Update syncAttributes
|
||||||
syncAttributes.version = file.versionTag;
|
syncAttributes.version = file.versionTag;
|
||||||
syncAttributes.contentCRC = remoteContentCRC;
|
syncAttributes.contentCRC = remoteContentCRC;
|
||||||
utils.storeAttributes(syncAttributes);
|
utils.storeAttributes(syncAttributes);
|
||||||
});
|
});
|
||||||
localStorage[PROVIDER_DROPBOX + ".lastChangeId"] = newChangeId;
|
localStorage[PROVIDER_DROPBOX + ".lastChangeId"] = newChangeId;
|
||||||
callback();
|
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) {
|
dropboxProvider.publish = function(publishAttributes, title, content, callback) {
|
||||||
var publishAttributes = {};
|
var path = checkPath(publishAttributes.path);
|
||||||
publishAttributes.path = utils.getInputTextValue("#input-publish-dropbox-path", event);
|
if(path === undefined) {
|
||||||
if(event.isPropagationStopped()) {
|
callback(true);
|
||||||
return undefined;
|
return;
|
||||||
}
|
}
|
||||||
return publishAttributes;
|
dropboxHelper.upload(path, content, callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
return dropboxProvider;
|
dropboxProvider.newPublishAttributes = function(event) {
|
||||||
|
var publishAttributes = {};
|
||||||
|
publishAttributes.path = utils.getInputTextValue("#input-publish-dropbox-path", event);
|
||||||
|
if(event.isPropagationStopped()) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return publishAttributes;
|
||||||
|
};
|
||||||
|
|
||||||
|
return dropboxProvider;
|
||||||
});
|
});
|
@ -1,4 +1,4 @@
|
|||||||
define( [
|
define([
|
||||||
"jquery",
|
"jquery",
|
||||||
"underscore",
|
"underscore",
|
||||||
"utils",
|
"utils",
|
||||||
@ -20,167 +20,162 @@ define( [
|
|||||||
"extensions/scroll-link",
|
"extensions/scroll-link",
|
||||||
"lib/bootstrap"
|
"lib/bootstrap"
|
||||||
], function($, _, utils, settings) {
|
], function($, _, utils, settings) {
|
||||||
|
|
||||||
var extensionMgr = {};
|
|
||||||
|
|
||||||
// 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
|
var extensionMgr = {};
|
||||||
function getExtensionCallbackList(hookName) {
|
|
||||||
return _.chain(
|
// Create a list of extensions
|
||||||
extensionList
|
var extensionList = _.chain(arguments).map(function(argument) {
|
||||||
).map(function(extension) {
|
return _.isObject(argument) && argument.extensionId && argument;
|
||||||
return extension.config.enabled && extension[hookName];
|
}).compact().value();
|
||||||
}).compact().value();
|
|
||||||
}
|
// Return every named callbacks implemented in extensions
|
||||||
|
function getExtensionCallbackList(hookName) {
|
||||||
// Return a function that calls every callbacks from extensions
|
return _.chain(extensionList).map(function(extension) {
|
||||||
function createHook(hookName) {
|
return extension.config.enabled && extension[hookName];
|
||||||
var callbackList = getExtensionCallbackList(hookName);
|
}).compact().value();
|
||||||
return function() {
|
}
|
||||||
logger.debug(hookName, arguments);
|
|
||||||
var callbackArguments = arguments;
|
// Return a function that calls every callbacks from extensions
|
||||||
_.each(callbackList, function(callback) {
|
function createHook(hookName) {
|
||||||
callback.apply(null, callbackArguments);
|
var callbackList = getExtensionCallbackList(hookName);
|
||||||
});
|
return function() {
|
||||||
};
|
logger.debug(hookName, arguments);
|
||||||
}
|
var callbackArguments = arguments;
|
||||||
|
_.each(callbackList, function(callback) {
|
||||||
// Add a Hook to the extensionMgr
|
callback.apply(null, callbackArguments);
|
||||||
function addHook(hookName) {
|
});
|
||||||
extensionMgr[hookName] = createHook(hookName);
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set extension config
|
// Add a Hook to the extensionMgr
|
||||||
extensionSettings = settings.extensionSettings || {};
|
function addHook(hookName) {
|
||||||
_.each(extensionList, function(extension) {
|
extensionMgr[hookName] = createHook(hookName);
|
||||||
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 || {};
|
||||||
// Load/Save extension config from/to settings
|
_.each(extensionList, function(extension) {
|
||||||
extensionMgr["onLoadSettings"] = function() {
|
extension.config = _.extend({}, extension.defaultConfig, extensionSettings[extension.extensionId]);
|
||||||
logger.debug("onLoadSettings");
|
extension.config.enabled = !extension.optional || extension.config.enabled === undefined || extension.config.enabled === true;
|
||||||
_.each(extensionList, function(extension) {
|
});
|
||||||
utils.setInputChecked("#input-enable-extension-" + extension.extensionId, extension.config.enabled);
|
|
||||||
var onLoadSettingsCallback = extension.onLoadSettings;
|
// Load/Save extension config from/to settings
|
||||||
onLoadSettingsCallback && onLoadSettingsCallback();
|
extensionMgr["onLoadSettings"] = function() {
|
||||||
});
|
logger.debug("onLoadSettings");
|
||||||
};
|
_.each(extensionList, function(extension) {
|
||||||
extensionMgr["onSaveSettings"] = function(newExtensionSettings, event) {
|
utils.setInputChecked("#input-enable-extension-" + extension.extensionId, extension.config.enabled);
|
||||||
logger.debug("onSaveSettings");
|
var onLoadSettingsCallback = extension.onLoadSettings;
|
||||||
_.each(extensionList, function(extension) {
|
onLoadSettingsCallback && onLoadSettingsCallback();
|
||||||
var newExtensionConfig = extension.defaultConfig || {};
|
});
|
||||||
newExtensionConfig.enabled = utils.getInputChecked("#input-enable-extension-" + extension.extensionId);
|
};
|
||||||
var onSaveSettingsCallback = extension.onSaveSettings;
|
extensionMgr["onSaveSettings"] = function(newExtensionSettings, event) {
|
||||||
onSaveSettingsCallback && onSaveSettingsCallback(newExtensionConfig, event);
|
logger.debug("onSaveSettings");
|
||||||
newExtensionSettings[extension.extensionId] = newExtensionConfig;
|
_.each(extensionList, function(extension) {
|
||||||
});
|
var newExtensionConfig = _.extend({}, extension.defaultConfig);
|
||||||
};
|
newExtensionConfig.enabled = utils.getInputChecked("#input-enable-extension-" + extension.extensionId);
|
||||||
|
var onSaveSettingsCallback = extension.onSaveSettings;
|
||||||
addHook("onReady");
|
onSaveSettingsCallback && onSaveSettingsCallback(newExtensionConfig, event);
|
||||||
addHook("onMessage");
|
newExtensionSettings[extension.extensionId] = newExtensionConfig;
|
||||||
addHook("onError");
|
});
|
||||||
addHook("onOfflineChanged");
|
};
|
||||||
addHook("onAsyncRunning");
|
|
||||||
|
addHook("onReady");
|
||||||
// To access modules that are loaded after extensions
|
addHook("onMessage");
|
||||||
addHook("onFileMgrCreated");
|
addHook("onError");
|
||||||
addHook("onSynchronizerCreated");
|
addHook("onOfflineChanged");
|
||||||
addHook("onPublisherCreated");
|
addHook("onAsyncRunning");
|
||||||
|
|
||||||
// Operations on files
|
// To access modules that are loaded after extensions
|
||||||
addHook("onFileCreated");
|
addHook("onFileMgrCreated");
|
||||||
addHook("onFileDeleted");
|
addHook("onSynchronizerCreated");
|
||||||
addHook("onFileSelected");
|
addHook("onPublisherCreated");
|
||||||
addHook("onContentChanged");
|
|
||||||
addHook("onTitleChanged");
|
// Operations on files
|
||||||
|
addHook("onFileCreated");
|
||||||
// Sync events
|
addHook("onFileDeleted");
|
||||||
addHook("onSyncRunning");
|
addHook("onFileSelected");
|
||||||
addHook("onSyncSuccess");
|
addHook("onContentChanged");
|
||||||
addHook("onSyncImportSuccess");
|
addHook("onTitleChanged");
|
||||||
addHook("onSyncExportSuccess");
|
|
||||||
addHook("onSyncRemoved");
|
// Sync events
|
||||||
|
addHook("onSyncRunning");
|
||||||
// Publish events
|
addHook("onSyncSuccess");
|
||||||
addHook("onPublishRunning");
|
addHook("onSyncImportSuccess");
|
||||||
addHook("onPublishSuccess");
|
addHook("onSyncExportSuccess");
|
||||||
addHook("onNewPublishSuccess");
|
addHook("onSyncRemoved");
|
||||||
addHook("onPublishRemoved");
|
|
||||||
|
// Publish events
|
||||||
// Operations on Layout
|
addHook("onPublishRunning");
|
||||||
addHook("onLayoutConfigure");
|
addHook("onPublishSuccess");
|
||||||
addHook("onLayoutCreated");
|
addHook("onNewPublishSuccess");
|
||||||
|
addHook("onPublishRemoved");
|
||||||
// Operations on PageDown
|
|
||||||
addHook("onEditorConfigure");
|
// Operations on Layout
|
||||||
|
addHook("onLayoutConfigure");
|
||||||
var onPreviewFinished = createHook("onPreviewFinished");
|
addHook("onLayoutCreated");
|
||||||
var onAsyncPreviewCallbackList = getExtensionCallbackList("onAsyncPreview");
|
|
||||||
extensionMgr["onAsyncPreview"] = function() {
|
// Operations on PageDown
|
||||||
logger.debug("onAsyncPreview");
|
addHook("onEditorConfigure");
|
||||||
// Call onPreviewFinished callbacks when all async preview are finished
|
|
||||||
var counter = 0;
|
var onPreviewFinished = createHook("onPreviewFinished");
|
||||||
function tryFinished() {
|
var onAsyncPreviewCallbackList = getExtensionCallbackList("onAsyncPreview");
|
||||||
if(counter === onAsyncPreviewCallbackList.length) {
|
extensionMgr["onAsyncPreview"] = function() {
|
||||||
onPreviewFinished();
|
logger.debug("onAsyncPreview");
|
||||||
}
|
// Call onPreviewFinished callbacks when all async preview are finished
|
||||||
}
|
var counter = 0;
|
||||||
_.each(onAsyncPreviewCallbackList, function(asyncPreviewCallback) {
|
function tryFinished() {
|
||||||
asyncPreviewCallback(function() {
|
if(counter === onAsyncPreviewCallbackList.length) {
|
||||||
counter++;
|
onPreviewFinished();
|
||||||
tryFinished();
|
}
|
||||||
});
|
}
|
||||||
});
|
_.each(onAsyncPreviewCallbackList, function(asyncPreviewCallback) {
|
||||||
tryFinished();
|
asyncPreviewCallback(function() {
|
||||||
};
|
counter++;
|
||||||
|
tryFinished();
|
||||||
var accordionTmpl = [
|
});
|
||||||
|
});
|
||||||
|
tryFinished();
|
||||||
|
};
|
||||||
|
|
||||||
|
var accordionTmpl = [
|
||||||
'<div class="accordion-group">',
|
'<div class="accordion-group">',
|
||||||
'<div class="accordion-heading">',
|
' <div class="accordion-heading">',
|
||||||
'<label class="checkbox pull-right">',
|
' <label class="checkbox pull-right">',
|
||||||
'<input id="input-enable-extension-<%= extensionId %>" type="checkbox" <% if(!optional) { %> disabled <% } %>> enabled',
|
' <input id="input-enable-extension-<%= extensionId %>" type="checkbox" <% if(!optional) { %> disabled <% } %>> enabled',
|
||||||
'</label>',
|
' </label>',
|
||||||
'<a id="accordion-toggle-test" data-toggle="collapse" data-parent="#accordion-extensions" class="accordion-toggle" href="#collapse-<%= extensionId %>">',
|
' <a id="accordion-toggle-test" data-toggle="collapse" data-parent="#accordion-extensions" class="accordion-toggle" href="#collapse-<%= extensionId %>">',
|
||||||
'<%= extensionName %>',
|
' <%= extensionName %>',
|
||||||
'</a>',
|
' </a>',
|
||||||
'</div>',
|
' </div>',
|
||||||
'<div id="collapse-<%= extensionId %>" class="accordion-body collapse">',
|
' <div id="collapse-<%= extensionId %>" class="accordion-body collapse">',
|
||||||
'<div class="accordion-inner"><%= settingsBloc %></div>',
|
' <div class="accordion-inner"><%= settingsBloc %></div>',
|
||||||
'</div>',
|
' </div>',
|
||||||
'</div>'].join("");
|
'</div>'
|
||||||
|
].join("");
|
||||||
function createSettings(extension) {
|
|
||||||
$("#accordion-extensions").append($(_.template(accordionTmpl, {
|
|
||||||
extensionId: extension.extensionId,
|
|
||||||
extensionName: extension.extensionName,
|
|
||||||
optional: extension.optional,
|
|
||||||
settingsBloc: extension.settingsBloc
|
|
||||||
})));
|
|
||||||
}
|
|
||||||
|
|
||||||
$(function() {
|
function createSettings(extension) {
|
||||||
// Create accordion in settings dialog
|
$("#accordion-extensions").append($(_.template(accordionTmpl, {
|
||||||
_.chain(
|
extensionId: extension.extensionId,
|
||||||
extensionList
|
extensionName: extension.extensionName,
|
||||||
).sortBy(function(extension) {
|
optional: extension.optional,
|
||||||
return extension.extensionName.toLowerCase();
|
settingsBloc: extension.settingsBloc
|
||||||
}).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()));
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
$(function() {
|
||||||
|
// Create accordion in settings dialog
|
||||||
return extensionMgr;
|
_.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()));
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
return extensionMgr;
|
||||||
});
|
});
|
@ -2,78 +2,79 @@ define([
|
|||||||
"jquery",
|
"jquery",
|
||||||
"underscore"
|
"underscore"
|
||||||
], function($, _) {
|
], function($, _) {
|
||||||
|
|
||||||
var buttonPublish = {
|
var buttonPublish = {
|
||||||
extensionId: "buttonPublish",
|
extensionId: "buttonPublish",
|
||||||
extensionName: 'Button "Publish"',
|
extensionName: 'Button "Publish"',
|
||||||
settingsBloc: '<p>Adds a "Publish document" button in the navigation bar.</p>'
|
settingsBloc: '<p>Adds a "Publish document" button in the navigation bar.</p>'
|
||||||
};
|
};
|
||||||
|
|
||||||
var button = undefined;
|
var button = undefined;
|
||||||
var currentFileDesc = undefined;
|
var currentFileDesc = undefined;
|
||||||
var publishRunning = false;
|
var publishRunning = false;
|
||||||
var hasPublications = false;
|
var hasPublications = false;
|
||||||
var isOffline = false;
|
var isOffline = false;
|
||||||
// Enable/disable the button
|
// Enable/disable the button
|
||||||
function updateButtonState() {
|
function updateButtonState() {
|
||||||
if(button === undefined) {
|
if(button === undefined) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(publishRunning === true || hasPublications === false || isOffline === true) {
|
if(publishRunning === true || hasPublications === false || isOffline === true) {
|
||||||
button.addClass("disabled");
|
button.addClass("disabled");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
button.removeClass("disabled");
|
button.removeClass("disabled");
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
;
|
||||||
var publisher = undefined;
|
|
||||||
buttonPublish.onPublisherCreated = function(publisherParameter) {
|
var publisher = undefined;
|
||||||
publisher = publisherParameter;
|
buttonPublish.onPublisherCreated = function(publisherParameter) {
|
||||||
};
|
publisher = publisherParameter;
|
||||||
|
};
|
||||||
buttonPublish.onCreateButton = function() {
|
|
||||||
button = $([
|
buttonPublish.onCreateButton = function() {
|
||||||
'<button class="btn" title="Publish this document">',
|
button = $([
|
||||||
'<i class="icon-share"></i>',
|
'<button class="btn" title="Publish this document">',
|
||||||
'</button>'].join("")
|
' <i class="icon-share"></i>',
|
||||||
).click(function() {
|
'</button>'
|
||||||
if(!$(this).hasClass("disabled")) {
|
].join("")).click(function() {
|
||||||
publisher.publish();
|
if(!$(this).hasClass("disabled")) {
|
||||||
}
|
publisher.publish();
|
||||||
});
|
}
|
||||||
return button;
|
});
|
||||||
};
|
return button;
|
||||||
|
};
|
||||||
buttonPublish.onPublishRunning = function(isRunning) {
|
|
||||||
publishRunning = isRunning;
|
buttonPublish.onPublishRunning = function(isRunning) {
|
||||||
updateButtonState();
|
publishRunning = isRunning;
|
||||||
};
|
updateButtonState();
|
||||||
|
};
|
||||||
buttonPublish.onOfflineChanged = function(isOfflineParameter) {
|
|
||||||
isOffline = isOfflineParameter;
|
buttonPublish.onOfflineChanged = function(isOfflineParameter) {
|
||||||
updateButtonState();
|
isOffline = isOfflineParameter;
|
||||||
};
|
updateButtonState();
|
||||||
|
};
|
||||||
// Check that current file has publications
|
|
||||||
var checkPublication = function() {
|
// Check that current file has publications
|
||||||
if(_.size(currentFileDesc.publishLocations) === 0) {
|
var checkPublication = function() {
|
||||||
hasPublications = false;
|
if(_.size(currentFileDesc.publishLocations) === 0) {
|
||||||
}
|
hasPublications = false;
|
||||||
else {
|
}
|
||||||
hasPublications = true;
|
else {
|
||||||
}
|
hasPublications = true;
|
||||||
updateButtonState();
|
}
|
||||||
};
|
updateButtonState();
|
||||||
|
};
|
||||||
buttonPublish.onFileSelected = function(fileDesc) {
|
|
||||||
currentFileDesc = fileDesc;
|
buttonPublish.onFileSelected = function(fileDesc) {
|
||||||
checkPublication();
|
currentFileDesc = fileDesc;
|
||||||
};
|
checkPublication();
|
||||||
|
};
|
||||||
buttonPublish.onPublishRemoved = checkPublication;
|
|
||||||
buttonPublish.onNewPublishSuccess = checkPublication;
|
buttonPublish.onPublishRemoved = checkPublication;
|
||||||
|
buttonPublish.onNewPublishSuccess = checkPublication;
|
||||||
return buttonPublish;
|
|
||||||
|
return buttonPublish;
|
||||||
|
|
||||||
});
|
});
|
@ -2,71 +2,73 @@ define([
|
|||||||
"jquery",
|
"jquery",
|
||||||
"underscore"
|
"underscore"
|
||||||
], function($, _) {
|
], function($, _) {
|
||||||
|
|
||||||
var buttonShare = {
|
var buttonShare = {
|
||||||
extensionId: "buttonShare",
|
extensionId: "buttonShare",
|
||||||
extensionName: 'Button "Share"',
|
extensionName: 'Button "Share"',
|
||||||
optional: true,
|
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() {
|
buttonShare.onCreateButton = function() {
|
||||||
return $([
|
return $([
|
||||||
'<button class="btn dropdown-toggle" data-toggle="dropdown" title="Share this document">',
|
'<button class="btn dropdown-toggle" data-toggle="dropdown" title="Share this document">',
|
||||||
'<i class="icon-link"></i>',
|
' <i class="icon-link"></i>',
|
||||||
'</button>',
|
'</button>',
|
||||||
'<div id="link-container" class="dropdown-menu pull-right">',
|
'<div id="link-container" class="dropdown-menu pull-right">',
|
||||||
'<div class="link-list"></div>',
|
' <h3 class="muted">Sharing</h3>',
|
||||||
'<p class="no-link">To share this document you need first to ',
|
' <div class="link-list"></div>',
|
||||||
'<a href="#" class="action-publish-gist">publish it as a Gist</a>',
|
' <p class="no-link">To share this document you need first to ',
|
||||||
' in Markdown format.',
|
' <a href="#" class="action-publish-gist">publish it as a Gist</a>',
|
||||||
'</p>',
|
' in Markdown format.',
|
||||||
'<blockquote class="muted">',
|
' </p>',
|
||||||
'<b>NOTE:</b> You can open any URL within StackEdit using ',
|
' <blockquote class="muted">',
|
||||||
'<a href="viewer.html?url=https://raw.github.com/benweet/stackedit/master/README.md"',
|
' <b>NOTE:</b> You can open any URL within StackEdit using',
|
||||||
'title="Sharing example">viewer.html?url=...</a>',
|
' <a href="viewer.html?url=https://raw.github.com/benweet/stackedit/master/README.md"',
|
||||||
'</blockquote>',
|
' title="Sharing example">viewer.html?url=...</a>',
|
||||||
'</div>'].join("")
|
' </blockquote>',
|
||||||
);
|
'</div>'
|
||||||
};
|
].join(""));
|
||||||
|
};
|
||||||
var fileDesc = undefined;
|
|
||||||
var lineTemplate = [
|
var fileDesc = undefined;
|
||||||
|
var lineTemplate = [
|
||||||
'<div class="input-prepend">',
|
'<div class="input-prepend">',
|
||||||
'<a href="<%= link %>" class="add-on" title="Sharing location"><i class="icon-link"></i></a>',
|
' <a href="<%= link %>" class="add-on" title="Sharing location"><i class="icon-link"></i></a>',
|
||||||
'<input class="span2" type="text" value="<%= link %>" readonly />',
|
' <input class="span2" type="text" value="<%= link %>" readonly />',
|
||||||
'</div>'].join("");
|
'</div>'
|
||||||
var refreshDocumentSharing = function(fileDescParameter) {
|
].join("");
|
||||||
if(fileDescParameter !== undefined && fileDescParameter !== fileDesc) {
|
var refreshDocumentSharing = function(fileDescParameter) {
|
||||||
return;
|
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) {
|
var attributesList = _.values(fileDesc.publishLocations);
|
||||||
if(attributes.sharingLink) {
|
_.each(attributesList, function(attributes) {
|
||||||
var lineElement = $(_.template(lineTemplate, {
|
if(attributes.sharingLink) {
|
||||||
link: attributes.sharingLink
|
var lineElement = $(_.template(lineTemplate, {
|
||||||
}));
|
link: attributes.sharingLink
|
||||||
lineElement.click(function(event) {
|
}));
|
||||||
event.stopPropagation();
|
lineElement.click(function(event) {
|
||||||
});
|
event.stopPropagation();
|
||||||
linkList.append(lineElement);
|
});
|
||||||
$("#link-container .no-link").hide();
|
linkList.append(lineElement);
|
||||||
}
|
$("#link-container .no-link").hide();
|
||||||
});
|
}
|
||||||
};
|
});
|
||||||
|
};
|
||||||
buttonShare.onFileSelected = function(fileDescParameter) {
|
|
||||||
fileDesc = fileDescParameter;
|
buttonShare.onFileSelected = function(fileDescParameter) {
|
||||||
refreshDocumentSharing(fileDescParameter);
|
fileDesc = fileDescParameter;
|
||||||
};
|
refreshDocumentSharing(fileDescParameter);
|
||||||
|
};
|
||||||
buttonShare.onNewPublishSuccess = refreshDocumentSharing;
|
|
||||||
buttonShare.onPublishRemoved = refreshDocumentSharing;
|
buttonShare.onNewPublishSuccess = refreshDocumentSharing;
|
||||||
|
buttonShare.onPublishRemoved = refreshDocumentSharing;
|
||||||
return buttonShare;
|
|
||||||
|
return buttonShare;
|
||||||
|
|
||||||
});
|
});
|
@ -1,72 +1,78 @@
|
|||||||
define([
|
define([
|
||||||
"jquery",
|
"jquery",
|
||||||
"underscore"
|
"underscore",
|
||||||
], function($, _) {
|
"utils"
|
||||||
|
], function($, _, utils) {
|
||||||
var buttonStat = {
|
|
||||||
extensionId: "buttonStat",
|
var buttonStat = {
|
||||||
extensionName: 'Button "Statistics"',
|
extensionId: "buttonStat",
|
||||||
|
extensionName: 'Button "Statistics"',
|
||||||
optional: true,
|
optional: true,
|
||||||
settingsBloc: '<p>Adds a "Document statistics" button in the navigation bar.</p>'
|
defaultConfig: {
|
||||||
};
|
name1: "Words",
|
||||||
|
value1: "\\S+",
|
||||||
buttonStat.onCreateButton = function() {
|
name2: "Characters",
|
||||||
return $([
|
value2: "\\S",
|
||||||
'<button class="btn dropdown-toggle" data-toggle="dropdown" title="Document statistics">',
|
name3: "Paragraphs",
|
||||||
'<i class="icon-stat"></i>',
|
value3: ".+",
|
||||||
'</button>',
|
},
|
||||||
'<div id="statistics-container" class="dropdown-menu pull-right">',
|
settingsBloc: [
|
||||||
'<div class="link-list"></div>',
|
'<p>Adds a "Document statistics" button in the navigation bar.</p>',
|
||||||
'<p class="no-link">To share this document you need first to <a',
|
'<p><div class="form-inline">',
|
||||||
'href="#" class="action-publish-gist">publish it as a Gist</a> in',
|
' <label class="label-text" for="input-stat-name1">Title</label>',
|
||||||
'Markdown format.',
|
' <input id="input-stat-name1" type="text" class="input-small">',
|
||||||
'</p>',
|
' <label class="label-text" for="input-stat-value1">RegExp</label>',
|
||||||
'<blockquote class="muted">',
|
' <input id="input-stat-value1" type="text" class="span2">',
|
||||||
'<b>NOTE:</b> You can open any URL within StackEdit using <a',
|
'</div></p>',
|
||||||
'href="viewer.html?url=https://raw.github.com/benweet/stackedit/master/README.md"',
|
'<p><div class="form-inline">',
|
||||||
'title="Sharing example">viewer.html?url=...</a>',
|
' <label class="label-text" for="input-stat-name2">Title</label>',
|
||||||
'</blockquote>',
|
' <input id="input-stat-name2" type="text" class="input-small">',
|
||||||
'</div>'].join("")
|
' <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">',
|
||||||
var fileDesc = undefined;
|
' <label class="label-text" for="input-stat-name3">Title</label>',
|
||||||
var lineTemplate = [
|
' <input id="input-stat-name3" type="text" class="input-small">',
|
||||||
'<div class="input-prepend">',
|
' <label class="label-text" for="input-stat-value3">RegExp</label>',
|
||||||
'<a href="<%= link %>" class="add-on" title="Sharing location"><i class="icon-link"></i></a>',
|
' <input id="input-stat-value3" type="text" class="span2">',
|
||||||
'<input class="span2" type="text" value="<%= link %>" readonly />',
|
'</div></p>'].join("")
|
||||||
'</div>'].join("");
|
};
|
||||||
var refreshDocumentSharing = function(fileDescParameter) {
|
|
||||||
if(fileDescParameter !== undefined && fileDescParameter !== fileDesc) {
|
buttonStat.onLoadSettings = function() {
|
||||||
return;
|
_.each(buttonStat.defaultConfig, function(value, key) {
|
||||||
}
|
utils.setInputValue("#input-stat-" + key, buttonStat.config[key]);
|
||||||
|
});
|
||||||
var linkList = $("#link-container .link-list").empty();
|
};
|
||||||
$("#link-container .no-link").show();
|
|
||||||
|
buttonStat.onSaveSettings = function(newConfig, event) {
|
||||||
var attributesList = _.values(fileDesc.publishLocations);
|
_.each(buttonStat.defaultConfig, function(value, key) {
|
||||||
_.each(attributesList, function(attributes) {
|
newConfig[key] = utils.getInputTextValue("#input-stat-" + key, event);
|
||||||
if(attributes.sharingLink) {
|
});
|
||||||
var lineElement = $(_.template(lineTemplate, {
|
};
|
||||||
link: attributes.sharingLink
|
|
||||||
}));
|
buttonStat.onCreateButton = function() {
|
||||||
lineElement.click(function(event) {
|
return $([
|
||||||
event.stopPropagation();
|
'<button class="btn dropdown-toggle" data-toggle="dropdown" title="Document statistics">',
|
||||||
});
|
' <i class="icon-stat"></i>',
|
||||||
linkList.append(lineElement);
|
'</button>',
|
||||||
$("#link-container .no-link").hide();
|
'<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>',
|
||||||
buttonStat.onFileSelected = function(fileDescParameter) {
|
' <div>' + buttonStat.config.name3 + ': <span id="span-stat-value3"></span></div>',
|
||||||
fileDesc = fileDescParameter;
|
' </div>',
|
||||||
refreshDocumentSharing(fileDescParameter);
|
'</div>'
|
||||||
};
|
].join(""));
|
||||||
|
};
|
||||||
buttonStat.onNewPublishSuccess = refreshDocumentSharing;
|
|
||||||
buttonStat.onPublishRemoved = refreshDocumentSharing;
|
buttonStat.onPreviewFinished = function() {
|
||||||
|
var text = $("#wmd-preview").text();
|
||||||
return buttonStat;
|
$("#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);
|
||||||
|
};
|
||||||
|
|
||||||
|
return buttonStat;
|
||||||
|
|
||||||
});
|
});
|
@ -2,77 +2,77 @@ define([
|
|||||||
"jquery",
|
"jquery",
|
||||||
"underscore"
|
"underscore"
|
||||||
], function($, _) {
|
], function($, _) {
|
||||||
|
|
||||||
var buttonSync = {
|
var buttonSync = {
|
||||||
extensionId: "buttonSync",
|
extensionId: "buttonSync",
|
||||||
extensionName: 'Button "Synchronize"',
|
extensionName: 'Button "Synchronize"',
|
||||||
settingsBloc: '<p>Adds a "Synchronize documents" button in the navigation bar.</p>'
|
settingsBloc: '<p>Adds a "Synchronize documents" button in the navigation bar.</p>'
|
||||||
};
|
};
|
||||||
|
|
||||||
var button = undefined;
|
var button = undefined;
|
||||||
var syncRunning = false;
|
var syncRunning = false;
|
||||||
var uploadPending = false;
|
var uploadPending = false;
|
||||||
var isOffline = false;
|
var isOffline = false;
|
||||||
// Enable/disable the button
|
// Enable/disable the button
|
||||||
var updateButtonState = function() {
|
var updateButtonState = function() {
|
||||||
if(button === undefined) {
|
if(button === undefined) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(syncRunning === true || uploadPending === false || isOffline) {
|
if(syncRunning === true || uploadPending === false || isOffline) {
|
||||||
button.addClass("disabled");
|
button.addClass("disabled");
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
button.removeClass("disabled");
|
button.removeClass("disabled");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var synchronizer = undefined;
|
var synchronizer = undefined;
|
||||||
buttonSync.onSynchronizerCreated = function(synchronizerParameter) {
|
buttonSync.onSynchronizerCreated = function(synchronizerParameter) {
|
||||||
synchronizer = synchronizerParameter;
|
synchronizer = synchronizerParameter;
|
||||||
};
|
};
|
||||||
|
|
||||||
buttonSync.onCreateButton = function() {
|
buttonSync.onCreateButton = function() {
|
||||||
button = $([
|
button = $([
|
||||||
'<button class="btn" title="Synchronize all documents">',
|
'<button class="btn" title="Synchronize all documents">',
|
||||||
'<i class="icon-refresh"></i>',
|
' <i class="icon-refresh"></i>',
|
||||||
'</button>'].join("")
|
'</button>'
|
||||||
).click(function() {
|
].join("")).click(function() {
|
||||||
if(!$(this).hasClass("disabled")) {
|
if(!$(this).hasClass("disabled")) {
|
||||||
synchronizer.forceSync();
|
synchronizer.forceSync();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return button;
|
return button;
|
||||||
};
|
};
|
||||||
|
|
||||||
buttonSync.onReady = updateButtonState;
|
buttonSync.onReady = updateButtonState;
|
||||||
|
|
||||||
buttonSync.onSyncRunning = function(isRunning) {
|
buttonSync.onSyncRunning = function(isRunning) {
|
||||||
syncRunning = isRunning;
|
syncRunning = isRunning;
|
||||||
uploadPending = true;
|
uploadPending = true;
|
||||||
updateButtonState();
|
updateButtonState();
|
||||||
};
|
};
|
||||||
|
|
||||||
buttonSync.onSyncSuccess = function() {
|
buttonSync.onSyncSuccess = function() {
|
||||||
uploadPending = false;
|
uploadPending = false;
|
||||||
updateButtonState();
|
updateButtonState();
|
||||||
};
|
};
|
||||||
|
|
||||||
buttonSync.onOfflineChanged = function(isOfflineParameter) {
|
buttonSync.onOfflineChanged = function(isOfflineParameter) {
|
||||||
isOffline = isOfflineParameter;
|
isOffline = isOfflineParameter;
|
||||||
updateButtonState();
|
updateButtonState();
|
||||||
};
|
};
|
||||||
|
|
||||||
// Check that a file has synchronized locations
|
// Check that a file has synchronized locations
|
||||||
var checkSynchronization = function(fileDesc) {
|
var checkSynchronization = function(fileDesc) {
|
||||||
if(_.size(fileDesc.syncLocations) !== 0) {
|
if(_.size(fileDesc.syncLocations) !== 0) {
|
||||||
uploadPending = true;
|
uploadPending = true;
|
||||||
updateButtonState();
|
updateButtonState();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
buttonSync.onContentChanged = checkSynchronization;
|
buttonSync.onContentChanged = checkSynchronization;
|
||||||
buttonSync.onTitleChanged = checkSynchronization;
|
buttonSync.onTitleChanged = checkSynchronization;
|
||||||
|
|
||||||
return buttonSync;
|
return buttonSync;
|
||||||
|
|
||||||
});
|
});
|
@ -3,70 +3,99 @@ define([
|
|||||||
"underscore",
|
"underscore",
|
||||||
"file-system"
|
"file-system"
|
||||||
], function($, _, fileSystem) {
|
], function($, _, fileSystem) {
|
||||||
|
|
||||||
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 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("");
|
|
||||||
}
|
|
||||||
|
|
||||||
liMap = {};
|
var documentSelector = {
|
||||||
$("#file-selector li:not(.stick)").empty();
|
extensionId: "documentSelector",
|
||||||
_.chain(
|
extensionName: "Document selector",
|
||||||
fileSystem
|
settingsBloc: '<p>Builds the "Open document" dropdown menu.</p>'
|
||||||
).sortBy(function(fileDesc) {
|
};
|
||||||
return fileDesc.title.toLowerCase();
|
|
||||||
}).each(function(fileDesc) {
|
var fileMgr = undefined;
|
||||||
var a = $('<a href="#">').html(composeTitle(fileDesc)).click(function() {
|
documentSelector.onFileMgrCreated = function(fileMgrParameter) {
|
||||||
if(!liMap[fileDesc.fileIndex].is(".disabled")) {
|
fileMgr = fileMgrParameter;
|
||||||
fileMgr.selectFile(fileDesc);
|
};
|
||||||
}
|
|
||||||
});
|
var liMap = undefined;
|
||||||
var li = $("<li>").append(a);
|
var buildSelector = function() {
|
||||||
liMap[fileDesc.fileIndex] = li;
|
|
||||||
$("#file-selector").append(li);
|
function composeTitle(fileDesc) {
|
||||||
});
|
var result = [];
|
||||||
};
|
var syncAttributesList = _.values(fileDesc.syncLocations);
|
||||||
|
var publishAttributesList = _.values(fileDesc.publishLocations);
|
||||||
documentSelector.onFileSelected = function(fileDesc) {
|
var attributesList = syncAttributesList.concat(publishAttributesList);
|
||||||
if(liMap === undefined) {
|
_.chain(attributesList).sortBy(function(attributes) {
|
||||||
buildSelector();
|
return attributes.provider.providerId;
|
||||||
}
|
}).each(function(attributes) {
|
||||||
$("#file-selector li:not(.stick)").removeClass("disabled");
|
result.push('<i class="icon-' + attributes.provider.providerId + '"></i>');
|
||||||
liMap[fileDesc.fileIndex].addClass("disabled");
|
});
|
||||||
};
|
result.push(" ");
|
||||||
|
result.push(fileDesc.title);
|
||||||
documentSelector.onFileCreated = buildSelector;
|
return result.join("");
|
||||||
documentSelector.onFileDeleted = buildSelector;
|
}
|
||||||
documentSelector.onTitleChanged = buildSelector;
|
|
||||||
documentSelector.onSyncExportSuccess = buildSelector;
|
liMap = {};
|
||||||
documentSelector.onSyncRemoved = buildSelector;
|
$("#file-selector li:not(.stick)").empty();
|
||||||
documentSelector.onNewPublishSuccess = buildSelector;
|
_.chain(fileSystem).sortBy(function(fileDesc) {
|
||||||
documentSelector.onPublishRemoved = buildSelector;
|
return fileDesc.title.toLowerCase();
|
||||||
|
}).each(function(fileDesc) {
|
||||||
return documentSelector;
|
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.onFileCreated = buildSelector;
|
||||||
|
documentSelector.onFileDeleted = buildSelector;
|
||||||
|
documentSelector.onTitleChanged = buildSelector;
|
||||||
|
documentSelector.onSyncExportSuccess = buildSelector;
|
||||||
|
documentSelector.onSyncRemoved = buildSelector;
|
||||||
|
documentSelector.onNewPublishSuccess = buildSelector;
|
||||||
|
documentSelector.onPublishRemoved = buildSelector;
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
|
||||||
});
|
});
|
@ -2,62 +2,62 @@ define([
|
|||||||
"jquery",
|
"jquery",
|
||||||
"underscore"
|
"underscore"
|
||||||
], function($, _) {
|
], function($, _) {
|
||||||
|
|
||||||
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 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("");
|
|
||||||
}
|
|
||||||
|
|
||||||
var title = fileDesc.title;
|
var documentTitle = {
|
||||||
document.title = "StackEdit - " + title;
|
extensionId: "documentTitle",
|
||||||
$("#file-title").html(composeTitle(fileDesc));
|
extensionName: "Document title",
|
||||||
$(".file-title").text(title);
|
settingsBloc: '<p>Responsible for showing the document title in the navigation bar.</p>'
|
||||||
$("#file-title-input").val(title);
|
};
|
||||||
|
|
||||||
if(layout !== undefined) {
|
var layout = undefined;
|
||||||
// Use defer to make sure UI has been updated
|
documentTitle.onLayoutCreated = function(layoutParameter) {
|
||||||
_.defer(layout.resizeAll);
|
layout = layoutParameter;
|
||||||
}
|
};
|
||||||
};
|
|
||||||
|
var fileDesc = undefined;
|
||||||
documentTitle.onFileSelected = function(fileDescParameter) {
|
var updateTitle = function(fileDescParameter) {
|
||||||
fileDesc = fileDescParameter;
|
if(fileDescParameter !== fileDesc) {
|
||||||
updateTitle(fileDescParameter);
|
return;
|
||||||
};
|
}
|
||||||
|
|
||||||
documentTitle.onTitleChanged = updateTitle;
|
function composeTitle(fileDesc) {
|
||||||
documentTitle.onSyncExportSuccess = updateTitle;
|
var result = [];
|
||||||
documentTitle.onSyncRemoved = updateTitle;
|
var syncAttributesList = _.values(fileDesc.syncLocations);
|
||||||
documentTitle.onNewPublishSuccess = updateTitle;
|
var publishAttributesList = _.values(fileDesc.publishLocations);
|
||||||
documentTitle.onPublishRemoved = updateTitle;
|
var attributesList = syncAttributesList.concat(publishAttributesList);
|
||||||
|
_.chain(attributesList).sortBy(function(attributes) {
|
||||||
return documentTitle;
|
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);
|
||||||
|
|
||||||
|
if(layout !== undefined) {
|
||||||
|
// Use defer to make sure UI has been updated
|
||||||
|
_.defer(layout.resizeAll);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
documentTitle.onFileSelected = function(fileDescParameter) {
|
||||||
|
fileDesc = fileDescParameter;
|
||||||
|
updateTitle(fileDescParameter);
|
||||||
|
};
|
||||||
|
|
||||||
|
documentTitle.onTitleChanged = updateTitle;
|
||||||
|
documentTitle.onSyncExportSuccess = updateTitle;
|
||||||
|
documentTitle.onSyncRemoved = updateTitle;
|
||||||
|
documentTitle.onNewPublishSuccess = updateTitle;
|
||||||
|
documentTitle.onPublishRemoved = updateTitle;
|
||||||
|
|
||||||
|
return documentTitle;
|
||||||
|
|
||||||
});
|
});
|
@ -1,20 +1,19 @@
|
|||||||
define(function() {
|
define(function() {
|
||||||
|
|
||||||
var emailConverter = {
|
var emailConverter = {
|
||||||
extensionId: "emailConverter",
|
extensionId: "emailConverter",
|
||||||
extensionName: "Email Converter",
|
extensionName: "Email Converter",
|
||||||
optional: true,
|
optional: true,
|
||||||
settingsBloc: '<p>Converts email adresses in the form <email@example.com> into a clickable links.</p>'
|
settingsBloc: '<p>Converts email adresses in the form <email@example.com> into a clickable links.</p>'
|
||||||
};
|
};
|
||||||
|
|
||||||
emailConverter.onEditorConfigure = function(editor) {
|
emailConverter.onEditorConfigure = function(editor) {
|
||||||
editor.getConverter().hooks.chain("postConversion", function(text) {
|
editor.getConverter().hooks.chain("postConversion", function(text) {
|
||||||
return text.replace(/<(mailto\:)?([^\s>]+@[^\s>]+\.\S+?)>/g, function(match, mailto, email) {
|
return text.replace(/<(mailto\:)?([^\s>]+@[^\s>]+\.\S+?)>/g, function(match, mailto, email) {
|
||||||
return '<a href="mailto:' + email + '">' + email + '</a>';
|
return '<a href="mailto:' + email + '">' + email + '</a>';
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
return emailConverter;
|
return emailConverter;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -2,65 +2,67 @@ define([
|
|||||||
"jquery",
|
"jquery",
|
||||||
"underscore"
|
"underscore"
|
||||||
], function($, _) {
|
], function($, _) {
|
||||||
|
|
||||||
var managePublication = {
|
var managePublication = {
|
||||||
extensionId: "managePublication",
|
extensionId: "managePublication",
|
||||||
extensionName: "Manage publication",
|
extensionName: "Manage publication",
|
||||||
settingsBloc: '<p>Populates the "Manage publication" dialog box.</p>'
|
settingsBloc: '<p>Populates the "Manage publication" dialog box.</p>'
|
||||||
};
|
};
|
||||||
|
|
||||||
var fileMgr = undefined;
|
var fileMgr = undefined;
|
||||||
managePublication.onFileMgrCreated = function(fileMgrParameter) {
|
managePublication.onFileMgrCreated = function(fileMgrParameter) {
|
||||||
fileMgr = fileMgrParameter;
|
fileMgr = fileMgrParameter;
|
||||||
};
|
};
|
||||||
|
|
||||||
var fileDesc = undefined;
|
var fileDesc = undefined;
|
||||||
var lineTemplate = [
|
var lineTemplate = [
|
||||||
'<div class="input-prepend input-append">',
|
'<div class="input-prepend input-append">',
|
||||||
'<span class="add-on" title="<%= provider.providerName %>">',
|
' <span class="add-on" title="<%= provider.providerName %>">',
|
||||||
'<i class="icon-<%= provider.providerId %>"></i>',
|
' <i class="icon-<%= provider.providerId %>"></i>',
|
||||||
'</span>',
|
' </span>',
|
||||||
'<input class="span5" type="text" value="<%= publishDesc %>" disabled />',
|
' <input class="span5" type="text" value="<%= publishDesc %>" disabled />',
|
||||||
'</div>'].join("");
|
'</div>'
|
||||||
var removeButtonTemplate = '<a class="btn" title="Remove this location"><i class="icon-trash"></i></a>';
|
].join("");
|
||||||
var refreshDialog = function(fileDescParameter) {
|
var removeButtonTemplate = '<a class="btn" title="Remove this location"><i class="icon-trash"></i></a>';
|
||||||
if(fileDescParameter !== undefined && fileDescParameter !== fileDesc) {
|
var refreshDialog = function(fileDescParameter) {
|
||||||
return;
|
if(fileDescParameter !== undefined && fileDescParameter !== fileDesc) {
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
var publishAttributesList = _.values(fileDesc.publishLocations);
|
|
||||||
$(".msg-no-publish, .msg-publish-list").addClass("hide");
|
var publishAttributesList = _.values(fileDesc.publishLocations);
|
||||||
var publishList = $("#manage-publish-list").empty();
|
$(".msg-no-publish, .msg-publish-list").addClass("hide");
|
||||||
if (publishAttributesList.length > 0) {
|
var publishList = $("#manage-publish-list").empty();
|
||||||
$(".msg-publish-list").removeClass("hide");
|
if(publishAttributesList.length > 0) {
|
||||||
} else {
|
$(".msg-publish-list").removeClass("hide");
|
||||||
$(".msg-no-publish").removeClass("hide");
|
}
|
||||||
}
|
else {
|
||||||
_.each(publishAttributesList, function(publishAttributes) {
|
$(".msg-no-publish").removeClass("hide");
|
||||||
formattedAttributes = _.omit(publishAttributes, "provider", "publishIndex", "sharingLink");
|
}
|
||||||
if(formattedAttributes.password) {
|
_.each(publishAttributesList, function(publishAttributes) {
|
||||||
formattedAttributes.password = "********";
|
formattedAttributes = _.omit(publishAttributes, "provider", "publishIndex", "sharingLink");
|
||||||
}
|
if(formattedAttributes.password) {
|
||||||
var publishDesc = JSON.stringify(formattedAttributes).replace(/{|}|"/g, "").replace(/,/g, ", ");
|
formattedAttributes.password = "********";
|
||||||
var lineElement = $(_.template(lineTemplate, {
|
}
|
||||||
provider: publishAttributes.provider,
|
var publishDesc = JSON.stringify(formattedAttributes).replace(/{|}|"/g, "").replace(/,/g, ", ");
|
||||||
publishDesc: publishDesc
|
var lineElement = $(_.template(lineTemplate, {
|
||||||
}));
|
provider: publishAttributes.provider,
|
||||||
lineElement.append($(removeButtonTemplate).click(function() {
|
publishDesc: publishDesc
|
||||||
fileMgr.removePublish(publishAttributes);
|
}));
|
||||||
}));
|
lineElement.append($(removeButtonTemplate).click(function() {
|
||||||
publishList.append(lineElement);
|
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;
|
||||||
return managePublication;
|
managePublication.onPublishRemoved = refreshDialog;
|
||||||
|
|
||||||
|
return managePublication;
|
||||||
|
|
||||||
});
|
});
|
@ -2,61 +2,63 @@ define([
|
|||||||
"jquery",
|
"jquery",
|
||||||
"underscore"
|
"underscore"
|
||||||
], function($, _) {
|
], function($, _) {
|
||||||
|
|
||||||
var manageSynchronization = {
|
var manageSynchronization = {
|
||||||
extensionId: "manageSynchronization",
|
extensionId: "manageSynchronization",
|
||||||
extensionName: "Manage synchronization",
|
extensionName: "Manage synchronization",
|
||||||
settingsBloc: '<p>Populates the "Manage synchronization" dialog box.</p>'
|
settingsBloc: '<p>Populates the "Manage synchronization" dialog box.</p>'
|
||||||
};
|
};
|
||||||
|
|
||||||
var fileMgr = undefined;
|
var fileMgr = undefined;
|
||||||
manageSynchronization.onFileMgrCreated = function(fileMgrParameter) {
|
manageSynchronization.onFileMgrCreated = function(fileMgrParameter) {
|
||||||
fileMgr = fileMgrParameter;
|
fileMgr = fileMgrParameter;
|
||||||
};
|
};
|
||||||
|
|
||||||
var fileDesc = undefined;
|
var fileDesc = undefined;
|
||||||
var lineTemplate = [
|
var lineTemplate = [
|
||||||
'<div class="input-prepend input-append">',
|
'<div class="input-prepend input-append">',
|
||||||
'<span class="add-on" title="<%= provider.providerName %>">',
|
' <span class="add-on" title="<%= provider.providerName %>">',
|
||||||
'<i class="icon-<%= provider.providerId %>"></i>',
|
' <i class="icon-<%= provider.providerId %>"></i>',
|
||||||
'</span>',
|
' </span>',
|
||||||
'<input class="span5" type="text" value="<%= syncDesc %>" disabled />',
|
' <input class="span5" type="text" value="<%= syncDesc %>" disabled />',
|
||||||
'</div>'].join("");
|
'</div>'
|
||||||
var removeButtonTemplate = '<a class="btn" title="Remove this location"><i class="icon-trash"></i></a>';
|
].join("");
|
||||||
var refreshDialog = function(fileDescParameter) {
|
var removeButtonTemplate = '<a class="btn" title="Remove this location"><i class="icon-trash"></i></a>';
|
||||||
if(fileDescParameter !== undefined && fileDescParameter !== fileDesc) {
|
var refreshDialog = function(fileDescParameter) {
|
||||||
return;
|
if(fileDescParameter !== undefined && fileDescParameter !== fileDesc) {
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
var syncAttributesList = _.values(fileDesc.syncLocations);
|
|
||||||
$(".msg-no-sync, .msg-sync-list").addClass("hide");
|
var syncAttributesList = _.values(fileDesc.syncLocations);
|
||||||
var syncList = $("#manage-sync-list").empty();
|
$(".msg-no-sync, .msg-sync-list").addClass("hide");
|
||||||
if (syncAttributesList.length > 0) {
|
var syncList = $("#manage-sync-list").empty();
|
||||||
$(".msg-sync-list").removeClass("hide");
|
if(syncAttributesList.length > 0) {
|
||||||
} else {
|
$(".msg-sync-list").removeClass("hide");
|
||||||
$(".msg-no-sync").removeClass("hide");
|
}
|
||||||
}
|
else {
|
||||||
_.each(syncAttributesList, function(syncAttributes) {
|
$(".msg-no-sync").removeClass("hide");
|
||||||
var syncDesc = syncAttributes.id || syncAttributes.path;
|
}
|
||||||
var lineElement = $(_.template(lineTemplate, {
|
_.each(syncAttributesList, function(syncAttributes) {
|
||||||
provider: syncAttributes.provider,
|
var syncDesc = syncAttributes.id || syncAttributes.path;
|
||||||
syncDesc: syncDesc
|
var lineElement = $(_.template(lineTemplate, {
|
||||||
}));
|
provider: syncAttributes.provider,
|
||||||
lineElement.append($(removeButtonTemplate).click(function() {
|
syncDesc: syncDesc
|
||||||
fileMgr.removeSync(syncAttributes);
|
}));
|
||||||
}));
|
lineElement.append($(removeButtonTemplate).click(function() {
|
||||||
syncList.append(lineElement);
|
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;
|
||||||
return manageSynchronization;
|
manageSynchronization.onSyncRemoved = refreshDialog;
|
||||||
|
|
||||||
|
return manageSynchronization;
|
||||||
|
|
||||||
});
|
});
|
@ -2,44 +2,44 @@ define([
|
|||||||
"utils",
|
"utils",
|
||||||
"lib/Markdown.Extra"
|
"lib/Markdown.Extra"
|
||||||
], function(utils) {
|
], function(utils) {
|
||||||
|
|
||||||
var markdownExtra = {
|
var markdownExtra = {
|
||||||
extensionId: "markdownExtra",
|
extensionId: "markdownExtra",
|
||||||
extensionName: "Markdown Extra",
|
extensionName: "Markdown Extra",
|
||||||
optional: true,
|
optional: true,
|
||||||
defaultConfig: {
|
defaultConfig: {
|
||||||
prettify: true
|
prettify: true
|
||||||
},
|
},
|
||||||
settingsBloc: [
|
settingsBloc: [
|
||||||
'<p>Adds extra features to the original Markdown syntax.</p>',
|
'<p>Adds extra features to the original Markdown syntax.</p>',
|
||||||
'<div class="form-horizontal">',
|
'<div class="form-horizontal">',
|
||||||
'<div class="control-group">',
|
' <div class="control-group">',
|
||||||
'<label class="control-label" for="input-markdownextra-prettify">Prettify syntax highlighting</label>',
|
' <label class="control-label" for="input-markdownextra-prettify">Prettify syntax highlighting</label>',
|
||||||
'<div class="controls">',
|
' <div class="controls">',
|
||||||
'<input type="checkbox" id="input-markdownextra-prettify">',
|
' <input type="checkbox" id="input-markdownextra-prettify">',
|
||||||
'</div>',
|
' </div>',
|
||||||
'</div>',
|
' </div>',
|
||||||
'</div>'
|
'</div>'
|
||||||
].join("")
|
].join("")
|
||||||
};
|
};
|
||||||
|
|
||||||
markdownExtra.onLoadSettings = function() {
|
markdownExtra.onLoadSettings = function() {
|
||||||
utils.setInputChecked("#input-markdownextra-prettify", markdownExtra.config.prettify);
|
utils.setInputChecked("#input-markdownextra-prettify", markdownExtra.config.prettify);
|
||||||
};
|
};
|
||||||
|
|
||||||
markdownExtra.onSaveSettings = function(newConfig, event) {
|
markdownExtra.onSaveSettings = function(newConfig, event) {
|
||||||
newConfig.prettify = utils.getInputChecked("#input-markdownextra-prettify");
|
newConfig.prettify = utils.getInputChecked("#input-markdownextra-prettify");
|
||||||
};
|
};
|
||||||
|
|
||||||
markdownExtra.onEditorConfigure = function(editor) {
|
markdownExtra.onEditorConfigure = function(editor) {
|
||||||
var converter = editor.getConverter();
|
var converter = editor.getConverter();
|
||||||
var options = {};
|
var options = {};
|
||||||
if(markdownExtra.config.prettify === true) {
|
if(markdownExtra.config.prettify === true) {
|
||||||
options.highlighter = "prettify";
|
options.highlighter = "prettify";
|
||||||
editor.hooks.chain("onPreviewRefresh", prettyPrint);
|
editor.hooks.chain("onPreviewRefresh", prettyPrint);
|
||||||
}
|
}
|
||||||
Markdown.Extra.init(converter, options);
|
Markdown.Extra.init(converter, options);
|
||||||
};
|
};
|
||||||
|
|
||||||
return markdownExtra;
|
return markdownExtra;
|
||||||
});
|
});
|
@ -4,117 +4,117 @@ define([
|
|||||||
"utils",
|
"utils",
|
||||||
"jgrowl"
|
"jgrowl"
|
||||||
], function($, _, utils, jGrowl) {
|
], function($, _, utils, jGrowl) {
|
||||||
|
|
||||||
var notifications = {
|
var notifications = {
|
||||||
extensionId: "notifications",
|
extensionId: "notifications",
|
||||||
extensionName: "Notifications",
|
extensionName: "Notifications",
|
||||||
defaultConfig: {
|
defaultConfig: {
|
||||||
timeout: 5000
|
timeout: 5000
|
||||||
},
|
},
|
||||||
settingsBloc: [
|
settingsBloc: [
|
||||||
'<p>Shows notification messages in the bottom-right corner of the screen.</p>',
|
'<p>Shows notification messages in the bottom-right corner of the screen.</p>',
|
||||||
'<div class="form-horizontal">',
|
'<div class="form-horizontal">',
|
||||||
'<div class="control-group">',
|
' <div class="control-group">',
|
||||||
'<label class="control-label" for="input-notifications-timeout">Timeout</label>',
|
' <label class="control-label" for="input-notifications-timeout">Timeout</label>',
|
||||||
'<div class="controls">',
|
' <div class="controls">',
|
||||||
'<input type="text" id="input-notifications-timeout" class="input-mini">',
|
' <input type="text" id="input-notifications-timeout" class="input-mini">',
|
||||||
'<span class="help-inline">ms</span>',
|
' <span class="help-inline">ms</span>',
|
||||||
'</div>',
|
' </div>',
|
||||||
'</div>',
|
' </div>',
|
||||||
'</div>'
|
'</div>'
|
||||||
].join("")
|
].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) {
|
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() {
|
notifications.onReady = function() {
|
||||||
// jGrowl configuration
|
// jGrowl configuration
|
||||||
jGrowl.defaults.life = notifications.config.timeout;
|
jGrowl.defaults.life = notifications.config.timeout;
|
||||||
jGrowl.defaults.closer = false;
|
jGrowl.defaults.closer = false;
|
||||||
jGrowl.defaults.closeTemplate = '';
|
jGrowl.defaults.closeTemplate = '';
|
||||||
jGrowl.defaults.position = 'bottom-right';
|
jGrowl.defaults.position = 'bottom-right';
|
||||||
};
|
};
|
||||||
|
|
||||||
function showMessage(msg, iconClass, options) {
|
function showMessage(msg, iconClass, options) {
|
||||||
if(!msg) {
|
if(!msg) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var endOfMsg = msg.indexOf("|");
|
var endOfMsg = msg.indexOf("|");
|
||||||
if(endOfMsg !== -1) {
|
if(endOfMsg !== -1) {
|
||||||
msg = msg.substring(0, endOfMsg);
|
msg = msg.substring(0, endOfMsg);
|
||||||
if(!msg) {
|
if(!msg) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
options = options || {};
|
options = options || {};
|
||||||
iconClass = iconClass || "icon-info-sign";
|
iconClass = iconClass || "icon-info-sign";
|
||||||
jGrowl("<i class='icon-white " + iconClass + "'></i> " + _.escape(msg), options);
|
jGrowl("<i class='icon-white " + iconClass + "'></i> " + _.escape(msg), options);
|
||||||
}
|
}
|
||||||
|
|
||||||
notifications.onMessage = function(message) {
|
notifications.onMessage = function(message) {
|
||||||
logger.log(message);
|
logger.log(message);
|
||||||
showMessage(message);
|
showMessage(message);
|
||||||
};
|
};
|
||||||
|
|
||||||
notifications.onError = function(error) {
|
notifications.onError = function(error) {
|
||||||
logger.error(error);
|
logger.error(error);
|
||||||
if(_.isString(error)) {
|
if(_.isString(error)) {
|
||||||
showMessage(error, "icon-warning-sign");
|
showMessage(error, "icon-warning-sign");
|
||||||
}
|
}
|
||||||
else if(_.isObject(error)) {
|
else if(_.isObject(error)) {
|
||||||
showMessage(error.message, "icon-warning-sign");
|
showMessage(error.message, "icon-warning-sign");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
notifications.onOfflineChanged = function(isOffline) {
|
notifications.onOfflineChanged = function(isOffline) {
|
||||||
if(isOffline === true) {
|
if(isOffline === true) {
|
||||||
showMessage("You are offline.", "icon-exclamation-sign msg-offline", {
|
showMessage("You are offline.", "icon-exclamation-sign msg-offline", {
|
||||||
sticky : true,
|
sticky: true,
|
||||||
close : function() {
|
close: function() {
|
||||||
showMessage("You are back online!", "icon-signal");
|
showMessage("You are back online!", "icon-signal");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
} else {
|
}
|
||||||
$(".msg-offline").parents(".jGrowl-notification").trigger(
|
else {
|
||||||
'jGrowl.beforeClose');
|
$(".msg-offline").parents(".jGrowl-notification").trigger('jGrowl.beforeClose');
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
notifications.onSyncImportSuccess = function(fileDescList, provider) {
|
notifications.onSyncImportSuccess = function(fileDescList, provider) {
|
||||||
if(!fileDescList) {
|
if(!fileDescList) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var titles = _.map(fileDescList, function(fileDesc) {
|
var titles = _.map(fileDescList, function(fileDesc) {
|
||||||
return fileDesc.title;
|
return fileDesc.title;
|
||||||
}).join(", ");
|
}).join(", ");
|
||||||
showMessage(titles + ' imported successfully from ' + provider.providerName + '.');
|
showMessage(titles + ' imported successfully from ' + provider.providerName + '.');
|
||||||
};
|
};
|
||||||
|
|
||||||
notifications.onSyncExportSuccess = function(fileDesc, syncAttributes) {
|
notifications.onSyncExportSuccess = function(fileDesc, syncAttributes) {
|
||||||
showMessage('"' + fileDesc.title + '" will now be synchronized on ' + syncAttributes.provider.providerName + '.');
|
showMessage('"' + fileDesc.title + '" will now be synchronized on ' + syncAttributes.provider.providerName + '.');
|
||||||
};
|
};
|
||||||
|
|
||||||
notifications.onSyncRemoved = function(fileDesc, syncAttributes) {
|
notifications.onSyncRemoved = function(fileDesc, syncAttributes) {
|
||||||
showMessage(syncAttributes.provider.providerName + " synchronized location has been removed.");
|
showMessage(syncAttributes.provider.providerName + " synchronized location has been removed.");
|
||||||
};
|
};
|
||||||
|
|
||||||
notifications.onPublishSuccess = function(fileDesc) {
|
notifications.onPublishSuccess = function(fileDesc) {
|
||||||
showMessage('"' + fileDesc.title + '" successfully published.');
|
showMessage('"' + fileDesc.title + '" successfully published.');
|
||||||
};
|
};
|
||||||
|
|
||||||
notifications.onNewPublishSuccess = function(fileDesc, publishIndex, publishAttributes) {
|
notifications.onNewPublishSuccess = function(fileDesc, publishIndex, publishAttributes) {
|
||||||
showMessage('"' + fileDesc.title + '" is now published on ' + publishAttributes.provider.providerName + '.');
|
showMessage('"' + fileDesc.title + '" is now published on ' + publishAttributes.provider.providerName + '.');
|
||||||
};
|
};
|
||||||
|
|
||||||
notifications.onPublishRemoved = function(fileDesc, publishAttributes) {
|
notifications.onPublishRemoved = function(fileDesc, publishAttributes) {
|
||||||
showMessage(publishAttributes.provider.providerName + " publish location has been removed.");
|
showMessage(publishAttributes.provider.providerName + " publish location has been removed.");
|
||||||
};
|
};
|
||||||
|
|
||||||
return notifications;
|
return notifications;
|
||||||
});
|
});
|
@ -1,199 +1,202 @@
|
|||||||
define([
|
define([
|
||||||
"jquery",
|
"jquery",
|
||||||
"underscore",
|
"underscore",
|
||||||
"lib/css_browser_selector"
|
"lib/css_browser_selector"
|
||||||
], function($, _) {
|
], function($, _) {
|
||||||
|
|
||||||
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("")
|
|
||||||
};
|
|
||||||
|
|
||||||
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 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);
|
|
||||||
|
|
||||||
// -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) {
|
var scrollLink = {
|
||||||
layoutConfig.onresize = buildSections;
|
extensionId: "scrollLink",
|
||||||
};
|
extensionName: "Scroll Link",
|
||||||
|
optional: true,
|
||||||
scrollLink.onLayoutCreated = function() {
|
settingsBloc: [
|
||||||
$("#wmd-preview").scroll(function() {
|
'<p>Binds together editor and preview scrollbars.</p>',
|
||||||
isScrollPreview = true;
|
'<blockquote class="muted"><b>NOTE:</b> ',
|
||||||
runScrollLink();
|
' 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.',
|
||||||
$("#wmd-input").scroll(function() {
|
'</bloquote>'
|
||||||
isScrollPreview = false;
|
].join("")
|
||||||
runScrollLink();
|
};
|
||||||
});
|
|
||||||
};
|
var mdSectionList = [];
|
||||||
|
var htmlSectionList = [];
|
||||||
scrollLink.onEditorConfigure = function(editor) {
|
function pxToFloat(px) {
|
||||||
skipScrollLink = true;
|
return parseFloat(px.substring(0, px.length - 2));
|
||||||
lastPreviewScrollTop = 0;
|
}
|
||||||
editor.hooks.chain("onPreviewRefresh", function() {
|
var buildSections = _.debounce(function() {
|
||||||
skipScrollLink = true;
|
|
||||||
});
|
// Try to find Markdown sections by looking for titles
|
||||||
};
|
var editorElt = $("#wmd-input");
|
||||||
|
mdSectionList = [];
|
||||||
scrollLink.onPreviewFinished = function() {
|
// This textarea is used to measure sections height
|
||||||
// MathJax may have change the scrolling position. Restore it.
|
var textareaElt = $("#md-section-helper");
|
||||||
if(lastPreviewScrollTop >= 0) {
|
// It has to be the same width than wmd-input
|
||||||
$("#wmd-preview").scrollTop(lastPreviewScrollTop);
|
textareaElt.width(editorElt.width());
|
||||||
}
|
// Consider wmd-input top padding
|
||||||
_.defer(function() {
|
var padding = pxToFloat(editorElt.css('padding-top'));
|
||||||
// Modify scroll position of the preview not the editor
|
var offset = 0, mdSectionOffset = 0;
|
||||||
lastEditorScrollTop = -9;
|
function addMdSection(sectionText) {
|
||||||
buildSections();
|
var sectionHeight = padding;
|
||||||
// Preview may change if images are loading
|
if(sectionText !== undefined) {
|
||||||
$("#wmd-preview img").load(function() {
|
textareaElt.val(sectionText);
|
||||||
lastEditorScrollTop = -9;
|
sectionHeight += textareaElt.prop('scrollHeight');
|
||||||
buildSections();
|
}
|
||||||
});
|
var newSectionOffset = mdSectionOffset + sectionHeight;
|
||||||
});
|
mdSectionList.push({
|
||||||
};
|
startOffset: mdSectionOffset,
|
||||||
|
endOffset: newSectionOffset,
|
||||||
return scrollLink;
|
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
|
||||||
|
});
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
|
||||||
|
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.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();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return scrollLink;
|
||||||
});
|
});
|
@ -3,116 +3,106 @@ define([
|
|||||||
"underscore",
|
"underscore",
|
||||||
"utils"
|
"utils"
|
||||||
], function($, _, utils) {
|
], function($, _, utils) {
|
||||||
|
|
||||||
var toc = {
|
var toc = {
|
||||||
extensionId: "toc",
|
extensionId: "toc",
|
||||||
extensionName: "Table of content",
|
extensionName: "Table of content",
|
||||||
optional: true,
|
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
|
// TOC element description
|
||||||
function TocElement(tagName, anchor, text) {
|
function TocElement(tagName, anchor, text) {
|
||||||
this.tagName = tagName;
|
this.tagName = tagName;
|
||||||
this.anchor = anchor;
|
this.anchor = anchor;
|
||||||
this.text = text;
|
this.text = text;
|
||||||
this.children = [];
|
this.children = [];
|
||||||
}
|
}
|
||||||
TocElement.prototype.childrenToString = function() {
|
TocElement.prototype.childrenToString = function() {
|
||||||
if(this.children.length === 0) {
|
if(this.children.length === 0) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
var result = "<ul>";
|
var result = "<ul>";
|
||||||
_.each(this.children, function(child) {
|
_.each(this.children, function(child) {
|
||||||
result += child.toString();
|
result += child.toString();
|
||||||
});
|
});
|
||||||
result += "</ul>";
|
result += "</ul>";
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
TocElement.prototype.toString = function() {
|
TocElement.prototype.toString = function() {
|
||||||
var result = "<li>";
|
var result = "<li>";
|
||||||
if(this.anchor && this.text) {
|
if(this.anchor && this.text) {
|
||||||
result += '<a href="#' + this.anchor + '">' + this.text + '</a>';
|
result += '<a href="#' + this.anchor + '">' + this.text + '</a>';
|
||||||
}
|
}
|
||||||
result += this.childrenToString() + "</li>";
|
result += this.childrenToString() + "</li>";
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Transform flat list of TocElement into a tree
|
// Transform flat list of TocElement into a tree
|
||||||
function groupTags(array, level) {
|
function groupTags(array, level) {
|
||||||
level = level || 1;
|
level = level || 1;
|
||||||
var tagName = "H" + level;
|
var tagName = "H" + level;
|
||||||
var result = [];
|
var result = [];
|
||||||
|
|
||||||
var currentElement = undefined;
|
var currentElement = undefined;
|
||||||
function pushCurrentElement() {
|
function pushCurrentElement() {
|
||||||
if(currentElement !== undefined) {
|
if(currentElement !== undefined) {
|
||||||
if(currentElement.children.length > 0) {
|
if(currentElement.children.length > 0) {
|
||||||
currentElement.children = groupTags(currentElement.children, level + 1);
|
currentElement.children = groupTags(currentElement.children, level + 1);
|
||||||
}
|
}
|
||||||
result.push(currentElement);
|
result.push(currentElement);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_.each(array, function(element, index) {
|
_.each(array, function(element, index) {
|
||||||
if(element.tagName != tagName) {
|
if(element.tagName != tagName) {
|
||||||
if(currentElement === undefined) {
|
if(currentElement === undefined) {
|
||||||
currentElement = new TocElement();
|
currentElement = new TocElement();
|
||||||
}
|
}
|
||||||
currentElement.children.push(element);
|
currentElement.children.push(element);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
pushCurrentElement();
|
pushCurrentElement();
|
||||||
currentElement = element;
|
currentElement = element;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
pushCurrentElement();
|
pushCurrentElement();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build the TOC
|
// Build the TOC
|
||||||
function buildToc() {
|
function buildToc() {
|
||||||
var anchorList = {};
|
var anchorList = {};
|
||||||
function createAnchor(element) {
|
function createAnchor(element) {
|
||||||
var id = element.prop("id") || utils.slugify(element.text());
|
var id = element.prop("id") || utils.slugify(element.text());
|
||||||
var anchor = id;
|
var anchor = id;
|
||||||
var index = 0;
|
var index = 0;
|
||||||
while(_.has(anchorList, anchor)) {
|
while (_.has(anchorList, anchor)) {
|
||||||
anchor = id + "-" + (++index);
|
anchor = id + "-" + (++index);
|
||||||
}
|
}
|
||||||
anchorList[anchor] = true;
|
anchorList[anchor] = true;
|
||||||
// Update the id of the element
|
// Update the id of the element
|
||||||
element.prop("id", anchor);
|
element.prop("id", anchor);
|
||||||
return anchor;
|
return anchor;
|
||||||
}
|
}
|
||||||
|
|
||||||
var elementList = [];
|
var elementList = [];
|
||||||
$("#wmd-preview > h1," +
|
$("#wmd-preview > h1," + "#wmd-preview > h2," + "#wmd-preview > h3," + "#wmd-preview > h4," + "#wmd-preview > h5," + "#wmd-preview > h6").each(function() {
|
||||||
"#wmd-preview > h2," +
|
elementList.push(new TocElement($(this).prop("tagName"), createAnchor($(this)), $(this).text()));
|
||||||
"#wmd-preview > h3," +
|
});
|
||||||
"#wmd-preview > h4," +
|
elementList = groupTags(elementList);
|
||||||
"#wmd-preview > h5," +
|
return '<div class="toc"><ul>' + elementList.toString() + '</ul></div>';
|
||||||
"#wmd-preview > h6").each(function() {
|
}
|
||||||
elementList.push(new TocElement(
|
|
||||||
$(this).prop("tagName"),
|
toc.onEditorConfigure = function(editor) {
|
||||||
createAnchor($(this)),
|
// Run TOC generation when conversion is finished directly on HTML
|
||||||
$(this).text()
|
editor.hooks.chain("onPreviewRefresh", function() {
|
||||||
));
|
var toc = buildToc();
|
||||||
});
|
var html = $("#wmd-preview").html();
|
||||||
elementList = groupTags(elementList);
|
html = html.replace(/<p>\[TOC\]<\/p>/g, toc);
|
||||||
return '<div class="toc"><ul>' + elementList.toString() + '</ul></div>';
|
$("#wmd-preview").html(html);
|
||||||
}
|
});
|
||||||
|
};
|
||||||
toc.onEditorConfigure = function(editor) {
|
|
||||||
// Run TOC generation when conversion is finished directly on HTML
|
return toc;
|
||||||
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;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,24 +1,25 @@
|
|||||||
define([
|
define([
|
||||||
"jquery",
|
"jquery",
|
||||||
"underscore"
|
"underscore"
|
||||||
], function($, _) {
|
], function($, _) {
|
||||||
|
|
||||||
var workingIndicator = {
|
var workingIndicator = {
|
||||||
extensionId: "workingIndicator",
|
extensionId: "workingIndicator",
|
||||||
extensionName: "Working indicator",
|
extensionName: "Working indicator",
|
||||||
settingsBloc: '<p>Displays an animated image when a network operation is running.</p>'
|
settingsBloc: '<p>Displays an animated image when a network operation is running.</p>'
|
||||||
};
|
};
|
||||||
|
|
||||||
workingIndicator.onAsyncRunning = function(isRunning) {
|
workingIndicator.onAsyncRunning = function(isRunning) {
|
||||||
if (isRunning === false) {
|
if(isRunning === false) {
|
||||||
$(".working-indicator").removeClass("show");
|
$(".working-indicator").removeClass("show");
|
||||||
$("body").removeClass("working");
|
$("body").removeClass("working");
|
||||||
} else {
|
}
|
||||||
$(".working-indicator").addClass("show");
|
else {
|
||||||
$("body").addClass("working");
|
$(".working-indicator").addClass("show");
|
||||||
}
|
$("body").addClass("working");
|
||||||
};
|
}
|
||||||
|
};
|
||||||
return workingIndicator;
|
|
||||||
|
return workingIndicator;
|
||||||
|
|
||||||
});
|
});
|
@ -8,347 +8,316 @@ define([
|
|||||||
"file-system",
|
"file-system",
|
||||||
"lib/text!../WELCOME.md"
|
"lib/text!../WELCOME.md"
|
||||||
], function($, _, core, utils, settings, extensionMgr, fileSystem, welcomeContent) {
|
], function($, _, core, utils, settings, extensionMgr, fileSystem, welcomeContent) {
|
||||||
|
|
||||||
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);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Load file descriptors from localStorage
|
var fileMgr = {};
|
||||||
_.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;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 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(fileMgr.isCurrentFile(fileDesc) === false) {
|
|
||||||
fileMgr.setCurrentFile(fileDesc);
|
|
||||||
|
|
||||||
// Notify extensions
|
|
||||||
extensionMgr.onFileSelected(fileDesc);
|
|
||||||
|
|
||||||
// Hide the viewer pencil button
|
// Defines a file descriptor in the file system (fileDesc objects)
|
||||||
if(fileDesc.fileIndex == TEMPORARY_FILE_INDEX) {
|
function FileDescriptor(fileIndex, title, syncLocations, publishLocations) {
|
||||||
$(".action-edit-document").removeClass("hide");
|
this.fileIndex = fileIndex;
|
||||||
}
|
this._title = title;
|
||||||
else {
|
this.__defineGetter__("title", function() {
|
||||||
$(".action-edit-document").addClass("hide");
|
return this._title;
|
||||||
}
|
});
|
||||||
}
|
this.__defineSetter__("title", function(title) {
|
||||||
|
this._title = title;
|
||||||
// Recreate the editor
|
localStorage[this.fileIndex + ".title"] = title;
|
||||||
$("#wmd-input").val(fileDesc.getContent());
|
extensionMgr.onTitleChanged(this);
|
||||||
core.createEditor(function() {
|
});
|
||||||
// Callback to save content when textarea changes
|
this.__defineGetter__("content", function() {
|
||||||
fileMgr.saveFile();
|
return localStorage[this.fileIndex + ".content"];
|
||||||
});
|
});
|
||||||
};
|
this.__defineSetter__("content", function(content) {
|
||||||
|
localStorage[this.fileIndex + ".content"] = content;
|
||||||
fileMgr.createFile = function(title, content, syncLocations, isTemporary) {
|
extensionMgr.onContentChanged(this);
|
||||||
content = content !== undefined ? content : settings.defaultContent;
|
});
|
||||||
if (!title) {
|
this.syncLocations = syncLocations || {};
|
||||||
// Create a file title
|
this.publishLocations = publishLocations || {};
|
||||||
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));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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"] = ";";
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
};
|
|
||||||
|
|
||||||
fileMgr.deleteFile = function(fileDesc) {
|
// Load file descriptors from localStorage
|
||||||
fileDesc = fileDesc || fileMgr.getCurrentFile();
|
_.chain(localStorage["file.list"].split(";")).compact().each(function(fileIndex) {
|
||||||
if(fileMgr.isCurrentFile(fileDesc) === true) {
|
fileSystem[fileIndex] = new FileDescriptor(fileIndex, localStorage[fileIndex + ".title"]);
|
||||||
// Unset the current fileDesc
|
});
|
||||||
fileMgr.setCurrentFile();
|
|
||||||
// Refresh the editor with an other file
|
|
||||||
fileMgr.selectFile();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove synchronized locations
|
// Defines the current file
|
||||||
_.each(fileDesc.syncLocations, function(syncAttributes) {
|
var currentFile = undefined;
|
||||||
fileMgr.removeSync(syncAttributes, true);
|
fileMgr.getCurrentFile = function() {
|
||||||
});
|
return currentFile;
|
||||||
|
};
|
||||||
// Remove publish locations
|
fileMgr.isCurrentFile = function(fileDesc) {
|
||||||
_.each(fileDesc.publishLocations, function(publishAttributes) {
|
return fileDesc === currentFile;
|
||||||
fileMgr.removePublish(publishAttributes, true);
|
};
|
||||||
});
|
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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Remove the index from the file list
|
// Caution: this function recreates the editor (reset undo operations)
|
||||||
var fileIndex = fileDesc.fileIndex;
|
fileMgr.selectFile = function(fileDesc) {
|
||||||
localStorage["file.list"] = localStorage["file.list"].replace(";"
|
fileDesc = fileDesc || fileMgr.getCurrentFile();
|
||||||
+ fileIndex + ";", ";");
|
|
||||||
|
|
||||||
localStorage.removeItem(fileIndex + ".title");
|
|
||||||
localStorage.removeItem(fileIndex + ".content");
|
|
||||||
localStorage.removeItem(fileIndex + ".sync");
|
|
||||||
localStorage.removeItem(fileIndex + ".publish");
|
|
||||||
|
|
||||||
fileSystem.removeItem(fileIndex);
|
|
||||||
extensionMgr.onFileDeleted(fileDesc);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Save current file in localStorage
|
if(fileDesc === undefined) {
|
||||||
fileMgr.saveFile = function() {
|
var fileSystemSize = _.size(fileSystem);
|
||||||
var content = $("#wmd-input").val();
|
// If fileSystem empty create one file
|
||||||
var fileDesc = fileMgr.getCurrentFile();
|
if(fileSystemSize === 0) {
|
||||||
fileDesc.setContent(content);
|
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];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Add a synchronized location to a file
|
if(fileMgr.isCurrentFile(fileDesc) === false) {
|
||||||
fileMgr.addSync = function(fileDesc, syncAttributes) {
|
fileMgr.setCurrentFile(fileDesc);
|
||||||
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);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 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];
|
|
||||||
};
|
|
||||||
|
|
||||||
// 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
|
// Notify extensions
|
||||||
fileMgr.addPublish = function(fileDesc, publishAttributes) {
|
extensionMgr.onFileSelected(fileDesc);
|
||||||
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);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// 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() {
|
|
||||||
|
|
||||||
fileMgr.selectFile();
|
|
||||||
|
|
||||||
$(".action-create-file").click(function() {
|
// Hide the viewer pencil button
|
||||||
var fileDesc = fileMgr.createFile();
|
if(fileDesc.fileIndex == TEMPORARY_FILE_INDEX) {
|
||||||
fileMgr.selectFile(fileDesc);
|
$(".action-edit-document").removeClass("hide");
|
||||||
var wmdInput = $("#wmd-input").focus().get(0);
|
}
|
||||||
if(wmdInput.setSelectionRange) {
|
else {
|
||||||
wmdInput.setSelectionRange(0, 0);
|
$(".action-edit-document").addClass("hide");
|
||||||
}
|
}
|
||||||
$("#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);
|
// Recreate the editor
|
||||||
return fileMgr;
|
$("#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++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 + ";";
|
||||||
|
}, ";");
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
// 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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 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");
|
||||||
|
|
||||||
|
fileSystem.removeItem(fileIndex);
|
||||||
|
extensionMgr.onFileDeleted(fileDesc);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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 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;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get the file descriptor associated to a publishIndex
|
||||||
|
fileMgr.getFileFromPublishIndex = function(publishIndex) {
|
||||||
|
return _.find(fileSystem, function(fileDesc) {
|
||||||
|
return _.has(fileDesc.publishLocations, publishIndex);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
core.onReady(function() {
|
||||||
|
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
extensionMgr.onFileMgrCreated(fileMgr);
|
||||||
|
return fileMgr;
|
||||||
});
|
});
|
||||||
|
@ -6,280 +6,275 @@ define([
|
|||||||
"file-manager",
|
"file-manager",
|
||||||
"google-helper"
|
"google-helper"
|
||||||
], function(_, core, utils, extensionMgr, fileMgr, googleHelper) {
|
], function(_, core, utils, extensionMgr, fileMgr, googleHelper) {
|
||||||
|
|
||||||
var PROVIDER_GDRIVE = "gdrive";
|
|
||||||
|
|
||||||
var gdriveProvider = {
|
|
||||||
providerId: PROVIDER_GDRIVE,
|
|
||||||
providerName: "Google Drive",
|
|
||||||
defaultPublishFormat: "template",
|
|
||||||
exportPreferencesInputIds: ["gdrive-parentid"]
|
|
||||||
};
|
|
||||||
|
|
||||||
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 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() {
|
var PROVIDER_GDRIVE = "gdrive";
|
||||||
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.exportManual = function(event, title, content, callback) {
|
var gdriveProvider = {
|
||||||
var id = utils.getInputTextValue("#input-sync-manual-gdrive-id", event);
|
providerId: PROVIDER_GDRIVE,
|
||||||
if(!id) {
|
providerName: "Google Drive",
|
||||||
return;
|
defaultPublishFormat: "template",
|
||||||
}
|
exportPreferencesInputIds: [
|
||||||
// Check that file is not synchronized with another an existing document
|
"gdrive-parentid"
|
||||||
var syncIndex = createSyncIndex(id);
|
]
|
||||||
var fileDesc = fileMgr.getFileFromSyncIndex(syncIndex);
|
};
|
||||||
if(fileDesc !== undefined) {
|
|
||||||
extensionMgr.onError('File ID is already synchronized with "' + fileDesc.title + '"');
|
function createSyncIndex(id) {
|
||||||
callback(true);
|
return "sync." + PROVIDER_GDRIVE + "." + id;
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
googleHelper.upload(id, undefined, title, content, undefined, function(error, result) {
|
function createSyncAttributes(id, etag, content, title) {
|
||||||
if (error) {
|
var syncAttributes = {};
|
||||||
callback(error);
|
syncAttributes.provider = gdriveProvider;
|
||||||
return;
|
syncAttributes.id = id;
|
||||||
}
|
syncAttributes.etag = etag;
|
||||||
var syncAttributes = createSyncAttributes(result.id, result.etag, content, title);
|
syncAttributes.contentCRC = utils.crc32(content);
|
||||||
callback(undefined, syncAttributes);
|
syncAttributes.titleCRC = utils.crc32(title);
|
||||||
});
|
syncAttributes.syncIndex = createSyncIndex(id);
|
||||||
};
|
utils.storeAttributes(syncAttributes);
|
||||||
|
return syncAttributes;
|
||||||
gdriveProvider.syncUp = function(uploadContent, uploadContentCRC, uploadTitle, uploadTitleCRC, syncAttributes, callback) {
|
}
|
||||||
var syncContentCRC = syncAttributes.contentCRC;
|
|
||||||
var syncTitleCRC = syncAttributes.titleCRC;
|
function importFilesFromIds(ids) {
|
||||||
// Skip if CRC has not changed
|
googleHelper.downloadMetadata(ids, function(error, result) {
|
||||||
if(uploadContentCRC == syncContentCRC && uploadTitleCRC == syncTitleCRC) {
|
if(error) {
|
||||||
callback(undefined, false);
|
return;
|
||||||
return;
|
}
|
||||||
}
|
googleHelper.downloadContent(result, function(error, result) {
|
||||||
googleHelper.upload(syncAttributes.id, undefined, uploadTitle, uploadContent, syncAttributes.etag, function(error, result) {
|
if(error) {
|
||||||
if(error) {
|
return;
|
||||||
callback(error, true);
|
}
|
||||||
return;
|
var fileDescList = [];
|
||||||
}
|
_.each(result, function(file) {
|
||||||
syncAttributes.etag = result.etag;
|
var syncAttributes = createSyncAttributes(file.id, file.etag, file.content, file.title);
|
||||||
syncAttributes.contentCRC = uploadContentCRC;
|
var syncLocations = {};
|
||||||
syncAttributes.titleCRC = uploadTitleCRC;
|
syncLocations[syncAttributes.syncIndex] = syncAttributes;
|
||||||
callback(undefined, true);
|
var fileDesc = fileMgr.createFile(file.title, file.content, syncLocations);
|
||||||
});
|
fileMgr.selectFile(fileDesc);
|
||||||
};
|
fileDescList.push(fileDesc);
|
||||||
|
});
|
||||||
gdriveProvider.syncDown = function(callback) {
|
extensionMgr.onSyncImportSuccess(fileDescList, gdriveProvider);
|
||||||
var lastChangeId = parseInt(localStorage[PROVIDER_GDRIVE + ".lastChangeId"]);
|
});
|
||||||
googleHelper.checkChanges(lastChangeId, function(error, changes, newChangeId) {
|
});
|
||||||
if (error) {
|
}
|
||||||
callback(error);
|
;
|
||||||
return;
|
|
||||||
}
|
gdriveProvider.importFiles = function() {
|
||||||
var interestingChanges = [];
|
googleHelper.picker(function(error, ids) {
|
||||||
_.each(changes, function(change) {
|
if(error || ids.length === 0) {
|
||||||
var syncIndex = createSyncIndex(change.fileId);
|
return;
|
||||||
var syncAttributes = fileMgr.getSyncAttributes(syncIndex);
|
}
|
||||||
if(syncAttributes === undefined) {
|
var importIds = [];
|
||||||
return;
|
_.each(ids, function(id) {
|
||||||
}
|
var syncIndex = createSyncIndex(id);
|
||||||
// Store syncAttributes to avoid 2 times searching
|
var fileDesc = fileMgr.getFileFromSyncIndex(syncIndex);
|
||||||
change.syncAttributes = syncAttributes;
|
if(fileDesc !== undefined) {
|
||||||
// Delete
|
extensionMgr.onError('"' + fileDesc.title + '" was already imported');
|
||||||
if(change.deleted === true) {
|
return;
|
||||||
interestingChanges.push(change);
|
}
|
||||||
return;
|
importIds.push(id);
|
||||||
}
|
});
|
||||||
// Modify
|
importFilesFromIds(importIds);
|
||||||
if(syncAttributes.etag != change.file.etag) {
|
});
|
||||||
interestingChanges.push(change);
|
};
|
||||||
}
|
|
||||||
});
|
gdriveProvider.exportFile = function(event, title, content, callback) {
|
||||||
googleHelper.downloadContent(interestingChanges, function(error, changes) {
|
var parentId = utils.getInputTextValue("#input-sync-export-gdrive-parentid");
|
||||||
if (error) {
|
googleHelper.upload(undefined, parentId, title, content, undefined, function(error, result) {
|
||||||
callback(error);
|
if(error) {
|
||||||
return;
|
callback(error);
|
||||||
}
|
return;
|
||||||
_.each(changes, function(change) {
|
}
|
||||||
var syncAttributes = change.syncAttributes;
|
var syncAttributes = createSyncAttributes(result.id, result.etag, content, title);
|
||||||
var syncIndex = syncAttributes.syncIndex;
|
callback(undefined, syncAttributes);
|
||||||
var fileDesc = fileMgr.getFileFromSyncIndex(syncIndex);
|
});
|
||||||
// No file corresponding (file may have been deleted locally)
|
};
|
||||||
if(fileDesc === undefined) {
|
|
||||||
return;
|
gdriveProvider.exportManual = function(event, title, content, callback) {
|
||||||
}
|
var id = utils.getInputTextValue("#input-sync-manual-gdrive-id", event);
|
||||||
var localTitle = fileDesc.title;
|
if(!id) {
|
||||||
// File deleted
|
return;
|
||||||
if (change.deleted === true) {
|
}
|
||||||
extensionMgr.onError('"' + localTitle + '" has been removed from Google Drive.');
|
// Check that file is not synchronized with another an existing document
|
||||||
fileMgr.removeSync(syncAttributes);
|
var syncIndex = createSyncIndex(id);
|
||||||
return;
|
var fileDesc = fileMgr.getFileFromSyncIndex(syncIndex);
|
||||||
}
|
if(fileDesc !== undefined) {
|
||||||
var localTitleChanged = syncAttributes.titleCRC != utils.crc32(localTitle);
|
extensionMgr.onError('File ID is already synchronized with "' + fileDesc.title + '"');
|
||||||
var localContent = fileDesc.getContent();
|
callback(true);
|
||||||
var localContentChanged = syncAttributes.contentCRC != utils.crc32(localContent);
|
return;
|
||||||
var file = change.file;
|
}
|
||||||
|
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.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 remoteTitleCRC = utils.crc32(file.title);
|
||||||
var remoteTitleChanged = syncAttributes.titleCRC != remoteTitleCRC;
|
var remoteTitleChanged = syncAttributes.titleCRC != remoteTitleCRC;
|
||||||
var fileTitleChanged = localTitle != file.title;
|
var fileTitleChanged = localTitle != file.title;
|
||||||
var remoteContentCRC = utils.crc32(file.content);
|
var remoteContentCRC = utils.crc32(file.content);
|
||||||
var remoteContentChanged = syncAttributes.contentCRC != remoteContentCRC;
|
var remoteContentChanged = syncAttributes.contentCRC != remoteContentCRC;
|
||||||
var fileContentChanged = localContent != file.content;
|
var fileContentChanged = localContent != file.content;
|
||||||
// Conflict detection
|
// Conflict detection
|
||||||
if ((fileTitleChanged === true && localTitleChanged === true && remoteTitleChanged === true)
|
if((fileTitleChanged === true && localTitleChanged === true && remoteTitleChanged === true) || (fileContentChanged === true && localContentChanged === true && remoteContentChanged === true)) {
|
||||||
|| (fileContentChanged === true && localContentChanged === true && remoteContentChanged === true)) {
|
fileMgr.createFile(localTitle + " (backup)", localContent);
|
||||||
fileMgr.createFile(localTitle + " (backup)", localContent);
|
extensionMgr.onMessage('Conflict detected on "' + localTitle + '". A backup has been created locally.');
|
||||||
extensionMgr.onMessage('Conflict detected on "' + localTitle + '". A backup has been created locally.');
|
}
|
||||||
}
|
// If file title changed
|
||||||
// If file title changed
|
if(fileTitleChanged && remoteTitleChanged === true) {
|
||||||
if(fileTitleChanged && remoteTitleChanged === true) {
|
fileDesc.title = file.title;
|
||||||
fileDesc.setTitle(file.title);
|
extensionMgr.onMessage('"' + localTitle + '" has been renamed to "' + file.title + '" on Google Drive.');
|
||||||
extensionMgr.onMessage('"' + localTitle + '" has been renamed to "' + file.title + '" on Google Drive.');
|
}
|
||||||
}
|
// If file content changed
|
||||||
// If file content changed
|
if(fileContentChanged && remoteContentChanged === true) {
|
||||||
if(fileContentChanged && remoteContentChanged === true) {
|
fileDesc.content = file.content;
|
||||||
fileDesc.setContent(file.content);
|
extensionMgr.onMessage('"' + file.title + '" has been updated from Google Drive.');
|
||||||
extensionMgr.onMessage('"' + file.title + '" has been updated from Google Drive.');
|
if(fileMgr.isCurrentFile(fileDesc)) {
|
||||||
if(fileMgr.isCurrentFile(fileDesc)) {
|
fileMgr.selectFile(); // Refresh editor
|
||||||
fileMgr.selectFile(); // Refresh editor
|
}
|
||||||
}
|
}
|
||||||
}
|
// Update syncAttributes
|
||||||
// Update syncAttributes
|
syncAttributes.etag = file.etag;
|
||||||
syncAttributes.etag = file.etag;
|
syncAttributes.contentCRC = remoteContentCRC;
|
||||||
syncAttributes.contentCRC = remoteContentCRC;
|
syncAttributes.titleCRC = remoteTitleCRC;
|
||||||
syncAttributes.titleCRC = remoteTitleCRC;
|
utils.storeAttributes(syncAttributes);
|
||||||
utils.storeAttributes(syncAttributes);
|
});
|
||||||
});
|
localStorage[PROVIDER_GDRIVE + ".lastChangeId"] = newChangeId;
|
||||||
localStorage[PROVIDER_GDRIVE + ".lastChangeId"] = newChangeId;
|
callback();
|
||||||
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) {
|
gdriveProvider.publish = function(publishAttributes, title, content, callback) {
|
||||||
var publishAttributes = {};
|
googleHelper.upload(publishAttributes.id, undefined, publishAttributes.fileName || title, content, undefined, function(error, result) {
|
||||||
publishAttributes.id = utils.getInputTextValue("#input-publish-gdrive-fileid");
|
if(error) {
|
||||||
publishAttributes.fileName = utils.getInputTextValue("#input-publish-gdrive-filename");
|
callback(error);
|
||||||
if(event.isPropagationStopped()) {
|
return;
|
||||||
return undefined;
|
}
|
||||||
}
|
publishAttributes.id = result.id;
|
||||||
return publishAttributes;
|
callback();
|
||||||
};
|
});
|
||||||
|
};
|
||||||
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;
|
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);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return gdriveProvider;
|
||||||
});
|
});
|
@ -2,42 +2,43 @@ define([
|
|||||||
"utils",
|
"utils",
|
||||||
"github-helper"
|
"github-helper"
|
||||||
], function(utils, githubHelper) {
|
], function(utils, githubHelper) {
|
||||||
|
|
||||||
var PROVIDER_GIST = "gist";
|
|
||||||
|
|
||||||
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.newPublishAttributes = function(event) {
|
var PROVIDER_GIST = "gist";
|
||||||
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);
|
|
||||||
};
|
|
||||||
|
|
||||||
return gistProvider;
|
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.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);
|
||||||
|
};
|
||||||
|
|
||||||
|
return gistProvider;
|
||||||
});
|
});
|
@ -6,247 +6,249 @@ define([
|
|||||||
"async-runner"
|
"async-runner"
|
||||||
], function($, core, utils, extensionMgr, asyncRunner) {
|
], function($, core, utils, extensionMgr, asyncRunner) {
|
||||||
|
|
||||||
var connected = undefined;
|
var connected = undefined;
|
||||||
var github = undefined;
|
var github = undefined;
|
||||||
|
|
||||||
var githubHelper = {};
|
var githubHelper = {};
|
||||||
|
|
||||||
// Try to connect github by downloading js file
|
// Try to connect github by downloading js file
|
||||||
function connect(task) {
|
function connect(task) {
|
||||||
task.onRun(function() {
|
task.onRun(function() {
|
||||||
if(core.isOffline === true) {
|
if(core.isOffline === true) {
|
||||||
connected = false;
|
connected = false;
|
||||||
task.error(new Error("Operation not available in offline mode.|stopPublish"));
|
task.error(new Error("Operation not available in offline mode.|stopPublish"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (connected === true) {
|
if(connected === true) {
|
||||||
task.chain();
|
task.chain();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url : "lib/github.js",
|
url: "lib/github.js",
|
||||||
dataType : "script", timeout : AJAX_TIMEOUT
|
dataType: "script",
|
||||||
}).done(function() {
|
timeout: AJAX_TIMEOUT
|
||||||
connected = true;
|
}).done(function() {
|
||||||
task.chain();
|
connected = true;
|
||||||
}).fail(function(jqXHR) {
|
task.chain();
|
||||||
var error = {
|
}).fail(function(jqXHR) {
|
||||||
error: jqXHR.status,
|
var error = {
|
||||||
message: jqXHR.statusText
|
error: jqXHR.status,
|
||||||
};
|
message: jqXHR.statusText
|
||||||
handleError(error, task);
|
};
|
||||||
});
|
handleError(error, task);
|
||||||
});
|
});
|
||||||
}
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Try to authenticate with Oauth
|
// Try to authenticate with Oauth
|
||||||
function authenticate(task) {
|
function authenticate(task) {
|
||||||
var authWindow = undefined;
|
var authWindow = undefined;
|
||||||
var intervalId = undefined;
|
var intervalId = undefined;
|
||||||
task.onRun(function() {
|
task.onRun(function() {
|
||||||
if (github !== undefined) {
|
if(github !== undefined) {
|
||||||
task.chain();
|
task.chain();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var token = localStorage["githubToken"];
|
var token = localStorage["githubToken"];
|
||||||
if(token !== undefined) {
|
if(token !== undefined) {
|
||||||
github = new Github({
|
github = new Github({
|
||||||
token: token,
|
token: token,
|
||||||
auth: "oauth"
|
auth: "oauth"
|
||||||
});
|
});
|
||||||
task.chain();
|
task.chain();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
extensionMgr.onMessage("Please make sure the Github authorization popup is not blocked by your browser.");
|
extensionMgr.onMessage("Please make sure the Github authorization popup is not blocked by your browser.");
|
||||||
var errorMsg = "Failed to retrieve a token from GitHub.";
|
var errorMsg = "Failed to retrieve a token from GitHub.";
|
||||||
// We add time for user to enter his credentials
|
// We add time for user to enter his credentials
|
||||||
task.timeout = ASYNC_TASK_LONG_TIMEOUT;
|
task.timeout = ASYNC_TASK_LONG_TIMEOUT;
|
||||||
var code = undefined;
|
var code = undefined;
|
||||||
function getCode() {
|
function getCode() {
|
||||||
localStorage.removeItem("githubCode");
|
localStorage.removeItem("githubCode");
|
||||||
authWindow = utils.popupWindow(
|
authWindow = utils.popupWindow('github-oauth-client.html?client_id=' + GITHUB_CLIENT_ID, 'stackedit-github-oauth', 960, 600);
|
||||||
'github-oauth-client.html?client_id=' + GITHUB_CLIENT_ID,
|
authWindow.focus();
|
||||||
'stackedit-github-oauth', 960, 600);
|
intervalId = setInterval(function() {
|
||||||
authWindow.focus();
|
if(authWindow.closed === true) {
|
||||||
intervalId = setInterval(function() {
|
clearInterval(intervalId);
|
||||||
if(authWindow.closed === true) {
|
authWindow = undefined;
|
||||||
clearInterval(intervalId);
|
intervalId = undefined;
|
||||||
authWindow = undefined;
|
code = localStorage["githubCode"];
|
||||||
intervalId = undefined;
|
if(code === undefined) {
|
||||||
code = localStorage["githubCode"];
|
task.error(new Error(errorMsg));
|
||||||
if(code === undefined) {
|
return;
|
||||||
task.error(new Error(errorMsg));
|
}
|
||||||
return;
|
localStorage.removeItem("githubCode");
|
||||||
}
|
task.chain(getToken);
|
||||||
localStorage.removeItem("githubCode");
|
}
|
||||||
task.chain(getToken);
|
}, 500);
|
||||||
}
|
}
|
||||||
}, 500);
|
function getToken() {
|
||||||
}
|
$.getJSON(GATEKEEPER_URL + "authenticate/" + code, function(data) {
|
||||||
function getToken() {
|
if(data.token !== undefined) {
|
||||||
$.getJSON(GATEKEEPER_URL + "authenticate/" + code, function(data) {
|
token = data.token;
|
||||||
if(data.token !== undefined) {
|
localStorage["githubToken"] = token;
|
||||||
token = data.token;
|
github = new Github({
|
||||||
localStorage["githubToken"] = token;
|
token: token,
|
||||||
github = new Github({
|
auth: "oauth"
|
||||||
token: token,
|
});
|
||||||
auth: "oauth"
|
task.chain();
|
||||||
});
|
}
|
||||||
task.chain();
|
else {
|
||||||
}
|
task.error(new Error(errorMsg));
|
||||||
else {
|
}
|
||||||
task.error(new Error(errorMsg));
|
});
|
||||||
}
|
}
|
||||||
});
|
task.chain(getCode);
|
||||||
}
|
});
|
||||||
task.chain(getCode);
|
task.onError(function() {
|
||||||
});
|
if(intervalId !== undefined) {
|
||||||
task.onError(function() {
|
clearInterval(intervalId);
|
||||||
if(intervalId !== undefined) {
|
}
|
||||||
clearInterval(intervalId);
|
if(authWindow !== undefined) {
|
||||||
}
|
authWindow.close();
|
||||||
if(authWindow !== undefined) {
|
}
|
||||||
authWindow.close();
|
});
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
githubHelper.upload = function(reponame, branch, path, content, commitMsg, callback) {
|
githubHelper.upload = function(reponame, branch, path, content, commitMsg, callback) {
|
||||||
var task = asyncRunner.createTask();
|
var task = asyncRunner.createTask();
|
||||||
connect(task);
|
connect(task);
|
||||||
authenticate(task);
|
authenticate(task);
|
||||||
task.onRun(function() {
|
task.onRun(function() {
|
||||||
var userLogin = undefined;
|
var userLogin = undefined;
|
||||||
function getUserLogin() {
|
function getUserLogin() {
|
||||||
var user = github.getUser();
|
var user = github.getUser();
|
||||||
user.show(undefined, function(err, result) {
|
user.show(undefined, function(err, result) {
|
||||||
if(err) {
|
if(err) {
|
||||||
handleError(err, task);
|
handleError(err, task);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
userLogin = result.login;
|
userLogin = result.login;
|
||||||
task.chain(write);
|
task.chain(write);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
function write() {
|
function write() {
|
||||||
var repo = github.getRepo(userLogin, reponame);
|
var repo = github.getRepo(userLogin, reponame);
|
||||||
repo.write(branch, path, content, commitMsg, function(err) {
|
repo.write(branch, path, content, commitMsg, function(err) {
|
||||||
if(err) {
|
if(err) {
|
||||||
handleError(err, task);
|
handleError(err, task);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
task.chain();
|
task.chain();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
task.chain(getUserLogin);
|
task.chain(getUserLogin);
|
||||||
});
|
});
|
||||||
task.onSuccess(function() {
|
task.onSuccess(function() {
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
task.onError(function(error) {
|
task.onError(function(error) {
|
||||||
callback(error);
|
callback(error);
|
||||||
});
|
});
|
||||||
asyncRunner.addTask(task);
|
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);
|
|
||||||
};
|
|
||||||
|
|
||||||
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;
|
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);
|
||||||
|
};
|
||||||
|
|
||||||
|
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;
|
||||||
});
|
});
|
||||||
|
@ -3,31 +3,33 @@ define([
|
|||||||
"settings",
|
"settings",
|
||||||
"github-helper"
|
"github-helper"
|
||||||
], function(utils, settings, githubHelper) {
|
], function(utils, settings, githubHelper) {
|
||||||
|
|
||||||
var PROVIDER_GITHUB = "github";
|
|
||||||
|
|
||||||
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.newPublishAttributes = function(event) {
|
var PROVIDER_GITHUB = "github";
|
||||||
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;
|
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.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;
|
||||||
});
|
});
|
1027
js/google-helper.js
1027
js/google-helper.js
File diff suppressed because it is too large
Load Diff
455
js/publisher.js
455
js/publisher.js
@ -18,236 +18,229 @@ define([
|
|||||||
"wordpress-provider"
|
"wordpress-provider"
|
||||||
], function($, _, core, utils, settings, extensionMgr, fileSystem, fileMgr, sharing) {
|
], 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();
|
|
||||||
|
|
||||||
// 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
|
// Create a map with providerId: providerModule
|
||||||
publisher.applyTemplate = function(publishAttributes) {
|
var providerMap = _.chain(arguments).map(function(argument) {
|
||||||
var fileDesc = fileMgr.getCurrentFile();
|
return argument && argument.providerId && [
|
||||||
try {
|
argument.providerId,
|
||||||
return _.template(settings.template, {
|
argument
|
||||||
documentTitle: fileDesc.title,
|
];
|
||||||
documentMarkdown: $("#wmd-input").val(),
|
}).compact().object().value();
|
||||||
documentHTML: $("#wmd-preview").html(),
|
|
||||||
publishAttributes: publishAttributes
|
// Retrieve publish locations from localStorage
|
||||||
});
|
_.each(fileSystem, function(fileDesc) {
|
||||||
} catch(e) {
|
_.chain(localStorage[fileDesc.fileIndex + ".publish"].split(";")).compact().each(function(publishIndex) {
|
||||||
extensionMgr.onError(e);
|
var publishAttributes = JSON.parse(localStorage[publishIndex]);
|
||||||
throw e;
|
// Store publishIndex
|
||||||
}
|
publishAttributes.publishIndex = publishIndex;
|
||||||
};
|
// Replace provider ID by provider module in attributes
|
||||||
|
publishAttributes.provider = providerMap[publishAttributes.provider];
|
||||||
// Used to get content to publish
|
fileDesc.publishLocations[publishIndex] = publishAttributes;
|
||||||
function getPublishContent(publishAttributes) {
|
});
|
||||||
if(publishAttributes.format === undefined) {
|
});
|
||||||
publishAttributes.format = $("input:radio[name=radio-publish-format]:checked").prop("value");
|
|
||||||
}
|
// Apply template to the current document
|
||||||
if(publishAttributes.format == "markdown") {
|
publisher.applyTemplate = function(publishAttributes) {
|
||||||
return $("#wmd-input").val();
|
var fileDesc = fileMgr.getCurrentFile();
|
||||||
}
|
try {
|
||||||
else if(publishAttributes.format == "html") {
|
return _.template(settings.template, {
|
||||||
return $("#wmd-preview").html();
|
documentTitle: fileDesc.title,
|
||||||
}
|
documentMarkdown: $("#wmd-input").val(),
|
||||||
else {
|
documentHTML: $("#wmd-preview").html(),
|
||||||
return publisher.applyTemplate(publishAttributes);
|
publishAttributes: publishAttributes
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
catch (e) {
|
||||||
// Recursive function to publish a file on multiple locations
|
extensionMgr.onError(e);
|
||||||
var publishAttributesList = [];
|
throw e;
|
||||||
var publishTitle = undefined;
|
}
|
||||||
function publishLocation(callback, errorFlag) {
|
};
|
||||||
|
|
||||||
// No more publish location for this document
|
// Used to get content to publish
|
||||||
if (publishAttributesList.length === 0) {
|
function getPublishContent(publishAttributes) {
|
||||||
callback(errorFlag);
|
if(publishAttributes.format === undefined) {
|
||||||
return;
|
publishAttributes.format = $("input:radio[name=radio-publish-format]:checked").prop("value");
|
||||||
}
|
}
|
||||||
|
if(publishAttributes.format == "markdown") {
|
||||||
// Dequeue a synchronized location
|
return $("#wmd-input").val();
|
||||||
var publishAttributes = publishAttributesList.pop();
|
}
|
||||||
var content = getPublishContent(publishAttributes);
|
else if(publishAttributes.format == "html") {
|
||||||
|
return $("#wmd-preview").html();
|
||||||
// Call the provider
|
}
|
||||||
publishAttributes.provider.publish(publishAttributes, publishTitle, content, function(error) {
|
else {
|
||||||
if(error !== undefined) {
|
return publisher.applyTemplate(publishAttributes);
|
||||||
var errorMsg = error.toString();
|
}
|
||||||
if(errorMsg.indexOf("|removePublish") !== -1) {
|
}
|
||||||
fileMgr.removePublish(publishAttributes);
|
|
||||||
}
|
// Recursive function to publish a file on multiple locations
|
||||||
if(errorMsg.indexOf("|stopPublish") !== -1) {
|
var publishAttributesList = [];
|
||||||
callback(error);
|
var publishTitle = undefined;
|
||||||
return;
|
function publishLocation(callback, errorFlag) {
|
||||||
}
|
|
||||||
}
|
// No more publish location for this document
|
||||||
publishLocation(callback, errorFlag || error );
|
if(publishAttributesList.length === 0) {
|
||||||
});
|
callback(errorFlag);
|
||||||
}
|
return;
|
||||||
|
}
|
||||||
var publishRunning = false;
|
|
||||||
publisher.publish = function() {
|
// Dequeue a synchronized location
|
||||||
// If publish is running or offline
|
var publishAttributes = publishAttributesList.pop();
|
||||||
if(publishRunning === true || core.isOffline) {
|
var content = getPublishContent(publishAttributes);
|
||||||
return;
|
|
||||||
}
|
// Call the provider
|
||||||
|
publishAttributes.provider.publish(publishAttributes, publishTitle, content, function(error) {
|
||||||
publishRunning = true;
|
if(error !== undefined) {
|
||||||
extensionMgr.onPublishRunning(true);
|
var errorMsg = error.toString();
|
||||||
var fileDesc = fileMgr.getCurrentFile();
|
if(errorMsg.indexOf("|removePublish") !== -1) {
|
||||||
publishTitle = fileDesc.title;
|
fileMgr.removePublish(publishAttributes);
|
||||||
publishAttributesList = _.values(fileDesc.publishLocations);
|
}
|
||||||
publishLocation(function(errorFlag) {
|
if(errorMsg.indexOf("|stopPublish") !== -1) {
|
||||||
publishRunning = false;
|
callback(error);
|
||||||
extensionMgr.onPublishRunning(false);
|
return;
|
||||||
if(errorFlag === undefined) {
|
}
|
||||||
extensionMgr.onPublishSuccess(fileDesc);
|
}
|
||||||
}
|
publishLocation(callback, errorFlag || error);
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
// Generate a publishIndex associated to a file and store publishAttributes
|
var publishRunning = false;
|
||||||
function createPublishIndex(fileDesc, publishAttributes) {
|
publisher.publish = function() {
|
||||||
var publishIndex = undefined;
|
// If publish is running or offline
|
||||||
do {
|
if(publishRunning === true || core.isOffline) {
|
||||||
publishIndex = "publish." + utils.randomString();
|
return;
|
||||||
} while(_.has(localStorage, publishIndex));
|
}
|
||||||
publishAttributes.publishIndex = publishIndex;
|
|
||||||
utils.storeAttributes(publishAttributes);
|
publishRunning = true;
|
||||||
fileMgr.addPublish(fileDesc, publishAttributes);
|
extensionMgr.onPublishRunning(true);
|
||||||
}
|
var fileDesc = fileMgr.getCurrentFile();
|
||||||
|
publishTitle = fileDesc.title;
|
||||||
// Initialize the "New publication" dialog
|
publishAttributesList = _.values(fileDesc.publishLocations);
|
||||||
var newLocationProvider = undefined;
|
publishLocation(function(errorFlag) {
|
||||||
function initNewLocation(provider) {
|
publishRunning = false;
|
||||||
var defaultPublishFormat = provider.defaultPublishFormat || "markdown";
|
extensionMgr.onPublishRunning(false);
|
||||||
newLocationProvider = provider;
|
if(errorFlag === undefined) {
|
||||||
$(".publish-provider-name").text(provider.providerName);
|
extensionMgr.onPublishSuccess(fileDesc);
|
||||||
|
}
|
||||||
// Show/hide controls depending on provider
|
});
|
||||||
$('div[class*=" modal-publish-"]').hide().filter(".modal-publish-" + provider.providerId).show();
|
};
|
||||||
|
|
||||||
// Reset fields
|
// Generate a publishIndex associated to a file and store publishAttributes
|
||||||
utils.resetModalInputs();
|
function createPublishIndex(fileDesc, publishAttributes) {
|
||||||
$("input:radio[name=radio-publish-format][value=" + defaultPublishFormat + "]").prop("checked", true);
|
var publishIndex = undefined;
|
||||||
|
do {
|
||||||
// Load preferences
|
publishIndex = "publish." + utils.randomString();
|
||||||
var serializedPreferences = localStorage[provider.providerId + ".publishPreferences"];
|
} while (_.has(localStorage, publishIndex));
|
||||||
if(serializedPreferences) {
|
publishAttributes.publishIndex = publishIndex;
|
||||||
var publishPreferences = JSON.parse(serializedPreferences);
|
utils.storeAttributes(publishAttributes);
|
||||||
_.each(provider.publishPreferencesInputIds, function(inputId) {
|
fileMgr.addPublish(fileDesc, publishAttributes);
|
||||||
utils.setInputValue("#input-publish-" + inputId, publishPreferences[inputId]);
|
}
|
||||||
});
|
|
||||||
utils.setInputRadio("radio-publish-format", publishPreferences.format);
|
// Initialize the "New publication" dialog
|
||||||
}
|
var newLocationProvider = undefined;
|
||||||
|
function initNewLocation(provider) {
|
||||||
// Open dialog box
|
var defaultPublishFormat = provider.defaultPublishFormat || "markdown";
|
||||||
$("#modal-publish").modal();
|
newLocationProvider = provider;
|
||||||
}
|
$(".publish-provider-name").text(provider.providerName);
|
||||||
|
|
||||||
// Add a new publish location to a local document
|
// Show/hide controls depending on provider
|
||||||
function performNewLocation(event) {
|
$('div[class*=" modal-publish-"]').hide().filter(".modal-publish-" + provider.providerId).show();
|
||||||
var provider = newLocationProvider;
|
|
||||||
var publishAttributes = provider.newPublishAttributes(event);
|
// Reset fields
|
||||||
if(publishAttributes === undefined) {
|
utils.resetModalInputs();
|
||||||
return;
|
$("input:radio[name=radio-publish-format][value=" + defaultPublishFormat + "]").prop("checked", true);
|
||||||
}
|
|
||||||
|
// Load preferences
|
||||||
// Perform provider's publishing
|
var serializedPreferences = localStorage[provider.providerId + ".publishPreferences"];
|
||||||
var fileDesc = fileMgr.getCurrentFile();
|
if(serializedPreferences) {
|
||||||
var title = fileDesc.title;
|
var publishPreferences = JSON.parse(serializedPreferences);
|
||||||
var content = getPublishContent(publishAttributes);
|
_.each(provider.publishPreferencesInputIds, function(inputId) {
|
||||||
provider.publish(publishAttributes, title, content, function(error) {
|
utils.setInputValue("#input-publish-" + inputId, publishPreferences[inputId]);
|
||||||
if(error === undefined) {
|
});
|
||||||
publishAttributes.provider = provider.providerId;
|
utils.setInputRadio("radio-publish-format", publishPreferences.format);
|
||||||
sharing.createLink(publishAttributes, function() {
|
}
|
||||||
createPublishIndex(fileDesc, publishAttributes);
|
|
||||||
});
|
// Open dialog box
|
||||||
}
|
$("#modal-publish").modal();
|
||||||
});
|
}
|
||||||
|
|
||||||
// Store input values as preferences for next time we open the publish dialog
|
// Add a new publish location to a local document
|
||||||
var publishPreferences = {};
|
function performNewLocation(event) {
|
||||||
_.each(provider.publishPreferencesInputIds, function(inputId) {
|
var provider = newLocationProvider;
|
||||||
publishPreferences[inputId] = $("#input-publish-" + inputId).val();
|
var publishAttributes = provider.newPublishAttributes(event);
|
||||||
});
|
if(publishAttributes === undefined) {
|
||||||
publishPreferences.format = publishAttributes.format;
|
return;
|
||||||
localStorage[provider.providerId + ".publishPreferences"] = JSON.stringify(publishPreferences);
|
}
|
||||||
}
|
|
||||||
|
// Perform provider's publishing
|
||||||
// Retrieve file's publish locations from localStorage
|
var fileDesc = fileMgr.getCurrentFile();
|
||||||
publisher.populatePublishLocations = function(fileDesc) {
|
var title = fileDesc.title;
|
||||||
_.chain(
|
var content = getPublishContent(publishAttributes);
|
||||||
localStorage[fileDesc.fileIndex + ".publish"].split(";")
|
provider.publish(publishAttributes, title, content, function(error) {
|
||||||
).compact().each(function(publishIndex) {
|
if(error === undefined) {
|
||||||
var publishAttributes = JSON.parse(localStorage[publishIndex]);
|
publishAttributes.provider = provider.providerId;
|
||||||
// Store publishIndex
|
sharing.createLink(publishAttributes, function() {
|
||||||
publishAttributes.publishIndex = publishIndex;
|
createPublishIndex(fileDesc, publishAttributes);
|
||||||
// Replace provider ID by provider module in attributes
|
});
|
||||||
publishAttributes.provider = providerMap[publishAttributes.provider];
|
}
|
||||||
fileDesc.publishLocations[publishIndex] = publishAttributes;
|
});
|
||||||
});
|
|
||||||
};
|
// Store input values as preferences for next time we open the publish
|
||||||
|
// dialog
|
||||||
core.onReady(function() {
|
var publishPreferences = {};
|
||||||
// Add every provider
|
_.each(provider.publishPreferencesInputIds, function(inputId) {
|
||||||
var publishMenu = $("#publish-menu");
|
publishPreferences[inputId] = $("#input-publish-" + inputId).val();
|
||||||
_.each(providerMap, function(provider) {
|
});
|
||||||
// Provider's publish button
|
publishPreferences.format = publishAttributes.format;
|
||||||
publishMenu.append(
|
localStorage[provider.providerId + ".publishPreferences"] = JSON.stringify(publishPreferences);
|
||||||
$("<li>").append(
|
}
|
||||||
$('<a href="#"><i class="icon-' + provider.providerId + '"></i> ' + provider.providerName + '</a>')
|
|
||||||
.click(function() {
|
// Retrieve file's publish locations from localStorage
|
||||||
initNewLocation(provider);
|
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;
|
||||||
// Action links (if any)
|
// Replace provider ID by provider module in attributes
|
||||||
$(".action-publish-" + provider.providerId).click(function() {
|
publishAttributes.provider = providerMap[publishAttributes.provider];
|
||||||
initNewLocation(provider);
|
fileDesc.publishLocations[publishIndex] = publishAttributes;
|
||||||
});
|
});
|
||||||
});
|
};
|
||||||
|
|
||||||
$(".action-process-publish").click(performNewLocation);
|
core.onReady(function() {
|
||||||
|
// Add every provider
|
||||||
// Save As menu items
|
var publishMenu = $("#publish-menu");
|
||||||
$(".action-download-md").click(function() {
|
_.each(providerMap, function(provider) {
|
||||||
var content = $("#wmd-input").val();
|
// Provider's publish button
|
||||||
var title = fileMgr.getCurrentFile().title;
|
publishMenu.append($("<li>").append($('<a href="#"><i class="icon-' + provider.providerId + '"></i> ' + provider.providerName + '</a>').click(function() {
|
||||||
utils.saveAs(content, title + ".md");
|
initNewLocation(provider);
|
||||||
});
|
})));
|
||||||
$(".action-download-html").click(function() {
|
// Action links (if any)
|
||||||
var content = $("#wmd-preview").html();
|
$(".action-publish-" + provider.providerId).click(function() {
|
||||||
var title = fileMgr.getCurrentFile().title;
|
initNewLocation(provider);
|
||||||
utils.saveAs(content, title + ".html");
|
});
|
||||||
});
|
});
|
||||||
$(".action-download-template").click(function() {
|
|
||||||
var content = publisher.applyTemplate();
|
$(".action-process-publish").click(performNewLocation);
|
||||||
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;
|
||||||
extensionMgr.onPublisherCreated(publisher);
|
utils.saveAs(content, title + ".md");
|
||||||
return publisher;
|
});
|
||||||
|
$(".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;
|
||||||
});
|
});
|
@ -2,28 +2,29 @@ define([
|
|||||||
"underscore",
|
"underscore",
|
||||||
"config"
|
"config"
|
||||||
], function(_) {
|
], function(_) {
|
||||||
|
|
||||||
var settings = {
|
var settings = {
|
||||||
layoutOrientation : "horizontal",
|
layoutOrientation: "horizontal",
|
||||||
lazyRendering : true,
|
lazyRendering: true,
|
||||||
editorFontSize : 14,
|
editorFontSize: 14,
|
||||||
defaultContent: "\n\n\n> Written with [StackEdit](http://benweet.github.io/stackedit/).",
|
defaultContent: "\n\n\n> Written with [StackEdit](http://benweet.github.io/stackedit/).",
|
||||||
commitMsg : "Published by http://benweet.github.io/stackedit",
|
commitMsg: "Published by http://benweet.github.io/stackedit",
|
||||||
template : [
|
template: [
|
||||||
'<!DOCTYPE html>\n',
|
'<!DOCTYPE html>\n',
|
||||||
'<html>\n',
|
'<html>\n',
|
||||||
'<head>\n',
|
'<head>\n',
|
||||||
'<title><%= documentTitle %></title>\n',
|
'<title><%= documentTitle %></title>\n',
|
||||||
'</head>\n',
|
'</head>\n',
|
||||||
'<body><%= documentHTML %></body>\n',
|
'<body><%= documentHTML %></body>\n',
|
||||||
'</html>'].join(""),
|
'</html>'
|
||||||
sshProxy : SSH_PROXY_URL,
|
].join(""),
|
||||||
extensionSettings: {}
|
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;
|
||||||
});
|
});
|
243
js/sharing.js
243
js/sharing.js
@ -10,125 +10,128 @@ define([
|
|||||||
"download-provider",
|
"download-provider",
|
||||||
"gist-provider"
|
"gist-provider"
|
||||||
], function($, _, core, utils, extensionMgr, fileMgr, asyncRunner) {
|
], function($, _, core, utils, extensionMgr, fileMgr, asyncRunner) {
|
||||||
|
|
||||||
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();
|
|
||||||
|
|
||||||
// Used to populate the "Sharing" dropdown box
|
var sharing = {};
|
||||||
var lineTemplate = ['<div class="input-prepend">',
|
|
||||||
'<a href="<%= link %>" class="add-on" title="Sharing location"><i class="icon-link"></i></a>',
|
// Create a map with providerId: providerModule
|
||||||
'<input class="span2" type="text" value="<%= link %>" readonly />',
|
var providerMap = _.chain(arguments).map(function(argument) {
|
||||||
'</div>'].join("");
|
return argument && argument.providerId && [
|
||||||
sharing.refreshDocumentSharing = function(attributesList) {
|
argument.providerId,
|
||||||
var linkList = $("#link-container .link-list").empty();
|
argument
|
||||||
$("#link-container .no-link").show();
|
];
|
||||||
_.each(attributesList, function(attributes) {
|
}).compact().object().value();
|
||||||
if(attributes.sharingLink) {
|
|
||||||
var lineElement = $(_.template(lineTemplate, {
|
// Used to populate the "Sharing" dropdown box
|
||||||
link: attributes.sharingLink
|
var lineTemplate = [
|
||||||
}));
|
'<div class="input-prepend">',
|
||||||
lineElement.click(function(event) {
|
' <a href="<%= link %>" class="add-on" title="Sharing location"><i class="icon-link"></i></a>',
|
||||||
event.stopPropagation();
|
' <input class="span2" type="text" value="<%= link %>" readonly />',
|
||||||
});
|
'</div>'
|
||||||
linkList.append(lineElement);
|
].join("");
|
||||||
$("#link-container .no-link").hide();
|
sharing.refreshDocumentSharing = function(attributesList) {
|
||||||
}
|
var linkList = $("#link-container .link-list").empty();
|
||||||
});
|
$("#link-container .no-link").show();
|
||||||
};
|
_.each(attributesList, function(attributes) {
|
||||||
|
if(attributes.sharingLink) {
|
||||||
sharing.createLink = function(attributes, callback) {
|
var lineElement = $(_.template(lineTemplate, {
|
||||||
var provider = providerMap[attributes.provider];
|
link: attributes.sharingLink
|
||||||
// Don't create link if link already exists or provider is not compatible for sharing
|
}));
|
||||||
if(attributes.sharingLink !== undefined || provider === undefined
|
lineElement.click(function(event) {
|
||||||
// Or document is not published in markdown format
|
event.stopPropagation();
|
||||||
|| attributes.format != "markdown") {
|
});
|
||||||
callback();
|
linkList.append(lineElement);
|
||||||
return;
|
$("#link-container .no-link").hide();
|
||||||
}
|
}
|
||||||
var task = asyncRunner.createTask();
|
});
|
||||||
var shortUrl = undefined;
|
};
|
||||||
task.onRun(function() {
|
|
||||||
if(core.isOffline === true) {
|
sharing.createLink = function(attributes, callback) {
|
||||||
task.chain();
|
var provider = providerMap[attributes.provider];
|
||||||
return;
|
// Don't create link if link already exists or provider is not
|
||||||
}
|
// compatible for sharing
|
||||||
var url = [MAIN_URL, 'viewer.html?provider=', attributes.provider];
|
if(attributes.sharingLink !== undefined || provider === undefined
|
||||||
_.each(provider.sharingAttributes, function(attributeName) {
|
// Or document is not published in markdown format
|
||||||
url.push('&');
|
|| attributes.format != "markdown") {
|
||||||
url.push(attributeName);
|
callback();
|
||||||
url.push('=');
|
return;
|
||||||
url.push(encodeURIComponent(attributes[attributeName]));
|
}
|
||||||
});
|
var task = asyncRunner.createTask();
|
||||||
url = url.join("");
|
var shortUrl = undefined;
|
||||||
$.getJSON(
|
task.onRun(function() {
|
||||||
"https://api-ssl.bitly.com/v3/shorten",
|
if(core.isOffline === true) {
|
||||||
{
|
task.chain();
|
||||||
"access_token": BITLY_ACCESS_TOKEN,
|
return;
|
||||||
"longUrl": url
|
}
|
||||||
},
|
var url = [
|
||||||
function(response)
|
MAIN_URL,
|
||||||
{
|
'viewer.html?provider=',
|
||||||
if(response.data) {
|
attributes.provider
|
||||||
shortUrl = response.data.url;
|
];
|
||||||
attributes.sharingLink = shortUrl;
|
_.each(provider.sharingAttributes, function(attributeName) {
|
||||||
}
|
url.push('&');
|
||||||
else {
|
url.push(attributeName);
|
||||||
extensionMgr.onError("An error occured while creating sharing link.");
|
url.push('=');
|
||||||
attributes.sharingLink = url;
|
url.push(encodeURIComponent(attributes[attributeName]));
|
||||||
}
|
});
|
||||||
task.chain();
|
url = url.join("");
|
||||||
}
|
$.getJSON("https://api-ssl.bitly.com/v3/shorten", {
|
||||||
);
|
"access_token": BITLY_ACCESS_TOKEN,
|
||||||
});
|
"longUrl": url
|
||||||
function onFinish() {
|
}, function(response) {
|
||||||
callback();
|
if(response.data) {
|
||||||
}
|
shortUrl = response.data.url;
|
||||||
task.onSuccess(onFinish);
|
attributes.sharingLink = shortUrl;
|
||||||
task.onError(onFinish);
|
}
|
||||||
asyncRunner.addTask(task);
|
else {
|
||||||
};
|
extensionMgr.onError("An error occured while creating sharing link.");
|
||||||
|
attributes.sharingLink = url;
|
||||||
core.onReady(function() {
|
}
|
||||||
if(viewerMode === false) {
|
task.chain();
|
||||||
return;
|
});
|
||||||
}
|
});
|
||||||
// Check parameters to see if we have to download a shared document
|
function onFinish() {
|
||||||
var providerId = utils.getURLParameter("provider");
|
callback();
|
||||||
if(providerId === undefined) {
|
}
|
||||||
providerId = "download";
|
task.onSuccess(onFinish);
|
||||||
}
|
task.onError(onFinish);
|
||||||
var provider = providerMap[providerId];
|
asyncRunner.addTask(task);
|
||||||
if(provider === undefined) {
|
};
|
||||||
return;
|
|
||||||
}
|
core.onReady(function() {
|
||||||
var importParameters = {};
|
if(viewerMode === false) {
|
||||||
_.each(provider.sharingAttributes, function(attributeName) {
|
return;
|
||||||
var parameter = utils.getURLParameter(attributeName);
|
}
|
||||||
if(!parameter) {
|
// Check parameters to see if we have to download a shared document
|
||||||
importParameters = undefined;
|
var providerId = utils.getURLParameter("provider");
|
||||||
return;
|
if(providerId === undefined) {
|
||||||
}
|
providerId = "download";
|
||||||
importParameters[attributeName] = parameter;
|
}
|
||||||
});
|
var provider = providerMap[providerId];
|
||||||
if(importParameters === undefined) {
|
if(provider === undefined) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
$("#wmd-preview, #file-title").hide();
|
var importParameters = {};
|
||||||
provider.importPublic(importParameters, function(error, title, content) {
|
_.each(provider.sharingAttributes, function(attributeName) {
|
||||||
$("#wmd-preview, #file-title").show();
|
var parameter = utils.getURLParameter(attributeName);
|
||||||
if(error) {
|
if(!parameter) {
|
||||||
return;
|
importParameters = undefined;
|
||||||
}
|
return;
|
||||||
var fileDesc = fileMgr.createFile(title, content, undefined, true);
|
}
|
||||||
fileMgr.selectFile(fileDesc);
|
importParameters[attributeName] = parameter;
|
||||||
});
|
});
|
||||||
});
|
if(importParameters === undefined) {
|
||||||
|
return;
|
||||||
return sharing;
|
}
|
||||||
|
$("#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;
|
||||||
});
|
});
|
146
js/ssh-helper.js
146
js/ssh-helper.js
@ -4,80 +4,80 @@ define([
|
|||||||
"async-runner"
|
"async-runner"
|
||||||
], function($, core, asyncRunner) {
|
], function($, core, asyncRunner) {
|
||||||
|
|
||||||
var sshHelper = {};
|
var sshHelper = {};
|
||||||
|
|
||||||
// Only used to check the offline status
|
// Only used to check the offline status
|
||||||
function connect(task) {
|
function connect(task) {
|
||||||
task.onRun(function() {
|
task.onRun(function() {
|
||||||
if(core.isOffline === true) {
|
if(core.isOffline === true) {
|
||||||
task.error(new Error("Operation not available in offline mode.|stopPublish"));
|
task.error(new Error("Operation not available in offline mode.|stopPublish"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
task.chain();
|
task.chain();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
sshHelper.upload = function(host, port, username, password, path, title, content, callback) {
|
sshHelper.upload = function(host, port, username, password, path, title, content, callback) {
|
||||||
var task = asyncRunner.createTask();
|
var task = asyncRunner.createTask();
|
||||||
connect(task);
|
connect(task);
|
||||||
task.onRun(function() {
|
task.onRun(function() {
|
||||||
var url = SSH_PROXY_URL + "upload";
|
var url = SSH_PROXY_URL + "upload";
|
||||||
var data = {
|
var data = {
|
||||||
host: host,
|
host: host,
|
||||||
port: port,
|
port: port,
|
||||||
username: username,
|
username: username,
|
||||||
password: password,
|
password: password,
|
||||||
path: path,
|
path: path,
|
||||||
title: title,
|
title: title,
|
||||||
content: content
|
content: content
|
||||||
};
|
};
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url : url,
|
url: url,
|
||||||
data: data,
|
data: data,
|
||||||
type: "POST",
|
type: "POST",
|
||||||
dataType : "json",
|
dataType: "json",
|
||||||
timeout : AJAX_TIMEOUT
|
timeout: AJAX_TIMEOUT
|
||||||
}).done(function(response, textStatus, jqXHR) {
|
}).done(function(response, textStatus, jqXHR) {
|
||||||
if(response.error === undefined) {
|
if(response.error === undefined) {
|
||||||
task.chain();
|
task.chain();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
handleError(response.error, task);
|
handleError(response.error, task);
|
||||||
}).fail(function(jqXHR) {
|
}).fail(function(jqXHR) {
|
||||||
var error = {
|
var error = {
|
||||||
code: jqXHR.status,
|
code: jqXHR.status,
|
||||||
message: jqXHR.statusText
|
message: jqXHR.statusText
|
||||||
};
|
};
|
||||||
handleError(error, task);
|
handleError(error, task);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
task.onSuccess(function() {
|
task.onSuccess(function() {
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
task.onError(function(error) {
|
task.onError(function(error) {
|
||||||
callback(error);
|
callback(error);
|
||||||
});
|
});
|
||||||
asyncRunner.addTask(task);
|
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));
|
|
||||||
}
|
|
||||||
|
|
||||||
return sshHelper;
|
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;
|
||||||
});
|
});
|
||||||
|
@ -3,46 +3,35 @@ define([
|
|||||||
"ssh-helper"
|
"ssh-helper"
|
||||||
], function(utils, sshHelper) {
|
], function(utils, sshHelper) {
|
||||||
|
|
||||||
var PROVIDER_SSH = "ssh";
|
var PROVIDER_SSH = "ssh";
|
||||||
|
|
||||||
var sshProvider = {
|
var sshProvider = {
|
||||||
providerId : PROVIDER_SSH,
|
providerId: PROVIDER_SSH,
|
||||||
providerName : "SSH server",
|
providerName: "SSH server",
|
||||||
publishPreferencesInputIds: ["ssh-host", "ssh-port", "ssh-username", "ssh-password"]
|
publishPreferencesInputIds: [
|
||||||
};
|
"ssh-host",
|
||||||
|
"ssh-port",
|
||||||
|
"ssh-username",
|
||||||
|
"ssh-password"
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
sshProvider.publish = function(publishAttributes, title, content, callback) {
|
sshProvider.publish = function(publishAttributes, title, content, callback) {
|
||||||
sshHelper.upload(
|
sshHelper.upload(publishAttributes.host, publishAttributes.port, publishAttributes.username, publishAttributes.password, publishAttributes.path, title, content, callback);
|
||||||
publishAttributes.host,
|
};
|
||||||
publishAttributes.port,
|
|
||||||
publishAttributes.username,
|
|
||||||
publishAttributes.password,
|
|
||||||
publishAttributes.path,
|
|
||||||
title,
|
|
||||||
content,
|
|
||||||
callback);
|
|
||||||
};
|
|
||||||
|
|
||||||
sshProvider.newPublishAttributes = function(event) {
|
sshProvider.newPublishAttributes = function(event) {
|
||||||
var publishAttributes = {};
|
var publishAttributes = {};
|
||||||
publishAttributes.host = utils
|
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])$/);
|
||||||
.getInputTextValue(
|
publishAttributes.port = utils.getInputIntValue("#input-publish-ssh-port", undefined, 0);
|
||||||
"#input-publish-ssh-host",
|
publishAttributes.username = utils.getInputTextValue("#input-publish-ssh-username", event);
|
||||||
event,
|
publishAttributes.password = utils.getInputTextValue("#input-publish-ssh-password", 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.path = utils.getInputTextValue("#input-publish-file-path", event);
|
||||||
publishAttributes.port = utils.getInputIntValue(
|
if(event.isPropagationStopped()) {
|
||||||
"#input-publish-ssh-port", undefined, 0);
|
return undefined;
|
||||||
publishAttributes.username = utils.getInputTextValue(
|
}
|
||||||
"#input-publish-ssh-username", event);
|
return publishAttributes;
|
||||||
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;
|
||||||
});
|
});
|
251
js/storage.js
251
js/storage.js
@ -1,131 +1,128 @@
|
|||||||
// Setup an empty localStorage or upgrade an existing one
|
// Setup an empty localStorage or upgrade an existing one
|
||||||
define([
|
define([
|
||||||
"underscore"
|
"underscore"
|
||||||
], function(_) {
|
], function(_) {
|
||||||
|
|
||||||
// Create the file system if not exist
|
// Create the file system if not exist
|
||||||
if (localStorage["file.list"] === undefined) {
|
if(localStorage["file.list"] === undefined) {
|
||||||
localStorage["file.list"] = ";";
|
localStorage["file.list"] = ";";
|
||||||
}
|
}
|
||||||
var fileIndexList = _.compact(localStorage["file.list"].split(";"));
|
var fileIndexList = _.compact(localStorage["file.list"].split(";"));
|
||||||
|
|
||||||
// localStorage versioning
|
// localStorage versioning
|
||||||
var version = localStorage["version"];
|
var version = localStorage["version"];
|
||||||
|
|
||||||
// Upgrade from v0 to v1
|
// Upgrade from v0 to v1
|
||||||
if(version === undefined) {
|
if(version === undefined) {
|
||||||
|
|
||||||
// Not used anymore
|
// Not used anymore
|
||||||
localStorage.removeItem("sync.queue");
|
localStorage.removeItem("sync.queue");
|
||||||
localStorage.removeItem("sync.current");
|
localStorage.removeItem("sync.current");
|
||||||
localStorage.removeItem("file.counter");
|
localStorage.removeItem("file.counter");
|
||||||
|
|
||||||
_.each(fileIndexList, function(fileIndex) {
|
_.each(fileIndexList, function(fileIndex) {
|
||||||
localStorage[fileIndex + ".publish"] = ";";
|
localStorage[fileIndex + ".publish"] = ";";
|
||||||
var syncIndexList = _.compact(localStorage[fileIndex + ".sync"].split(";"));
|
var syncIndexList = _.compact(localStorage[fileIndex + ".sync"].split(";"));
|
||||||
_.each(syncIndexList, function(syncIndex) {
|
_.each(syncIndexList, function(syncIndex) {
|
||||||
localStorage[syncIndex + ".contentCRC"] = "0";
|
localStorage[syncIndex + ".contentCRC"] = "0";
|
||||||
// We store title CRC only for Google Drive synchronization
|
// We store title CRC only for Google Drive synchronization
|
||||||
if(localStorage[syncIndex + ".etag"] !== undefined) {
|
if(localStorage[syncIndex + ".etag"] !== undefined) {
|
||||||
localStorage[syncIndex + ".titleCRC"] = "0";
|
localStorage[syncIndex + ".titleCRC"] = "0";
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
version = "v1";
|
version = "v1";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Upgrade from v1 to v2
|
// Upgrade from v1 to v2
|
||||||
if(version == "v1") {
|
if(version == "v1") {
|
||||||
var gdriveLastChangeId = localStorage["sync.gdrive.lastChangeId"];
|
var gdriveLastChangeId = localStorage["sync.gdrive.lastChangeId"];
|
||||||
if(gdriveLastChangeId) {
|
if(gdriveLastChangeId) {
|
||||||
localStorage["gdrive.lastChangeId"] = gdriveLastChangeId;
|
localStorage["gdrive.lastChangeId"] = gdriveLastChangeId;
|
||||||
localStorage.removeItem("sync.gdrive.lastChangeId");
|
localStorage.removeItem("sync.gdrive.lastChangeId");
|
||||||
}
|
}
|
||||||
var dropboxLastChangeId = localStorage["sync.dropbox.lastChangeId"];
|
var dropboxLastChangeId = localStorage["sync.dropbox.lastChangeId"];
|
||||||
if(dropboxLastChangeId) {
|
if(dropboxLastChangeId) {
|
||||||
localStorage["dropbox.lastChangeId"] = dropboxLastChangeId;
|
localStorage["dropbox.lastChangeId"] = dropboxLastChangeId;
|
||||||
localStorage.removeItem("sync.dropbox.lastChangeId");
|
localStorage.removeItem("sync.dropbox.lastChangeId");
|
||||||
}
|
}
|
||||||
|
|
||||||
var PROVIDER_GDRIVE = "gdrive";
|
var PROVIDER_GDRIVE = "gdrive";
|
||||||
var PROVIDER_DROPBOX = "dropbox";
|
var PROVIDER_DROPBOX = "dropbox";
|
||||||
var SYNC_PROVIDER_GDRIVE = "sync." + PROVIDER_GDRIVE + ".";
|
var SYNC_PROVIDER_GDRIVE = "sync." + PROVIDER_GDRIVE + ".";
|
||||||
var SYNC_PROVIDER_DROPBOX = "sync." + PROVIDER_DROPBOX + ".";
|
var SYNC_PROVIDER_DROPBOX = "sync." + PROVIDER_DROPBOX + ".";
|
||||||
_.each(fileIndexList, function(fileIndex) {
|
_.each(fileIndexList, function(fileIndex) {
|
||||||
var syncIndexList = _.compact(localStorage[fileIndex + ".sync"].split(";"));
|
var syncIndexList = _.compact(localStorage[fileIndex + ".sync"].split(";"));
|
||||||
_.each(syncIndexList, function(syncIndex) {
|
_.each(syncIndexList, function(syncIndex) {
|
||||||
var syncAttributes = {};
|
var syncAttributes = {};
|
||||||
if (syncIndex.indexOf(SYNC_PROVIDER_GDRIVE) === 0) {
|
if(syncIndex.indexOf(SYNC_PROVIDER_GDRIVE) === 0) {
|
||||||
syncAttributes.provider = PROVIDER_GDRIVE;
|
syncAttributes.provider = PROVIDER_GDRIVE;
|
||||||
syncAttributes.id = syncIndex.substring(SYNC_PROVIDER_GDRIVE.length);
|
syncAttributes.id = syncIndex.substring(SYNC_PROVIDER_GDRIVE.length);
|
||||||
syncAttributes.etag = localStorage[syncIndex + ".etag"];
|
syncAttributes.etag = localStorage[syncIndex + ".etag"];
|
||||||
syncAttributes.contentCRC = localStorage[syncIndex + ".contentCRC"];
|
syncAttributes.contentCRC = localStorage[syncIndex + ".contentCRC"];
|
||||||
syncAttributes.titleCRC = localStorage[syncIndex + ".titleCRC"];
|
syncAttributes.titleCRC = localStorage[syncIndex + ".titleCRC"];
|
||||||
}
|
}
|
||||||
else if (syncIndex.indexOf(SYNC_PROVIDER_DROPBOX) === 0) {
|
else if(syncIndex.indexOf(SYNC_PROVIDER_DROPBOX) === 0) {
|
||||||
syncAttributes.provider = PROVIDER_DROPBOX;
|
syncAttributes.provider = PROVIDER_DROPBOX;
|
||||||
syncAttributes.path = decodeURIComponent(syncIndex.substring(SYNC_PROVIDER_DROPBOX.length));
|
syncAttributes.path = decodeURIComponent(syncIndex.substring(SYNC_PROVIDER_DROPBOX.length));
|
||||||
syncAttributes.version = localStorage[syncIndex + ".version"];
|
syncAttributes.version = localStorage[syncIndex + ".version"];
|
||||||
syncAttributes.contentCRC = localStorage[syncIndex + ".contentCRC"];
|
syncAttributes.contentCRC = localStorage[syncIndex + ".contentCRC"];
|
||||||
}
|
}
|
||||||
localStorage[syncIndex] = JSON.stringify(syncAttributes);
|
localStorage[syncIndex] = JSON.stringify(syncAttributes);
|
||||||
localStorage.removeItem(syncIndex + ".etag");
|
localStorage.removeItem(syncIndex + ".etag");
|
||||||
localStorage.removeItem(syncIndex + ".version");
|
localStorage.removeItem(syncIndex + ".version");
|
||||||
localStorage.removeItem(syncIndex + ".contentCRC");
|
localStorage.removeItem(syncIndex + ".contentCRC");
|
||||||
localStorage.removeItem(syncIndex + ".titleCRC");
|
localStorage.removeItem(syncIndex + ".titleCRC");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
version = "v2";
|
version = "v2";
|
||||||
}
|
}
|
||||||
|
|
||||||
// Upgrade from v2 to v3
|
// Upgrade from v2 to v3
|
||||||
if(version == "v2") {
|
if(version == "v2") {
|
||||||
_.each(fileIndexList, function(fileIndex) {
|
_.each(fileIndexList, function(fileIndex) {
|
||||||
if(!_.has(localStorage, fileIndex + ".sync")) {
|
if(!_.has(localStorage, fileIndex + ".sync")) {
|
||||||
localStorage.removeItem(fileIndex + ".title");
|
localStorage.removeItem(fileIndex + ".title");
|
||||||
localStorage.removeItem(fileIndex + ".publish");
|
localStorage.removeItem(fileIndex + ".publish");
|
||||||
localStorage.removeItem(fileIndex + ".content");
|
localStorage.removeItem(fileIndex + ".content");
|
||||||
localStorage["file.list"] = localStorage["file.list"].replace(";"
|
localStorage["file.list"] = localStorage["file.list"].replace(";" + fileIndex + ";", ";");
|
||||||
+ fileIndex + ";", ";");
|
}
|
||||||
}
|
});
|
||||||
});
|
version = "v3";
|
||||||
version = "v3";
|
}
|
||||||
}
|
|
||||||
|
// Upgrade from v3 to v4
|
||||||
// Upgrade from v3 to v4
|
if(version == "v3") {
|
||||||
if(version == "v3") {
|
var currentFileIndex = localStorage["file.current"];
|
||||||
var currentFileIndex = localStorage["file.current"];
|
if(currentFileIndex !== undefined && localStorage["file.list"].indexOf(";" + currentFileIndex + ";") === -1) {
|
||||||
if(currentFileIndex !== undefined &&
|
localStorage.removeItem("file.current");
|
||||||
localStorage["file.list"].indexOf(";" + currentFileIndex + ";") === -1)
|
}
|
||||||
{
|
version = "v4";
|
||||||
localStorage.removeItem("file.current");
|
}
|
||||||
}
|
|
||||||
version = "v4";
|
// Upgrade from v4 to v5
|
||||||
}
|
if(version == "v4") {
|
||||||
|
// Recreate GitHub token
|
||||||
// Upgrade from v4 to v5
|
localStorage.removeItem("githubToken");
|
||||||
if(version == "v4") {
|
version = "v5";
|
||||||
// Recreate GitHub token
|
}
|
||||||
localStorage.removeItem("githubToken");
|
|
||||||
version = "v5";
|
// Upgrade from v5 to v6
|
||||||
}
|
if(version == "v5") {
|
||||||
|
_.each(fileIndexList, function(fileIndex) {
|
||||||
// Upgrade from v5 to v6
|
var publishIndexList = _.compact(localStorage[fileIndex + ".publish"].split(";"));
|
||||||
if(version == "v5") {
|
_.each(publishIndexList, function(publishIndex) {
|
||||||
_.each(fileIndexList, function(fileIndex) {
|
var publishAttributes = JSON.parse(localStorage[publishIndex]);
|
||||||
var publishIndexList = _.compact(localStorage[fileIndex + ".publish"].split(";"));
|
if(publishAttributes.provider == "gdrive") {
|
||||||
_.each(publishIndexList, function(publishIndex) {
|
// Change fileId to Id to be consistent with syncAttributes
|
||||||
var publishAttributes = JSON.parse(localStorage[publishIndex]);
|
publishAttributes.id = publishAttributes.fileId;
|
||||||
if(publishAttributes.provider == "gdrive") {
|
publishAttributes.fileId = undefined;
|
||||||
// Change fileId to Id to be consistent with syncAttributes
|
localStorage[publishIndex] = JSON.stringify(publishAttributes);
|
||||||
publishAttributes.id = publishAttributes.fileId;
|
}
|
||||||
publishAttributes.fileId = undefined;
|
});
|
||||||
localStorage[publishIndex] = JSON.stringify(publishAttributes);
|
});
|
||||||
}
|
version = "v6";
|
||||||
});
|
}
|
||||||
});
|
|
||||||
version = "v6";
|
localStorage["version"] = version;
|
||||||
}
|
|
||||||
|
|
||||||
localStorage["version"] = version;
|
|
||||||
});
|
});
|
@ -9,250 +9,244 @@ define([
|
|||||||
"dropbox-provider",
|
"dropbox-provider",
|
||||||
"gdrive-provider"
|
"gdrive-provider"
|
||||||
], function($, _, core, utils, extensionMgr, fileSystem, fileMgr) {
|
], function($, _, core, utils, extensionMgr, fileSystem, fileMgr) {
|
||||||
|
|
||||||
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();
|
|
||||||
|
|
||||||
// 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
|
var synchronizer = {};
|
||||||
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) {
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Recursive function to upload multiple files
|
// Create a map with providerId: providerModule
|
||||||
var uploadFileList = [];
|
var providerMap = _.chain(arguments).map(function(argument) {
|
||||||
function fileUp(callback) {
|
return argument && argument.providerId && [
|
||||||
|
argument.providerId,
|
||||||
// No more fileDesc to synchronize
|
argument
|
||||||
if (uploadFileList.length === 0) {
|
];
|
||||||
syncUp(callback);
|
}).compact().object().value();
|
||||||
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
|
// Retrieve sync locations from localStorage
|
||||||
uploadContent = fileDesc.getContent();
|
_.each(fileSystem, function(fileDesc) {
|
||||||
uploadContentCRC = utils.crc32(uploadContent);
|
_.chain(localStorage[fileDesc.fileIndex + ".sync"].split(";")).compact().each(function(syncIndex) {
|
||||||
uploadTitle = fileDesc.title;
|
var syncAttributes = JSON.parse(localStorage[syncIndex]);
|
||||||
uploadTitleCRC = utils.crc32(uploadTitle);
|
// Store syncIndex
|
||||||
locationUp(callback);
|
syncAttributes.syncIndex = syncIndex;
|
||||||
}
|
// Replace provider ID by provider module in attributes
|
||||||
|
syncAttributes.provider = providerMap[syncAttributes.provider];
|
||||||
|
fileDesc.syncLocations[syncIndex] = syncAttributes;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
// Entry point for up synchronization (upload changes)
|
// Force the synchronization
|
||||||
var uploadCycle = false;
|
synchronizer.forceSync = function() {
|
||||||
function syncUp(callback) {
|
lastSync = 0;
|
||||||
if(uploadCycle === true) {
|
synchronizer.sync();
|
||||||
// New upload cycle
|
};
|
||||||
uploadCycle = false;
|
|
||||||
uploadFileList = _.values(fileSystem);
|
|
||||||
fileUp(callback);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Recursive function to download changes from multiple providers
|
// Recursive function to upload a single file on multiple locations
|
||||||
var providerList = [];
|
var uploadSyncAttributesList = [];
|
||||||
function providerDown(callback) {
|
var uploadContent = undefined;
|
||||||
if(providerList.length === 0) {
|
var uploadContentCRC = undefined;
|
||||||
callback();
|
var uploadTitle = undefined;
|
||||||
return;
|
var uploadTitleCRC = undefined;
|
||||||
}
|
function locationUp(callback) {
|
||||||
var provider = providerList.pop();
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
|
|
||||||
function isError(error) {
|
|
||||||
if(error !== undefined) {
|
|
||||||
syncRunning = false;
|
|
||||||
extensionMgr.onSyncRunning(false);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
syncDown(function(error) {
|
// No more synchronized location for this document
|
||||||
if(isError(error)) {
|
if(uploadSyncAttributesList.length === 0) {
|
||||||
return;
|
fileUp(callback);
|
||||||
}
|
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) {
|
|
||||||
|
|
||||||
// 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]);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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) {
|
|
||||||
|
|
||||||
// Perform the provider's export
|
// Dequeue a synchronized location
|
||||||
var fileDesc = fileMgr.getCurrentFile();
|
var syncAttributes = uploadSyncAttributesList.pop();
|
||||||
provider.exportFile(event, fileDesc.title, fileDesc.getContent(), function(error, syncAttributes) {
|
// Use the specified provider to perform the upload
|
||||||
if(error) {
|
syncAttributes.provider.syncUp(uploadContent, uploadContentCRC, uploadTitle, uploadTitleCRC, syncAttributes, function(error, uploadFlag) {
|
||||||
return;
|
if(uploadFlag === true) {
|
||||||
}
|
// If uploadFlag is true, request another upload cycle
|
||||||
fileMgr.addSync(fileDesc, syncAttributes);
|
uploadCycle = true;
|
||||||
});
|
}
|
||||||
|
if(error) {
|
||||||
// Store input values as preferences for next time we open the export dialog
|
callback(error);
|
||||||
var exportPreferences = {};
|
return;
|
||||||
_.each(provider.exportPreferencesInputIds, function(inputId) {
|
}
|
||||||
exportPreferences[inputId] = $("#input-sync-export-" + inputId).val();
|
if(uploadFlag) {
|
||||||
});
|
// Update syncAttributes in localStorage
|
||||||
localStorage[provider.providerId + ".exportPreferences"] = JSON.stringify(exportPreferences);
|
utils.storeAttributes(syncAttributes);
|
||||||
});
|
}
|
||||||
// Provider's manual export button
|
locationUp(callback);
|
||||||
$(".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);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
extensionMgr.onSynchronizerCreated(synchronizer);
|
// Recursive function to upload multiple files
|
||||||
return synchronizer;
|
var uploadFileList = [];
|
||||||
|
function fileUp(callback) {
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
;
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
|
||||||
|
// 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]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
|
||||||
|
// 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.content, function(error, syncAttributes) {
|
||||||
|
if(error) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fileMgr.addSync(fileDesc, syncAttributes);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
extensionMgr.onSynchronizerCreated(synchronizer);
|
||||||
|
return synchronizer;
|
||||||
});
|
});
|
||||||
|
@ -6,164 +6,163 @@ define([
|
|||||||
"async-runner"
|
"async-runner"
|
||||||
], function($, core, utils, extensionMgr, asyncRunner) {
|
], function($, core, utils, extensionMgr, asyncRunner) {
|
||||||
|
|
||||||
var oauthParams = undefined;
|
var oauthParams = undefined;
|
||||||
|
|
||||||
var tumblrHelper = {};
|
var tumblrHelper = {};
|
||||||
|
|
||||||
// Only used to check the offline status
|
// Only used to check the offline status
|
||||||
function connect(task) {
|
function connect(task) {
|
||||||
task.onRun(function() {
|
task.onRun(function() {
|
||||||
if(core.isOffline === true) {
|
if(core.isOffline === true) {
|
||||||
task.error(new Error("Operation not available in offline mode.|stopPublish"));
|
task.error(new Error("Operation not available in offline mode.|stopPublish"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
task.chain();
|
task.chain();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to authenticate with OAuth
|
// Try to authenticate with OAuth
|
||||||
function authenticate(task) {
|
function authenticate(task) {
|
||||||
var authWindow = undefined;
|
var authWindow = undefined;
|
||||||
var intervalId = undefined;
|
var intervalId = undefined;
|
||||||
task.onRun(function() {
|
task.onRun(function() {
|
||||||
if (oauthParams !== undefined) {
|
if(oauthParams !== undefined) {
|
||||||
task.chain();
|
task.chain();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var serializedOauthParams = localStorage["tumblrOauthParams"];
|
var serializedOauthParams = localStorage["tumblrOauthParams"];
|
||||||
if(serializedOauthParams !== undefined) {
|
if(serializedOauthParams !== undefined) {
|
||||||
oauthParams = JSON.parse(serializedOauthParams);
|
oauthParams = JSON.parse(serializedOauthParams);
|
||||||
task.chain();
|
task.chain();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
extensionMgr.onMessage("Please make sure the Tumblr authorization popup is not blocked by your browser.");
|
extensionMgr.onMessage("Please make sure the Tumblr authorization popup is not blocked by your browser.");
|
||||||
var errorMsg = "Failed to retrieve a token from Tumblr.";
|
var errorMsg = "Failed to retrieve a token from Tumblr.";
|
||||||
// We add time for user to enter his credentials
|
// We add time for user to enter his credentials
|
||||||
task.timeout = ASYNC_TASK_LONG_TIMEOUT;
|
task.timeout = ASYNC_TASK_LONG_TIMEOUT;
|
||||||
var oauth_object = undefined;
|
var oauth_object = undefined;
|
||||||
function getOauthToken() {
|
function getOauthToken() {
|
||||||
$.getJSON(TUMBLR_PROXY_URL + "request_token", function(data) {
|
$.getJSON(TUMBLR_PROXY_URL + "request_token", function(data) {
|
||||||
if(data.oauth_token !== undefined) {
|
if(data.oauth_token !== undefined) {
|
||||||
oauth_object = data;
|
oauth_object = data;
|
||||||
task.chain(getVerifier);
|
task.chain(getVerifier);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
task.error(new Error(errorMsg));
|
task.error(new Error(errorMsg));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
function getVerifier() {
|
function getVerifier() {
|
||||||
localStorage.removeItem("tumblrVerifier");
|
localStorage.removeItem("tumblrVerifier");
|
||||||
authWindow = utils.popupWindow(
|
authWindow = utils.popupWindow('tumblr-oauth-client.html?oauth_token=' + oauth_object.oauth_token, 'stackedit-tumblr-oauth', 800, 600);
|
||||||
'tumblr-oauth-client.html?oauth_token=' + oauth_object.oauth_token,
|
authWindow.focus();
|
||||||
'stackedit-tumblr-oauth', 800, 600);
|
intervalId = setInterval(function() {
|
||||||
authWindow.focus();
|
if(authWindow.closed === true) {
|
||||||
intervalId = setInterval(function() {
|
clearInterval(intervalId);
|
||||||
if(authWindow.closed === true) {
|
authWindow = undefined;
|
||||||
clearInterval(intervalId);
|
intervalId = undefined;
|
||||||
authWindow = undefined;
|
oauth_object.oauth_verifier = localStorage["tumblrVerifier"];
|
||||||
intervalId = undefined;
|
if(oauth_object.oauth_verifier === undefined) {
|
||||||
oauth_object.oauth_verifier = localStorage["tumblrVerifier"];
|
task.error(new Error(errorMsg));
|
||||||
if(oauth_object.oauth_verifier === undefined) {
|
return;
|
||||||
task.error(new Error(errorMsg));
|
}
|
||||||
return;
|
localStorage.removeItem("tumblrVerifier");
|
||||||
}
|
task.chain(getAccessToken);
|
||||||
localStorage.removeItem("tumblrVerifier");
|
}
|
||||||
task.chain(getAccessToken);
|
}, 500);
|
||||||
}
|
}
|
||||||
}, 500);
|
function getAccessToken() {
|
||||||
}
|
$.getJSON(TUMBLR_PROXY_URL + "access_token", oauth_object, function(data) {
|
||||||
function getAccessToken() {
|
if(data.access_token !== undefined && data.access_token_secret !== undefined) {
|
||||||
$.getJSON(TUMBLR_PROXY_URL + "access_token", oauth_object, function(data) {
|
localStorage["tumblrOauthParams"] = JSON.stringify(data);
|
||||||
if(data.access_token !== undefined && data.access_token_secret !== undefined) {
|
oauthParams = data;
|
||||||
localStorage["tumblrOauthParams"] = JSON.stringify(data);
|
task.chain();
|
||||||
oauthParams = data;
|
}
|
||||||
task.chain();
|
else {
|
||||||
}
|
task.error(new Error(errorMsg));
|
||||||
else {
|
}
|
||||||
task.error(new Error(errorMsg));
|
});
|
||||||
}
|
}
|
||||||
});
|
task.chain(getOauthToken);
|
||||||
}
|
});
|
||||||
task.chain(getOauthToken);
|
task.onError(function() {
|
||||||
});
|
if(intervalId !== undefined) {
|
||||||
task.onError(function() {
|
clearInterval(intervalId);
|
||||||
if(intervalId !== undefined) {
|
}
|
||||||
clearInterval(intervalId);
|
if(authWindow !== undefined) {
|
||||||
}
|
authWindow.close();
|
||||||
if(authWindow !== undefined) {
|
}
|
||||||
authWindow.close();
|
});
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
tumblrHelper.upload = function(blogHostname, postId, tags, format, title, content, callback) {
|
tumblrHelper.upload = function(blogHostname, postId, tags, format, title, content, callback) {
|
||||||
var task = asyncRunner.createTask();
|
var task = asyncRunner.createTask();
|
||||||
connect(task);
|
connect(task);
|
||||||
authenticate(task);
|
authenticate(task);
|
||||||
task.onRun(function() {
|
task.onRun(function() {
|
||||||
var data = $.extend({
|
var data = $.extend({
|
||||||
blog_hostname: blogHostname,
|
blog_hostname: blogHostname,
|
||||||
post_id: postId,
|
post_id: postId,
|
||||||
tags: tags,
|
tags: tags,
|
||||||
format: format,
|
format: format,
|
||||||
title: title,
|
title: title,
|
||||||
content: content
|
content: content
|
||||||
}, oauthParams);
|
}, oauthParams);
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url : TUMBLR_PROXY_URL + "post",
|
url: TUMBLR_PROXY_URL + "post",
|
||||||
data: data,
|
data: data,
|
||||||
type: "POST",
|
type: "POST",
|
||||||
dataType : "json",
|
dataType: "json",
|
||||||
timeout : AJAX_TIMEOUT
|
timeout: AJAX_TIMEOUT
|
||||||
}).done(function(post, textStatus, jqXHR) {
|
}).done(function(post, textStatus, jqXHR) {
|
||||||
postId = post.id;
|
postId = post.id;
|
||||||
task.chain();
|
task.chain();
|
||||||
}).fail(function(jqXHR) {
|
}).fail(function(jqXHR) {
|
||||||
var error = {
|
var error = {
|
||||||
code: jqXHR.status,
|
code: jqXHR.status,
|
||||||
message: jqXHR.statusText
|
message: jqXHR.statusText
|
||||||
};
|
};
|
||||||
// Handle error
|
// Handle error
|
||||||
if(error.code === 404 && postId !== undefined) {
|
if(error.code === 404 && postId !== undefined) {
|
||||||
error = 'Post ' + postId + ' not found on Tumblr.|removePublish';
|
error = 'Post ' + postId + ' not found on Tumblr.|removePublish';
|
||||||
}
|
}
|
||||||
handleError(error, task);
|
handleError(error, task);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
task.onSuccess(function() {
|
task.onSuccess(function() {
|
||||||
callback(undefined, postId);
|
callback(undefined, postId);
|
||||||
});
|
});
|
||||||
task.onError(function(error) {
|
task.onError(function(error) {
|
||||||
callback(error);
|
callback(error);
|
||||||
});
|
});
|
||||||
asyncRunner.addTask(task);
|
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));
|
|
||||||
}
|
|
||||||
|
|
||||||
return tumblrHelper;
|
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;
|
||||||
});
|
});
|
||||||
|
@ -2,48 +2,38 @@ define([
|
|||||||
"utils",
|
"utils",
|
||||||
"tumblr-helper"
|
"tumblr-helper"
|
||||||
], function(utils, tumblrHelper) {
|
], function(utils, tumblrHelper) {
|
||||||
|
|
||||||
var PROVIDER_TUMBLR = "tumblr";
|
|
||||||
|
|
||||||
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.newPublishAttributes = function(event) {
|
var PROVIDER_TUMBLR = "tumblr";
|
||||||
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;
|
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.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;
|
||||||
});
|
});
|
799
js/utils.js
799
js/utils.js
@ -3,311 +3,518 @@ define([
|
|||||||
"underscore",
|
"underscore",
|
||||||
"lib/FileSaver"
|
"lib/FileSaver"
|
||||||
], function($, _) {
|
], function($, _) {
|
||||||
|
|
||||||
var utils = {};
|
|
||||||
|
|
||||||
// Return a parameter from the URL
|
var utils = {};
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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();
|
|
||||||
};
|
|
||||||
|
|
||||||
// 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 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");
|
|
||||||
};
|
|
||||||
|
|
||||||
// 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");
|
|
||||||
};
|
|
||||||
|
|
||||||
// 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("");
|
|
||||||
};
|
|
||||||
|
|
||||||
// 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
|
|
||||||
};
|
|
||||||
|
|
||||||
// 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 "";
|
|
||||||
}
|
|
||||||
|
|
||||||
// UTF-8 to byte array
|
|
||||||
var bytes = [], offset = 0, length, char;
|
|
||||||
|
|
||||||
str = encodeURI(str);
|
// Return a parameter from the URL
|
||||||
length = str.length;
|
utils.getURLParameter = function(name) {
|
||||||
|
var regex = new RegExp(name + "=(.+?)(&|$)");
|
||||||
|
try {
|
||||||
|
return decodeURIComponent(regex.exec(location.search)[1]);
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
while (offset < length) {
|
// Transform a selector into a jQuery object
|
||||||
char = str[offset];
|
function jqElt(element) {
|
||||||
offset += 1;
|
if(_.isString(element)) {
|
||||||
|
return $(element);
|
||||||
|
}
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
if ('%' !== char) {
|
// For input control
|
||||||
bytes.push(char.charCodeAt(0));
|
function inputError(element, event) {
|
||||||
} else {
|
if(event !== undefined) {
|
||||||
char = str[offset] + str[offset + 1];
|
element.stop(true, true).addClass("error").delay(1000).switchClass("error");
|
||||||
bytes.push(parseInt(char, 16));
|
event.stopPropagation();
|
||||||
offset += 2;
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// byte array to base64
|
// Return input value
|
||||||
var padchar = '=';
|
utils.getInputValue = function(element) {
|
||||||
var alpha = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
element = jqElt(element);
|
||||||
|
return element.val();
|
||||||
|
};
|
||||||
|
|
||||||
var i, b10;
|
// Set input value
|
||||||
var x = [];
|
utils.setInputValue = function(element, value) {
|
||||||
|
element = jqElt(element);
|
||||||
|
element.val(value);
|
||||||
|
};
|
||||||
|
|
||||||
var imax = bytes.length - bytes.length % 3;
|
// 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;
|
||||||
|
};
|
||||||
|
|
||||||
for (i = 0; i < imax; i += 3) {
|
// Return input integer value
|
||||||
b10 = (bytes[i] << 16) | (bytes[i+1] << 8) | bytes[i+2];
|
utils.getInputIntValue = function(element, event, min, max) {
|
||||||
x.push(alpha.charAt(b10 >> 18));
|
element = jqElt(element);
|
||||||
x.push(alpha.charAt((b10 >> 12) & 0x3F));
|
var value = utils.getInputTextValue(element, event);
|
||||||
x.push(alpha.charAt((b10 >> 6) & 0x3f));
|
if(value === undefined) {
|
||||||
x.push(alpha.charAt(b10 & 0x3f));
|
return undefined;
|
||||||
}
|
}
|
||||||
switch (bytes.length - imax) {
|
value = parseInt(value);
|
||||||
case 1:
|
if((value === NaN) || (min !== undefined && value < min) || (max !== undefined && value > max)) {
|
||||||
b10 = bytes[i] << 16;
|
inputError(element, event);
|
||||||
x.push(alpha.charAt(b10 >> 18) + alpha.charAt((b10 >> 12) & 0x3F) +
|
return undefined;
|
||||||
padchar + padchar);
|
}
|
||||||
break;
|
return value;
|
||||||
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);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Create an centered popup window
|
|
||||||
utils.popupWindow = function(url, title, width, height) {
|
|
||||||
var left = (screen.width / 2) - (width / 2);
|
|
||||||
var top = (screen.height / 2) - (height / 2);
|
|
||||||
return window.open(
|
|
||||||
url,
|
|
||||||
title,
|
|
||||||
'toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=no, resizable=no, copyhistory=no, width='
|
|
||||||
+ width
|
|
||||||
+ ', height='
|
|
||||||
+ height
|
|
||||||
+ ', top='
|
|
||||||
+ top
|
|
||||||
+ ', left='
|
|
||||||
+ left);
|
|
||||||
};
|
|
||||||
|
|
||||||
// Export data on disk
|
|
||||||
utils.saveAs = function(content, filename) {
|
|
||||||
if(saveAs !== undefined) {
|
|
||||||
var blob = new Blob([content], {type: "text/plain;charset=utf-8"});
|
|
||||||
saveAs(blob, filename);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
var uriContent = "data:application/octet-stream;base64,"
|
|
||||||
+ utils.encodeBase64(content);
|
|
||||||
window.open(uriContent, 'file');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Generates a random string
|
// Return checkbox boolean value
|
||||||
utils.randomString = function() {
|
utils.getInputChecked = function(element) {
|
||||||
return _.random(4294967296).toString(36);
|
element = jqElt(element);
|
||||||
};
|
return element.prop("checked");
|
||||||
|
};
|
||||||
// Time shared by others modules
|
|
||||||
utils.updateCurrentTime = function() {
|
|
||||||
utils.currentTime = new Date().getTime();
|
|
||||||
};
|
|
||||||
utils.updateCurrentTime();
|
|
||||||
|
|
||||||
|
|
||||||
// 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;
|
// 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");
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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("");
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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(""));
|
||||||
|
};
|
||||||
|
|
||||||
|
// Export data on disk
|
||||||
|
utils.saveAs = function(content, filename) {
|
||||||
|
if(saveAs !== undefined) {
|
||||||
|
var blob = new Blob([
|
||||||
|
content
|
||||||
|
], {
|
||||||
|
type: "text/plain;charset=utf-8"
|
||||||
|
});
|
||||||
|
saveAs(blob, filename);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var uriContent = "data:application/octet-stream;base64," + utils.encodeBase64(content);
|
||||||
|
window.open(uriContent, 'file');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Generates a random string
|
||||||
|
utils.randomString = function() {
|
||||||
|
return _.random(4294967296).toString(36);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Time shared by others modules
|
||||||
|
utils.updateCurrentTime = function() {
|
||||||
|
utils.currentTime = new Date().getTime();
|
||||||
|
};
|
||||||
|
utils.updateCurrentTime();
|
||||||
|
|
||||||
|
// Serialize sync/publish attributes 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);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Base64 conversion
|
||||||
|
utils.encodeBase64 = function(str) {
|
||||||
|
if(str.length === 0) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
// UTF-8 to byte array
|
||||||
|
var bytes = [], offset = 0, length, char;
|
||||||
|
|
||||||
|
str = encodeURI(str);
|
||||||
|
length = str.length;
|
||||||
|
|
||||||
|
while (offset < length) {
|
||||||
|
char = str[offset];
|
||||||
|
offset += 1;
|
||||||
|
|
||||||
|
if('%' !== char) {
|
||||||
|
bytes.push(char.charCodeAt(0));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
char = str[offset] + str[offset + 1];
|
||||||
|
bytes.push(parseInt(char, 16));
|
||||||
|
offset += 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// byte array to base64
|
||||||
|
var padchar = '=';
|
||||||
|
var alpha = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
|
||||||
|
|
||||||
|
var i, b10;
|
||||||
|
var x = [];
|
||||||
|
|
||||||
|
var imax = bytes.length - bytes.length % 3;
|
||||||
|
|
||||||
|
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);
|
||||||
|
};
|
||||||
|
|
||||||
|
return utils;
|
||||||
});
|
});
|
||||||
|
@ -6,161 +6,160 @@ define([
|
|||||||
"async-runner"
|
"async-runner"
|
||||||
], function($, core, utils, extensionMgr, asyncRunner) {
|
], function($, core, utils, extensionMgr, asyncRunner) {
|
||||||
|
|
||||||
var token = undefined;
|
var token = undefined;
|
||||||
|
|
||||||
var wordpressHelper = {};
|
var wordpressHelper = {};
|
||||||
|
|
||||||
// Only used to check the offline status
|
// Only used to check the offline status
|
||||||
function connect(task) {
|
function connect(task) {
|
||||||
task.onRun(function() {
|
task.onRun(function() {
|
||||||
if(core.isOffline === true) {
|
if(core.isOffline === true) {
|
||||||
task.error(new Error("Operation not available in offline mode.|stopPublish"));
|
task.error(new Error("Operation not available in offline mode.|stopPublish"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
task.chain();
|
task.chain();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to authenticate with OAuth
|
// Try to authenticate with OAuth
|
||||||
function authenticate(task) {
|
function authenticate(task) {
|
||||||
var authWindow = undefined;
|
var authWindow = undefined;
|
||||||
var intervalId = undefined;
|
var intervalId = undefined;
|
||||||
task.onRun(function() {
|
task.onRun(function() {
|
||||||
token = localStorage["wordpressToken"];
|
token = localStorage["wordpressToken"];
|
||||||
if(token !== undefined) {
|
if(token !== undefined) {
|
||||||
task.chain();
|
task.chain();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
extensionMgr.onMessage("Please make sure the Wordpress authorization popup is not blocked by your browser.");
|
extensionMgr.onMessage("Please make sure the Wordpress authorization popup is not blocked by your browser.");
|
||||||
var errorMsg = "Failed to retrieve a token from Wordpress.";
|
var errorMsg = "Failed to retrieve a token from Wordpress.";
|
||||||
// We add time for user to enter his credentials
|
// We add time for user to enter his credentials
|
||||||
task.timeout = ASYNC_TASK_LONG_TIMEOUT;
|
task.timeout = ASYNC_TASK_LONG_TIMEOUT;
|
||||||
var code = undefined;
|
var code = undefined;
|
||||||
function getCode() {
|
function getCode() {
|
||||||
localStorage.removeItem("wordpressCode");
|
localStorage.removeItem("wordpressCode");
|
||||||
authWindow = utils.popupWindow(
|
authWindow = utils.popupWindow('wordpress-oauth-client.html?client_id=' + WORDPRESS_CLIENT_ID, 'stackedit-wordpress-oauth', 960, 600);
|
||||||
'wordpress-oauth-client.html?client_id=' + WORDPRESS_CLIENT_ID,
|
authWindow.focus();
|
||||||
'stackedit-wordpress-oauth', 960, 600);
|
intervalId = setInterval(function() {
|
||||||
authWindow.focus();
|
if(authWindow.closed === true) {
|
||||||
intervalId = setInterval(function() {
|
clearInterval(intervalId);
|
||||||
if(authWindow.closed === true) {
|
authWindow = undefined;
|
||||||
clearInterval(intervalId);
|
intervalId = undefined;
|
||||||
authWindow = undefined;
|
code = localStorage["wordpressCode"];
|
||||||
intervalId = undefined;
|
if(code === undefined) {
|
||||||
code = localStorage["wordpressCode"];
|
task.error(new Error(errorMsg));
|
||||||
if(code === undefined) {
|
return;
|
||||||
task.error(new Error(errorMsg));
|
}
|
||||||
return;
|
localStorage.removeItem("wordpressCode");
|
||||||
}
|
task.chain(getToken);
|
||||||
localStorage.removeItem("wordpressCode");
|
}
|
||||||
task.chain(getToken);
|
}, 500);
|
||||||
}
|
}
|
||||||
}, 500);
|
function getToken() {
|
||||||
}
|
$.getJSON(WORDPRESS_PROXY_URL + "authenticate/" + code, function(data) {
|
||||||
function getToken() {
|
if(data.token !== undefined) {
|
||||||
$.getJSON(WORDPRESS_PROXY_URL + "authenticate/" + code, function(data) {
|
token = data.token;
|
||||||
if(data.token !== undefined) {
|
localStorage["wordpressToken"] = token;
|
||||||
token = data.token;
|
task.chain();
|
||||||
localStorage["wordpressToken"] = token;
|
}
|
||||||
task.chain();
|
else {
|
||||||
}
|
task.error(new Error(errorMsg));
|
||||||
else {
|
}
|
||||||
task.error(new Error(errorMsg));
|
});
|
||||||
}
|
}
|
||||||
});
|
task.chain(getCode);
|
||||||
}
|
});
|
||||||
task.chain(getCode);
|
task.onError(function() {
|
||||||
});
|
if(intervalId !== undefined) {
|
||||||
task.onError(function() {
|
clearInterval(intervalId);
|
||||||
if(intervalId !== undefined) {
|
}
|
||||||
clearInterval(intervalId);
|
if(authWindow !== undefined) {
|
||||||
}
|
authWindow.close();
|
||||||
if(authWindow !== undefined) {
|
}
|
||||||
authWindow.close();
|
});
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
wordpressHelper.upload = function(site, postId, tags, title, content, callback) {
|
wordpressHelper.upload = function(site, postId, tags, title, content, callback) {
|
||||||
var task = asyncRunner.createTask();
|
var task = asyncRunner.createTask();
|
||||||
connect(task);
|
connect(task);
|
||||||
authenticate(task);
|
authenticate(task);
|
||||||
task.onRun(function() {
|
task.onRun(function() {
|
||||||
var url = WORDPRESS_PROXY_URL + "post";
|
var url = WORDPRESS_PROXY_URL + "post";
|
||||||
var data = {
|
var data = {
|
||||||
token: token,
|
token: token,
|
||||||
site: site,
|
site: site,
|
||||||
postId: postId,
|
postId: postId,
|
||||||
tags: tags,
|
tags: tags,
|
||||||
title: title,
|
title: title,
|
||||||
content: content
|
content: content
|
||||||
};
|
};
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url : url,
|
url: url,
|
||||||
data: data,
|
data: data,
|
||||||
type: "POST",
|
type: "POST",
|
||||||
dataType : "json",
|
dataType: "json",
|
||||||
timeout : AJAX_TIMEOUT
|
timeout: AJAX_TIMEOUT
|
||||||
}).done(function(response, textStatus, jqXHR) {
|
}).done(function(response, textStatus, jqXHR) {
|
||||||
if(response.body.ID) {
|
if(response.body.ID) {
|
||||||
postId = response.body.ID;
|
postId = response.body.ID;
|
||||||
task.chain();
|
task.chain();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
var error = {
|
var error = {
|
||||||
code: response.code,
|
code: response.code,
|
||||||
message: response.body.error
|
message: response.body.error
|
||||||
};
|
};
|
||||||
// Handle error
|
// Handle error
|
||||||
if(error.code === 404) {
|
if(error.code === 404) {
|
||||||
if(error.message == "unknown_blog") {
|
if(error.message == "unknown_blog") {
|
||||||
error = 'Site "' + site + '" not found on WordPress.|removePublish';
|
error = 'Site "' + site + '" not found on WordPress.|removePublish';
|
||||||
}
|
}
|
||||||
else if(error.message == "unknown_post"){
|
else if(error.message == "unknown_post") {
|
||||||
error = 'Post ' + postId + ' not found on WordPress.|removePublish';
|
error = 'Post ' + postId + ' not found on WordPress.|removePublish';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
handleError(error, task);
|
handleError(error, task);
|
||||||
}).fail(function(jqXHR) {
|
}).fail(function(jqXHR) {
|
||||||
var error = {
|
var error = {
|
||||||
code: jqXHR.status,
|
code: jqXHR.status,
|
||||||
message: jqXHR.statusText
|
message: jqXHR.statusText
|
||||||
};
|
};
|
||||||
handleError(error, task);
|
handleError(error, task);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
task.onSuccess(function() {
|
task.onSuccess(function() {
|
||||||
callback(undefined, postId);
|
callback(undefined, postId);
|
||||||
});
|
});
|
||||||
task.onError(function(error) {
|
task.onError(function(error) {
|
||||||
callback(error);
|
callback(error);
|
||||||
});
|
});
|
||||||
asyncRunner.addTask(task);
|
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));
|
|
||||||
}
|
|
||||||
|
|
||||||
return wordpressHelper;
|
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;
|
||||||
});
|
});
|
||||||
|
@ -2,48 +2,39 @@ define([
|
|||||||
"utils",
|
"utils",
|
||||||
"wordpress-helper"
|
"wordpress-helper"
|
||||||
], function(utils, wordpressHelper) {
|
], function(utils, wordpressHelper) {
|
||||||
|
|
||||||
var PROVIDER_WORDPRESS = "wordpress";
|
|
||||||
|
|
||||||
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.newPublishAttributes = function(event) {
|
var PROVIDER_WORDPRESS = "wordpress";
|
||||||
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;
|
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.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;
|
||||||
});
|
});
|
267
tools/eclipse-formatter-config.xml
Normal file
267
tools/eclipse-formatter-config.xml
Normal 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>
|
Loading…
Reference in New Issue
Block a user