New extension pattern

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

View File

@ -327,7 +327,6 @@ hr {
div.dropdown-menu { 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 31 KiB

View File

@ -50,15 +50,15 @@ define([
}; };
/** /**
* 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();
} }
@ -68,7 +68,7 @@ define([
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;
@ -78,11 +78,11 @@ define([
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");
@ -97,12 +97,12 @@ define([
* 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;
} }
@ -121,17 +121,17 @@ define([
_.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;
} }
@ -145,7 +145,7 @@ define([
} }
// Run the task // Run the task
if (currentTaskStartTime <= utils.currentTime) { if(currentTaskStartTime <= utils.currentTime) {
currentTaskRunning = true; currentTaskRunning = true;
currentTask.chain(); currentTask.chain();
} }
@ -162,14 +162,15 @@ define([
} }
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 { }
else {
asyncRunner.runTask(); asyncRunner.runTask();
} }
} }
@ -183,7 +184,7 @@ define([
// 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;
} }
}; };

View File

@ -10,18 +10,13 @@ define([
providerId: PROVIDER_BLOGGER, providerId: PROVIDER_BLOGGER,
providerName: "Blogger", providerName: "Blogger",
defaultPublishFormat: "html", defaultPublishFormat: "html",
publishPreferencesInputIds: ["blogger-url"] publishPreferencesInputIds: [
"blogger-url"
]
}; };
bloggerProvider.publish = function(publishAttributes, title, content, callback) { bloggerProvider.publish = function(publishAttributes, title, content, callback) {
googleHelper.uploadBlogger( googleHelper.uploadBlogger(publishAttributes.blogUrl, publishAttributes.blogId, publishAttributes.postId, publishAttributes.labelList, title, content, function(error, blogId, postId) {
publishAttributes.blogUrl,
publishAttributes.blogId,
publishAttributes.postId,
publishAttributes.labelList,
title,
content,
function(error, blogId, postId) {
if(error) { if(error) {
callback(error); callback(error);
return; return;
@ -29,8 +24,7 @@ define([
publishAttributes.blogId = blogId; publishAttributes.blogId = blogId;
publishAttributes.postId = postId; publishAttributes.postId = postId;
callback(); callback();
} });
);
}; };
bloggerProvider.newPublishAttributes = function(event) { bloggerProvider.newPublishAttributes = function(event) {
@ -43,9 +37,7 @@ define([
publishAttributes.labelList = []; publishAttributes.labelList = [];
var labels = utils.getInputTextValue("#input-publish-labels"); var labels = utils.getInputTextValue("#input-publish-labels");
if(labels !== undefined) { if(labels !== undefined) {
publishAttributes.labelList = _.chain( publishAttributes.labelList = _.chain(labels.split(",")).map(function(label) {
labels.split(",")
).map(function(label) {
return utils.trim(label); return utils.trim(label);
}).compact().value(); }).compact().value();
} }

View File

@ -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.install",
"https://www.googleapis.com/auth/drive", "https://www.googleapis.com/auth/drive",
"https://www.googleapis.com/auth/blogger" ]; "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";
@ -25,7 +27,7 @@ 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();
} }
} }

View File

@ -29,10 +29,10 @@ define([
userReal = true; userReal = true;
userActive = true; userActive = true;
userLastActivity = utils.currentTime; userLastActivity = utils.currentTime;
}; }
function isUserActive() { function isUserActive() {
if(userActive === true if(userActive === true && utils.currentTime - userLastActivity > USER_IDLE_THRESHOLD) {
&& utils.currentTime - userLastActivity > USER_IDLE_THRESHOLD) {
userActive = false; userActive = false;
} }
return userActive && windowUnique; return userActive && windowUnique;
@ -80,13 +80,13 @@ define([
} }
function checkOnline() { function checkOnline() {
// Try to reconnect if we are offline but we have some network // Try to reconnect if we are offline but we have some network
if (core.isOffline === true && navigator.onLine === true if(core.isOffline === true && navigator.onLine === true && offlineTime + CHECK_ONLINE_PERIOD < utils.currentTime) {
&& offlineTime + CHECK_ONLINE_PERIOD < utils.currentTime) {
offlineTime = utils.currentTime; offlineTime = utils.currentTime;
// Try to download anything to test the connection // Try to download anything to test the connection
$.ajax({ $.ajax({
url : "//www.google.com/jsapi", url: "//www.google.com/jsapi",
timeout : AJAX_TIMEOUT, dataType : "script" timeout: AJAX_TIMEOUT,
dataType: "script"
}).done(function() { }).done(function() {
setOnline(); setOnline();
}); });
@ -156,47 +156,41 @@ define([
return; return;
} }
var layoutGlobalConfig = { var layoutGlobalConfig = {
closable : true, closable: true,
resizable : false, resizable: false,
slidable : false, slidable: false,
livePaneResizing : true, livePaneResizing: true,
enableCursorHotkey : false, enableCursorHotkey: false,
spacing_open : 15, spacing_open: 15,
spacing_closed : 15, spacing_closed: 15,
togglerLength_open : 90, togglerLength_open: 90,
togglerLength_closed : 90, togglerLength_closed: 90,
stateManagement__enabled : false, stateManagement__enabled: false,
center__minWidth : 200, center__minWidth: 200,
center__minHeight : 200 center__minHeight: 200
}; };
extensionMgr.onLayoutConfigure(layoutGlobalConfig); extensionMgr.onLayoutConfigure(layoutGlobalConfig);
if (settings.layoutOrientation == "horizontal") { if(settings.layoutOrientation == "horizontal") {
$(".ui-layout-south").remove(); $(".ui-layout-south").remove();
$(".ui-layout-east").addClass("well").prop("id", "wmd-preview"); $(".ui-layout-east").addClass("well").prop("id", "wmd-preview");
layout = $('body').layout( layout = $('body').layout($.extend(layoutGlobalConfig, {
$.extend(layoutGlobalConfig, { east__resizable: true,
east__resizable : true, east__size: .5,
east__size : .5, east__minSize: 200
east__minSize : 200 }));
}) }
); else if(settings.layoutOrientation == "vertical") {
} else if (settings.layoutOrientation == "vertical") {
$(".ui-layout-east").remove(); $(".ui-layout-east").remove();
$(".ui-layout-south").addClass("well").prop("id", "wmd-preview"); $(".ui-layout-south").addClass("well").prop("id", "wmd-preview");
layout = $('body').layout( layout = $('body').layout($.extend(layoutGlobalConfig, {
$.extend(layoutGlobalConfig, { south__resizable: true,
south__resizable : true, south__size: .5,
south__size : .5, south__minSize: 200
south__minSize : 200 }));
})
);
} }
$(".ui-layout-toggler-north").addClass("btn").append( $(".ui-layout-toggler-north").addClass("btn").append($("<b>").addClass("caret"));
$("<b>").addClass("caret")); $(".ui-layout-toggler-south").addClass("btn").append($("<b>").addClass("caret"));
$(".ui-layout-toggler-south").addClass("btn").append( $(".ui-layout-toggler-east").addClass("btn").append($("<b>").addClass("caret"));
$("<b>").addClass("caret"));
$(".ui-layout-toggler-east").addClass("btn").append(
$("<b>").addClass("caret"));
$("#navbar").click(function() { $("#navbar").click(function() {
layout.allowOverflow('north'); layout.allowOverflow('north');
}); });
@ -210,7 +204,7 @@ define([
var converter = new Markdown.Converter(); var converter = new Markdown.Converter();
var editor = new Markdown.Editor(converter); var editor = new Markdown.Editor(converter);
// Custom insert link dialog // Custom insert link dialog
editor.hooks.set("insertLinkDialog", function (callback) { editor.hooks.set("insertLinkDialog", function(callback) {
insertLinkCallback = callback; insertLinkCallback = callback;
utils.resetModalInputs(); utils.resetModalInputs();
$("#modal-insert-link").modal(); $("#modal-insert-link").modal();
@ -220,7 +214,7 @@ define([
return true; return true;
}); });
// Custom insert image dialog // Custom insert image dialog
editor.hooks.set("insertImageDialog", function (callback) { editor.hooks.set("insertImageDialog", function(callback) {
insertLinkCallback = callback; insertLinkCallback = callback;
utils.resetModalInputs(); utils.resetModalInputs();
$("#modal-insert-image").modal(); $("#modal-insert-image").modal();
@ -270,8 +264,7 @@ define([
firstChange = false; firstChange = false;
// Hide default buttons // Hide default buttons
$(".wmd-button-row").addClass("btn-group").find("li:not(.wmd-spacer)") $(".wmd-button-row").addClass("btn-group").find("li:not(.wmd-spacer)").addClass("btn").css("left", 0).find("span").hide();
.addClass("btn").css("left", 0).find("span").hide();
// Add customized buttons // Add customized buttons
$("#wmd-bold-button").append($("<i>").addClass("icon-bold")); $("#wmd-bold-button").append($("<i>").addClass("icon-bold"));
@ -319,7 +312,7 @@ define([
// listen to online/offline events // listen to online/offline events
$(window).on('offline', core.setOffline); $(window).on('offline', core.setOffline);
$(window).on('online', setOnline); $(window).on('online', setOnline);
if (navigator.onLine === false) { if(navigator.onLine === false) {
core.setOffline(); core.setOffline();
} }
@ -378,7 +371,7 @@ define([
$("#wmd-input, #md-section-helper").css({ $("#wmd-input, #md-section-helper").css({
// Apply editor font size // Apply editor font size
"font-size": settings.editorFontSize + "px", "font-size": settings.editorFontSize + "px",
"line-height": Math.round(settings.editorFontSize * (20/14)) + "px" "line-height": Math.round(settings.editorFontSize * (20 / 14)) + "px"
}); });
// Manage tab key // Manage tab key
@ -398,17 +391,6 @@ define([
}); });
// Tooltips // Tooltips
$(".tooltip-scroll-link").tooltip({
html: true,
container: '#modal-settings',
placement: 'right',
title: ['Scroll Link is a feature that binds together editor and preview scrollbars. ',
'It allows you to keep an eye on the preview while scrolling the editor and vice versa. ',
'<br><br>',
'The mapping between Markdown and HTML is based on the position of the title elements (h1, h2, ...) in the page. ',
'Therefore, if your document does not contain any title, the mapping will be linear and consequently less efficient.',
].join("")
});
$(".tooltip-lazy-rendering").tooltip({ $(".tooltip-lazy-rendering").tooltip({
container: '#modal-settings', container: '#modal-settings',
placement: 'right', placement: 'right',
@ -425,18 +407,22 @@ define([
container: '#modal-settings', container: '#modal-settings',
placement: 'right', placement: 'right',
trigger: 'manual', trigger: 'manual',
title: ['Available variables:<br>', title: [
'<ul><li><b>documentTitle</b>: document title</li>', 'Available variables:<br>',
'<li><b>documentMarkdown</b>: document in Markdown format</li>', '<ul>',
'<li><b>documentHTML</b>: document in HTML format</li>', ' <li><b>documentTitle</b>: document title</li>',
'<li><b>publishAttributes</b>: attributes of the publish location (undefined when using "Save")</li></ul>', ' <li><b>documentMarkdown</b>: document in Markdown format</li>',
'Examples:<br>', ' <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>'), _.escape('<title><%= documentTitle %></title>'),
'<br>', '<br />',
_.escape('<div><%- documentHTML %></div>'), _.escape('<div><%- documentHTML %></div>'),
'<br>', '<br />',
_.escape('<% if(publishAttributes.provider == "github") print(documentMarkdown); %>'), _.escape('<% if(publishAttributes.provider == "github") print(documentMarkdown); %>'),
'<br><br><a target="_blank" href="http://underscorejs.org/#template">More info</a>', '<br /><br />',
'<a target="_blank" href="http://underscorejs.org/#template">More info</a>',
].join("") ].join("")
}).click(function(e) { }).click(function(e) {
$(this).tooltip('show'); $(this).tooltip('show');
@ -467,4 +453,3 @@ define([
return core; return core;
}); });

View File

@ -8,7 +8,9 @@ define([
var downloadProvider = { var downloadProvider = {
providerId: PROVIDER_DOWNLOAD, providerId: PROVIDER_DOWNLOAD,
sharingAttributes: ["url"] sharingAttributes: [
"url"
]
}; };
downloadProvider.importPublic = function(importParameters, callback) { downloadProvider.importPublic = function(importParameters, callback) {
@ -24,10 +26,10 @@ define([
} }
title = url.substring(slashUrl + 1); title = url.substring(slashUrl + 1);
$.ajax({ $.ajax({
url : DOWNLOAD_PROXY_URL + "download?url=" + url, url: DOWNLOAD_PROXY_URL + "download?url=" + url,
type: "GET", type: "GET",
dataType : "text", dataType: "text",
timeout : AJAX_TIMEOUT timeout: AJAX_TIMEOUT
}).done(function(result, textStatus, jqXHR) { }).done(function(result, textStatus, jqXHR) {
content = result; content = result;
task.chain(); task.chain();

View File

@ -19,13 +19,14 @@ define([
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",
timeout: AJAX_TIMEOUT
}).done(function() { }).done(function() {
client = new Dropbox.Client({ client = new Dropbox.Client({
key: DROPBOX_APP_KEY, key: DROPBOX_APP_KEY,
@ -49,27 +50,30 @@ define([
// 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
// credentials
task.timeout = ASYNC_TASK_LONG_TIMEOUT; task.timeout = ASYNC_TASK_LONG_TIMEOUT;
} }
client.reset(); client.reset();
client.authenticate({interactive: !immediate}, function(error, client) { client.authenticate({
interactive: !immediate
}, function(error, client) {
// Success // Success
if (client.authState === Dropbox.Client.DONE) { if(client.authState === Dropbox.Client.DONE) {
authenticated = true; authenticated = true;
task.chain(); task.chain();
return; return;
} }
// If immediate did not work retry without immediate flag // If immediate did not work retry without immediate flag
if (immediate === true) { if(immediate === true) {
immediate = false; immediate = false;
task.chain(localAuthenticate); task.chain(localAuthenticate);
return; return;
@ -89,7 +93,7 @@ define([
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;
@ -119,7 +123,7 @@ define([
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;
} }
@ -128,9 +132,10 @@ define([
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 { }
else {
task.chain(); task.chain();
} }
}); });
@ -229,26 +234,26 @@ define([
function handleError(error, task) { function handleError(error, task) {
var errorMsg = true; var errorMsg = true;
if (error) { if(error) {
logger.error(error); logger.error(error);
// Try to analyze the error // Try to analyze the error
if (typeof error === "string") { if(typeof error === "string") {
errorMsg = error; errorMsg = error;
} }
else { else {
errorMsg = "Dropbox error (" errorMsg = "Dropbox error (" + error.status + ": " + error.responseText + ").";
+ error.status + ": " + error.responseText + ").";
if (error.status === 401 || error.status === 403) { if(error.status === 401 || error.status === 403) {
authenticated = false; authenticated = false;
errorMsg = "Access to Dropbox account is not authorized."; errorMsg = "Access to Dropbox account is not authorized.";
task.retry(new Error(errorMsg), 1); task.retry(new Error(errorMsg), 1);
return; return;
} else if(error.status === 400 && error.responseText }
.indexOf("oauth_nonce") !== -1) { else if(error.status === 400 && error.responseText.indexOf("oauth_nonce") !== -1) {
// A bug I guess... // A bug I guess...
_.each(_.keys(localStorage), function(key) { _.each(_.keys(localStorage), function(key) {
// We have to remove the Oauth cache from the localStorage // We have to remove the Oauth cache from the
// localStorage
if(key.indexOf("dropbox-auth") === 0) { if(key.indexOf("dropbox-auth") === 0) {
localStorage.removeItem(key); localStorage.removeItem(key);
} }
@ -256,7 +261,8 @@ define([
authenticated = false; authenticated = false;
task.retry(new Error(errorMsg), 1); task.retry(new Error(errorMsg), 1);
return; return;
} else if (error.status <= 0) { }
else if(error.status <= 0) {
client = undefined; client = undefined;
authenticated = false; authenticated = false;
core.setOffline(); core.setOffline();
@ -270,13 +276,14 @@ define([
var pickerLoaded = false; var pickerLoaded = false;
function loadPicker(task) { function loadPicker(task) {
task.onRun(function() { task.onRun(function() {
if (pickerLoaded === true) { if(pickerLoaded === true) {
task.chain(); task.chain();
return; return;
} }
$.ajax({ $.ajax({
url : "https://www.dropbox.com/static/api/1/dropbox.js", url: "https://www.dropbox.com/static/api/1/dropbox.js",
dataType : "script", timeout : AJAX_TIMEOUT dataType: "script",
timeout: AJAX_TIMEOUT
}).done(function() { }).done(function() {
pickerLoaded = true; pickerLoaded = true;
task.chain(); task.chain();
@ -302,7 +309,7 @@ define([
options.multiselect = true; options.multiselect = true;
options.linkType = "direct"; options.linkType = "direct";
options.success = function(files) { options.success = function(files) {
for(var i=0; i<files.length; i++) { for ( var i = 0; i < files.length; i++) {
var path = files[i].link; var path = files[i].link;
path = path.replace(/.*\/view\/[^\/]*/, ""); path = path.replace(/.*\/view\/[^\/]*/, "");
paths.push(decodeURI(path)); paths.push(decodeURI(path));

View File

@ -101,7 +101,7 @@ define([
return; return;
} }
dropboxHelper.upload(path, content, function(error, result) { dropboxHelper.upload(path, content, function(error, result) {
if (error) { if(error) {
callback(error); callback(error);
return; return;
} }
@ -141,7 +141,7 @@ define([
dropboxProvider.syncDown = function(callback) { dropboxProvider.syncDown = function(callback) {
var lastChangeId = localStorage[PROVIDER_DROPBOX + ".lastChangeId"]; var lastChangeId = localStorage[PROVIDER_DROPBOX + ".lastChangeId"];
dropboxHelper.checkChanges(lastChangeId, function(error, changes, newChangeId) { dropboxHelper.checkChanges(lastChangeId, function(error, changes, newChangeId) {
if (error) { if(error) {
callback(error); callback(error);
return; return;
} }
@ -165,7 +165,7 @@ define([
} }
}); });
dropboxHelper.downloadContent(interestingChanges, function(error, changes) { dropboxHelper.downloadContent(interestingChanges, function(error, changes) {
if (error) { if(error) {
callback(error); callback(error);
return; return;
} }
@ -173,31 +173,32 @@ define([
var syncAttributes = change.syncAttributes; var syncAttributes = change.syncAttributes;
var syncIndex = syncAttributes.syncIndex; var syncIndex = syncAttributes.syncIndex;
var fileDesc = fileMgr.getFileFromSyncIndex(syncIndex); var fileDesc = fileMgr.getFileFromSyncIndex(syncIndex);
// No file corresponding (file may have been deleted locally) // No file corresponding (file may have been deleted
// locally)
if(fileDesc === undefined) { if(fileDesc === undefined) {
return; return;
} }
var localTitle = fileDesc.title; var localTitle = fileDesc.title;
// File deleted // File deleted
if (change.wasRemoved === true) { if(change.wasRemoved === true) {
extensionMgr.onError('"' + localTitle + '" has been removed from Dropbox.'); extensionMgr.onError('"' + localTitle + '" has been removed from Dropbox.');
fileMgr.removeSync(syncAttributes); fileMgr.removeSync(syncAttributes);
return; return;
} }
var localContent = fileDesc.getContent(); var localContent = fileDesc.content;
var localContentChanged = syncAttributes.contentCRC != utils.crc32(localContent); var localContentChanged = syncAttributes.contentCRC != utils.crc32(localContent);
var file = change.stat; 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

View File

@ -1,4 +1,4 @@
define( [ define([
"jquery", "jquery",
"underscore", "underscore",
"utils", "utils",
@ -24,17 +24,13 @@ define( [
var extensionMgr = {}; var extensionMgr = {};
// Create a list of extensions // Create a list of extensions
var extensionList = _.chain( var extensionList = _.chain(arguments).map(function(argument) {
arguments
).map(function(argument) {
return _.isObject(argument) && argument.extensionId && argument; return _.isObject(argument) && argument.extensionId && argument;
}).compact().value(); }).compact().value();
// Return every named callbacks implemented in extensions // Return every named callbacks implemented in extensions
function getExtensionCallbackList(hookName) { function getExtensionCallbackList(hookName) {
return _.chain( return _.chain(extensionList).map(function(extension) {
extensionList
).map(function(extension) {
return extension.config.enabled && extension[hookName]; return extension.config.enabled && extension[hookName];
}).compact().value(); }).compact().value();
} }
@ -75,7 +71,7 @@ define( [
extensionMgr["onSaveSettings"] = function(newExtensionSettings, event) { extensionMgr["onSaveSettings"] = function(newExtensionSettings, event) {
logger.debug("onSaveSettings"); logger.debug("onSaveSettings");
_.each(extensionList, function(extension) { _.each(extensionList, function(extension) {
var newExtensionConfig = extension.defaultConfig || {}; var newExtensionConfig = _.extend({}, extension.defaultConfig);
newExtensionConfig.enabled = utils.getInputChecked("#input-enable-extension-" + extension.extensionId); newExtensionConfig.enabled = utils.getInputChecked("#input-enable-extension-" + extension.extensionId);
var onSaveSettingsCallback = extension.onSaveSettings; var onSaveSettingsCallback = extension.onSaveSettings;
onSaveSettingsCallback && onSaveSettingsCallback(newExtensionConfig, event); onSaveSettingsCallback && onSaveSettingsCallback(newExtensionConfig, event);
@ -143,18 +139,19 @@ define( [
var accordionTmpl = [ 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) { function createSettings(extension) {
$("#accordion-extensions").append($(_.template(accordionTmpl, { $("#accordion-extensions").append($(_.template(accordionTmpl, {
@ -167,9 +164,7 @@ define( [
$(function() { $(function() {
// Create accordion in settings dialog // Create accordion in settings dialog
_.chain( _.chain(extensionList).sortBy(function(extension) {
extensionList
).sortBy(function(extension) {
return extension.extensionName.toLowerCase(); return extension.extensionName.toLowerCase();
}).each(createSettings); }).each(createSettings);

View File

@ -25,7 +25,8 @@ define([
else { else {
button.removeClass("disabled"); button.removeClass("disabled");
} }
}; }
;
var publisher = undefined; var publisher = undefined;
buttonPublish.onPublisherCreated = function(publisherParameter) { buttonPublish.onPublisherCreated = function(publisherParameter) {
@ -35,9 +36,9 @@ define([
buttonPublish.onCreateButton = function() { buttonPublish.onCreateButton = function() {
button = $([ button = $([
'<button class="btn" title="Publish this document">', '<button class="btn" title="Publish this document">',
'<i class="icon-share"></i>', ' <i class="icon-share"></i>',
'</button>'].join("") '</button>'
).click(function() { ].join("")).click(function() {
if(!$(this).hasClass("disabled")) { if(!$(this).hasClass("disabled")) {
publisher.publish(); publisher.publish();
} }

View File

@ -13,29 +13,31 @@ define([
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 ',
' <a href="#" class="action-publish-gist">publish it as a Gist</a>',
' in Markdown format.', ' in Markdown format.',
'</p>', ' </p>',
'<blockquote class="muted">', ' <blockquote class="muted">',
'<b>NOTE:</b> You can open any URL within StackEdit using ', ' <b>NOTE:</b> You can open any URL within StackEdit using',
'<a href="viewer.html?url=https://raw.github.com/benweet/stackedit/master/README.md"', ' <a href="viewer.html?url=https://raw.github.com/benweet/stackedit/master/README.md"',
'title="Sharing example">viewer.html?url=...</a>', ' title="Sharing example">viewer.html?url=...</a>',
'</blockquote>', ' </blockquote>',
'</div>'].join("") '</div>'
); ].join(""));
}; };
var fileDesc = undefined; var fileDesc = undefined;
var lineTemplate = [ 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>'
].join("");
var refreshDocumentSharing = function(fileDescParameter) { var refreshDocumentSharing = function(fileDescParameter) {
if(fileDescParameter !== undefined && fileDescParameter !== fileDesc) { if(fileDescParameter !== undefined && fileDescParameter !== fileDesc) {
return; return;

View File

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

View File

@ -34,9 +34,9 @@ define([
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();
} }

View File

@ -35,9 +35,7 @@ define([
liMap = {}; liMap = {};
$("#file-selector li:not(.stick)").empty(); $("#file-selector li:not(.stick)").empty();
_.chain( _.chain(fileSystem).sortBy(function(fileDesc) {
fileSystem
).sortBy(function(fileDesc) {
return fileDesc.title.toLowerCase(); return fileDesc.title.toLowerCase();
}).each(function(fileDesc) { }).each(function(fileDesc) {
var a = $('<a href="#">').html(composeTitle(fileDesc)).click(function() { var a = $('<a href="#">').html(composeTitle(fileDesc)).click(function() {
@ -67,6 +65,37 @@ define([
documentSelector.onNewPublishSuccess = buildSelector; documentSelector.onNewPublishSuccess = buildSelector;
documentSelector.onPublishRemoved = 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; return documentSelector;
}); });

View File

@ -17,4 +17,3 @@ define(function() {
return emailConverter; return emailConverter;
}); });

View File

@ -17,11 +17,12 @@ define([
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>'
].join("");
var removeButtonTemplate = '<a class="btn" title="Remove this location"><i class="icon-trash"></i></a>'; var removeButtonTemplate = '<a class="btn" title="Remove this location"><i class="icon-trash"></i></a>';
var refreshDialog = function(fileDescParameter) { var refreshDialog = function(fileDescParameter) {
if(fileDescParameter !== undefined && fileDescParameter !== fileDesc) { if(fileDescParameter !== undefined && fileDescParameter !== fileDesc) {
@ -31,9 +32,10 @@ define([
var publishAttributesList = _.values(fileDesc.publishLocations); var publishAttributesList = _.values(fileDesc.publishLocations);
$(".msg-no-publish, .msg-publish-list").addClass("hide"); $(".msg-no-publish, .msg-publish-list").addClass("hide");
var publishList = $("#manage-publish-list").empty(); var publishList = $("#manage-publish-list").empty();
if (publishAttributesList.length > 0) { if(publishAttributesList.length > 0) {
$(".msg-publish-list").removeClass("hide"); $(".msg-publish-list").removeClass("hide");
} else { }
else {
$(".msg-no-publish").removeClass("hide"); $(".msg-no-publish").removeClass("hide");
} }
_.each(publishAttributesList, function(publishAttributes) { _.each(publishAttributesList, function(publishAttributes) {

View File

@ -17,11 +17,12 @@ define([
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>'
].join("");
var removeButtonTemplate = '<a class="btn" title="Remove this location"><i class="icon-trash"></i></a>'; var removeButtonTemplate = '<a class="btn" title="Remove this location"><i class="icon-trash"></i></a>';
var refreshDialog = function(fileDescParameter) { var refreshDialog = function(fileDescParameter) {
if(fileDescParameter !== undefined && fileDescParameter !== fileDesc) { if(fileDescParameter !== undefined && fileDescParameter !== fileDesc) {
@ -31,9 +32,10 @@ define([
var syncAttributesList = _.values(fileDesc.syncLocations); var syncAttributesList = _.values(fileDesc.syncLocations);
$(".msg-no-sync, .msg-sync-list").addClass("hide"); $(".msg-no-sync, .msg-sync-list").addClass("hide");
var syncList = $("#manage-sync-list").empty(); var syncList = $("#manage-sync-list").empty();
if (syncAttributesList.length > 0) { if(syncAttributesList.length > 0) {
$(".msg-sync-list").removeClass("hide"); $(".msg-sync-list").removeClass("hide");
} else { }
else {
$(".msg-no-sync").removeClass("hide"); $(".msg-no-sync").removeClass("hide");
} }
_.each(syncAttributesList, function(syncAttributes) { _.each(syncAttributesList, function(syncAttributes) {

View File

@ -13,12 +13,12 @@ define([
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("")
}; };

View File

@ -14,13 +14,13 @@ define([
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("")
}; };
@ -75,14 +75,14 @@ define([
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');
} }
}; };

View File

@ -11,8 +11,8 @@ define([
settingsBloc: [ settingsBloc: [
'<p>Binds together editor and preview scrollbars.</p>', '<p>Binds together editor and preview scrollbars.</p>',
'<blockquote class="muted"><b>NOTE:</b> ', '<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. ', ' 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.', ' Therefore, if your document does not contain any title, the mapping will be linear and consequently less accurate.',
'</bloquote>' '</bloquote>'
].join("") ].join("")
}; };
@ -20,7 +20,7 @@ define([
var mdSectionList = []; var mdSectionList = [];
var htmlSectionList = []; var htmlSectionList = [];
function pxToFloat(px) { function pxToFloat(px) {
return parseFloat(px.substring(0, px.length-2)); return parseFloat(px.substring(0, px.length - 2));
} }
var buildSections = _.debounce(function() { var buildSections = _.debounce(function() {
@ -51,25 +51,23 @@ define([
} }
// Create MD sections by finding title patterns (excluding gfm blocs) // Create MD sections by finding title patterns (excluding gfm blocs)
var text = editorElt.val() + "\n\n"; 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, text.replace(/^```.*\n[\s\S]*?\n```|(^.+[ \t]*\n=+[ \t]*\n+|^.+[ \t]*\n-+[ \t]*\n+|^\#{1,6}[ \t]*.+?[ \t]*\#*\n+)/gm, function(match, title, matchOffset) {
function(match, title, matchOffset) {
if(title) { if(title) {
// We just found a title which means end of the previous section // We just found a title which means end of the previous section
// Exclude last \n of the section // Exclude last \n of the section
var sectionText = undefined; var sectionText = undefined;
if(matchOffset > offset) { if(matchOffset > offset) {
sectionText = text.substring(offset, matchOffset-1); sectionText = text.substring(offset, matchOffset - 1);
} }
addMdSection(sectionText); addMdSection(sectionText);
offset = matchOffset; offset = matchOffset;
} }
return ""; return "";
} });
);
// Last section // Last section
// Consider wmd-input bottom padding and exclude \n\n previously added // Consider wmd-input bottom padding and exclude \n\n previously added
padding += pxToFloat(editorElt.css('padding-bottom')); padding += pxToFloat(editorElt.css('padding-bottom'));
addMdSection(text.substring(offset, text.length-2)); addMdSection(text.substring(offset, text.length - 2));
// Try to find corresponding sections in the preview // Try to find corresponding sections in the preview
var previewElt = $("#wmd-preview"); var previewElt = $("#wmd-preview");
@ -129,12 +127,17 @@ define([
var posInSection = (srcScrollTop - srcSection.startOffset) / srcSection.height; var posInSection = (srcScrollTop - srcSection.startOffset) / srcSection.height;
var destSection = destSectionList[sectionIndex]; var destSection = destSectionList[sectionIndex];
var destScrollTop = destSection.startOffset + destSection.height * posInSection; var destScrollTop = destSection.startOffset + destSection.height * posInSection;
destScrollTop = _.min([destScrollTop, destElt.prop('scrollHeight') - destElt.outerHeight()]); destScrollTop = _.min([
destScrollTop,
destElt.prop('scrollHeight') - destElt.outerHeight()
]);
if(Math.abs(destScrollTop - lastDestScrollTop) < 5) { if(Math.abs(destScrollTop - lastDestScrollTop) < 5) {
// Skip the animation in case it's not necessary // Skip the animation in case it's not necessary
return; return;
} }
destElt.animate({scrollTop: destScrollTop}, 600, function() { destElt.animate({
scrollTop: destScrollTop
}, 600, function() {
callback(destScrollTop); callback(destScrollTop);
}); });
} }

View File

@ -77,7 +77,7 @@ define([
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;
@ -87,17 +87,8 @@ define([
} }
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," +
"#wmd-preview > h5," +
"#wmd-preview > h6").each(function() {
elementList.push(new TocElement(
$(this).prop("tagName"),
createAnchor($(this)),
$(this).text()
));
}); });
elementList = groupTags(elementList); elementList = groupTags(elementList);
return '<div class="toc"><ul>' + elementList.toString() + '</ul></div>'; return '<div class="toc"><ul>' + elementList.toString() + '</ul></div>';
@ -115,4 +106,3 @@ define([
return toc; return toc;
}); });

View File

@ -10,10 +10,11 @@ define([
}; };
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 { }
else {
$(".working-indicator").addClass("show"); $(".working-indicator").addClass("show");
$("body").addClass("working"); $("body").addClass("working");
} }

View File

@ -14,27 +14,28 @@ define([
// Defines a file descriptor in the file system (fileDesc objects) // Defines a file descriptor in the file system (fileDesc objects)
function FileDescriptor(fileIndex, title, syncLocations, publishLocations) { function FileDescriptor(fileIndex, title, syncLocations, publishLocations) {
this.fileIndex = fileIndex; this.fileIndex = fileIndex;
this.title = title; this._title = title;
this.__defineGetter__("title", function() {
return this._title;
});
this.__defineSetter__("title", function(title) {
this._title = title;
localStorage[this.fileIndex + ".title"] = title;
extensionMgr.onTitleChanged(this);
});
this.__defineGetter__("content", function() {
return localStorage[this.fileIndex + ".content"];
});
this.__defineSetter__("content", function(content) {
localStorage[this.fileIndex + ".content"] = content;
extensionMgr.onContentChanged(this);
});
this.syncLocations = syncLocations || {}; this.syncLocations = syncLocations || {};
this.publishLocations = publishLocations || {}; 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 // Load file descriptors from localStorage
_.chain( _.chain(localStorage["file.list"].split(";")).compact().each(function(fileIndex) {
localStorage["file.list"].split(";")
).compact().each(function(fileIndex) {
fileSystem[fileIndex] = new FileDescriptor(fileIndex, localStorage[fileIndex + ".title"]); fileSystem[fileIndex] = new FileDescriptor(fileIndex, localStorage[fileIndex + ".title"]);
}); });
@ -63,7 +64,7 @@ define([
if(fileDesc === undefined) { if(fileDesc === undefined) {
var fileSystemSize = _.size(fileSystem); var fileSystemSize = _.size(fileSystem);
// If fileSystem empty create one file // If fileSystem empty create one file
if (fileSystemSize === 0) { if(fileSystemSize === 0) {
fileDesc = fileMgr.createFile(WELCOME_DOCUMENT_TITLE, welcomeContent); fileDesc = fileMgr.createFile(WELCOME_DOCUMENT_TITLE, welcomeContent);
} }
else { else {
@ -92,7 +93,7 @@ define([
} }
// Recreate the editor // Recreate the editor
$("#wmd-input").val(fileDesc.getContent()); $("#wmd-input").val(fileDesc.content);
core.createEditor(function() { core.createEditor(function() {
// Callback to save content when textarea changes // Callback to save content when textarea changes
fileMgr.saveFile(); fileMgr.saveFile();
@ -101,11 +102,11 @@ define([
fileMgr.createFile = function(title, content, syncLocations, isTemporary) { fileMgr.createFile = function(title, content, syncLocations, isTemporary) {
content = content !== undefined ? content : settings.defaultContent; content = content !== undefined ? content : settings.defaultContent;
if (!title) { if(!title) {
// Create a file title // Create a file title
title = DEFAULT_FILE_TITLE; title = DEFAULT_FILE_TITLE;
var indicator = 2; var indicator = 2;
while(_.some(fileSystem, function(fileDesc) { while (_.some(fileSystem, function(fileDesc) {
return fileDesc.title == title; return fileDesc.title == title;
})) { })) {
title = DEFAULT_FILE_TITLE + indicator++; title = DEFAULT_FILE_TITLE + indicator++;
@ -117,7 +118,7 @@ define([
if(!isTemporary) { if(!isTemporary) {
do { do {
fileIndex = "file." + utils.randomString(); fileIndex = "file." + utils.randomString();
} while(_.has(fileSystem, fileIndex)); } while (_.has(fileSystem, fileIndex));
} }
// syncIndex associations // syncIndex associations
@ -164,8 +165,7 @@ define([
// Remove the index from the file list // Remove the index from the file list
var fileIndex = fileDesc.fileIndex; var fileIndex = fileDesc.fileIndex;
localStorage["file.list"] = localStorage["file.list"].replace(";" localStorage["file.list"] = localStorage["file.list"].replace(";" + fileIndex + ";", ";");
+ fileIndex + ";", ";");
localStorage.removeItem(fileIndex + ".title"); localStorage.removeItem(fileIndex + ".title");
localStorage.removeItem(fileIndex + ".content"); localStorage.removeItem(fileIndex + ".content");
@ -178,9 +178,8 @@ define([
// Save current file in localStorage // Save current file in localStorage
fileMgr.saveFile = function() { fileMgr.saveFile = function() {
var content = $("#wmd-input").val();
var fileDesc = fileMgr.getCurrentFile(); var fileDesc = fileMgr.getCurrentFile();
fileDesc.setContent(content); fileDesc.content = $("#wmd-input").val();
}; };
// Add a synchronized location to a file // Add a synchronized location to a file
@ -195,8 +194,7 @@ define([
fileMgr.removeSync = function(syncAttributes, skipExtensions) { fileMgr.removeSync = function(syncAttributes, skipExtensions) {
var fileDesc = fileMgr.getFileFromSyncIndex(syncAttributes.syncIndex); var fileDesc = fileMgr.getFileFromSyncIndex(syncAttributes.syncIndex);
if(fileDesc !== undefined) { if(fileDesc !== undefined) {
localStorage[fileDesc.fileIndex + ".sync"] = localStorage[fileDesc.fileIndex + ".sync"].replace(";" localStorage[fileDesc.fileIndex + ".sync"] = localStorage[fileDesc.fileIndex + ".sync"].replace(";" + syncAttributes.syncIndex + ";", ";");
+ syncAttributes.syncIndex + ";", ";");
} }
// Remove sync attributes // Remove sync attributes
localStorage.removeItem(syncAttributes.syncIndex); localStorage.removeItem(syncAttributes.syncIndex);
@ -239,8 +237,7 @@ define([
fileMgr.removePublish = function(publishAttributes, skipExtensions) { fileMgr.removePublish = function(publishAttributes, skipExtensions) {
var fileDesc = fileMgr.getFileFromPublishIndex(publishAttributes.publishIndex); var fileDesc = fileMgr.getFileFromPublishIndex(publishAttributes.publishIndex);
if(fileDesc !== undefined) { if(fileDesc !== undefined) {
localStorage[fileDesc.fileIndex + ".publish"] = localStorage[fileDesc.fileIndex + ".publish"].replace(";" localStorage[fileDesc.fileIndex + ".publish"] = localStorage[fileDesc.fileIndex + ".publish"].replace(";" + publishAttributes.publishIndex + ";", ";");
+ publishAttributes.publishIndex + ";", ";");
} }
// Remove publish attributes // Remove publish attributes
localStorage.removeItem(publishAttributes.publishIndex); localStorage.removeItem(publishAttributes.publishIndex);
@ -257,23 +254,6 @@ define([
}); });
}; };
// Filter for search input in file selector
function filterFileSelector(filter) {
var liList = $("#file-selector li:not(.stick)");
liList.show();
if(filter) {
var words = filter.toLowerCase().split(/\s+/);
liList.each(function() {
var fileTitle = $(this).text().toLowerCase();
if(_.some(words, function(word) {
return fileTitle.indexOf(word) === -1;
})) {
$(this).hide();
}
});
}
}
core.onReady(function() { core.onReady(function() {
fileMgr.selectFile(); fileMgr.selectFile();
@ -305,8 +285,8 @@ define([
$("#file-title").show(); $("#file-title").show();
var title = $.trim(input.val()); var title = $.trim(input.val());
var fileDesc = fileMgr.getCurrentFile(); var fileDesc = fileMgr.getCurrentFile();
if (title && title != fileDesc.title) { if(title && title != fileDesc.title) {
fileDesc.setTitle(title); fileDesc.title = title;
} }
input.val(fileDesc.title); input.val(fileDesc.title);
$("#wmd-input").focus(); $("#wmd-input").focus();
@ -314,25 +294,14 @@ define([
$("#file-title-input").blur(function() { $("#file-title-input").blur(function() {
applyTitle($(this)); applyTitle($(this));
}).keyup(function(e) { }).keyup(function(e) {
if (e.keyCode == 13) { if(e.keyCode == 13) {
applyTitle($(this)); applyTitle($(this));
} }
if (e.keyCode == 27) { if(e.keyCode == 27) {
$(this).val(""); $(this).val("");
applyTitle($(this)); 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() { $(".action-open-stackedit").click(function() {
window.location.href = "."; window.location.href = ".";
}); });

View File

@ -13,7 +13,9 @@ define([
providerId: PROVIDER_GDRIVE, providerId: PROVIDER_GDRIVE,
providerName: "Google Drive", providerName: "Google Drive",
defaultPublishFormat: "template", defaultPublishFormat: "template",
exportPreferencesInputIds: ["gdrive-parentid"] exportPreferencesInputIds: [
"gdrive-parentid"
]
}; };
function createSyncIndex(id) { function createSyncIndex(id) {
@ -53,7 +55,8 @@ define([
extensionMgr.onSyncImportSuccess(fileDescList, gdriveProvider); extensionMgr.onSyncImportSuccess(fileDescList, gdriveProvider);
}); });
}); });
}; }
;
gdriveProvider.importFiles = function() { gdriveProvider.importFiles = function() {
googleHelper.picker(function(error, ids) { googleHelper.picker(function(error, ids) {
@ -77,7 +80,7 @@ define([
gdriveProvider.exportFile = function(event, title, content, callback) { gdriveProvider.exportFile = function(event, title, content, callback) {
var parentId = utils.getInputTextValue("#input-sync-export-gdrive-parentid"); var parentId = utils.getInputTextValue("#input-sync-export-gdrive-parentid");
googleHelper.upload(undefined, parentId, title, content, undefined, function(error, result) { googleHelper.upload(undefined, parentId, title, content, undefined, function(error, result) {
if (error) { if(error) {
callback(error); callback(error);
return; return;
} }
@ -100,7 +103,7 @@ define([
return; return;
} }
googleHelper.upload(id, undefined, title, content, undefined, function(error, result) { googleHelper.upload(id, undefined, title, content, undefined, function(error, result) {
if (error) { if(error) {
callback(error); callback(error);
return; return;
} }
@ -132,7 +135,7 @@ define([
gdriveProvider.syncDown = function(callback) { gdriveProvider.syncDown = function(callback) {
var lastChangeId = parseInt(localStorage[PROVIDER_GDRIVE + ".lastChangeId"]); var lastChangeId = parseInt(localStorage[PROVIDER_GDRIVE + ".lastChangeId"]);
googleHelper.checkChanges(lastChangeId, function(error, changes, newChangeId) { googleHelper.checkChanges(lastChangeId, function(error, changes, newChangeId) {
if (error) { if(error) {
callback(error); callback(error);
return; return;
} }
@ -156,7 +159,7 @@ define([
} }
}); });
googleHelper.downloadContent(interestingChanges, function(error, changes) { googleHelper.downloadContent(interestingChanges, function(error, changes) {
if (error) { if(error) {
callback(error); callback(error);
return; return;
} }
@ -164,19 +167,20 @@ define([
var syncAttributes = change.syncAttributes; var syncAttributes = change.syncAttributes;
var syncIndex = syncAttributes.syncIndex; var syncIndex = syncAttributes.syncIndex;
var fileDesc = fileMgr.getFileFromSyncIndex(syncIndex); var fileDesc = fileMgr.getFileFromSyncIndex(syncIndex);
// No file corresponding (file may have been deleted locally) // No file corresponding (file may have been deleted
// locally)
if(fileDesc === undefined) { if(fileDesc === undefined) {
return; return;
} }
var localTitle = fileDesc.title; var localTitle = fileDesc.title;
// File deleted // File deleted
if (change.deleted === true) { if(change.deleted === true) {
extensionMgr.onError('"' + localTitle + '" has been removed from Google Drive.'); extensionMgr.onError('"' + localTitle + '" has been removed from Google Drive.');
fileMgr.removeSync(syncAttributes); fileMgr.removeSync(syncAttributes);
return; return;
} }
var localTitleChanged = syncAttributes.titleCRC != utils.crc32(localTitle); var localTitleChanged = syncAttributes.titleCRC != utils.crc32(localTitle);
var localContent = fileDesc.getContent(); var localContent = fileDesc.content;
var localContentChanged = syncAttributes.contentCRC != utils.crc32(localContent); var localContentChanged = syncAttributes.contentCRC != utils.crc32(localContent);
var file = change.file; var file = change.file;
var remoteTitleCRC = utils.crc32(file.title); var remoteTitleCRC = utils.crc32(file.title);
@ -186,19 +190,18 @@ define([
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.setTitle(file.title); fileDesc.title = 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.setContent(file.content); fileDesc.content = 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
@ -217,21 +220,14 @@ define([
}; };
gdriveProvider.publish = function(publishAttributes, title, content, callback) { gdriveProvider.publish = function(publishAttributes, title, content, callback) {
googleHelper.upload( googleHelper.upload(publishAttributes.id, undefined, publishAttributes.fileName || title, content, undefined, function(error, result) {
publishAttributes.id,
undefined,
publishAttributes.fileName || title,
content,
undefined,
function(error, result) {
if(error) { if(error) {
callback(error); callback(error);
return; return;
} }
publishAttributes.id = result.id; publishAttributes.id = result.id;
callback(); callback();
} });
);
}; };
gdriveProvider.newPublishAttributes = function(event) { gdriveProvider.newPublishAttributes = function(event) {
@ -251,9 +247,8 @@ define([
} }
localStorage.removeItem(PROVIDER_GDRIVE + ".state"); localStorage.removeItem(PROVIDER_GDRIVE + ".state");
state = JSON.parse(state); state = JSON.parse(state);
if (state.action == "create") { if(state.action == "create") {
googleHelper.upload(undefined, state.folderId, GDRIVE_DEFAULT_FILE_TITLE, googleHelper.upload(undefined, state.folderId, GDRIVE_DEFAULT_FILE_TITLE, "", undefined, function(error, file) {
"", undefined, function(error, file) {
if(error) { if(error) {
return; return;
} }
@ -265,7 +260,7 @@ define([
extensionMgr.onMessage('"' + file.title + '" created successfully on Google Drive.'); extensionMgr.onMessage('"' + file.title + '" created successfully on Google Drive.');
}); });
} }
else if (state.action == "open") { else if(state.action == "open") {
var importIds = []; var importIds = [];
_.each(state.ids, function(id) { _.each(state.ids, function(id) {
var syncIndex = createSyncIndex(id); var syncIndex = createSyncIndex(id);

View File

@ -8,20 +8,21 @@ define([
var gistProvider = { var gistProvider = {
providerId: PROVIDER_GIST, providerId: PROVIDER_GIST,
providerName: "Gist", providerName: "Gist",
sharingAttributes: ["gistId", "filename"] sharingAttributes: [
"gistId",
"filename"
]
}; };
gistProvider.publish = function(publishAttributes, title, content, callback) { gistProvider.publish = function(publishAttributes, title, content, callback) {
githubHelper.uploadGist(publishAttributes.gistId, publishAttributes.filename, publishAttributes.isPublic, githubHelper.uploadGist(publishAttributes.gistId, publishAttributes.filename, publishAttributes.isPublic, title, content, function(error, gistId) {
title, content, function(error, gistId) {
if(error) { if(error) {
callback(error); callback(error);
return; return;
} }
publishAttributes.gistId = gistId; publishAttributes.gistId = gistId;
callback(); callback();
} });
);
}; };
gistProvider.newPublishAttributes = function(event) { gistProvider.newPublishAttributes = function(event) {

View File

@ -19,13 +19,14 @@ define([
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",
timeout: AJAX_TIMEOUT
}).done(function() { }).done(function() {
connected = true; connected = true;
task.chain(); task.chain();
@ -44,7 +45,7 @@ define([
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;
} }
@ -64,9 +65,7 @@ define([
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,
'stackedit-github-oauth', 960, 600);
authWindow.focus(); authWindow.focus();
intervalId = setInterval(function() { intervalId = setInterval(function() {
if(authWindow.closed === true) { if(authWindow.closed === true) {
@ -156,7 +155,9 @@ define([
task.onRun(function() { task.onRun(function() {
var gist = github.getGist(gistId); var gist = github.getGist(gistId);
var files = {}; var files = {};
files[filename] = {content: content}; files[filename] = {
content: content
};
githubFunction = gist.update; githubFunction = gist.update;
if(gistId === undefined) { if(gistId === undefined) {
githubFunction = gist.create; githubFunction = gist.create;
@ -223,21 +224,22 @@ define([
function handleError(error, task) { function handleError(error, task) {
var errorMsg = undefined; var errorMsg = undefined;
if (error) { if(error) {
logger.error(error); logger.error(error);
// Try to analyze the error // Try to analyze the error
if (typeof error === "string") { if(typeof error === "string") {
errorMsg = error; errorMsg = error;
} }
else { else {
errorMsg = "Could not publish on GitHub."; errorMsg = "Could not publish on GitHub.";
if (error.error === 401 || error.error === 403) { if(error.error === 401 || error.error === 403) {
github = undefined; github = undefined;
localStorage.removeItem("githubToken"); localStorage.removeItem("githubToken");
errorMsg = "Access to GitHub account is not authorized."; errorMsg = "Access to GitHub account is not authorized.";
task.retry(new Error(errorMsg), 1); task.retry(new Error(errorMsg), 1);
return; return;
} else if (error.error <= 0) { }
else if(error.error <= 0) {
connected = false; connected = false;
github = undefined; github = undefined;
core.setOffline(); core.setOffline();

View File

@ -9,13 +9,15 @@ define([
var githubProvider = { var githubProvider = {
providerId: PROVIDER_GITHUB, providerId: PROVIDER_GITHUB,
providerName: "GitHub", providerName: "GitHub",
publishPreferencesInputIds: ["github-reponame", "github-branch"] publishPreferencesInputIds: [
"github-reponame",
"github-branch"
]
}; };
githubProvider.publish = function(publishAttributes, title, content, callback) { githubProvider.publish = function(publishAttributes, title, content, callback) {
var commitMsg = settings.commitMsg; var commitMsg = settings.commitMsg;
githubHelper.upload(publishAttributes.repository, publishAttributes.branch, githubHelper.upload(publishAttributes.repository, publishAttributes.branch, publishAttributes.path, content, commitMsg, callback);
publishAttributes.path, content, commitMsg, callback);
}; };
githubProvider.newPublishAttributes = function(event) { githubProvider.newPublishAttributes = function(event) {

View File

@ -19,7 +19,7 @@ define([
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;
} }
@ -28,8 +28,9 @@ define([
task.chain(); task.chain();
}; };
$.ajax({ $.ajax({
url : "https://apis.google.com/js/client.js?onload=runDelayedFunction", url: "https://apis.google.com/js/client.js?onload=runDelayedFunction",
dataType : "script", timeout : AJAX_TIMEOUT dataType: "script",
timeout: AJAX_TIMEOUT
}).fail(function(jqXHR) { }).fail(function(jqXHR) {
var error = { var error = {
code: jqXHR.status, code: jqXHR.status,
@ -43,24 +44,28 @@ define([
// 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 Google authorization popup is not blocked by your browser."); extensionMgr.onMessage("Please make sure the Google authorization popup is not blocked by your browser.");
// If not immediate we add time for user to enter his credentials // If not immediate we add time for user to enter his
// credentials
task.timeout = ASYNC_TASK_LONG_TIMEOUT; task.timeout = ASYNC_TASK_LONG_TIMEOUT;
} }
gapi.auth.authorize({ 'client_id' : GOOGLE_CLIENT_ID, gapi.auth.authorize({
'scope' : GOOGLE_SCOPES, 'immediate' : immediate }, function( 'client_id': GOOGLE_CLIENT_ID,
authResult) { 'scope': GOOGLE_SCOPES,
'immediate': immediate
}, function(authResult) {
gapi.client.load('drive', 'v2', function() { gapi.client.load('drive', 'v2', function() {
if (!authResult || authResult.error) { if(!authResult || authResult.error) {
// If immediate did not work retry without immediate flag // If immediate did not work retry without immediate
if (connected === true && immediate === true) { // flag
if(connected === true && immediate === true) {
immediate = false; immediate = false;
task.chain(localAuthenticate); task.chain(localAuthenticate);
return; return;
@ -89,43 +94,49 @@ define([
var delimiter = "\r\n--" + boundary + "\r\n"; var delimiter = "\r\n--" + boundary + "\r\n";
var close_delim = "\r\n--" + boundary + "--"; var close_delim = "\r\n--" + boundary + "--";
var contentType = 'text/x-markdown'; var contentType = 'text/x-markdown';
var metadata = { title : title, mimeType : contentType }; var metadata = {
if (parentId !== undefined) { title: title,
mimeType: contentType
};
if(parentId !== undefined) {
// Specify the directory // Specify the directory
metadata.parents = [ { kind : 'drive#fileLink', metadata.parents = [
id : parentId } ]; {
kind: 'drive#fileLink',
id: parentId
}
];
} }
var path = '/upload/drive/v2/files'; var path = '/upload/drive/v2/files';
var method = 'POST'; var method = 'POST';
if (fileId !== undefined) { if(fileId !== undefined) {
// If it's an update // If it's an update
path += "/" + fileId; path += "/" + fileId;
method = 'PUT'; method = 'PUT';
} }
var headers = { 'Content-Type' : 'multipart/mixed; boundary="' var headers = {
+ boundary + '"', }; 'Content-Type': 'multipart/mixed; boundary="' + boundary + '"',
};
if(etag !== undefined) { if(etag !== undefined) {
// Sometimes we have error 412 from Google even with the correct etag // Sometimes we have error 412 from Google even with the correct
//headers["If-Match"] = etag; // etag
// headers["If-Match"] = etag;
} }
var base64Data = utils.encodeBase64(content); var base64Data = utils.encodeBase64(content);
var multipartRequestBody = delimiter var multipartRequestBody = delimiter + 'Content-Type: application/json\r\n\r\n' + JSON.stringify(metadata) + delimiter + 'Content-Type: ' + contentType + '\r\n' + 'Content-Transfer-Encoding: base64\r\n' + '\r\n' + base64Data + close_delim;
+ 'Content-Type: application/json\r\n\r\n'
+ JSON.stringify(metadata) + delimiter + 'Content-Type: '
+ contentType + '\r\n'
+ 'Content-Transfer-Encoding: base64\r\n' + '\r\n'
+ base64Data + close_delim;
var request = gapi.client var request = gapi.client.request({
.request({ 'path': path,
'path' : path, 'method': method,
'method' : method, 'params': {
'params' : { 'uploadType' : 'multipart', }, 'uploadType': 'multipart',
'headers' : headers, },
'body' : multipartRequestBody, }); 'headers': headers,
'body': multipartRequestBody,
});
request.execute(function(response) { request.execute(function(response) {
if (response && response.id) { if(response && response.id) {
// Upload success // Upload success
result = response; result = response;
task.chain(); task.chain();
@ -166,16 +177,18 @@ define([
function retrievePageOfChanges() { function retrievePageOfChanges() {
var request = undefined; var request = undefined;
if(nextPageToken === undefined) { if(nextPageToken === undefined) {
request = gapi.client.drive.changes request = gapi.client.drive.changes.list({
.list({ 'startChangeId' : newChangeId + 1 }); 'startChangeId': newChangeId + 1
});
} }
else { else {
request = gapi.client.drive.changes request = gapi.client.drive.changes.list({
.list({ 'pageToken' : nextPageToken }); 'pageToken': nextPageToken
});
} }
request.execute(function(response) { request.execute(function(response) {
if (!response || !response.largestChangeId) { if(!response || !response.largestChangeId) {
// Handle error // Handle error
handleError(response.error, task); handleError(response.error, task);
return; return;
@ -183,10 +196,10 @@ define([
// Retrieve success // Retrieve success
newChangeId = response.largestChangeId; newChangeId = response.largestChangeId;
nextPageToken = response.nextPageToken; nextPageToken = response.nextPageToken;
if (response.items !== undefined) { if(response.items !== undefined) {
changes = changes.concat(response.items); changes = changes.concat(response.items);
} }
if (nextPageToken !== undefined) { if(nextPageToken !== undefined) {
task.chain(retrievePageOfChanges); task.chain(retrievePageOfChanges);
} }
else { else {
@ -225,11 +238,13 @@ define([
headers.Authorization = "Bearer " + token.access_token; headers.Authorization = "Bearer " + token.access_token;
} }
$.ajax({ $.ajax({
url : "https://www.googleapis.com/drive/v2/files/" + id, url: "https://www.googleapis.com/drive/v2/files/" + id,
headers : headers, headers: headers,
data : {key: GOOGLE_API_KEY}, data: {
dataType : "json", key: GOOGLE_API_KEY
timeout : AJAX_TIMEOUT },
dataType: "json",
timeout: AJAX_TIMEOUT
}).done(function(data, textStatus, jqXHR) { }).done(function(data, textStatus, jqXHR) {
result.push(data); result.push(data);
ids.shift(); ids.shift();
@ -294,11 +309,13 @@ define([
headers.Authorization = "Bearer " + token.access_token; headers.Authorization = "Bearer " + token.access_token;
} }
$.ajax({ $.ajax({
url : file.downloadUrl, url: file.downloadUrl,
headers : headers, headers: headers,
data : {key: GOOGLE_API_KEY}, data: {
dataType : "text", key: GOOGLE_API_KEY
timeout : AJAX_TIMEOUT },
dataType: "text",
timeout: AJAX_TIMEOUT
}).done(function(data, textStatus, jqXHR) { }).done(function(data, textStatus, jqXHR) {
file.content = data; file.content = data;
objects.shift(); objects.shift();
@ -325,25 +342,26 @@ define([
function handleError(error, task) { function handleError(error, task) {
var errorMsg = undefined; var errorMsg = undefined;
if (error) { if(error) {
logger.error(error); logger.error(error);
// Try to analyze the error // Try to analyze the error
if (typeof error === "string") { if(typeof error === "string") {
errorMsg = error; errorMsg = error;
} }
else { else {
errorMsg = "Google error (" + error.code + ": " errorMsg = "Google error (" + error.code + ": " + error.message + ").";
+ error.message + ")."; if(error.code >= 500 && error.code < 600) {
if (error.code >= 500 && error.code < 600) {
// Retry as described in Google's best practices // Retry as described in Google's best practices
task.retry(new Error(errorMsg)); task.retry(new Error(errorMsg));
return; return;
} else if (error.code === 401 || error.code === 403) { }
else if(error.code === 401 || error.code === 403) {
authenticated = false; authenticated = false;
errorMsg = "Access to Google account is not authorized."; errorMsg = "Access to Google account is not authorized.";
task.retry(new Error(errorMsg), 1); task.retry(new Error(errorMsg), 1);
return; return;
} else if (error.code <= 0) { }
else if(error.code <= 0) {
connected = false; connected = false;
authenticated = false; authenticated = false;
core.setOffline(); core.setOffline();
@ -357,17 +375,21 @@ define([
var pickerLoaded = false; var pickerLoaded = false;
function loadPicker(task) { function loadPicker(task) {
task.onRun(function() { task.onRun(function() {
if (pickerLoaded === true) { if(pickerLoaded === true) {
task.chain(); task.chain();
return; return;
} }
$.ajax({ $.ajax({
url : "//www.google.com/jsapi", url: "//www.google.com/jsapi",
data : {key: GOOGLE_API_KEY}, data: {
dataType : "script", key: GOOGLE_API_KEY
timeout : AJAX_TIMEOUT },
dataType: "script",
timeout: AJAX_TIMEOUT
}).done(function() { }).done(function() {
google.load('picker', '1', {callback: task.chain}); google.load('picker', '1', {
callback: task.chain
});
pickerLoaded = true; pickerLoaded = true;
}).fail(function(jqXHR) { }).fail(function(jqXHR) {
var error = { var error = {
@ -405,10 +427,9 @@ define([
pickerBuilder.addView(view); pickerBuilder.addView(view);
pickerBuilder.addView(new google.picker.DocsUploadView()); pickerBuilder.addView(new google.picker.DocsUploadView());
pickerBuilder.setCallback(function(data) { pickerBuilder.setCallback(function(data) {
if (data.action == google.picker.Action.PICKED || if(data.action == google.picker.Action.PICKED || data.action == google.picker.Action.CANCEL) {
data.action == google.picker.Action.CANCEL) {
if(data.action == google.picker.Action.PICKED) { if(data.action == google.picker.Action.PICKED) {
for(var i=0; i<data.docs.length; i++) { for ( var i = 0; i < data.docs.length; i++) {
ids.push(data.docs[i].id); ids.push(data.docs[i].id);
} }
} }
@ -447,7 +468,9 @@ define([
var url = "https://www.googleapis.com/blogger/v3/blogs/" + blogId + "/posts/"; var url = "https://www.googleapis.com/blogger/v3/blogs/" + blogId + "/posts/";
var data = { var data = {
kind: "blogger#post", kind: "blogger#post",
blog: { id: blogId }, blog: {
id: blogId
},
labels: labelList, labels: labelList,
title: title, title: title,
content: content content: content
@ -460,13 +483,13 @@ define([
type = "PUT"; type = "PUT";
} }
$.ajax({ $.ajax({
url : url, url: url,
data: JSON.stringify(data), data: JSON.stringify(data),
headers : headers, headers: headers,
type: type, type: type,
contentType: "application/json", contentType: "application/json",
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();
@ -488,11 +511,13 @@ define([
return; return;
} }
$.ajax({ $.ajax({
url : "https://www.googleapis.com/blogger/v3/blogs/byurl", url: "https://www.googleapis.com/blogger/v3/blogs/byurl",
data: { url: blogUrl }, data: {
headers : headers, url: blogUrl
dataType : "json", },
timeout : AJAX_TIMEOUT headers: headers,
dataType: "json",
timeout: AJAX_TIMEOUT
}).done(function(blog, textStatus, jqXHR) { }).done(function(blog, textStatus, jqXHR) {
blogId = blog.id; blogId = blog.id;
task.chain(publish); task.chain(publish);

View File

@ -21,17 +21,16 @@ define([
var publisher = {}; var publisher = {};
// Create a map with providerId: providerModule // Create a map with providerId: providerModule
var providerMap = _.chain( var providerMap = _.chain(arguments).map(function(argument) {
arguments return argument && argument.providerId && [
).map(function(argument) { argument.providerId,
return argument && argument.providerId && [argument.providerId, argument]; argument
];
}).compact().object().value(); }).compact().object().value();
// Retrieve publish locations from localStorage // Retrieve publish locations from localStorage
_.each(fileSystem, function(fileDesc) { _.each(fileSystem, function(fileDesc) {
_.chain( _.chain(localStorage[fileDesc.fileIndex + ".publish"].split(";")).compact().each(function(publishIndex) {
localStorage[fileDesc.fileIndex + ".publish"].split(";")
).compact().each(function(publishIndex) {
var publishAttributes = JSON.parse(localStorage[publishIndex]); var publishAttributes = JSON.parse(localStorage[publishIndex]);
// Store publishIndex // Store publishIndex
publishAttributes.publishIndex = publishIndex; publishAttributes.publishIndex = publishIndex;
@ -51,7 +50,8 @@ define([
documentHTML: $("#wmd-preview").html(), documentHTML: $("#wmd-preview").html(),
publishAttributes: publishAttributes publishAttributes: publishAttributes
}); });
} catch(e) { }
catch (e) {
extensionMgr.onError(e); extensionMgr.onError(e);
throw e; throw e;
} }
@ -79,7 +79,7 @@ define([
function publishLocation(callback, errorFlag) { function publishLocation(callback, errorFlag) {
// No more publish location for this document // No more publish location for this document
if (publishAttributesList.length === 0) { if(publishAttributesList.length === 0) {
callback(errorFlag); callback(errorFlag);
return; return;
} }
@ -100,7 +100,7 @@ define([
return; return;
} }
} }
publishLocation(callback, errorFlag || error ); publishLocation(callback, errorFlag || error);
}); });
} }
@ -130,7 +130,7 @@ define([
var publishIndex = undefined; var publishIndex = undefined;
do { do {
publishIndex = "publish." + utils.randomString(); publishIndex = "publish." + utils.randomString();
} while(_.has(localStorage, publishIndex)); } while (_.has(localStorage, publishIndex));
publishAttributes.publishIndex = publishIndex; publishAttributes.publishIndex = publishIndex;
utils.storeAttributes(publishAttributes); utils.storeAttributes(publishAttributes);
fileMgr.addPublish(fileDesc, publishAttributes); fileMgr.addPublish(fileDesc, publishAttributes);
@ -185,7 +185,8 @@ define([
} }
}); });
// Store input values as preferences for next time we open the publish dialog // Store input values as preferences for next time we open the publish
// dialog
var publishPreferences = {}; var publishPreferences = {};
_.each(provider.publishPreferencesInputIds, function(inputId) { _.each(provider.publishPreferencesInputIds, function(inputId) {
publishPreferences[inputId] = $("#input-publish-" + inputId).val(); publishPreferences[inputId] = $("#input-publish-" + inputId).val();
@ -196,9 +197,7 @@ define([
// Retrieve file's publish locations from localStorage // Retrieve file's publish locations from localStorage
publisher.populatePublishLocations = function(fileDesc) { publisher.populatePublishLocations = function(fileDesc) {
_.chain( _.chain(localStorage[fileDesc.fileIndex + ".publish"].split(";")).compact().each(function(publishIndex) {
localStorage[fileDesc.fileIndex + ".publish"].split(";")
).compact().each(function(publishIndex) {
var publishAttributes = JSON.parse(localStorage[publishIndex]); var publishAttributes = JSON.parse(localStorage[publishIndex]);
// Store publishIndex // Store publishIndex
publishAttributes.publishIndex = publishIndex; publishAttributes.publishIndex = publishIndex;
@ -213,15 +212,9 @@ define([
var publishMenu = $("#publish-menu"); var publishMenu = $("#publish-menu");
_.each(providerMap, function(provider) { _.each(providerMap, function(provider) {
// Provider's publish button // Provider's publish button
publishMenu.append( publishMenu.append($("<li>").append($('<a href="#"><i class="icon-' + provider.providerId + '"></i> ' + provider.providerName + '</a>').click(function() {
$("<li>").append(
$('<a href="#"><i class="icon-' + provider.providerId + '"></i> ' + provider.providerName + '</a>')
.click(function() {
initNewLocation(provider); initNewLocation(provider);
} })));
)
)
);
// Action links (if any) // Action links (if any)
$(".action-publish-" + provider.providerId).click(function() { $(".action-publish-" + provider.providerId).click(function() {
initNewLocation(provider); initNewLocation(provider);

View File

@ -4,24 +4,25 @@ define([
], 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(""),
sshProxy: SSH_PROXY_URL,
extensionSettings: {} extensionSettings: {}
}; };
if (_.has(localStorage, "settings")) { if(_.has(localStorage, "settings")) {
_.extend(settings, JSON.parse(localStorage.settings)); _.extend(settings, JSON.parse(localStorage.settings));
} }

View File

@ -14,17 +14,20 @@ define([
var sharing = {}; var sharing = {};
// Create a map with providerId: providerModule // Create a map with providerId: providerModule
var providerMap = _.chain( var providerMap = _.chain(arguments).map(function(argument) {
arguments return argument && argument.providerId && [
).map(function(argument) { argument.providerId,
return argument && argument.providerId && [argument.providerId, argument]; argument
];
}).compact().object().value(); }).compact().object().value();
// Used to populate the "Sharing" dropdown box // Used to populate the "Sharing" dropdown box
var lineTemplate = ['<div class="input-prepend">', var lineTemplate = [
'<a href="<%= link %>" class="add-on" title="Sharing location"><i class="icon-link"></i></a>', '<div class="input-prepend">',
'<input class="span2" type="text" value="<%= link %>" readonly />', ' <a href="<%= link %>" class="add-on" title="Sharing location"><i class="icon-link"></i></a>',
'</div>'].join(""); ' <input class="span2" type="text" value="<%= link %>" readonly />',
'</div>'
].join("");
sharing.refreshDocumentSharing = function(attributesList) { sharing.refreshDocumentSharing = function(attributesList) {
var linkList = $("#link-container .link-list").empty(); var linkList = $("#link-container .link-list").empty();
$("#link-container .no-link").show(); $("#link-container .no-link").show();
@ -44,7 +47,8 @@ define([
sharing.createLink = function(attributes, callback) { sharing.createLink = function(attributes, callback) {
var provider = providerMap[attributes.provider]; var provider = providerMap[attributes.provider];
// Don't create link if link already exists or provider is not compatible for sharing // Don't create link if link already exists or provider is not
// compatible for sharing
if(attributes.sharingLink !== undefined || provider === undefined if(attributes.sharingLink !== undefined || provider === undefined
// Or document is not published in markdown format // Or document is not published in markdown format
|| attributes.format != "markdown") { || attributes.format != "markdown") {
@ -58,7 +62,11 @@ define([
task.chain(); task.chain();
return; return;
} }
var url = [MAIN_URL, 'viewer.html?provider=', attributes.provider]; var url = [
MAIN_URL,
'viewer.html?provider=',
attributes.provider
];
_.each(provider.sharingAttributes, function(attributeName) { _.each(provider.sharingAttributes, function(attributeName) {
url.push('&'); url.push('&');
url.push(attributeName); url.push(attributeName);
@ -66,14 +74,10 @@ define([
url.push(encodeURIComponent(attributes[attributeName])); url.push(encodeURIComponent(attributes[attributeName]));
}); });
url = url.join(""); url = url.join("");
$.getJSON( $.getJSON("https://api-ssl.bitly.com/v3/shorten", {
"https://api-ssl.bitly.com/v3/shorten",
{
"access_token": BITLY_ACCESS_TOKEN, "access_token": BITLY_ACCESS_TOKEN,
"longUrl": url "longUrl": url
}, }, function(response) {
function(response)
{
if(response.data) { if(response.data) {
shortUrl = response.data.url; shortUrl = response.data.url;
attributes.sharingLink = shortUrl; attributes.sharingLink = shortUrl;
@ -83,8 +87,7 @@ define([
attributes.sharingLink = url; attributes.sharingLink = url;
} }
task.chain(); task.chain();
} });
);
}); });
function onFinish() { function onFinish() {
callback(); callback();

View File

@ -32,11 +32,11 @@ define([
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();
@ -62,15 +62,15 @@ define([
function handleError(error, task) { function handleError(error, task) {
var errorMsg = undefined; var errorMsg = undefined;
if (error) { if(error) {
logger.error(error); logger.error(error);
// Try to analyze the error // Try to analyze the error
if (typeof error === "string") { if(typeof error === "string") {
errorMsg = "SSH error: " + error + "."; errorMsg = "SSH error: " + error + ".";
} }
else { else {
errorMsg = "Could not publish on SSH server."; errorMsg = "Could not publish on SSH server.";
if (error.code <= 0) { if(error.code <= 0) {
core.setOffline(); core.setOffline();
errorMsg = "|stopPublish"; errorMsg = "|stopPublish";
} }

View File

@ -6,39 +6,28 @@ define([
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);
publishAttributes.username = utils.getInputTextValue(
"#input-publish-ssh-username", event);
publishAttributes.password = utils.getInputTextValue(
"#input-publish-ssh-password", event);
publishAttributes.path = utils.getInputTextValue(
"#input-publish-file-path", event);
if (event.isPropagationStopped()) {
return undefined; return undefined;
} }
return publishAttributes; return publishAttributes;

View File

@ -4,7 +4,7 @@ define([
], 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(";"));
@ -55,14 +55,14 @@ define([
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"];
@ -85,8 +85,7 @@ define([
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";
@ -95,9 +94,7 @@ define([
// 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 && if(currentFileIndex !== undefined && localStorage["file.list"].indexOf(";" + currentFileIndex + ";") === -1) {
localStorage["file.list"].indexOf(";" + currentFileIndex + ";") === -1)
{
localStorage.removeItem("file.current"); localStorage.removeItem("file.current");
} }
version = "v4"; version = "v4";

View File

@ -13,17 +13,16 @@ define([
var synchronizer = {}; var synchronizer = {};
// Create a map with providerId: providerModule // Create a map with providerId: providerModule
var providerMap = _.chain( var providerMap = _.chain(arguments).map(function(argument) {
arguments return argument && argument.providerId && [
).map(function(argument) { argument.providerId,
return argument && argument.providerId && [argument.providerId, argument]; argument
];
}).compact().object().value(); }).compact().object().value();
// Retrieve sync locations from localStorage // Retrieve sync locations from localStorage
_.each(fileSystem, function(fileDesc) { _.each(fileSystem, function(fileDesc) {
_.chain( _.chain(localStorage[fileDesc.fileIndex + ".sync"].split(";")).compact().each(function(syncIndex) {
localStorage[fileDesc.fileIndex + ".sync"].split(";")
).compact().each(function(syncIndex) {
var syncAttributes = JSON.parse(localStorage[syncIndex]); var syncAttributes = JSON.parse(localStorage[syncIndex]);
// Store syncIndex // Store syncIndex
syncAttributes.syncIndex = syncIndex; syncAttributes.syncIndex = syncIndex;
@ -48,7 +47,7 @@ define([
function locationUp(callback) { function locationUp(callback) {
// No more synchronized location for this document // No more synchronized location for this document
if (uploadSyncAttributesList.length === 0) { if(uploadSyncAttributesList.length === 0) {
fileUp(callback); fileUp(callback);
return; return;
} }
@ -56,13 +55,7 @@ define([
// Dequeue a synchronized location // Dequeue a synchronized location
var syncAttributes = uploadSyncAttributesList.pop(); var syncAttributes = uploadSyncAttributesList.pop();
// Use the specified provider to perform the upload // Use the specified provider to perform the upload
syncAttributes.provider.syncUp( syncAttributes.provider.syncUp(uploadContent, uploadContentCRC, uploadTitle, uploadTitleCRC, syncAttributes, function(error, uploadFlag) {
uploadContent,
uploadContentCRC,
uploadTitle,
uploadTitleCRC,
syncAttributes,
function(error, uploadFlag) {
if(uploadFlag === true) { if(uploadFlag === true) {
// If uploadFlag is true, request another upload cycle // If uploadFlag is true, request another upload cycle
uploadCycle = true; uploadCycle = true;
@ -76,8 +69,7 @@ define([
utils.storeAttributes(syncAttributes); utils.storeAttributes(syncAttributes);
} }
locationUp(callback); locationUp(callback);
} });
);
} }
// Recursive function to upload multiple files // Recursive function to upload multiple files
@ -85,7 +77,7 @@ define([
function fileUp(callback) { function fileUp(callback) {
// No more fileDesc to synchronize // No more fileDesc to synchronize
if (uploadFileList.length === 0) { if(uploadFileList.length === 0) {
syncUp(callback); syncUp(callback);
return; return;
} }
@ -99,7 +91,7 @@ define([
} }
// Get document title/content // Get document title/content
uploadContent = fileDesc.getContent(); uploadContent = fileDesc.content;
uploadContentCRC = utils.crc32(uploadContent); uploadContentCRC = utils.crc32(uploadContent);
uploadTitle = fileDesc.title; uploadTitle = fileDesc.title;
uploadTitleCRC = utils.crc32(uploadTitle); uploadTitleCRC = utils.crc32(uploadTitle);
@ -149,14 +141,15 @@ define([
function syncDown(callback) { function syncDown(callback) {
providerList = _.values(providerMap); providerList = _.values(providerMap);
providerDown(callback); providerDown(callback);
}; }
;
// Main entry point for synchronization // Main entry point for synchronization
var syncRunning = false; var syncRunning = false;
var lastSync = 0; var lastSync = 0;
synchronizer.sync = function() { synchronizer.sync = function() {
// If sync is already running or timeout is not reached or offline // If sync is already running or timeout is not reached or offline
if (syncRunning || lastSync + SYNC_PERIOD > utils.currentTime || core.isOffline) { if(syncRunning || lastSync + SYNC_PERIOD > utils.currentTime || core.isOffline) {
return; return;
} }
syncRunning = true; syncRunning = true;
@ -226,14 +219,15 @@ define([
// Perform the provider's export // Perform the provider's export
var fileDesc = fileMgr.getCurrentFile(); var fileDesc = fileMgr.getCurrentFile();
provider.exportFile(event, fileDesc.title, fileDesc.getContent(), function(error, syncAttributes) { provider.exportFile(event, fileDesc.title, fileDesc.content, function(error, syncAttributes) {
if(error) { if(error) {
return; return;
} }
fileMgr.addSync(fileDesc, syncAttributes); fileMgr.addSync(fileDesc, syncAttributes);
}); });
// Store input values as preferences for next time we open the export dialog // Store input values as preferences for next time we open the
// export dialog
var exportPreferences = {}; var exportPreferences = {};
_.each(provider.exportPreferencesInputIds, function(inputId) { _.each(provider.exportPreferencesInputIds, function(inputId) {
exportPreferences[inputId] = $("#input-sync-export-" + inputId).val(); exportPreferences[inputId] = $("#input-sync-export-" + inputId).val();
@ -243,7 +237,7 @@ define([
// Provider's manual export button // Provider's manual export button
$(".action-sync-manual-" + provider.providerId).click(function(event) { $(".action-sync-manual-" + provider.providerId).click(function(event) {
var fileDesc = fileMgr.getCurrentFile(); var fileDesc = fileMgr.getCurrentFile();
provider.exportManual(event, fileDesc.title, fileDesc.getContent(), function(error, syncAttributes) { provider.exportManual(event, fileDesc.title, fileDesc.content, function(error, syncAttributes) {
if(error) { if(error) {
return; return;
} }

View File

@ -26,7 +26,7 @@ define([
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;
} }
@ -54,9 +54,7 @@ define([
} }
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,
'stackedit-tumblr-oauth', 800, 600);
authWindow.focus(); authWindow.focus();
intervalId = setInterval(function() { intervalId = setInterval(function() {
if(authWindow.closed === true) { if(authWindow.closed === true) {
@ -111,11 +109,11 @@ define([
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();
@ -142,21 +140,22 @@ define([
function handleError(error, task) { function handleError(error, task) {
var errorMsg = undefined; var errorMsg = undefined;
if (error) { if(error) {
logger.error(error); logger.error(error);
// Try to analyze the error // Try to analyze the error
if (typeof error === "string") { if(typeof error === "string") {
errorMsg = error; errorMsg = error;
} }
else { else {
errorMsg = "Could not publish on Tumblr."; errorMsg = "Could not publish on Tumblr.";
if (error.code === 401 || error.code === 403) { if(error.code === 401 || error.code === 403) {
oauthParams = undefined; oauthParams = undefined;
localStorage.removeItem("tumblrOauthParams"); localStorage.removeItem("tumblrOauthParams");
errorMsg = "Access to Tumblr account is not authorized."; errorMsg = "Access to Tumblr account is not authorized.";
task.retry(new Error(errorMsg), 1); task.retry(new Error(errorMsg), 1);
return; return;
} else if (error.code <= 0) { }
else if(error.code <= 0) {
core.setOffline(); core.setOffline();
errorMsg = "|stopPublish"; errorMsg = "|stopPublish";
} }

View File

@ -8,35 +8,25 @@ define([
var tumblrProvider = { var tumblrProvider = {
providerId: PROVIDER_TUMBLR, providerId: PROVIDER_TUMBLR,
providerName: "Tumblr", providerName: "Tumblr",
publishPreferencesInputIds: ["tumblr-hostname"] publishPreferencesInputIds: [
"tumblr-hostname"
]
}; };
tumblrProvider.publish = function(publishAttributes, title, content, callback) { tumblrProvider.publish = function(publishAttributes, title, content, callback) {
tumblrHelper.upload( tumblrHelper.upload(publishAttributes.blogHostname, publishAttributes.postId, publishAttributes.tags, publishAttributes.format == "markdown" ? "markdown" : "html", title, content, function(error, postId) {
publishAttributes.blogHostname,
publishAttributes.postId,
publishAttributes.tags,
publishAttributes.format == "markdown" ? "markdown" : "html",
title,
content,
function(error, postId) {
if(error) { if(error) {
callback(error); callback(error);
return; return;
} }
publishAttributes.postId = postId; publishAttributes.postId = postId;
callback(); callback();
} });
);
}; };
tumblrProvider.newPublishAttributes = function(event) { tumblrProvider.newPublishAttributes = function(event) {
var publishAttributes = {}; var publishAttributes = {};
publishAttributes.blogHostname = utils 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])$/);
.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.postId = utils.getInputTextValue("#input-publish-postid");
publishAttributes.tags = utils.getInputTextValue("#input-publish-tags"); publishAttributes.tags = utils.getInputTextValue("#input-publish-tags");
if(event.isPropagationStopped()) { if(event.isPropagationStopped()) {

View File

@ -11,7 +11,8 @@ define([
var regex = new RegExp(name + "=(.+?)(&|$)"); var regex = new RegExp(name + "=(.+?)(&|$)");
try { try {
return decodeURIComponent(regex.exec(location.search)[1]); return decodeURIComponent(regex.exec(location.search)[1]);
} catch (e) { }
catch (e) {
return undefined; return undefined;
} }
}; };
@ -48,14 +49,13 @@ define([
utils.getInputTextValue = function(element, event, validationRegex) { utils.getInputTextValue = function(element, event, validationRegex) {
element = jqElt(element); element = jqElt(element);
var value = element.val(); var value = element.val();
if (value === undefined) { if(value === undefined) {
inputError(element, event); inputError(element, event);
return undefined; return undefined;
} }
// trim // trim
value = utils.trim(value); value = utils.trim(value);
if((value.length === 0) if((value.length === 0) || (validationRegex !== undefined && !value.match(validationRegex))) {
|| (validationRegex !== undefined && !value.match(validationRegex))) {
inputError(element, event); inputError(element, event);
return undefined; return undefined;
} }
@ -70,9 +70,7 @@ define([
return undefined; return undefined;
} }
value = parseInt(value); value = parseInt(value);
if((value === NaN) if((value === NaN) || (min !== undefined && value < min) || (max !== undefined && value > max)) {
|| (min !== undefined && value < min)
|| (max !== undefined && value > max)) {
inputError(element, event); inputError(element, event);
return undefined; return undefined;
} }
@ -113,8 +111,7 @@ define([
// Slug function // Slug function
utils.slugify = function(text) { utils.slugify = function(text) {
return text.toLowerCase() return text.toLowerCase().replace(/\s+/g, '-') // Replace spaces with -
.replace(/\s+/g, '-') // Replace spaces with -
.replace(/[^\w\-]+/g, '') // Remove all non-word chars .replace(/[^\w\-]+/g, '') // Remove all non-word chars
.replace(/\-\-+/g, '-') // Replace multiple - with single - .replace(/\-\-+/g, '-') // Replace multiple - with single -
.replace(/^-+/, '') // Trim - from start of text .replace(/^-+/, '') // Trim - from start of text
@ -135,9 +132,66 @@ define([
return 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 // Base64 conversion
utils.encodeBase64 = function(str) { utils.encodeBase64 = function(str) {
if (str.length === 0) { if(str.length === 0) {
return ""; return "";
} }
@ -151,9 +205,10 @@ define([
char = str[offset]; char = str[offset];
offset += 1; offset += 1;
if ('%' !== char) { if('%' !== char) {
bytes.push(char.charCodeAt(0)); bytes.push(char.charCodeAt(0));
} else { }
else {
char = str[offset] + str[offset + 1]; char = str[offset] + str[offset + 1];
bytes.push(parseInt(char, 16)); bytes.push(parseInt(char, 16));
offset += 2; offset += 2;
@ -170,7 +225,7 @@ define([
var imax = bytes.length - bytes.length % 3; var imax = bytes.length - bytes.length % 3;
for (i = 0; i < imax; i += 3) { for (i = 0; i < imax; i += 3) {
b10 = (bytes[i] << 16) | (bytes[i+1] << 8) | bytes[i+2]; b10 = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2];
x.push(alpha.charAt(b10 >> 18)); x.push(alpha.charAt(b10 >> 18));
x.push(alpha.charAt((b10 >> 12) & 0x3F)); x.push(alpha.charAt((b10 >> 12) & 0x3F));
x.push(alpha.charAt((b10 >> 6) & 0x3f)); x.push(alpha.charAt((b10 >> 6) & 0x3f));
@ -179,70 +234,275 @@ define([
switch (bytes.length - imax) { switch (bytes.length - imax) {
case 1: case 1:
b10 = bytes[i] << 16; b10 = bytes[i] << 16;
x.push(alpha.charAt(b10 >> 18) + alpha.charAt((b10 >> 12) & 0x3F) + x.push(alpha.charAt(b10 >> 18) + alpha.charAt((b10 >> 12) & 0x3F) + padchar + padchar);
padchar + padchar);
break; break;
case 2: case 2:
b10 = (bytes[i] << 16) | (bytes[i+1] << 8); b10 = (bytes[i] << 16) | (bytes[i + 1] << 8);
x.push(alpha.charAt(b10 >> 18) + alpha.charAt((b10 >> 12) & 0x3F) + x.push(alpha.charAt(b10 >> 18) + alpha.charAt((b10 >> 12) & 0x3F) + alpha.charAt((b10 >> 6) & 0x3f) + padchar);
alpha.charAt((b10 >> 6) & 0x3f) + padchar);
break; break;
} }
return x.join(''); return x.join('');
}; };
// CRC32 algorithm // CRC32 algorithm
var mHash = [ 0, 1996959894, 3993919788, 2567524794, 124634137, var mHash = [
1886057615, 3915621685, 2657392035, 249268274, 2044508324, 0,
3772115230, 2547177864, 162941995, 2125561021, 3887607047, 1996959894,
2428444049, 498536548, 1789927666, 4089016648, 2227061214, 3993919788,
450548861, 1843258603, 4107580753, 2211677639, 325883990, 2567524794,
1684777152, 4251122042, 2321926636, 335633487, 1661365465, 124634137,
4195302755, 2366115317, 997073096, 1281953886, 3579855332, 1886057615,
2724688242, 1006888145, 1258607687, 3524101629, 2768942443, 3915621685,
901097722, 1119000684, 3686517206, 2898065728, 853044451, 2657392035,
1172266101, 3705015759, 2882616665, 651767980, 1373503546, 249268274,
3369554304, 3218104598, 565507253, 1454621731, 3485111705, 2044508324,
3099436303, 671266974, 1594198024, 3322730930, 2970347812, 3772115230,
795835527, 1483230225, 3244367275, 3060149565, 1994146192, 2547177864,
31158534, 2563907772, 4023717930, 1907459465, 112637215, 162941995,
2680153253, 3904427059, 2013776290, 251722036, 2517215374, 2125561021,
3775830040, 2137656763, 141376813, 2439277719, 3865271297, 3887607047,
1802195444, 476864866, 2238001368, 4066508878, 1812370925, 2428444049,
453092731, 2181625025, 4111451223, 1706088902, 314042704, 498536548,
2344532202, 4240017532, 1658658271, 366619977, 2362670323, 1789927666,
4224994405, 1303535960, 984961486, 2747007092, 3569037538, 4089016648,
1256170817, 1037604311, 2765210733, 3554079995, 1131014506, 2227061214,
879679996, 2909243462, 3663771856, 1141124467, 855842277, 450548861,
2852801631, 3708648649, 1342533948, 654459306, 3188396048, 1843258603,
3373015174, 1466479909, 544179635, 3110523913, 3462522015, 4107580753,
1591671054, 702138776, 2966460450, 3352799412, 1504918807, 2211677639,
783551873, 3082640443, 3233442989, 3988292384, 2596254646, 325883990,
62317068, 1957810842, 3939845945, 2647816111, 81470997, 1943803523, 1684777152,
3814918930, 2489596804, 225274430, 2053790376, 3826175755, 4251122042,
2466906013, 167816743, 2097651377, 4027552580, 2265490386, 2321926636,
503444072, 1762050814, 4150417245, 2154129355, 426522225, 335633487,
1852507879, 4275313526, 2312317920, 282753626, 1742555852, 1661365465,
4189708143, 2394877945, 397917763, 1622183637, 3604390888, 4195302755,
2714866558, 953729732, 1340076626, 3518719985, 2797360999, 2366115317,
1068828381, 1219638859, 3624741850, 2936675148, 906185462, 997073096,
1090812512, 3747672003, 2825379669, 829329135, 1181335161, 1281953886,
3412177804, 3160834842, 628085408, 1382605366, 3423369109, 3579855332,
3138078467, 570562233, 1426400815, 3317316542, 2998733608, 2724688242,
733239954, 1555261956, 3268935591, 3050360625, 752459403, 1006888145,
1541320221, 2607071920, 3965973030, 1969922972, 40735498, 1258607687,
2617837225, 3943577151, 1913087877, 83908371, 2512341634, 3524101629,
3803740692, 2075208622, 213261112, 2463272603, 3855990285, 2768942443,
2094854071, 198958881, 2262029012, 4057260610, 1759359992, 901097722,
534414190, 2176718541, 4139329115, 1873836001, 414664567, 1119000684,
2282248934, 4279200368, 1711684554, 285281116, 2405801727, 3686517206,
4167216745, 1634467795, 376229701, 2685067896, 3608007406, 2898065728,
1308918612, 956543938, 2808555105, 3495958263, 1231636301, 853044451,
1047427035, 2932959818, 3654703836, 1088359270, 936918000, 1172266101,
2847714899, 3736837829, 1202900863, 817233897, 3183342108, 3705015759,
3401237130, 1404277552, 615818150, 3134207493, 3453421203, 2882616665,
1423857449, 601450431, 3009837614, 3294710456, 1567103746, 651767980,
711928724, 3020668471, 3272380065, 1510334235, 755167117 ]; 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) { utils.crc32 = function(str) {
var n = 0, crc = -1; var n = 0, crc = -1;
for ( var i = 0; i < str.length; i++) { for ( var i = 0; i < str.length; i++) {
@ -250,64 +510,11 @@ define([
crc = (crc >>> 8) ^ mHash[n]; crc = (crc >>> 8) ^ mHash[n];
} }
crc = crc ^ (-1); crc = crc ^ (-1);
if (crc < 0) { if(crc < 0) {
crc = 0xFFFFFFFF + crc + 1; crc = 0xFFFFFFFF + crc + 1;
} }
return crc.toString(16); return crc.toString(16);
}; };
// Create an centered popup window
utils.popupWindow = function(url, title, width, height) {
var left = (screen.width / 2) - (width / 2);
var top = (screen.height / 2) - (height / 2);
return window.open(
url,
title,
'toolbar=no, location=no, directories=no, status=no, menubar=no, scrollbars=no, resizable=no, copyhistory=no, width='
+ width
+ ', height='
+ height
+ ', top='
+ top
+ ', left='
+ left);
};
// Export data on disk
utils.saveAs = function(content, filename) {
if(saveAs !== undefined) {
var blob = new Blob([content], {type: "text/plain;charset=utf-8"});
saveAs(blob, filename);
}
else {
var uriContent = "data:application/octet-stream;base64,"
+ utils.encodeBase64(content);
window.open(uriContent, 'file');
}
};
// Generates a random string
utils.randomString = function() {
return _.random(4294967296).toString(36);
};
// Time shared by others modules
utils.updateCurrentTime = function() {
utils.currentTime = new Date().getTime();
};
utils.updateCurrentTime();
// Serialize sync/publish attributes and store it in the fileStorage
utils.storeAttributes = function(attributes) {
var storeIndex = attributes.syncIndex || attributes.publishIndex;
// Don't store sync/publish index
attributes = _.omit(attributes, "syncIndex", "publishIndex");
// Store providerId instead of provider
attributes.provider = attributes.provider.providerId;
localStorage[storeIndex] = JSON.stringify(attributes);
};
return utils; return utils;
}); });

View File

@ -38,9 +38,7 @@ define([
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,
'stackedit-wordpress-oauth', 960, 600);
authWindow.focus(); authWindow.focus();
intervalId = setInterval(function() { intervalId = setInterval(function() {
if(authWindow.closed === true) { if(authWindow.closed === true) {
@ -96,11 +94,11 @@ define([
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;
@ -116,7 +114,7 @@ define([
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';
} }
} }
@ -140,20 +138,21 @@ define([
function handleError(error, task) { function handleError(error, task) {
var errorMsg = undefined; var errorMsg = undefined;
if (error) { if(error) {
logger.error(error); logger.error(error);
// Try to analyze the error // Try to analyze the error
if (typeof error === "string") { if(typeof error === "string") {
errorMsg = error; errorMsg = error;
} }
else { else {
errorMsg = "Could not publish on WordPress."; errorMsg = "Could not publish on WordPress.";
if ((error.code === 400 && error.message == "invalid_token") || error.code === 401 || error.code === 403) { if((error.code === 400 && error.message == "invalid_token") || error.code === 401 || error.code === 403) {
localStorage.removeItem("wordpressToken"); localStorage.removeItem("wordpressToken");
errorMsg = "Access to WordPress account is not authorized."; errorMsg = "Access to WordPress account is not authorized.";
task.retry(new Error(errorMsg), 1); task.retry(new Error(errorMsg), 1);
return; return;
} else if (error.code <= 0) { }
else if(error.code <= 0) {
core.setOffline(); core.setOffline();
errorMsg = "|stopPublish"; errorMsg = "|stopPublish";
} }

View File

@ -9,34 +9,25 @@ define([
providerId: PROVIDER_WORDPRESS, providerId: PROVIDER_WORDPRESS,
providerName: "WordPress", providerName: "WordPress",
defaultPublishFormat: "html", defaultPublishFormat: "html",
publishPreferencesInputIds: ["wordpress-site"] publishPreferencesInputIds: [
"wordpress-site"
]
}; };
wordpressProvider.publish = function(publishAttributes, title, content, callback) { wordpressProvider.publish = function(publishAttributes, title, content, callback) {
wordpressHelper.upload( wordpressHelper.upload(publishAttributes.site, publishAttributes.postId, publishAttributes.tags, title, content, function(error, postId) {
publishAttributes.site,
publishAttributes.postId,
publishAttributes.tags,
title,
content,
function(error, postId) {
if(error) { if(error) {
callback(error); callback(error);
return; return;
} }
publishAttributes.postId = postId; publishAttributes.postId = postId;
callback(); callback();
} });
);
}; };
wordpressProvider.newPublishAttributes = function(event) { wordpressProvider.newPublishAttributes = function(event) {
var publishAttributes = {}; var publishAttributes = {};
publishAttributes.site = utils 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])$/);
.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.postId = utils.getInputTextValue("#input-publish-postid");
publishAttributes.tags = utils.getInputTextValue("#input-publish-tags"); publishAttributes.tags = utils.getInputTextValue("#input-publish-tags");
if(event.isPropagationStopped()) { if(event.isPropagationStopped()) {

View File

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