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; 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; 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 { table {
margin-bottom: 20px; margin-bottom: 20px;
} }

View File

@ -542,6 +542,8 @@
href="#tabpane-settings-editor" data-toggle="tab">Editor</a></li> href="#tabpane-settings-editor" data-toggle="tab">Editor</a></li>
<li><a class="action-load-settings" <li><a class="action-load-settings"
href="#tabpane-settings-publish" data-toggle="tab">Publish</a></li> 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" <li><a class="action-load-settings"
href="#tabpane-settings-utils" data-toggle="tab">Utils</a></li> href="#tabpane-settings-utils" data-toggle="tab">Utils</a></li>
</ul> </ul>
@ -570,24 +572,6 @@
your own theme...</a></span> your own theme...</a></span>
</div> </div>
</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"> <div class="control-group">
<label class="control-label" for="input-settings-lazy-rendering">Lazy <label class="control-label" for="input-settings-lazy-rendering">Lazy
rendering <a href="#" class="tooltip-lazy-rendering">(?)</a> rendering <a href="#" class="tooltip-lazy-rendering">(?)</a>
@ -596,14 +580,6 @@
<input type="checkbox" id="input-settings-lazy-rendering" /> <input type="checkbox" id="input-settings-lazy-rendering" />
</div> </div>
</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"> <div class="control-group">
<label class="control-label" <label class="control-label"
for="input-settings-editor-font-size">Editor font size</label> for="input-settings-editor-font-size">Editor font size</label>
@ -650,6 +626,13 @@
</div> </div>
</div> </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 class="tab-pane" id="tabpane-settings-utils">
<div style="width: 200px; margin: 10px auto;"> <div style="width: 200px; margin: 10px auto;">
<a href="#" class="btn btn-block btn-primary action-welcome-file" <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"; var PROVIDER_BLOGGER = "blogger";
@ -31,17 +31,17 @@ define(["jquery", "core", "google-helper"], function($, core, googleHelper) {
bloggerProvider.newPublishAttributes = function(event) { bloggerProvider.newPublishAttributes = function(event) {
var publishAttributes = {}; var publishAttributes = {};
var blogUrl = core.getInputValue($("#input-publish-blogger-url"), event); var blogUrl = utils.getInputTextValue("#input-publish-blogger-url", event);
if(blogUrl !== undefined) { 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 = []; publishAttributes.labelList = [];
var labels = $("#input-publish-labels").val() || undefined; var labels = utils.getInputTextValue("#input-publish-labels");
if(labels !== undefined) { if(labels !== undefined) {
publishAttributes.labelList = _.chain(labels.split(",")) publishAttributes.labelList = _.chain(labels.split(","))
.map(function(label) { .map(function(label) {
return core.trim(label); return utils.trim(label);
}).compact().value(); }).compact().value();
} }
if(event.isPropagationStopped()) { if(event.isPropagationStopped()) {

View File

@ -1,7 +1,7 @@
define( 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" ], "underscore", "FileSaver", "css_browser_selector" ],
function($, extensionManager) { function($, utils, extensionManager) {
var core = {}; var core = {};
@ -46,7 +46,7 @@ define(
return; return;
} }
if(windowId === undefined) { if(windowId === undefined) {
windowId = core.randomString(); windowId = utils.randomString();
localStorage["frontWindowId"] = windowId; localStorage["frontWindowId"] = windowId;
} }
var frontWindowId = localStorage["frontWindowId"]; 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 // Export data on disk
core.saveFile = function(content, filename) { core.saveFile = function(content, filename) {
@ -126,7 +71,7 @@ define(
} }
else { else {
var uriContent = "data:application/octet-stream;base64," var uriContent = "data:application/octet-stream;base64,"
+ core.encodeBase64(content); + utils.encodeBase64(content);
window.open(uriContent, 'file'); window.open(uriContent, 'file');
} }
}; };
@ -202,11 +147,8 @@ define(
// Setting management // Setting management
core.settings = { core.settings = {
converterType : "markdown-extra-prettify",
enableMathJax : true,
lazyRendering : true,
layoutOrientation : "horizontal", layoutOrientation : "horizontal",
scrollLink : 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",
@ -217,66 +159,58 @@ define(
'</head>\n', '</head>\n',
'<body><%= documentHTML %></body>\n', '<body><%= documentHTML %></body>\n',
'</html>'].join(""), '</html>'].join(""),
sshProxy : SSH_PROXY_URL, sshProxy : SSH_PROXY_URL
sshConnectionList : []
}; };
if (_.has(localStorage, "settings")) {
core.loadSettings = function() { _.extend(core.settings, JSON.parse(localStorage.settings));
if (localStorage.settings) { }
$.extend(core.settings, JSON.parse(localStorage.settings));
}
core.loadSettings = function() {
// Layout orientation // Layout orientation
$("input:radio[name=radio-layout-orientation][value=" utils.setInputRadio("radio-layout-orientation", core.settings.layoutOrientation);
+ core.settings.layoutOrientation + "]").prop("checked", true);
// Scroll Link
$("#input-settings-scroll-link").prop("checked", core.settings.scrollLink);
// Theme // Theme
$("#input-settings-theme").val(localStorage.theme); utils.setInputValue("#input-settings-theme", localStorage.theme);
// Converter type
$("#input-settings-converter-type").val(core.settings.converterType);
// MathJax
$("#input-settings-enable-mathjax").prop("checked", core.settings.enableMathJax);
// Lazy rendering // Lazy rendering
$("#input-settings-lazy-rendering").prop("checked", core.settings.lazyRendering); utils.setInputChecked("#input-settings-lazy-rendering", core.settings.lazyRendering);
// Editor font size // Editor font size
$("#input-settings-editor-font-size").val(core.settings.editorFontSize); utils.setInputValue("#input-settings-editor-font-size", core.settings.editorFontSize);
// Default content // Default content
$("#textarea-settings-default-content").val(core.settings.defaultContent); utils.setInputValue("#textarea-settings-default-content", core.settings.defaultContent);
// Commit message // Commit message
$("#input-settings-publish-commit-msg").val(core.settings.commitMsg); utils.setInputValue("#input-settings-publish-commit-msg", core.settings.commitMsg);
// Template // Template
$("#textarea-settings-publish-template").val(core.settings.template); utils.setInputValue("#textarea-settings-publish-template", core.settings.template);
// SSH proxy // 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) { core.saveSettings = function(event) {
var newSettings = {}; var newSettings = {};
// Layout orientation // Layout orientation
newSettings.layoutOrientation = $( newSettings.layoutOrientation = utils.getInputRadio("radio-layout-orientation");
"input:radio[name=radio-layout-orientation]:checked").prop("value");
// Theme // Theme
var theme = $("#input-settings-theme").val(); var theme = utils.getInputValue("#input-settings-theme");
// 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");
// Lazy Rendering // Lazy Rendering
newSettings.lazyRendering = $("#input-settings-lazy-rendering").prop("checked"); newSettings.lazyRendering = utils.getInputChecked("#input-settings-lazy-rendering");
// Editor font size // 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 // Default content
newSettings.defaultContent = $("#textarea-settings-default-content").val(); newSettings.defaultContent = utils.getInputValue("#textarea-settings-default-content");
// Commit message // Commit message
newSettings.commitMsg = core.getInputValue($("#input-settings-publish-commit-msg"), event); newSettings.commitMsg = utils.getInputTextValue("#input-settings-publish-commit-msg", event);
// Template // Template
newSettings.template = core.getInputValue($("#textarea-settings-publish-template"), event); newSettings.template = utils.getInputTextValue("#textarea-settings-publish-template", event);
// SSH proxy // 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()) { if(!event.isPropagationStopped()) {
$.extend(core.settings, newSettings); $.extend(core.settings, newSettings);
@ -428,142 +362,6 @@ define(
$("#wmd-undo-button").append($("<i>").addClass("icon-undo")); $("#wmd-undo-button").append($("<i>").addClass("icon-undo"));
$("#wmd-redo-button").append($("<i>").addClass("icon-share-alt")); $("#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 // Create an centered popup window
core.popupWindow = function(url, title, w, h) { core.popupWindow = function(url, title, w, h) {
@ -610,9 +408,8 @@ define(
}); });
core.onReady(function() { core.onReady(function() {
extensionManager.init(core.settings.extensionConfig); extensionManager.init(core.settings.extensionSettings);
}); });
core.onReady(extensionManager.onReady);
core.onReady(function() { core.onReady(function() {
// Load theme list // Load theme list
@ -637,13 +434,13 @@ define(
// Click events on "insert link" and "insert image" dialog buttons // Click events on "insert link" and "insert image" dialog buttons
$(".action-insert-link").click(function(e) { $(".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) { if(value !== undefined) {
insertLinkCallback(value); insertLinkCallback(value);
} }
}); });
$(".action-insert-image").click(function(e) { $(".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) { if(value !== undefined) {
insertLinkCallback(value); insertLinkCallback(value);
} }
@ -653,11 +450,9 @@ define(
}); });
// Settings loading/saving // Settings loading/saving
core.loadSettings();
$(".action-load-settings").click(function() { $(".action-load-settings").click(function() {
core.loadSettings(); core.loadSettings();
}); });
$(".action-apply-settings").click(function(e) { $(".action-apply-settings").click(function(e) {
core.saveSettings(e); core.saveSettings(e);
if(!e.isPropagationStopped()) { if(!e.isPropagationStopped()) {
@ -667,6 +462,7 @@ define(
$(".action-default-settings").click(function() { $(".action-default-settings").click(function() {
localStorage.removeItem("settings"); localStorage.removeItem("settings");
localStorage.removeItem("theme");
window.location.reload(); window.location.reload();
}); });
@ -723,7 +519,7 @@ define(
html: true, html: true,
container: '#modal-settings', container: '#modal-settings',
placement: 'right', 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({ $(".tooltip-template").tooltip({
html: true, 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"; var PROVIDER_DROPBOX = "dropbox";
@ -28,7 +28,7 @@ define(["jquery", "core", "dropbox-helper"], function($, core, dropboxHelper) {
syncAttributes.provider = PROVIDER_DROPBOX; syncAttributes.provider = PROVIDER_DROPBOX;
syncAttributes.path = path; syncAttributes.path = path;
syncAttributes.version = versionTag; syncAttributes.version = versionTag;
syncAttributes.contentCRC = core.crc32(content); syncAttributes.contentCRC = utils.crc32(content);
return syncAttributes; return syncAttributes;
} }
@ -107,12 +107,12 @@ define(["jquery", "core", "dropbox-helper"], function($, core, dropboxHelper) {
} }
dropboxProvider.exportFile = function(event, title, content, callback) { 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); exportFileToPath(path, title, content, callback);
}; };
dropboxProvider.exportManual = function(event, 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); exportFileToPath(path, title, content, callback);
}; };
@ -191,9 +191,9 @@ define(["jquery", "core", "dropbox-helper"], function($, core, dropboxHelper) {
} }
var syncAttributes = change.syncAttributes; var syncAttributes = change.syncAttributes;
var localContent = localStorage[fileIndex + ".content"]; var localContent = localStorage[fileIndex + ".content"];
var localContentChanged = syncAttributes.contentCRC != core.crc32(localContent); var localContentChanged = syncAttributes.contentCRC != utils.crc32(localContent);
var file = change.stat; var file = change.stat;
var remoteContentCRC = core.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
@ -236,7 +236,7 @@ define(["jquery", "core", "dropbox-helper"], function($, core, dropboxHelper) {
dropboxProvider.newPublishAttributes = function(event) { dropboxProvider.newPublishAttributes = function(event) {
var publishAttributes = {}; var publishAttributes = {};
publishAttributes.path = core.getInputValue($("#input-publish-dropbox-path"), event); publishAttributes.path = utils.getInputTextValue("#input-publish-dropbox-path", event);
if(event.isPropagationStopped()) { if(event.isPropagationStopped()) {
return undefined; return undefined;
} }

View File

@ -1,24 +1,27 @@
define( [ define( [
"jquery",
"utils",
"underscore", "underscore",
"bootstrap",
"extensions/notifications", "extensions/notifications",
"extensions/markdown-extra", "extensions/markdown-extra",
"extensions/math-jax", "extensions/math-jax",
"extensions/scroll-link" "extensions/scroll-link"
], function() { ], function($, utils) {
var extensionManager = {}; var extensionManager = {};
// Create a map with providerId: providerObject // Create a map with providerId: providerObject
var extensionList = _.chain(arguments) var extensionList = _.chain(arguments)
.map(function(argument) { .map(function(argument) {
return 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(callbackName) { function getExtensionCallbackList(callbackName) {
return _.chain(extensionList) return _.chain(extensionList)
.map(function(extension) { .map(function(extension) {
return extension[callbackName]; return extension.config.enabled && extension[callbackName];
}).compact().value(); }).compact().value();
} }
@ -38,39 +41,89 @@ define( [
extensionManager[callbackName] = createCallback(callbackName); extensionManager[callbackName] = createCallback(callbackName);
} }
extensionManager.init = function(extensionConfigMap) { var accordionTmpl = [
// Set the extension configuration '<div class="accordion-group">',
extensionConfigMap = extensionConfigMap || {}; '<div class="accordion-heading">',
_.each(extensionList, function(extension) { '<label class="checkbox pull-right">',
extension.config = _.extend({}, extension.defaultConfig, extensionConfigMap[extension.extensionId]); '<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 %>',
addCallback("onReady"); '</a>',
addCallback("onMessage"); '</div>',
addCallback("onError"); '<div id="collapse-<%= extensionId %>" class="accordion-body collapse">',
addCallback("onOfflineChanged"); '<div class="accordion-inner"><%= settingsBloc %></div>',
addCallback("onLayoutConfigure"); '</div>',
addCallback("onLayoutCreated"); '</div>'].join("");
addCallback("onEditorConfigure");
function createSettings(extension) {
$("#accordion-extensions").append($(_.template(accordionTmpl, {
extensionId: extension.extensionId,
extensionName: extension.extensionName,
optional: extension.optional,
settingsBloc: extension.settingsBloc
})));
}
var onPreviewFinished = createCallback("onPreviewFinished"); extensionManager.init = function(extensionSettings) {
var onAsyncPreviewCallbackList = getExtensionCallbackList("onAsyncPreview");
extensionManager["onAsyncPreview"] = function() { // Set extension config
// Call onPreviewFinished callbacks when all async preview are finished extensionSettings = extensionSettings || {};
var counter = 0; _.each(extensionList, function(extension) {
function tryFinished() { extension.config = _.extend({}, extension.defaultConfig, extensionSettings[extension.extensionId]);
if(counter === onAsyncPreviewCallbackList.length) { extension.config.enabled = !extension.optional || extension.config.enabled;
onPreviewFinished();
}
}
_.each(onAsyncPreviewCallbackList, function(asyncPreviewCallback) {
asyncPreviewCallback(function() {
counter++;
tryFinished();
});
}); });
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; return extensionManager;

View File

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

View File

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

View File

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

View File

@ -2,7 +2,15 @@ define( [ "jquery", "underscore" ], function($) {
var scrollLink = { var scrollLink = {
extensionId: "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 = []; var mdSectionList = [];

View File

@ -1,5 +1,5 @@
define(["jquery", "core", "synchronizer", "publisher", "sharing", "text!../WELCOME.md", "underscore"], define(["jquery", "core", "utils", "synchronizer", "publisher", "sharing", "text!../WELCOME.md", "underscore"],
function($, core, synchronizer, publisher, sharing, welcomeContent) { function($, core, utils, synchronizer, publisher, sharing, welcomeContent) {
var TEMPORARY_FILE_INDEX = "file.tempIndex"; var TEMPORARY_FILE_INDEX = "file.tempIndex";
@ -75,7 +75,7 @@ define(["jquery", "core", "synchronizer", "publisher", "sharing", "text!../WELCO
var fileIndex = TEMPORARY_FILE_INDEX; var fileIndex = TEMPORARY_FILE_INDEX;
if(!isTemporary) { if(!isTemporary) {
do { do {
fileIndex = "file." + core.randomString(); fileIndex = "file." + utils.randomString();
} while(_.has(localStorage, fileIndex + ".title")); } 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"; var PROVIDER_GDRIVE = "gdrive";
@ -15,8 +15,8 @@ define(["jquery", "core", "google-helper", "underscore"], function($, core, goog
syncAttributes.provider = PROVIDER_GDRIVE; syncAttributes.provider = PROVIDER_GDRIVE;
syncAttributes.id = id; syncAttributes.id = id;
syncAttributes.etag = etag; syncAttributes.etag = etag;
syncAttributes.contentCRC = core.crc32(content); syncAttributes.contentCRC = utils.crc32(content);
syncAttributes.titleCRC = core.crc32(title); syncAttributes.titleCRC = utils.crc32(title);
return syncAttributes; return syncAttributes;
} }
@ -71,7 +71,7 @@ define(["jquery", "core", "google-helper", "underscore"], function($, core, goog
}; };
gdriveProvider.exportFile = function(event, title, content, callback) { 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) { googleHelper.upload(undefined, parentId, title, content, undefined, function(error, result) {
if (error) { if (error) {
callback(error); callback(error);
@ -85,7 +85,7 @@ define(["jquery", "core", "google-helper", "underscore"], function($, core, goog
}; };
gdriveProvider.exportManual = function(event, title, content, callback) { 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) { if(!id) {
return; return;
} }
@ -186,14 +186,14 @@ define(["jquery", "core", "google-helper", "underscore"], function($, core, goog
return; return;
} }
var syncAttributes = change.syncAttributes; var syncAttributes = change.syncAttributes;
var localTitleChanged = syncAttributes.titleCRC != core.crc32(localTitle); var localTitleChanged = syncAttributes.titleCRC != utils.crc32(localTitle);
var localContent = localStorage[fileIndex + ".content"]; var localContent = localStorage[fileIndex + ".content"];
var localContentChanged = syncAttributes.contentCRC != core.crc32(localContent); var localContentChanged = syncAttributes.contentCRC != utils.crc32(localContent);
var file = change.file; var file = change.file;
var remoteTitleCRC = core.crc32(file.title); var remoteTitleCRC = utils.crc32(file.title);
var remoteTitleChanged = syncAttributes.titleCRC != remoteTitleCRC; var remoteTitleChanged = syncAttributes.titleCRC != remoteTitleCRC;
var fileTitleChanged = localTitle != file.title; var fileTitleChanged = localTitle != file.title;
var remoteContentCRC = core.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
@ -253,8 +253,8 @@ define(["jquery", "core", "google-helper", "underscore"], function($, core, goog
gdriveProvider.newPublishAttributes = function(event) { gdriveProvider.newPublishAttributes = function(event) {
var publishAttributes = {}; var publishAttributes = {};
publishAttributes.id = $("#input-publish-gdrive-fileid").val() || undefined; publishAttributes.id = utils.getInputTextValue("#input-publish-gdrive-fileid");
publishAttributes.fileName = $("#input-publish-gdrive-filename").val() || undefined; publishAttributes.fileName = utils.getInputTextValue("#input-publish-gdrive-filename");
if(event.isPropagationStopped()) { if(event.isPropagationStopped()) {
return undefined; 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"; var PROVIDER_GIST = "gist";
@ -23,9 +23,9 @@ define(["jquery", "core", "github-helper"], function($, core, githubHelper) {
gistProvider.newPublishAttributes = function(event) { gistProvider.newPublishAttributes = function(event) {
var publishAttributes = {}; var publishAttributes = {};
publishAttributes.gistId = $("#input-publish-gist-id").val() || undefined; publishAttributes.gistId = utils.getInputTextValue("#input-publish-gist-id");
publishAttributes.filename = core.getInputValue($("#input-publish-filename"), event); publishAttributes.filename = utils.getInputTextValue("#input-publish-filename", event);
publishAttributes.isPublic = $("#input-publish-gist-public").is(":checked"); publishAttributes.isPublic = utils.getInputChecked("#input-publish-gist-public");
if(event.isPropagationStopped()) { if(event.isPropagationStopped()) {
return undefined; 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"; var PROVIDER_GITHUB = "github";
@ -16,9 +16,9 @@ define(["jquery", "core", "github-helper"], function($, core, githubHelper) {
githubProvider.newPublishAttributes = function(event) { githubProvider.newPublishAttributes = function(event) {
var publishAttributes = {}; var publishAttributes = {};
publishAttributes.repository = core.getInputValue($("#input-publish-github-reponame"), event); publishAttributes.repository = utils.getInputTextValue("#input-publish-github-reponame", event);
publishAttributes.branch = core.getInputValue($("#input-publish-github-branch"), event); publishAttributes.branch = utils.getInputTextValue("#input-publish-github-branch", event);
publishAttributes.path = core.getInputValue($("#input-publish-file-path"), event); publishAttributes.path = utils.getInputTextValue("#input-publish-file-path", event);
if(event.isPropagationStopped()) { if(event.isPropagationStopped()) {
return undefined; return undefined;
} }

View File

@ -104,7 +104,7 @@ define(["jquery", "core", "async-runner"], function($, core, asyncRunner) {
//headers["If-Match"] = etag; //headers["If-Match"] = etag;
} }
var base64Data = core.encodeBase64(content); var base64Data = utils.encodeBase64(content);
var multipartRequestBody = delimiter var multipartRequestBody = delimiter
+ 'Content-Type: application/json\r\n\r\n' + 'Content-Type: application/json\r\n\r\n'
+ JSON.stringify(metadata) + delimiter + 'Content-Type: ' + 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"], 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) { function($, core, sharing, utils) {
var publisher = {}; var publisher = {};
@ -131,7 +131,7 @@ define(["jquery", "core", "sharing", "blogger-provider", "dropbox-provider", "gi
function createPublishIndex(fileIndex, publishAttributes) { function createPublishIndex(fileIndex, publishAttributes) {
var publishIndex = undefined; var publishIndex = undefined;
do { do {
publishIndex = "publish." + core.randomString(); publishIndex = "publish." + utils.randomString();
} while(_.has(localStorage, publishIndex)); } while(_.has(localStorage, publishIndex));
localStorage[publishIndex] = JSON.stringify(publishAttributes); localStorage[publishIndex] = JSON.stringify(publishAttributes);
localStorage[fileIndex + ".publish"] += publishIndex + ";"; localStorage[fileIndex + ".publish"] += publishIndex + ";";
@ -156,9 +156,9 @@ define(["jquery", "core", "sharing", "blogger-provider", "dropbox-provider", "gi
if(serializedPreferences) { if(serializedPreferences) {
var publishPreferences = JSON.parse(serializedPreferences); var publishPreferences = JSON.parse(serializedPreferences);
_.each(provider.publishPreferencesInputIds, function(inputId) { _.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 // 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 = {}; var sharing = {};
// Create a map with providerId: providerObject // Create a map with providerId: providerObject
@ -113,7 +113,7 @@ define(["jquery", "core", "async-runner", "download-provider", "gist-provider",
return; return;
} }
// Check parameters to see if we have to download a shared document // 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) { if(providerId === undefined) {
providerId = "download"; providerId = "download";
} }
@ -123,7 +123,7 @@ define(["jquery", "core", "async-runner", "download-provider", "gist-provider",
} }
var importParameters = {}; var importParameters = {};
_.each(provider.sharingAttributes, function(attributeName) { _.each(provider.sharingAttributes, function(attributeName) {
var parameter = core.getURLParameter(attributeName); var parameter = utils.getURLParameter(attributeName);
if(!parameter) { if(!parameter) {
importParameters = undefined; importParameters = undefined;
return; 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"; var PROVIDER_SSH = "ssh";
@ -22,19 +22,19 @@ define([ "jquery", "core", "ssh-helper" ], function($, core, sshHelper) {
sshProvider.newPublishAttributes = function(event) { sshProvider.newPublishAttributes = function(event) {
var publishAttributes = {}; var publishAttributes = {};
publishAttributes.host = core publishAttributes.host = utils
.getInputValue( .getInputTextValue(
$("#input-publish-ssh-host"), "#input-publish-ssh-host",
event, 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])$/); /^(([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( publishAttributes.port = utils.getInputIntValue(
$("#input-publish-ssh-port"), undefined, 0); "#input-publish-ssh-port", undefined, 0);
publishAttributes.username = core.getInputValue( publishAttributes.username = utils.getInputTextValue(
$("#input-publish-ssh-username"), event); "#input-publish-ssh-username", event);
publishAttributes.password = core.getInputValue( publishAttributes.password = utils.getInputTextValue(
$("#input-publish-ssh-password"), event); "#input-publish-ssh-password", event);
publishAttributes.path = core.getInputValue( publishAttributes.path = utils.getInputTextValue(
$("#input-publish-file-path"), event); "#input-publish-file-path", event);
if (event.isPropagationStopped()) { if (event.isPropagationStopped()) {
return undefined; 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 = {}; var synchronizer = {};
// Create a map with providerId: providerObject // Create a map with providerId: providerObject
@ -101,9 +101,9 @@ define(["jquery", "core", "dropbox-provider", "gdrive-provider", "underscore"],
// Get document title/content // Get document title/content
uploadContent = localStorage[fileIndex + ".content"]; uploadContent = localStorage[fileIndex + ".content"];
uploadContentCRC = core.crc32(uploadContent); uploadContentCRC = utils.crc32(uploadContent);
uploadTitle = localStorage[fileIndex + ".title"]; uploadTitle = localStorage[fileIndex + ".title"];
uploadTitleCRC = core.crc32(uploadTitle); uploadTitleCRC = utils.crc32(uploadTitle);
// Parse the list of synchronized locations associated to the document // Parse the list of synchronized locations associated to the document
uploadFileSyncIndexList = _.compact(fileSyncIndexes.split(";")); uploadFileSyncIndexList = _.compact(fileSyncIndexes.split(";"));
@ -247,7 +247,7 @@ define(["jquery", "core", "dropbox-provider", "gdrive-provider", "underscore"],
if(serializedPreferences) { if(serializedPreferences) {
var exportPreferences = JSON.parse(serializedPreferences); var exportPreferences = JSON.parse(serializedPreferences);
_.each(provider.exportPreferencesInputIds, function(inputId) { _.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"; var PROVIDER_TUMBLR = "tumblr";
@ -29,13 +29,13 @@ define(["jquery", "core", "tumblr-helper"], function($, core, tumblrHelper) {
tumblrProvider.newPublishAttributes = function(event) { tumblrProvider.newPublishAttributes = function(event) {
var publishAttributes = {}; var publishAttributes = {};
publishAttributes.blogHostname = core publishAttributes.blogHostname = utils
.getInputValue( .getInputTextValue(
$("#input-publish-tumblr-hostname"), "#input-publish-tumblr-hostname",
event, 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])$/); /^(([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.postId = utils.getInputTextValue("#input-publish-postid");
publishAttributes.tags = $("#input-publish-tags").val() || undefined; publishAttributes.tags = utils.getInputTextValue("#input-publish-tags");
if(event.isPropagationStopped()) { if(event.isPropagationStopped()) {
return undefined; 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"; var PROVIDER_WORDPRESS = "wordpress";
@ -29,13 +29,13 @@ define(["jquery", "core", "wordpress-helper"], function($, core, wordpressHelper
wordpressProvider.newPublishAttributes = function(event) { wordpressProvider.newPublishAttributes = function(event) {
var publishAttributes = {}; var publishAttributes = {};
publishAttributes.site = core publishAttributes.site = utils
.getInputValue( .getInputTextValue(
$("#input-publish-wordpress-site"), "#input-publish-wordpress-site",
event, 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])$/); /^(([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.postId = utils.getInputTextValue("#input-publish-postid");
publishAttributes.tags = $("#input-publish-tags").val() || undefined; publishAttributes.tags = utils.getInputTextValue("#input-publish-tags");
if(event.isPropagationStopped()) { if(event.isPropagationStopped()) {
return undefined; return undefined;
} }