Extension pattern

This commit is contained in:
benweet 2013-05-25 19:13:59 +01:00
parent e41f9919b4
commit c6244d3190
23 changed files with 552 additions and 643 deletions

View File

@ -454,10 +454,58 @@ div.dropdown-menu i {
max-width: 206px;
}
.tooltip-inner {
#modal-settings .accordion-group {
border: 0;
border-bottom: 1px solid #eee;
-webkit-border-radius: inherit;
-moz-border-radius: inherit;
border-radius: inherit;
margin-bottom: 10px;
}
#modal-settings .accordion-heading .checkbox {
padding: 8px 15px;
margin-bottom: 0px;
}
#modal-settings .accordion-inner {
border: 0;
padding: 10px 40px;
}
#modal-settings .accordion-inner .form-horizontal .control-group {
color: #999;
}
#modal-settings .accordion-inner .form-horizontal .control-label {
text-align: left;
}
.accordion-toggle {
cursor: help;
}
.nav-tabs > .active > a, .nav-tabs > .active > a:hover, .nav-tabs > .active > a:focus {
color: #fff;
background-color: #777;
border-color: #777;
border-bottom-color: transparent;
}
.nav-tabs {
border-bottom-color: #eee;
}
.nav > li > a:hover,
.nav > li > a:focus {
background-color: #eee;
}
.nav-tabs > li > a:hover,
.nav-tabs > li > a:focus {
border-color: #eee;
}
table {
margin-bottom: 20px;
}

View File

@ -542,6 +542,8 @@
href="#tabpane-settings-editor" data-toggle="tab">Editor</a></li>
<li><a class="action-load-settings"
href="#tabpane-settings-publish" data-toggle="tab">Publish</a></li>
<li><a class="action-load-settings"
href="#tabpane-settings-extensions" data-toggle="tab">Extensions</a></li>
<li><a class="action-load-settings"
href="#tabpane-settings-utils" data-toggle="tab">Utils</a></li>
</ul>
@ -570,24 +572,6 @@
your own theme...</a></span>
</div>
</div>
<div class="control-group">
<label class="control-label" for="input-settings-converter-type">Converter</label>
<div class="controls">
<select id="input-settings-converter-type">
<option value="markdown">Markdown</option>
<option value="markdown-extra">Markdown Extra</option>
<option value="markdown-extra-prettify">Markdown Extra
+ Prettify</option>
</select>
</div>
</div>
<div class="control-group">
<label class="control-label" for="input-settings-enable-mathjax">MathJax
support </label>
<div class="controls">
<input type="checkbox" id="input-settings-enable-mathjax" />
</div>
</div>
<div class="control-group">
<label class="control-label" for="input-settings-lazy-rendering">Lazy
rendering <a href="#" class="tooltip-lazy-rendering">(?)</a>
@ -596,14 +580,6 @@
<input type="checkbox" id="input-settings-lazy-rendering" />
</div>
</div>
<div class="control-group">
<label class="control-label" for="input-settings-scroll-link">Scroll
Link <a href="#" class="tooltip-scroll-link">(?)</a>
</label>
<div class="controls">
<input type="checkbox" id="input-settings-scroll-link" />
</div>
</div>
<div class="control-group">
<label class="control-label"
for="input-settings-editor-font-size">Editor font size</label>
@ -650,6 +626,13 @@
</div>
</div>
</div>
<div class="tab-pane" id="tabpane-settings-extensions">
<div class="accordion" id="accordion-extensions">
</div>
<span class="help-block pull-right"><a target="_blank"
href="https://github.com/benweet/stackedit/blob/master/doc/theming.md#stackedit-theming-guide">Create
your own extension...</a></span>
</div>
<div class="tab-pane" id="tabpane-settings-utils">
<div style="width: 200px; margin: 10px auto;">
<a href="#" class="btn btn-block btn-primary action-welcome-file"

View File

