Import files from disk
This commit is contained in:
parent
d334dfe2f8
commit
e5cc6e907b
192
js/classes/AsyncTask.js
Normal file
192
js/classes/AsyncTask.js
Normal file
@ -0,0 +1,192 @@
|
||||
define([
|
||||
"underscore",
|
||||
"core",
|
||||
"utils",
|
||||
"extensionMgr",
|
||||
"config",
|
||||
"libs/stacktrace",
|
||||
], function(_, core, utils, extensionMgr) {
|
||||
|
||||
var taskQueue = [];
|
||||
|
||||
function AsyncTask() {
|
||||
this.finished = false;
|
||||
this.timeout = ASYNC_TASK_DEFAULT_TIMEOUT;
|
||||
this.retryCounter = 0;
|
||||
this.callPath = [];
|
||||
this.runCallbacks = [];
|
||||
this.successCallbacks = [];
|
||||
this.errorCallbacks = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* onRun callbacks are called by chain(). These callbacks have to call
|
||||
* chain() themselves to chain with next onRun callback or error() to
|
||||
* throw an exception or retry() to restart the task.
|
||||
*/
|
||||
AsyncTask.prototype.onRun = function(callback) {
|
||||
this.runCallbacks.push(callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* onSuccess callbacks are called when every onRun callbacks have
|
||||
* succeed.
|
||||
*/
|
||||
AsyncTask.prototype.onSuccess = function(callback) {
|
||||
this.successCallbacks.push(callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* onError callbacks are called when error() is called in a onRun
|
||||
* callback.
|
||||
*/
|
||||
AsyncTask.prototype.onError = function(callback) {
|
||||
this.errorCallbacks.push(callback);
|
||||
};
|
||||
|
||||
/**
|
||||
* chain() calls the next onRun callback or the onSuccess callbacks when
|
||||
* finished. The optional callback parameter can be used to pass an
|
||||
* onRun callback during execution, bypassing the onRun queue.
|
||||
*/
|
||||
AsyncTask.prototype.chain = function(callback) {
|
||||
this.callPath.unshift(printStackTrace()[5]);
|
||||
if(this.finished === true) {
|
||||
return;
|
||||
}
|
||||
// If first execution
|
||||
if(this.queue === undefined) {
|
||||
// Create a copy of the onRun callbacks
|
||||
this.queue = this.runCallbacks.slice();
|
||||
}
|
||||
// If a callback is passed as a parameter
|
||||
if(callback !== undefined) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
// If all callbacks have been run
|
||||
if(this.queue.length === 0) {
|
||||
// Run the onSuccess callbacks
|
||||
runSafe(this, this.successCallbacks);
|
||||
return;
|
||||
}
|
||||
// Run the next callback
|
||||
var runCallback = this.queue.shift();
|
||||
runCallback();
|
||||
};
|
||||
|
||||
/**
|
||||
* error() calls the onError callbacks passing the error parameter and
|
||||
* ends the task by throwing an exception.
|
||||
*/
|
||||
AsyncTask.prototype.error = function(error) {
|
||||
this.callPath.unshift(printStackTrace()[5]);
|
||||
if(this.finished === true) {
|
||||
return;
|
||||
}
|
||||
error = error || new Error("Unknown error|\n" + this.callPath.join("\n"));
|
||||
if(error.message) {
|
||||
extensionMgr.onError(error);
|
||||
}
|
||||
runSafe(this, this.errorCallbacks, error);
|
||||
// Exit the current call stack
|
||||
throw error;
|
||||
};
|
||||
|
||||
/**
|
||||
* retry() can be called in an onRun callback to restart the task
|
||||
*/
|
||||
AsyncTask.prototype.retry = function(error, maxRetryCounter) {
|
||||
if(this.finished === true) {
|
||||
return;
|
||||
}
|
||||
maxRetryCounter = maxRetryCounter || 5;
|
||||
this.queue = undefined;
|
||||
if(this.retryCounter >= maxRetryCounter) {
|
||||
this.error(error);
|
||||
return;
|
||||
}
|
||||
// Implement an exponential backoff
|
||||
var delay = Math.pow(2, this.retryCounter++) * 1000;
|
||||
currentTaskStartTime = utils.currentTime + delay;
|
||||
currentTaskRunning = false;
|
||||
this.callPath = [];
|
||||
runTask();
|
||||
};
|
||||
|
||||
/**
|
||||
* enqueue() has to be called to add the task in the running task queue
|
||||
*/
|
||||
AsyncTask.prototype.enqueue = function() {
|
||||
taskQueue.push(this);
|
||||
runTask();
|
||||
};
|
||||
|
||||
var asyncRunning = false;
|
||||
var currentTask = undefined;
|
||||
var currentTaskRunning = false;
|
||||
var currentTaskStartTime = 0;
|
||||
|
||||
// Run the next task in the queue if any and no other running
|
||||
function runTask() {
|
||||
// Use defer to avoid stack overflow
|
||||
_.defer(function() {
|
||||
|
||||
// If there is a task currently running
|
||||
if(currentTaskRunning === true) {
|
||||
// If the current task takes too long
|
||||
if(currentTaskStartTime + currentTask.timeout < utils.currentTime) {
|
||||
currentTask.error(new Error("A timeout occurred.|\n" + currentTask.callPath.join("\n")));
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if(currentTask === undefined) {
|
||||
// If no task in the queue
|
||||
if(taskQueue.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Dequeue an enqueued task
|
||||
currentTask = taskQueue.shift();
|
||||
currentTaskStartTime = utils.currentTime;
|
||||
if(asyncRunning === false) {
|
||||
asyncRunning = true;
|
||||
extensionMgr.onAsyncRunning(true);
|
||||
}
|
||||
}
|
||||
|
||||
// Run the task
|
||||
if(currentTaskStartTime <= utils.currentTime) {
|
||||
currentTaskRunning = true;
|
||||
currentTask.chain();
|
||||
}
|
||||
});
|
||||
}
|
||||
// Run runTask function periodically
|
||||
core.addPeriodicCallback(runTask);
|
||||
|
||||
function runSafe(task, callbacks, param) {
|
||||
try {
|
||||
_.each(callbacks, function(callback) {
|
||||
callback(param);
|
||||
});
|
||||
}
|
||||
finally {
|
||||
task.finished = true;
|
||||
if(currentTask === task) {
|
||||
currentTask = undefined;
|
||||
currentTaskRunning = false;
|
||||
}
|
||||
if(taskQueue.length === 0) {
|
||||
asyncRunning = false;
|
||||
extensionMgr.onAsyncRunning(false);
|
||||
}
|
||||
else {
|
||||
runTask();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return AsyncTask;
|
||||
});
|
102
js/classes/FileDescriptor.js
Normal file
102
js/classes/FileDescriptor.js
Normal file
@ -0,0 +1,102 @@
|
||||
define(["utils"], function(utils) {
|
||||
|
||||
function FileDescriptor(fileIndex, title, syncLocations, publishLocations) {
|
||||
this.fileIndex = fileIndex;
|
||||
this._title = title;
|
||||
this._editorScrollTop = parseInt(localStorage[fileIndex + ".editorScrollTop"]) || 0;
|
||||
this._editorStart = parseInt(localStorage[fileIndex + ".editorStart"]) || 0;
|
||||
this._editorEnd = parseInt(localStorage[fileIndex + ".editorEnd"]) || 0;
|
||||
this._previewScrollTop = parseInt(localStorage[fileIndex + ".previewScrollTop"]) || 0;
|
||||
this._selectTime = parseInt(localStorage[fileIndex + ".selectTime"]) || 0;
|
||||
this.syncLocations = syncLocations || {};
|
||||
this.publishLocations = publishLocations || {};
|
||||
Object.defineProperty(this, 'title', {
|
||||
get: function() {
|
||||
return this._title;
|
||||
},
|
||||
set: function(title) {
|
||||
this._title = title;
|
||||
localStorage[this.fileIndex + ".title"] = title;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(this, 'content', {
|
||||
get: function() {
|
||||
return localStorage[this.fileIndex + ".content"];
|
||||
},
|
||||
set: function(content) {
|
||||
localStorage[this.fileIndex + ".content"] = content;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(this, 'editorScrollTop', {
|
||||
get: function() {
|
||||
return this._editorScrollTop;
|
||||
},
|
||||
set: function(editorScrollTop) {
|
||||
this._editorScrollTop = editorScrollTop;
|
||||
localStorage[this.fileIndex + ".editorScrollTop"] = editorScrollTop;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(this, 'editorStart', {
|
||||
get: function() {
|
||||
return this._editorStart;
|
||||
},
|
||||
set: function(editorStart) {
|
||||
this._editorStart = editorStart;
|
||||
localStorage[this.fileIndex + ".editorStart"] = editorStart;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(this, 'editorEnd', {
|
||||
get: function() {
|
||||
return this._editorEnd;
|
||||
},
|
||||
set: function(editorEnd) {
|
||||
this._editorEnd = editorEnd;
|
||||
localStorage[this.fileIndex + ".editorEnd"] = editorEnd;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(this, 'previewScrollTop', {
|
||||
get: function() {
|
||||
return this._previewScrollTop;
|
||||
},
|
||||
set: function(previewScrollTop) {
|
||||
this._previewScrollTop = previewScrollTop;
|
||||
localStorage[this.fileIndex + ".previewScrollTop"] = previewScrollTop;
|
||||
}
|
||||
});
|
||||
Object.defineProperty(this, 'selectTime', {
|
||||
get: function() {
|
||||
return this._selectTime;
|
||||
},
|
||||
set: function(selectTime) {
|
||||
this._selectTime = selectTime;
|
||||
localStorage[this.fileIndex + ".selectTime"] = selectTime;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
FileDescriptor.prototype.addSyncLocation = function(syncAttributes) {
|
||||
utils.storeAttributes(syncAttributes);
|
||||
utils.appendIndexToArray(this.fileIndex + ".sync", syncAttributes.syncIndex);
|
||||
this.syncLocations[syncAttributes.syncIndex] = syncAttributes;
|
||||
};
|
||||
|
||||
FileDescriptor.prototype.removeSyncLocation = function(syncAttributes) {
|
||||
utils.removeIndexFromArray(this.fileIndex + ".sync", syncAttributes.syncIndex);
|
||||
delete this.syncLocations[syncAttributes.syncIndex];
|
||||
localStorage.removeItem(syncAttributes.syncIndex);
|
||||
};
|
||||
|
||||
FileDescriptor.prototype.addPublishLocation = function(publishAttributes) {
|
||||
utils.storeAttributes(publishAttributes);
|
||||
utils.appendIndexToArray(this.fileIndex + ".publish", publishAttributes.publishIndex);
|
||||
this.publishLocations[publishAttributes.publishIndex] = publishAttributes;
|
||||
};
|
||||
|
||||
FileDescriptor.prototype.removePublishLocation = function(publishAttributes) {
|
||||
utils.removeIndexFromArray(this.fileIndex + ".publish", publishAttributes.publishIndex);
|
||||
delete this.publishLocations[publishAttributes.publishIndex];
|
||||
localStorage.removeItem(publishAttributes.publishIndex);
|
||||
};
|
||||
|
||||
return FileDescriptor;
|
||||
});
|
94
js/extensions/dialogOpenHarddrive.js
Normal file
94
js/extensions/dialogOpenHarddrive.js
Normal file
@ -0,0 +1,94 @@
|
||||
define([
|
||||
"jquery",
|
||||
"underscore",
|
||||
"toMarkdown",
|
||||
"config",
|
||||
], function($, _, toMarkdown) {
|
||||
|
||||
var dialogOpenHarddrive = {
|
||||
extensionId: "dialogOpenHarddrive",
|
||||
extensionName: 'Dialog "Open from"',
|
||||
settingsBloc: '<p>Handles the "Open from hard drive" and the "Convert HTML to Markdown" dialog boxes.</p>'
|
||||
};
|
||||
|
||||
var fileMgr = undefined;
|
||||
dialogOpenHarddrive.onFileMgrCreated = function(fileMgrParameter) {
|
||||
fileMgr = fileMgrParameter;
|
||||
};
|
||||
|
||||
var extensionMgr = undefined;
|
||||
dialogOpenHarddrive.onExtensionMgrCreated = function(extensionMgrParameter) {
|
||||
extensionMgr = extensionMgrParameter;
|
||||
};
|
||||
|
||||
var contentWrapper = undefined;
|
||||
var converter = undefined;
|
||||
var htmlContentWrapper = function(content) {
|
||||
return converter.makeMd(content);
|
||||
};
|
||||
function handleFileImport(evt) {
|
||||
evt.stopPropagation();
|
||||
evt.preventDefault();
|
||||
var files = (evt.dataTransfer || evt.target).files;
|
||||
$("#modal-import-harddrive-markdown, #modal-import-harddrive-html").modal("hide");
|
||||
_.each(files, function(file) {
|
||||
var reader = new FileReader();
|
||||
reader.onload = (function(importedFile) {
|
||||
return function(e) {
|
||||
var content = e.target.result;
|
||||
if(content.match(/\uFFFD/)) {
|
||||
extensionMgr.onError(importedFile.name + " is a binary file.");
|
||||
return;
|
||||
}
|
||||
content = contentWrapper ? contentWrapper(content) : content;
|
||||
if(content === undefined) {
|
||||
extensionMgr.onError(importedFile.name + " is not a valid HTML file.");
|
||||
return;
|
||||
}
|
||||
var title = importedFile.name;
|
||||
var dotPosition = title.lastIndexOf(".");
|
||||
title = dotPosition !== -1 ? title.substring(0, dotPosition) : title;
|
||||
var fileDesc = fileMgr.createFile(title, content);
|
||||
fileMgr.selectFile(fileDesc);
|
||||
};
|
||||
})(file);
|
||||
var blob = file.slice(0, IMPORT_FILE_MAX_CONTENT_SIZE);
|
||||
reader.readAsText(blob);
|
||||
});
|
||||
}
|
||||
|
||||
function handleMarkdownImport(evt) {
|
||||
contentWrapper = undefined;
|
||||
handleFileImport(evt);
|
||||
}
|
||||
|
||||
function handleHtmlImport(evt) {
|
||||
contentWrapper = htmlContentWrapper;
|
||||
handleFileImport(evt);
|
||||
}
|
||||
|
||||
function handleDragOver(evt) {
|
||||
evt.stopPropagation();
|
||||
evt.preventDefault();
|
||||
evt.dataTransfer.dropEffect = 'copy';
|
||||
}
|
||||
|
||||
dialogOpenHarddrive.onReady = function() {
|
||||
// Create toMarkdown converter
|
||||
converter = new toMarkdown.converter();
|
||||
|
||||
$("#input-file-import-harddrive-markdown").change(handleMarkdownImport);
|
||||
$('#dropzone-import-harddrive-markdown').each(function() {
|
||||
this.addEventListener('dragover', handleDragOver, false);
|
||||
this.addEventListener('drop', handleMarkdownImport, false);
|
||||
});
|
||||
$("#input-file-import-harddrive-html").change(handleHtmlImport);
|
||||
$('#dropzone-import-harddrive-html').each(function() {
|
||||
this.addEventListener('dragover', handleDragOver, false);
|
||||
this.addEventListener('drop', handleHtmlImport, false);
|
||||
});
|
||||
};
|
||||
|
||||
return dialogOpenHarddrive;
|
||||
|
||||
});
|
248
js/libs/to-markdown.js
Normal file
248
js/libs/to-markdown.js
Normal file
@ -0,0 +1,248 @@
|
||||
/*
|
||||
* to-markdown - an HTML to Markdown converter
|
||||
*
|
||||
* Copyright 2011-2012, Dom Christie
|
||||
* Licenced under the MIT licence
|
||||
*
|
||||
*/
|
||||
|
||||
(function() {
|
||||
|
||||
var root = this;
|
||||
var toMarkdown = {};
|
||||
var isNode = false;
|
||||
|
||||
if(typeof module !== 'undefined' && module.exports) {
|
||||
module.exports = toMarkdown;
|
||||
root.toMarkdown = toMarkdown;
|
||||
isNode = true;
|
||||
}
|
||||
else {
|
||||
root.toMarkdown = toMarkdown;
|
||||
}
|
||||
|
||||
toMarkdown.converter = function(options) {
|
||||
|
||||
if(options && options.elements && $.isArray(options.elements)) {
|
||||
ELEMENTS = ELEMENTS.concat(options.elements);
|
||||
}
|
||||
|
||||
this.makeMd = function(input, callback) {
|
||||
var result;
|
||||
if(isNode) {
|
||||
var jsdom = require('jsdom');
|
||||
jsdom.env({
|
||||
html: input,
|
||||
scripts: [
|
||||
'http://code.jquery.com/jquery-1.6.4.min.js'
|
||||
],
|
||||
done: function(errors, window) {
|
||||
if(typeof callback === 'function') {
|
||||
callback(process(input, window.$));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
result = process(input, $);
|
||||
}
|
||||
return result;
|
||||
};
|
||||
};
|
||||
|
||||
var process = function(input, $) {
|
||||
// Escape potential ol triggers
|
||||
// see bottom of lists section: http://daringfireball.net/projects/markdown/syntax#list
|
||||
input = input.replace(/(\d+)\. /g, '$1\\. ');
|
||||
|
||||
// Wrap in containing div
|
||||
var $container = $('<div/>');
|
||||
var $input = $container.html(input);
|
||||
|
||||
// Remove whitespace
|
||||
$input.find('*:not(pre, code)').contents().filter(function() {
|
||||
return this.nodeType === 3 && (/^\s+$/.test(this.nodeValue));
|
||||
}).remove();
|
||||
|
||||
var selectors = [];
|
||||
for(var i = 0, len = ELEMENTS.length; i < len; i++) {
|
||||
selectors.push(ELEMENTS[i].selector);
|
||||
}
|
||||
selectors = selectors.join(',');
|
||||
|
||||
while($input.find(selectors).length) {
|
||||
for(var i = 0, len = ELEMENTS.length; i < len; i++) {
|
||||
|
||||
// Find the innermost elements containing NO children that convert to markdown
|
||||
$matches = $input.find(ELEMENTS[i].selector + ':not(:has("' + selectors + '"))');
|
||||
|
||||
$matches.each(function(j, el) {
|
||||
var $el = $(el);
|
||||
$el.before(ELEMENTS[i].replacement($el.html(), $el)).remove();
|
||||
});
|
||||
}
|
||||
}
|
||||
return cleanUp($input.html());
|
||||
};
|
||||
|
||||
// =============
|
||||
// = Utilities =
|
||||
// =============
|
||||
|
||||
var trimNewLines = function(str) {
|
||||
return str.replace(/^[\n\r\f]+|[\n\r\f]+$/g, '');
|
||||
};
|
||||
|
||||
var decodeHtmlEntities = function(str) {
|
||||
return String(str).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
||||
};
|
||||
|
||||
var cleanUp = function(string) {
|
||||
string = string.replace(/^[\t\r\n]+|[\t\r\n]+$/g, ''); // trim leading/trailing whitespace
|
||||
string = string.replace(/\n\s+\n/g, '\n\n');
|
||||
string = string.replace(/\n{3,}/g, '\n\n'); // limit consecutive linebreaks to 2
|
||||
string = decodeHtmlEntities(string);
|
||||
return string;
|
||||
};
|
||||
|
||||
var strongReplacement = function(innerHTML) {
|
||||
innerHTML = trimNewLines(innerHTML);
|
||||
return innerHTML ? '**' + innerHTML + '**' : '';
|
||||
};
|
||||
var emReplacement = function(innerHTML) {
|
||||
innerHTML = trimNewLines(innerHTML);
|
||||
return innerHTML ? '_' + innerHTML + '_' : '';
|
||||
};
|
||||
|
||||
// ============
|
||||
// = Elements =
|
||||
// ============
|
||||
|
||||
var ELEMENTS = [
|
||||
{
|
||||
selector: 'p',
|
||||
replacement: function(innerHTML, el) {
|
||||
innerHTML = $.trim(innerHTML);
|
||||
return innerHTML ? '\n\n' + innerHTML + '\n\n' : '';
|
||||
}
|
||||
},
|
||||
{
|
||||
selector: 'br',
|
||||
replacement: function(innerHTML, el) {
|
||||
return '\n';
|
||||
}
|
||||
},
|
||||
{
|
||||
selector: 'h1,h2,h3,h4,h5,h6',
|
||||
replacement: function(innerHTML, $el) {
|
||||
innerHTML = $.trim(innerHTML);
|
||||
var hLevel = $el.prop("nodeName").charAt(1),
|
||||
prefix = '';
|
||||
for(var i = 0; i < hLevel; i++) {
|
||||
prefix += '#';
|
||||
}
|
||||
return innerHTML ? '\n\n' + prefix + ' ' + innerHTML + '\n\n' : '';
|
||||
}
|
||||
},
|
||||
{
|
||||
selector: 'hr',
|
||||
replacement: function(innerHTML, el) {
|
||||
return '\n\n* * *\n\n';
|
||||
}
|
||||
},
|
||||
{
|
||||
selector: 'a[href]',
|
||||
replacement: function(innerHTML, $el) {
|
||||
if(innerHTML) {
|
||||
innerHTML = trimNewLines(innerHTML);
|
||||
var href = $el.attr('href'),
|
||||
title = $el.attr('title') || '';
|
||||
return '[' + innerHTML + ']' + '(' + href + (title ? ' "' + title + '"' : '') + ')';
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
selector: 'b',
|
||||
replacement: strongReplacement
|
||||
},
|
||||
{
|
||||
selector: 'strong',
|
||||
replacement: strongReplacement
|
||||
},
|
||||
{
|
||||
selector: 'i',
|
||||
replacement: emReplacement
|
||||
},
|
||||
{
|
||||
selector: 'em',
|
||||
replacement: emReplacement
|
||||
},
|
||||
{
|
||||
selector: 'code',
|
||||
replacement: function(innerHTML, el) {
|
||||
innerHTML = trimNewLines(innerHTML);
|
||||
return innerHTML ? '`' + innerHTML + '`' : '';
|
||||
}
|
||||
},
|
||||
{
|
||||
selector: 'img',
|
||||
replacement: function(innerHTML, $el) {
|
||||
var alt = $el.attr('alt') || '',
|
||||
src = $el.attr('src') || '',
|
||||
title = $el.attr('title') || '';
|
||||
return '![' + alt + ']' + '(' + src + (title ? ' "' + title + '"' : '') + ')';
|
||||
}
|
||||
},
|
||||
{
|
||||
selector: 'pre',
|
||||
replacement: function(innerHTML, el) {
|
||||
if(/^\s*\`/.test(innerHTML)) {
|
||||
innerHTML = innerHTML.replace(/\`/g, '');
|
||||
return ' ' + innerHTML.replace(/\n/g, '\n ');
|
||||
}
|
||||
else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
selector: 'li',
|
||||
replacement: function(innerHTML, $el) {
|
||||
innerHTML = innerHTML.replace(/^\s+|\s+$/, '').replace(/\n/gm, '\n ');
|
||||
var prefix = '* ';
|
||||
var suffix = '';
|
||||
var $parent = $el.parent();
|
||||
var $children = $parent.contents().filter(function() {
|
||||
return (this.nodeType === 1 && this.nodeName === 'LI') || (this.nodeType === 3);
|
||||
});
|
||||
var index = $children.index($el) + 1;
|
||||
|
||||
prefix = $parent.is('ol') ? index + '. ' : '* ';
|
||||
if(index == $children.length) {
|
||||
if(!$el.parents('li').length) {
|
||||
suffix = '\n';
|
||||
}
|
||||
innerHTML = innerHTML.replace(/\s+$/, ''); // Trim
|
||||
$el.unwrap();
|
||||
}
|
||||
return prefix + innerHTML + suffix + '\n';
|
||||
}
|
||||
},
|
||||
{
|
||||
selector: 'blockquote',
|
||||
replacement: function(innerHTML, el) {
|
||||
innerHTML = innerHTML = $.trim(innerHTML).replace(/\n{3,}/g, '\n\n');
|
||||
innerHTML = innerHTML.replace(/\n/g, '\n> ');
|
||||
return "> " + innerHTML;
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
var NON_MD_BLOCK_ELEMENTS = ['address', 'article', 'aside', 'audio', 'canvas', 'div', 'dl', 'dd', 'dt',
|
||||
'fieldset', 'figcaption', 'figure', 'footer', 'form', 'header', 'hgroup', 'output',
|
||||
'table', 'tbody', 'td', 'tfoot', 'th', 'thead', 'tr', 'section', 'video'];
|
||||
|
||||
})();
|
Loading…
Reference in New Issue
Block a user