@ -1,4 +1,4 @@
define(["jquery", "core", "google-helper"], function($, core, googleHelper) {
define(["utils", "google-helper"], function(utils, googleHelper) {
var PROVIDER_BLOGGER = "blogger";
@ -31,17 +31,17 @@ define(["jquery", "core", "google-helper"], function($, core, googleHelper) {
bloggerProvider.newPublishAttributes = function(event) {
var publishAttributes = {};
var blogUrl = core.getInputValue($("#input-publish-blogger-url"), event);
var blogUrl = utils.getInputTextValue("#input-publish-blogger-url", event);
if(blogUrl !== undefined) {
publishAttributes.blogUrl = core.checkUrl(blogUrl);
publishAttributes.blogUrl = utils.checkUrl(blogUrl);
}
publishAttributes.postId = $("#input-publish-postid").val() || undefined;
publishAttributes.postId = utils.getInputTextValue("#input-publish-postid");
publishAttributes.labelList = [];
var labels = $("#input-publish-labels").val() || undefined;
var labels = utils.getInputTextValue("#input-publish-labels");
if(labels !== undefined) {
publishAttributes.labelList = _.chain(labels.split(","))
.map(function(label) {
return core.trim(label);
return utils.trim(label);
}).compact().value();
}
if(event.isPropagationStopped()) {

View File

@ -1,7 +1,7 @@
define(
[ "jquery", "extension-manager", "bootstrap", "layout", "Markdown.Editor", "storage", "config",
[ "jquery", "utils", "extension-manager", "bootstrap", "layout", "Markdown.Editor", "storage", "config",
"underscore", "FileSaver", "css_browser_selector" ],
function($, extensionManager) {
function($, utils, extensionManager) {
var core = {};
@ -46,7 +46,7 @@ define(
return;
}
if(windowId === undefined) {
windowId = core.randomString();
windowId = utils.randomString();
localStorage["frontWindowId"] = windowId;
}
var frontWindowId = localStorage["frontWindowId"];
@ -62,61 +62,6 @@ define(
});
}
};
// Useful function for input control
function inputError(element, event) {
if(event !== undefined) {
element.stop(true, true).addClass("error").delay(1000).switchClass("error");
event.stopPropagation();
}
}
core.getInputValue = function(element, event, validationRegex) {
var value = element.val();
if (value === undefined) {
inputError(element, event);
return undefined;
}
// trim
value = core.trim(value);
if((value.length === 0)
|| (validationRegex !== undefined && !value.match(validationRegex))) {
inputError(element, event);
return undefined;
}
return value;
};
core.getInputIntValue = function(element, event, min, max) {
var value = core.getInputValue(element, event);
if(value === undefined) {
return undefined;
}
value = parseInt(value);
if((value === NaN)
|| (min !== undefined && value < min)
|| (max !== undefined && value > max)) {
inputError(element, event);
return undefined;
}
return value;
};
core.resetModalInputs = function() {
$(".modal input[type=text]:not([disabled]), .modal input[type=password]").val("");
};
core.trim = function(str) {
return str.replace(/^\s+|\s+$/g, '');
};
core.checkUrl = function(url, addSlash) {
if(!url) {
return url;
}
if(url.indexOf("http") !== 0) {
url = "http://" + url;
}
if(addSlash && url.indexOf("/", url.length - 1) === -1) {
url += "/";
}
return url;
};
// Export data on disk
core.saveFile = function(content, filename) {
@ -126,7 +71,7 @@ define(
}
else {
var uriContent = "data:application/octet-stream;base64,"
+ core.encodeBase64(content);
+ utils.encodeBase64(content);
window.open(uriContent, 'file');
}
};
@ -202,11 +147,8 @@ define(
// Setting management
core.settings = {
converterType : "markdown-extra-prettify",
enableMathJax : true,
lazyRendering : true,
layoutOrientation : "horizontal",
scrollLink : true,
lazyRendering : true,
editorFontSize : 14,
defaultContent: "\n\n\n> Written with [StackEdit](http://benweet.github.io/stackedit/).",
commitMsg : "Published by http://benweet.github.io/stackedit",
@ -217,66 +159,58 @@ define(
'</head>\n',
'<body><%= documentHTML %></body>\n',
'</html>'].join(""),
sshProxy : SSH_PROXY_URL,
sshConnectionList : []
sshProxy : SSH_PROXY_URL
};
core.loadSettings = function() {
if (localStorage.settings) {
$.extend(core.settings, JSON.parse(localStorage.settings));
}
if (_.has(localStorage, "settings")) {
_.extend(core.settings, JSON.parse(localStorage.settings));
}
core.loadSettings = function() {
// Layout orientation
$("input:radio[name=radio-layout-orientation][value="
+ core.settings.layoutOrientation + "]").prop("checked", true);
// Scroll Link
$("#input-settings-scroll-link").prop("checked", core.settings.scrollLink);
utils.setInputRadio("radio-layout-orientation", core.settings.layoutOrientation);
// Theme
$("#input-settings-theme").val(localStorage.theme);
// Converter type
$("#input-settings-converter-type").val(core.settings.converterType);
// MathJax
$("#input-settings-enable-mathjax").prop("checked", core.settings.enableMathJax);
utils.setInputValue("#input-settings-theme", localStorage.theme);
// Lazy rendering
$("#input-settings-lazy-rendering").prop("checked", core.settings.lazyRendering);
utils.setInputChecked("#input-settings-lazy-rendering", core.settings.lazyRendering);
// Editor font size
$("#input-settings-editor-font-size").val(core.settings.editorFontSize);
utils.setInputValue("#input-settings-editor-font-size", core.settings.editorFontSize);
// Default content
$("#textarea-settings-default-content").val(core.settings.defaultContent);
utils.setInputValue("#textarea-settings-default-content", core.settings.defaultContent);
// Commit message
$("#input-settings-publish-commit-msg").val(core.settings.commitMsg);
utils.setInputValue("#input-settings-publish-commit-msg", core.settings.commitMsg);
// Template
$("#textarea-settings-publish-template").val(core.settings.template);
utils.setInputValue("#textarea-settings-publish-template", core.settings.template);
// SSH proxy
$("#input-settings-ssh-proxy").val(core.settings.sshProxy);
utils.setInputValue("#input-settings-ssh-proxy", core.settings.sshProxy);
// Load extension settings
extensionManager.onLoadSettings();
};
core.saveSettings = function(event) {
var newSettings = {};
// Layout orientation
newSettings.layoutOrientation = $(
"input:radio[name=radio-layout-orientation]:checked").prop("value");
newSettings.layoutOrientation = utils.getInputRadio("radio-layout-orientation");
// Theme
var theme = $("#input-settings-theme").val();
// Converter type
newSettings.converterType = $("#input-settings-converter-type").val();
// MathJax
newSettings.enableMathJax = $("#input-settings-enable-mathjax").prop("checked");
// Scroll Link
newSettings.scrollLink = $("#input-settings-scroll-link").prop("checked");
var theme = utils.getInputValue("#input-settings-theme");
// Lazy Rendering
newSettings.lazyRendering = $("#input-settings-lazy-rendering").prop("checked");
newSettings.lazyRendering = utils.getInputChecked("#input-settings-lazy-rendering");
// Editor font size
newSettings.editorFontSize = core.getInputIntValue($("#input-settings-editor-font-size"), event, 1, 99);
newSettings.editorFontSize = utils.getInputIntValue("#input-settings-editor-font-size", event, 1, 99);
// Default content
newSettings.defaultContent = $("#textarea-settings-default-content").val();
newSettings.defaultContent = utils.getInputValue("#textarea-settings-default-content");
// Commit message
newSettings.commitMsg = core.getInputValue($("#input-settings-publish-commit-msg"), event);
newSettings.commitMsg = utils.getInputTextValue("#input-settings-publish-commit-msg", event);
// Template
newSettings.template = core.getInputValue($("#textarea-settings-publish-template"), event);
newSettings.template = utils.getInputTextValue("#textarea-settings-publish-template", event);
// SSH proxy
newSettings.sshProxy = core.checkUrl(core.getInputValue($("#input-settings-ssh-proxy"), event), true);
newSettings.sshProxy = utils.checkUrl(utils.getInputTextValue("#input-settings-ssh-proxy", event), true);
// Save extension settings
newSettings.extensionSettings = {};
extensionManager.onSaveSettings(newSettings.extensionSettings, event);
if(!event.isPropagationStopped()) {
$.extend(core.settings, newSettings);
@ -428,142 +362,6 @@ define(
$("#wmd-undo-button").append($("<i>").addClass("icon-undo"));
$("#wmd-redo-button").append($("<i>").addClass("icon-share-alt"));
};
// Base64 conversion
core.encodeBase64 = function(str) {
if (str.length === 0) {
return "";
}
// UTF-8 to byte array
var bytes = [], offset = 0, length, char;
str = encodeURI(str);
length = str.length;
while (offset < length) {
char = str[offset];
offset += 1;
if ('%' !== char) {
bytes.push(char.charCodeAt(0));
} else {
char = str[offset] + str[offset + 1];
bytes.push(parseInt(char, 16));
offset += 2;
}
}
// byte array to base64
var padchar = '=';
var alpha = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
var i, b10;
var x = [];
var imax = bytes.length - bytes.length % 3;
for (i = 0; i < imax; i += 3) {
b10 = (bytes[i] << 16) | (bytes[i+1] << 8) | bytes[i+2];
x.push(alpha.charAt(b10 >> 18));
x.push(alpha.charAt((b10 >> 12) & 0x3F));
x.push(alpha.charAt((b10 >> 6) & 0x3f));
x.push(alpha.charAt(b10 & 0x3f));
}
switch (bytes.length - imax) {
case 1:
b10 = bytes[i] << 16;
x.push(alpha.charAt(b10 >> 18) + alpha.charAt((b10 >> 12) & 0x3F) +
padchar + padchar);
break;
case 2:
b10 = (bytes[i] << 16) | (bytes[i+1] << 8);
x.push(alpha.charAt(b10 >> 18) + alpha.charAt((b10 >> 12) & 0x3F) +
alpha.charAt((b10 >> 6) & 0x3f) + padchar);
break;
}
return x.join('');
};
// CRC32 algorithm
var mHash = [ 0, 1996959894, 3993919788, 2567524794, 124634137,
1886057615, 3915621685, 2657392035, 249268274, 2044508324,
3772115230, 2547177864, 162941995, 2125561021, 3887607047,
2428444049, 498536548, 1789927666, 4089016648, 2227061214,
450548861, 1843258603, 4107580753, 2211677639, 325883990,
1684777152, 4251122042, 2321926636, 335633487, 1661365465,
4195302755, 2366115317, 997073096, 1281953886, 3579855332,
2724688242, 1006888145, 1258607687, 3524101629, 2768942443,
901097722, 1119000684, 3686517206, 2898065728, 853044451,
1172266101, 3705015759, 2882616665, 651767980, 1373503546,
3369554304, 3218104598, 565507253, 1454621731, 3485111705,
3099436303, 671266974, 1594198024, 3322730930, 2970347812,
795835527, 1483230225, 3244367275, 3060149565, 1994146192,
31158534, 2563907772, 4023717930, 1907459465, 112637215,
2680153253, 3904427059, 2013776290, 251722036, 2517215374,
3775830040, 2137656763, 141376813, 2439277719, 3865271297,
1802195444, 476864866, 2238001368, 4066508878, 1812370925,
453092731, 2181625025, 4111451223, 1706088902, 314042704,
2344532202, 4240017532, 1658658271, 366619977, 2362670323,
4224994405, 1303535960, 984961486, 2747007092, 3569037538,
1256170817, 1037604311, 2765210733, 3554079995, 1131014506,
879679996, 2909243462, 3663771856, 1141124467, 855842277,
2852801631, 3708648649, 1342533948, 654459306, 3188396048,
3373015174, 1466479909, 544179635, 3110523913, 3462522015,
1591671054, 702138776, 2966460450, 3352799412, 1504918807,
783551873, 3082640443, 3233442989, 3988292384, 2596254646,
62317068, 1957810842, 3939845945, 2647816111, 81470997, 1943803523,
3814918930, 2489596804, 225274430, 2053790376, 3826175755,
2466906013, 167816743, 2097651377, 4027552580, 2265490386,
503444072, 1762050814, 4150417245, 2154129355, 426522225,
1852507879, 4275313526, 2312317920, 282753626, 1742555852,
4189708143, 2394877945, 397917763, 1622183637, 3604390888,
2714866558, 953729732, 1340076626, 3518719985, 2797360999,
1068828381, 1219638859, 3624741850, 2936675148, 906185462,
1090812512, 3747672003, 2825379669, 829329135, 1181335161,
3412177804, 3160834842, 628085408, 1382605366, 3423369109,
3138078467, 570562233, 1426400815, 3317316542, 2998733608,
733239954, 1555261956, 3268935591, 3050360625, 752459403,
1541320221, 2607071920, 3965973030, 1969922972, 40735498,
2617837225, 3943577151, 1913087877, 83908371, 2512341634,
3803740692, 2075208622, 213261112, 2463272603, 3855990285,
2094854071, 198958881, 2262029012, 4057260610, 1759359992,
534414190, 2176718541, 4139329115, 1873836001, 414664567,
2282248934, 4279200368, 1711684554, 285281116, 2405801727,
4167216745, 1634467795, 376229701, 2685067896, 3608007406,
1308918612, 956543938, 2808555105, 3495958263, 1231636301,
1047427035, 2932959818, 3654703836, 1088359270, 936918000,
2847714899, 3736837829, 1202900863, 817233897, 3183342108,
3401237130, 1404277552, 615818150, 3134207493, 3453421203,
1423857449, 601450431, 3009837614, 3294710456, 1567103746,
711928724, 3020668471, 3272380065, 1510334235, 755167117 ];
core.crc32 = function(str) {
var n = 0, crc = -1;
for ( var i = 0; i < str.length; i++) {
n = (crc ^ str.charCodeAt(i)) & 0xFF;
crc = (crc >>> 8) ^ mHash[n];
}
crc = crc ^ (-1);
if (crc < 0) {
crc = 0xFFFFFFFF + crc + 1;
}
return crc.toString(16);
};
// Generates a random string
core.randomString = function() {
return _.random(4294967296).toString(36);
};
// Access a URL parameter
core.getURLParameter = function(name) {
var regex = new RegExp(name + "=(.+?)(&|$)");
try {
return decodeURIComponent(regex.exec(location.search)[1]);
} catch (e) {
return undefined;
}
};
// Create an centered popup window
core.popupWindow = function(url, title, w, h) {
@ -610,9 +408,8 @@ define(
});
core.onReady(function() {
extensionManager.init(core.settings.extensionConfig);
extensionManager.init(core.settings.extensionSettings);
});
core.onReady(extensionManager.onReady);
core.onReady(function() {
// Load theme list
@ -637,13 +434,13 @@ define(
// Click events on "insert link" and "insert image" dialog buttons
$(".action-insert-link").click(function(e) {
var value = core.getInputValue($("#input-insert-link"), e);
var value = utils.getInputTextValue($("#input-insert-link"), e);
if(value !== undefined) {
insertLinkCallback(value);
}
});
$(".action-insert-image").click(function(e) {
var value = core.getInputValue($("#input-insert-image"), e);
var value = utils.getInputTextValue($("#input-insert-image"), e);
if(value !== undefined) {
insertLinkCallback(value);
}
@ -653,11 +450,9 @@ define(
});
// Settings loading/saving
core.loadSettings();
$(".action-load-settings").click(function() {
core.loadSettings();
});
$(".action-apply-settings").click(function(e) {
core.saveSettings(e);
if(!e.isPropagationStopped()) {
@ -667,6 +462,7 @@ define(
$(".action-default-settings").click(function() {
localStorage.removeItem("settings");
localStorage.removeItem("theme");
window.location.reload();
});
@ -723,7 +519,7 @@ define(
html: true,
container: '#modal-settings',
placement: 'right',
title: 'Thanks for supporting StackEdit by adding a backlink in your documents!'
title: 'Thank you for supporting StackEdit by adding a backlink in your documents!'
});
$(".tooltip-template").tooltip({
html: true,

View File

@ -1,4 +1,4 @@
define(["jquery", "core", "dropbox-helper"], function($, core, dropboxHelper) {
define(["core", "utils", "dropbox-helper"], function(core, utils, dropboxHelper) {
var PROVIDER_DROPBOX = "dropbox";
@ -28,7 +28,7 @@ define(["jquery", "core", "dropbox-helper"], function($, core, dropboxHelper) {
syncAttributes.provider = PROVIDER_DROPBOX;
syncAttributes.path = path;
syncAttributes.version = versionTag;
syncAttributes.contentCRC = core.crc32(content);
syncAttributes.contentCRC = utils.crc32(content);
return syncAttributes;
}
@ -107,12 +107,12 @@ define(["jquery", "core", "dropbox-helper"], function($, core, dropboxHelper) {
}
dropboxProvider.exportFile = function(event, title, content, callback) {
var path = core.getInputValue($("#input-sync-export-dropbox-path"), event);
var path = utils.getInputTextValue("#input-sync-export-dropbox-path", event);
exportFileToPath(path, title, content, callback);
};
dropboxProvider.exportManual = function(event, title, content, callback) {
var path = core.getInputValue($("#input-sync-manual-dropbox-path"), event);
var path = utils.getInputTextValue("#input-sync-manual-dropbox-path", event);
exportFileToPath(path, title, content, callback);
};
@ -191,9 +191,9 @@ define(["jquery", "core", "dropbox-helper"], function($, core, dropboxHelper) {
}
var syncAttributes = change.syncAttributes;
var localContent = localStorage[fileIndex + ".content"];
var localContentChanged = syncAttributes.contentCRC != core.crc32(localContent);
var localContentChanged = syncAttributes.contentCRC != utils.crc32(localContent);
var file = change.stat;
var remoteContentCRC = core.crc32(file.content);
var remoteContentCRC = utils.crc32(file.content);
var remoteContentChanged = syncAttributes.contentCRC != remoteContentCRC;
var fileContentChanged = localContent != file.content;
// Conflict detection
@ -236,7 +236,7 @@ define(["jquery", "core", "dropbox-helper"], function($, core, dropboxHelper) {
dropboxProvider.newPublishAttributes = function(event) {
var publishAttributes = {};
publishAttributes.path = core.getInputValue($("#input-publish-dropbox-path"), event);
publishAttributes.path = utils.getInputTextValue("#input-publish-dropbox-path", event);
if(event.isPropagationStopped()) {
return undefined;
}

View File

@ -1,24 +1,27 @@
define( [
"jquery",
"utils",
"underscore",
"bootstrap",
"extensions/notifications",
"extensions/markdown-extra",
"extensions/math-jax",
"extensions/scroll-link"
], function() {
], function($, utils) {
var extensionManager = {};
// Create a map with providerId: providerObject
var extensionList = _.chain(arguments)
.map(function(argument) {
return argument && argument.extensionId && argument;
return _.isObject(argument) && argument.extensionId && argument;
}).compact().value();
// Return every named callbacks implemented in extensions
function getExtensionCallbackList(callbackName) {
return _.chain(extensionList)
.map(function(extension) {
return extension[callbackName];
return extension.config.enabled && extension[callbackName];
}).compact().value();
}
@ -38,39 +41,89 @@ define( [
extensionManager[callbackName] = createCallback(callbackName);
}
extensionManager.init = function(extensionConfigMap) {
// Set the extension configuration
extensionConfigMap = extensionConfigMap || {};
_.each(extensionList, function(extension) {
extension.config = _.extend({}, extension.defaultConfig, extensionConfigMap[extension.extensionId]);
});
};
addCallback("onReady");
addCallback("onMessage");
addCallback("onError");
addCallback("onOfflineChanged");
addCallback("onLayoutConfigure");
addCallback("onLayoutCreated");
addCallback("onEditorConfigure");
var accordionTmpl = [
'<div class="accordion-group">',
'<div class="accordion-heading">',
'<label class="checkbox pull-right">',
'<input id="input-enable-extension-<%= extensionId %>" type="checkbox" <% if(!optional) { %> disabled <% } %>> enabled',
'</label>',
'<a id="accordion-toggle-test" data-toggle="collapse" data-parent="#accordion-extensions" class="accordion-toggle" href="#collapse-<%= extensionId %>">',
'<%= extensionName %>',
'</a>',
'</div>',
'<div id="collapse-<%= extensionId %>" class="accordion-body collapse">',
'<div class="accordion-inner"><%= settingsBloc %></div>',
'</div>',
'</div>'].join("");
function createSettings(extension) {
$("#accordion-extensions").append($(_.template(accordionTmpl, {
extensionId: extension.extensionId,
extensionName: extension.extensionName,
optional: extension.optional,
settingsBloc: extension.settingsBloc
})));
}
var onPreviewFinished = createCallback("onPreviewFinished");
var onAsyncPreviewCallbackList = getExtensionCallbackList("onAsyncPreview");
extensionManager["onAsyncPreview"] = function() {
// Call onPreviewFinished callbacks when all async preview are finished
var counter = 0;
function tryFinished() {
if(counter === onAsyncPreviewCallbackList.length) {
onPreviewFinished();
}
}
_.each(onAsyncPreviewCallbackList, function(asyncPreviewCallback) {
asyncPreviewCallback(function() {
counter++;
tryFinished();
});
extensionManager.init = function(extensionSettings) {
// Set extension config
extensionSettings = extensionSettings || {};
_.each(extensionList, function(extension) {
extension.config = _.extend({}, extension.defaultConfig, extensionSettings[extension.extensionId]);
extension.config.enabled = !extension.optional || extension.config.enabled;
});
tryFinished();
// Create accordion in settings dialog
_.each(extensionList, createSettings);
// Load/Save extension config from/to settings
addCallback("onLoadSettings");
extensionManager["onLoadSettings"] = function() {
_.each(extensionList, function(extension) {
utils.setInputChecked("#input-enable-extension-" + extension.extensionId, extension.config.enabled);
var onLoadSettingsCallback = extension.onLoadSettings;
onLoadSettingsCallback && onLoadSettingsCallback();
});
};
extensionManager["onSaveSettings"] = function(newExtensionSettings, event) {
_.each(extensionList, function(extension) {
var newExtensionConfig = extension.defaultConfig || {};
newExtensionConfig.enabled = utils.getInputChecked("#input-enable-extension-" + extension.extensionId);
var onSaveSettingsCallback = extension.onSaveSettings;
onSaveSettingsCallback && onSaveSettingsCallback(newExtensionConfig, event);
newExtensionSettings[extension.extensionId] = newExtensionConfig;
});
};
addCallback("onMessage");
addCallback("onError");
addCallback("onOfflineChanged");
addCallback("onLayoutConfigure");
addCallback("onLayoutCreated");
addCallback("onEditorConfigure");
var onPreviewFinished = createCallback("onPreviewFinished");
var onAsyncPreviewCallbackList = getExtensionCallbackList("onAsyncPreview");
extensionManager["onAsyncPreview"] = function() {
// Call onPreviewFinished callbacks when all async preview are finished
var counter = 0;
function tryFinished() {
if(counter === onAsyncPreviewCallbackList.length) {
onPreviewFinished();
}
}
_.each(onAsyncPreviewCallbackList, function(asyncPreviewCallback) {
asyncPreviewCallback(function() {
counter++;
tryFinished();
});
});
tryFinished();
};
// Call onReady callbacks
createCallback("onReady")();
};
return extensionManager;

View File

@ -1,11 +1,31 @@
define( [ "Markdown.Extra" ], function() {
define( [ "utils", "Markdown.Extra" ], function(utils) {
var markdownExtra = {
extensionId: "markdownExtra",
extensionName: "Markdown Extra",
optional: true,
defaultConfig: {
prettify: true
}
},
settingsBloc: [
'<p>Adds extra features to the original Markdown syntax.</p>',
'<div class="form-horizontal">',
'<div class="control-group">',
'<label class="control-label" for="input-markdownextra-prettify">Prettify syntax highlighting</label>',
'<div class="controls">',
'<input type="checkbox" id="input-markdownextra-prettify">',
'</div>',
'</div>',
'</div>'
].join("")
};
markdownExtra.onLoadSettings = function() {
utils.setInputChecked("#input-markdownextra-prettify", markdownExtra.config.prettify);
};
markdownExtra.onSaveSettings = function(newConfig, event) {
newConfig.prettify = utils.getInputChecked("#input-markdownextra-prettify");
};
markdownExtra.onEditorConfigure = function(editor) {

View File

@ -2,15 +2,19 @@ define( [ "MathJax" ], function($) {
var mathJax = {
extensionId: "mathJax",
extensionName: "MathJax"
extensionName: "MathJax",
optional: true,
settingsBloc: '<p>Allows StackEdit to interpret LaTex mathematical expressions.</p>'
};
MathJax.Hub.Config({"HTML-CSS": {preferredFont: "TeX",availableFonts: ["STIX", "TeX"],linebreaks: {automatic: true},EqnChunk: (MathJax.Hub.Browser.isMobile ? 10 : 50), imageFont: null},
tex2jax: {inlineMath: [["$", "$"], ["\\\\(", "\\\\)"]],displayMath: [["$$", "$$"], ["\\[", "\\]"]],processEscapes: true,ignoreClass: "tex2jax_ignore|dno"},
TeX: {noUndefined: {attributes: {mathcolor: "red",mathbackground: "#FFEEEE",mathsize: "90%"}},
Safe: {allow: {URLs: "safe",classes: "safe",cssIDs: "safe",styles: "safe",fontsize: "all"}}},
messageStyle: "none"
});
mathJax.onReady = function() {
MathJax.Hub.Config({"HTML-CSS": {preferredFont: "TeX",availableFonts: ["STIX", "TeX"],linebreaks: {automatic: true},EqnChunk: (MathJax.Hub.Browser.isMobile ? 10 : 50), imageFont: null},
tex2jax: {inlineMath: [["$", "$"], ["\\\\(", "\\\\)"]],displayMath: [["$$", "$$"], ["\\[", "\\]"]],processEscapes: true,ignoreClass: "tex2jax_ignore|dno"},
TeX: {noUndefined: {attributes: {mathcolor: "red",mathbackground: "#FFEEEE",mathsize: "90%"}},
Safe: {allow: {URLs: "safe",classes: "safe",cssIDs: "safe",styles: "safe",fontsize: "all"}}},
messageStyle: "none"
});
};
var ready = false; // true after initial typeset is complete
var pending = false; // true when MathJax has been requested

View File

@ -5,7 +5,8 @@ define( [ "jquery", "jgrowl", "underscore" ], function($) {
extensionName: "Notifications",
defaultConfig: {
showingTime: 5000
}
},
settingsBloc: "<p>Shows notification messages in the bottom-right corner of the screen.</p>"
};
notifications.onReady = function() {

View File

@ -2,7 +2,15 @@ define( [ "jquery", "underscore" ], function($) {
var scrollLink = {
extensionId: "scrollLink",
extensionName: "Scroll Link"
extensionName: "Scroll Link",
optional: true,
settingsBloc: [
'<p>Binds together editor and preview scrollbars.</p>',
'<blockquote class="muted"><b>NOTE:</b> ',
'The mapping between Markdown and HTML is based on the position of the title elements (h1, h2, ...) in the page. ',
'Therefore, if your document does not contain any title, the mapping will be linear and consequently less efficient.',
'</bloquote>'
].join("")
};
var mdSectionList = [];

View File

@ -1,5 +1,5 @@
define(["jquery", "core", "synchronizer", "publisher", "sharing", "text!../WELCOME.md", "underscore"],
function($, core, synchronizer, publisher, sharing, welcomeContent) {
define(["jquery", "core", "utils", "synchronizer", "publisher", "sharing", "text!../WELCOME.md", "underscore"],
function($, core, utils, synchronizer, publisher, sharing, welcomeContent) {
var TEMPORARY_FILE_INDEX = "file.tempIndex";
@ -75,7 +75,7 @@ define(["jquery", "core", "synchronizer", "publisher", "sharing", "text!../WELCO
var fileIndex = TEMPORARY_FILE_INDEX;
if(!isTemporary) {
do {
fileIndex = "file." + core.randomString();
fileIndex = "file." + utils.randomString();
} while(_.has(localStorage, fileIndex + ".title"));
}

View File

@ -1,4 +1,4 @@
define(["jquery", "core", "google-helper", "underscore"], function($, core, googleHelper) {
define(["core", "utils", "google-helper", "underscore"], function(core, utils, googleHelper) {
var PROVIDER_GDRIVE = "gdrive";
@ -15,8 +15,8 @@ define(["jquery", "core", "google-helper", "underscore"], function($, core, goog
syncAttributes.provider = PROVIDER_GDRIVE;
syncAttributes.id = id;
syncAttributes.etag = etag;
syncAttributes.contentCRC = core.crc32(content);
syncAttributes.titleCRC = core.crc32(title);
syncAttributes.contentCRC = utils.crc32(content);
syncAttributes.titleCRC = utils.crc32(title);
return syncAttributes;
}
@ -71,7 +71,7 @@ define(["jquery", "core", "google-helper", "underscore"], function($, core, goog
};
gdriveProvider.exportFile = function(event, title, content, callback) {
var parentId = core.getInputValue($("#input-sync-export-gdrive-parentid"));
var parentId = utils.getInputTextValue("#input-sync-export-gdrive-parentid");
googleHelper.upload(undefined, parentId, title, content, undefined, function(error, result) {
if (error) {
callback(error);
@ -85,7 +85,7 @@ define(["jquery", "core", "google-helper", "underscore"], function($, core, goog
};
gdriveProvider.exportManual = function(event, title, content, callback) {
var id = core.getInputValue($("#input-sync-manual-gdrive-id"), event);
var id = utils.getInputTextValue("#input-sync-manual-gdrive-id", event);
if(!id) {
return;
}
@ -186,14 +186,14 @@ define(["jquery", "core", "google-helper", "underscore"], function($, core, goog
return;
}
var syncAttributes = change.syncAttributes;
var localTitleChanged = syncAttributes.titleCRC != core.crc32(localTitle);
var localTitleChanged = syncAttributes.titleCRC != utils.crc32(localTitle);
var localContent = localStorage[fileIndex + ".content"];
var localContentChanged = syncAttributes.contentCRC != core.crc32(localContent);
var localContentChanged = syncAttributes.contentCRC != utils.crc32(localContent);
var file = change.file;
var remoteTitleCRC = core.crc32(file.title);
var remoteTitleCRC = utils.crc32(file.title);
var remoteTitleChanged = syncAttributes.titleCRC != remoteTitleCRC;
var fileTitleChanged = localTitle != file.title;
var remoteContentCRC = core.crc32(file.content);
var remoteContentCRC = utils.crc32(file.content);
var remoteContentChanged = syncAttributes.contentCRC != remoteContentCRC;
var fileContentChanged = localContent != file.content;
// Conflict detection
@ -253,8 +253,8 @@ define(["jquery", "core", "google-helper", "underscore"], function($, core, goog
gdriveProvider.newPublishAttributes = function(event) {
var publishAttributes = {};
publishAttributes.id = $("#input-publish-gdrive-fileid").val() || undefined;
publishAttributes.fileName = $("#input-publish-gdrive-filename").val() || undefined;
publishAttributes.id = utils.getInputTextValue("#input-publish-gdrive-fileid");
publishAttributes.fileName = utils.getInputTextValue("#input-publish-gdrive-filename");
if(event.isPropagationStopped()) {
return undefined;
}

View File

@ -1,4 +1,4 @@
define(["jquery", "core", "github-helper"], function($, core, githubHelper) {
define(["utils", "github-helper"], function(utils, githubHelper) {
var PROVIDER_GIST = "gist";
@ -23,9 +23,9 @@ define(["jquery", "core", "github-helper"], function($, core, githubHelper) {
gistProvider.newPublishAttributes = function(event) {
var publishAttributes = {};
publishAttributes.gistId = $("#input-publish-gist-id").val() || undefined;
publishAttributes.filename = core.getInputValue($("#input-publish-filename"), event);
publishAttributes.isPublic = $("#input-publish-gist-public").is(":checked");
publishAttributes.gistId = utils.getInputTextValue("#input-publish-gist-id");
publishAttributes.filename = utils.getInputTextValue("#input-publish-filename", event);
publishAttributes.isPublic = utils.getInputChecked("#input-publish-gist-public");
if(event.isPropagationStopped()) {
return undefined;
}

View File

@ -1,4 +1,4 @@
define(["jquery", "core", "github-helper"], function($, core, githubHelper) {
define(["core", "utils", "github-helper"], function(core, utils, githubHelper) {
var PROVIDER_GITHUB = "github";
@ -16,9 +16,9 @@ define(["jquery", "core", "github-helper"], function($, core, githubHelper) {
githubProvider.newPublishAttributes = function(event) {
var publishAttributes = {};
publishAttributes.repository = core.getInputValue($("#input-publish-github-reponame"), event);
publishAttributes.branch = core.getInputValue($("#input-publish-github-branch"), event);
publishAttributes.path = core.getInputValue($("#input-publish-file-path"), event);
publishAttributes.repository = utils.getInputTextValue("#input-publish-github-reponame", event);
publishAttributes.branch = utils.getInputTextValue("#input-publish-github-branch", event);
publishAttributes.path = utils.getInputTextValue("#input-publish-file-path", event);
if(event.isPropagationStopped()) {
return undefined;
}

View File

@ -104,7 +104,7 @@ define(["jquery", "core", "async-runner"], function($, core, asyncRunner) {
//headers["If-Match"] = etag;
}
var base64Data = core.encodeBase64(content);
var base64Data = utils.encodeBase64(content);
var multipartRequestBody = delimiter
+ 'Content-Type: application/json\r\n\r\n'
+ JSON.stringify(metadata) + delimiter + 'Content-Type: '

View File

@ -1,256 +0,0 @@
define([ "MathJax" ], function() {
MathJax.Hub.Config({"HTML-CSS": {preferredFont: "TeX",availableFonts: ["STIX", "TeX"],linebreaks: {automatic: true},EqnChunk: (MathJax.Hub.Browser.isMobile ? 10 : 50), imageFont: null},
tex2jax: {inlineMath: [["$", "$"], ["\\\\(", "\\\\)"]],displayMath: [["$$", "$$"], ["\\[", "\\]"]],processEscapes: true,ignoreClass: "tex2jax_ignore|dno"},
TeX: {noUndefined: {attributes: {mathcolor: "red",mathbackground: "#FFEEEE",mathsize: "90%"}},
Safe: {allow: {URLs: "safe",classes: "safe",cssIDs: "safe",styles: "safe",fontsize: "all"}}},
messageStyle: "none"
});
var ready = false; // true after initial typeset is complete
var pending = false; // true when MathJax has been requested
var preview = null; // the preview container
var inline = "$"; // the inline math delimiter
var blocks, start, end, last, braces; // used in searching for math
var math; // stores math until markdone is done
var HUB = MathJax.Hub;
//
// Runs after initial typeset
//
HUB.Queue(function() {
ready = true;
HUB.processUpdateTime = 50; // reduce update time so that we can cancel
// easier
HUB.Config({ "HTML-CSS" : { EqnChunk : 10, EqnChunkFactor : 1 }, // reduce
// chunk
// for
// more
// frequent
// updates
SVG : { EqnChunk : 10, EqnChunkFactor : 1 } });
});
//
// The pattern for math delimiters and special symbols
// needed for searching for math in the page.
//
var SPLIT = /(\$\$?|\\(?:begin|end)\{[a-z]*\*?\}|\\[\\{}$]|[{}]|(?:\n\s*)+|@@\d+@@)/i;
//
// The math is in blocks i through j, so
// collect it into one block and clear the others.
// Replace &, <, and > by named entities.
// For IE, put <br> at the ends of comments since IE removes \n.
// Clear the current math positions and store the index of the
// math, then push the math string onto the storage array.
//
function processMath(i, j) {
var block = blocks.slice(i, j + 1).join("").replace(/&/g, "&amp;") // use
// HTML
// entity
// for
// &
.replace(/</g, "&lt;") // use HTML entity for <
.replace(/>/g, "&gt;") // use HTML entity for >
;
if (HUB.Browser.isMSIE) {
block = block.replace(/(%[^\n]*)\n/g, "$1<br/>\n")
}
while (j > i) {
blocks[j] = "";
j--
}
blocks[i] = "@@" + math.length + "@@";
math.push(block);
start = end = last = null;
}
//
// Break up the text into its component parts and search
// through them for math delimiters, braces, linebreaks, etc.
// Math delimiters must match and braces must balance.
// Don't allow math to pass through a double linebreak
// (which will be a paragraph).
//
function removeMath(text) {
start = end = last = null; // for tracking math delimiters
math = []; // stores math strings for latter
blocks = text.replace(/\r\n?/g, "\n").split(SPLIT);
for ( var i = 1, m = blocks.length; i < m; i += 2) {
var block = blocks[i];
if (block.charAt(0) === "@") {
//
// Things that look like our math markers will get
// stored and then retrieved along with the math.
//
blocks[i] = "@@" + math.length + "@@";
math.push(block);
} else if (start) {
//
// If we are in math, look for the end delimiter,
// but don't go past double line breaks, and
// and balance braces within the math.
//
if (block === end) {
if (braces) {
last = i
} else {
processMath(start, i)
}
} else if (block.match(/\n.*\n/)) {
if (last) {
i = last;
processMath(start, i)
}
start = end = last = null;
braces = 0;
} else if (block === "{") {
braces++
} else if (block === "}" && braces) {
braces--
}
} else {
//
// Look for math start delimiters and when
// found, set up the end delimiter.
//
if (block === inline || block === "$$") {
start = i;
end = block;
braces = 0;
} else if (block.substr(1, 5) === "begin") {
start = i;
end = "\\end" + block.substr(6);
braces = 0;
}
}
}
if (last) {
processMath(start, last)
}
return blocks.join("");
}
//
// Put back the math strings that were saved,
// and clear the math array (no need to keep it around).
//
function replaceMath(text) {
text = text.replace(/@@(\d+)@@/g, function(match, n) {
return math[n]
});
math = null;
return text;
}
//
// This is run to restart MathJax after it has finished
// the previous run (that may have been canceled)
//
var beforeRefreshCallback = undefined;
var afterRefreshCallback = undefined;
function RestartMJ() {
pending = false;
HUB.cancelTypeset = false; // won't need to do this in the future
HUB.Queue(beforeRefreshCallback);
HUB.Queue([ "Typeset", HUB, preview ]);
HUB.Queue(afterRefreshCallback);
}
//
// When the preview changes, cancel MathJax and restart,
// if we haven't done that already.
//
function UpdateMJ() {
if (!pending && ready) {
pending = true;
HUB.Cancel();
HUB.Queue(RestartMJ);
}
}
//
// Save the preview ID and the inline math delimiter.
// Create a converter for the editor and register a preConversion hook
// to handle escaping the math.
// Create a preview refresh hook to handle starting MathJax.
//
function prepareWmdForMathJax(editorObject, delimiters, beforeRefresh, afterRefresh) {
beforeRefreshCallback = beforeRefresh;
afterRefreshCallback = afterRefresh;
preview = document.getElementById("wmd-preview");
inline = delimiters[0][0];
var converterObject = editorObject.getConverter();
converterObject.hooks.chain("preConversion", removeMath);
converterObject.hooks.chain("postConversion", replaceMath);
editorObject.hooks.chain("onPreviewRefresh", UpdateMJ);
}
//
// Set up MathJax to allow canceling of typesetting, if it
// doesn't already have that.
//
if (!HUB.Cancel) {
HUB.cancelTypeset = false;
var CANCELMESSAGE = "MathJax Canceled";
HUB.Register
.StartupHook(
"HTML-CSS Jax Config",
function() {
var HTMLCSS = MathJax.OutputJax["HTML-CSS"], TRANSLATE = HTMLCSS.Translate;
HTMLCSS.Augment({ Translate : function(script, state) {
if (HUB.cancelTypeset || state.cancelled) {
throw Error(CANCELMESSAGE)
}
return TRANSLATE.call(HTMLCSS, script, state);
} });
});
HUB.Register.StartupHook("SVG Jax Config", function() {
var SVG = MathJax.OutputJax["SVG"], TRANSLATE = SVG.Translate;
SVG.Augment({ Translate : function(script, state) {
if (HUB.cancelTypeset || state.cancelled) {
throw Error(CANCELMESSAGE)
}
return TRANSLATE.call(SVG, script, state);
} });
});
HUB.Register.StartupHook("TeX Jax Config", function() {
var TEX = MathJax.InputJax.TeX, TRANSLATE = TEX.Translate;
TEX.Augment({ Translate : function(script, state) {
if (HUB.cancelTypeset || state.cancelled) {
throw Error(CANCELMESSAGE)
}
return TRANSLATE.call(TEX, script, state);
} });
});
var PROCESSERROR = HUB.processError;
HUB.processError = function(error, state, type) {
if (error.message !== CANCELMESSAGE) {
return PROCESSERROR.call(HUB, error, state, type)
}
MathJax.Message.Clear(0, 0);
state.jaxIDs = [];
state.jax = {};
state.scripts = [];
state.i = state.j = 0;
state.cancelled = true;
return null;
};
HUB.Cancel = function() {
this.cancelTypeset = true
};
}
return { prepareWmdForMathJax : prepareWmdForMathJax };
});

View File

@ -1,5 +1,5 @@
define(["jquery", "core", "sharing", "blogger-provider", "dropbox-provider", "gist-provider", "github-provider", "gdrive-provider", "ssh-provider", "tumblr-provider", "wordpress-provider", "underscore"],
function($, core, sharing) {
define(["jquery", "core", "utils", "sharing", "blogger-provider", "dropbox-provider", "gist-provider", "github-provider", "gdrive-provider", "ssh-provider", "tumblr-provider", "wordpress-provider", "underscore"],
function($, core, sharing, utils) {
var publisher = {};
@ -131,7 +131,7 @@ define(["jquery", "core", "sharing", "blogger-provider", "dropbox-provider", "gi
function createPublishIndex(fileIndex, publishAttributes) {
var publishIndex = undefined;
do {
publishIndex = "publish." + core.randomString();
publishIndex = "publish." + utils.randomString();
} while(_.has(localStorage, publishIndex));
localStorage[publishIndex] = JSON.stringify(publishAttributes);
localStorage[fileIndex + ".publish"] += publishIndex + ";";
@ -156,9 +156,9 @@ define(["jquery", "core", "sharing", "blogger-provider", "dropbox-provider", "gi
if(serializedPreferences) {
var publishPreferences = JSON.parse(serializedPreferences);
_.each(provider.publishPreferencesInputIds, function(inputId) {
$("#input-publish-" + inputId).val(publishPreferences[inputId]);
utils.setInputValue("#input-publish-" + inputId, publishPreferences[inputId]);
});
$("input:radio[name=radio-publish-format][value=" + publishPreferences.format + "]").prop("checked", true);
utils.setInputRadio("radio-publish-format", publishPreferences.format);
}
// Open dialog box

View File

@ -1,4 +1,4 @@
define(["jquery", "core", "async-runner", "download-provider", "gist-provider", "underscore"], function($, core, asyncRunner) {
define(["jquery", "core", "utils", "async-runner", "download-provider", "gist-provider", "underscore"], function($, core, utils, asyncRunner) {
var sharing = {};
// Create a map with providerId: providerObject
@ -113,7 +113,7 @@ define(["jquery", "core", "async-runner", "download-provider", "gist-provider",
return;
}
// Check parameters to see if we have to download a shared document
var providerId = core.getURLParameter("provider");
var providerId = utils.getURLParameter("provider");
if(providerId === undefined) {
providerId = "download";
}
@ -123,7 +123,7 @@ define(["jquery", "core", "async-runner", "download-provider", "gist-provider",
}
var importParameters = {};
_.each(provider.sharingAttributes, function(attributeName) {
var parameter = core.getURLParameter(attributeName);
var parameter = utils.getURLParameter(attributeName);
if(!parameter) {
importParameters = undefined;
return;

View File

@ -1,4 +1,4 @@
define([ "jquery", "core", "ssh-helper" ], function($, core, sshHelper) {
define([ "utils", "ssh-helper" ], function(utils, sshHelper) {
var PROVIDER_SSH = "ssh";
@ -22,19 +22,19 @@ define([ "jquery", "core", "ssh-helper" ], function($, core, sshHelper) {
sshProvider.newPublishAttributes = function(event) {
var publishAttributes = {};
publishAttributes.host = core
.getInputValue(
$("#input-publish-ssh-host"),
publishAttributes.host = utils
.getInputTextValue(
"#input-publish-ssh-host",
event,
/^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$/);
publishAttributes.port = core.getInputIntValue(
$("#input-publish-ssh-port"), undefined, 0);
publishAttributes.username = core.getInputValue(
$("#input-publish-ssh-username"), event);
publishAttributes.password = core.getInputValue(
$("#input-publish-ssh-password"), event);
publishAttributes.path = core.getInputValue(
$("#input-publish-file-path"), event);
publishAttributes.port = utils.getInputIntValue(
"#input-publish-ssh-port", undefined, 0);
publishAttributes.username = utils.getInputTextValue(
"#input-publish-ssh-username", event);
publishAttributes.password = utils.getInputTextValue(
"#input-publish-ssh-password", event);
publishAttributes.path = utils.getInputTextValue(
"#input-publish-file-path", event);
if (event.isPropagationStopped()) {
return undefined;
}

View File

@ -1,4 +1,4 @@
define(["jquery", "core", "dropbox-provider", "gdrive-provider", "underscore"], function($, core) {
define(["jquery", "core", "utils", "dropbox-provider", "gdrive-provider", "underscore"], function($, core, utils) {
var synchronizer = {};
// Create a map with providerId: providerObject
@ -101,9 +101,9 @@ define(["jquery", "core", "dropbox-provider", "gdrive-provider", "underscore"],
// Get document title/content
uploadContent = localStorage[fileIndex + ".content"];
uploadContentCRC = core.crc32(uploadContent);
uploadContentCRC = utils.crc32(uploadContent);
uploadTitle = localStorage[fileIndex + ".title"];
uploadTitleCRC = core.crc32(uploadTitle);
uploadTitleCRC = utils.crc32(uploadTitle);
// Parse the list of synchronized locations associated to the document
uploadFileSyncIndexList = _.compact(fileSyncIndexes.split(";"));
@ -247,7 +247,7 @@ define(["jquery", "core", "dropbox-provider", "gdrive-provider", "underscore"],
if(serializedPreferences) {
var exportPreferences = JSON.parse(serializedPreferences);
_.each(provider.exportPreferencesInputIds, function(inputId) {
$("#input-sync-export-" + inputId).val(exportPreferences[inputId]);
utils.setInputValue("#input-sync-export-" + inputId, exportPreferences[inputId]);
});
}

View File

@ -1,4 +1,4 @@
define(["jquery", "core", "tumblr-helper"], function($, core, tumblrHelper) {
define(["utils", "tumblr-helper"], function(utils, tumblrHelper) {
var PROVIDER_TUMBLR = "tumblr";
@ -29,13 +29,13 @@ define(["jquery", "core", "tumblr-helper"], function($, core, tumblrHelper) {
tumblrProvider.newPublishAttributes = function(event) {
var publishAttributes = {};
publishAttributes.blogHostname = core
.getInputValue(
$("#input-publish-tumblr-hostname"),
publishAttributes.blogHostname = utils
.getInputTextValue(
"#input-publish-tumblr-hostname",
event,
/^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$/);
publishAttributes.postId = $("#input-publish-postid").val() || undefined;
publishAttributes.tags = $("#input-publish-tags").val() || undefined;
publishAttributes.postId = utils.getInputTextValue("#input-publish-postid");
publishAttributes.tags = utils.getInputTextValue("#input-publish-tags");
if(event.isPropagationStopped()) {
return undefined;
}

252
js/utils.js Normal file
View File

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

View File

@ -1,4 +1,4 @@
define(["jquery", "core", "wordpress-helper"], function($, core, wordpressHelper) {
define(["utils", "wordpress-helper"], function(utils, wordpressHelper) {
var PROVIDER_WORDPRESS = "wordpress";
@ -29,13 +29,13 @@ define(["jquery", "core", "wordpress-helper"], function($, core, wordpressHelper
wordpressProvider.newPublishAttributes = function(event) {
var publishAttributes = {};
publishAttributes.site = core
.getInputValue(
$("#input-publish-wordpress-site"),
publishAttributes.site = utils
.getInputTextValue(
"#input-publish-wordpress-site",
event,
/^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$/);
publishAttributes.postId = $("#input-publish-postid").val() || undefined;
publishAttributes.tags = $("#input-publish-tags").val() || undefined;
publishAttributes.postId = utils.getInputTextValue("#input-publish-postid");
publishAttributes.tags = utils.getInputTextValue("#input-publish-tags");
if(event.isPropagationStopped()) {
return undefined;
}