pen 0.1.2 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.codeclimate.yml +29 -0
- data/.csslintrc +2 -0
- data/.eslintignore +1 -0
- data/.eslintrc +213 -0
- data/.rubocop.yml +1156 -0
- data/LICENSE.txt +1 -1
- data/README.md +1 -1
- data/app/assets/fonts/pen/fontello.eot +0 -0
- data/app/assets/fonts/pen/fontello.svg +0 -0
- data/app/assets/fonts/pen/fontello.ttf +0 -0
- data/app/assets/fonts/pen/fontello.woff +0 -0
- data/app/assets/javascripts/pen/markdown.js +19 -13
- data/app/assets/javascripts/pen/pen.js +692 -230
- data/app/assets/stylesheets/pen/pen.css.scss +78 -7
- data/lib/pen/version.rb +1 -1
- metadata +15 -10
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# Pen
|
2
|
-
[](http://badge.fury.io/rb/pen) [](http://badge.fury.io/rb/pen) [](https://codeclimate.com/github/neowork/pen)
|
3
3
|
|
4
4
|
Pen Editor for Rails
|
5
5
|
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
@@ -1,8 +1,8 @@
|
|
1
1
|
/*! Licensed under MIT, https://github.com/sofish/pen */
|
2
|
-
(function() {
|
2
|
+
(function(root) {
|
3
3
|
|
4
4
|
// only works with Pen
|
5
|
-
if(!
|
5
|
+
if(!root.Pen) return;
|
6
6
|
|
7
7
|
// markdown covertor obj
|
8
8
|
var covertor = {
|
@@ -34,10 +34,17 @@
|
|
34
34
|
var code = e.keyCode || e.which;
|
35
35
|
|
36
36
|
// when `space` is pressed
|
37
|
-
if(code === 32) {
|
38
|
-
var
|
39
|
-
|
40
|
-
|
37
|
+
if (code === 32) {
|
38
|
+
var markdownSyntax = this.stack.join('');
|
39
|
+
// reset stack
|
40
|
+
this.stack = [];
|
41
|
+
|
42
|
+
var cmd = this.valid(markdownSyntax);
|
43
|
+
if (cmd) {
|
44
|
+
// prevents leading space after executing command
|
45
|
+
e.preventDefault();
|
46
|
+
return cmd;
|
47
|
+
}
|
41
48
|
}
|
42
49
|
|
43
50
|
// make cmd
|
@@ -50,23 +57,22 @@
|
|
50
57
|
covertor.action = function(pen, cmd) {
|
51
58
|
|
52
59
|
// only apply effect at line start
|
53
|
-
if(pen.
|
60
|
+
if(pen.selection.focusOffset > cmd[1]) return;
|
54
61
|
|
55
|
-
var node = pen.
|
62
|
+
var node = pen.selection.focusNode;
|
56
63
|
node.textContent = node.textContent.slice(cmd[1]);
|
57
|
-
pen.
|
58
|
-
pen.nostyle();
|
64
|
+
pen.execCommand(cmd[0]);
|
59
65
|
};
|
60
66
|
|
61
67
|
// init covertor
|
62
68
|
covertor.init = function(pen) {
|
63
|
-
pen.
|
69
|
+
pen.on('keypress', function(e) {
|
64
70
|
var cmd = covertor.parse(e);
|
65
71
|
if(cmd) return covertor.action(pen, cmd);
|
66
72
|
});
|
67
73
|
};
|
68
74
|
|
69
75
|
// append to Pen
|
70
|
-
|
76
|
+
root.Pen.prototype.markdown = covertor;
|
71
77
|
|
72
|
-
}());
|
78
|
+
}(window));
|
@@ -1,41 +1,76 @@
|
|
1
1
|
/*! Licensed under MIT, https://github.com/sofish/pen */
|
2
|
-
|
3
|
-
|
2
|
+
(function(root, doc) {
|
3
|
+
|
4
|
+
var Pen, debugMode, selection, utils = {};
|
5
|
+
var toString = Object.prototype.toString;
|
6
|
+
var slice = Array.prototype.slice;
|
7
|
+
|
8
|
+
// allow command list
|
9
|
+
var commandsReg = {
|
10
|
+
block: /^(?:p|h[1-6]|blockquote|pre)$/,
|
11
|
+
inline: /^(?:bold|italic|underline|insertorderedlist|insertunorderedlist|indent|outdent)$/,
|
12
|
+
source: /^(?:createlink|unlink)$/,
|
13
|
+
insert: /^(?:inserthorizontalrule|insertimage|insert)$/,
|
14
|
+
wrap: /^(?:code)$/
|
15
|
+
};
|
16
|
+
|
17
|
+
var lineBreakReg = /^(?:blockquote|pre|div)$/i;
|
18
|
+
|
19
|
+
var effectNodeReg = /(?:[pubia]|h[1-6]|blockquote|[uo]l|li)/i;
|
4
20
|
|
5
|
-
var
|
21
|
+
var strReg = {
|
22
|
+
whiteSpace: /(^\s+)|(\s+$)/g,
|
23
|
+
mailTo: /^(?!mailto:|.+\/|.+#|.+\?)(.*@.*\..+)$/,
|
24
|
+
http: /^(?!\w+?:\/\/|mailto:|\/|\.\/|\?|#)(.*)$/
|
25
|
+
};
|
26
|
+
|
27
|
+
var autoLinkReg = {
|
28
|
+
url: /((https?|ftp):\/\/|www\.)[^\s<]{3,}/gi,
|
29
|
+
prefix: /^(?:https?|ftp):\/\//i,
|
30
|
+
notLink: /^(?:img|a|input|audio|video|source|code|pre|script|head|title|style)$/i,
|
31
|
+
maxLength: 100
|
32
|
+
};
|
6
33
|
|
7
34
|
// type detect
|
8
35
|
utils.is = function(obj, type) {
|
9
|
-
return
|
36
|
+
return toString.call(obj).slice(8, -1) === type;
|
10
37
|
};
|
11
38
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
39
|
+
utils.forEach = function(obj, iterator, arrayLike) {
|
40
|
+
if (!obj) return;
|
41
|
+
if (arrayLike == null) arrayLike = utils.is(obj, 'Array');
|
42
|
+
if (arrayLike) {
|
43
|
+
for (var i = 0, l = obj.length; i < l; i++) iterator(obj[i], i, obj);
|
44
|
+
} else {
|
45
|
+
for (var key in obj) {
|
46
|
+
if (obj.hasOwnProperty(key)) iterator(obj[key], key, obj);
|
19
47
|
}
|
20
48
|
}
|
49
|
+
};
|
50
|
+
|
51
|
+
// copy props from a obj
|
52
|
+
utils.copy = function(defaults, source) {
|
53
|
+
utils.forEach(source, function (value, key) {
|
54
|
+
defaults[key] = utils.is(value, 'Object') ? utils.copy({}, value) :
|
55
|
+
utils.is(value, 'Array') ? utils.copy([], value) : value;
|
56
|
+
});
|
21
57
|
return defaults;
|
22
58
|
};
|
23
59
|
|
24
60
|
// log
|
25
61
|
utils.log = function(message, force) {
|
26
|
-
if(
|
62
|
+
if (debugMode || force)
|
63
|
+
console.log('%cPEN DEBUGGER: %c' + message, 'font-family:arial,sans-serif;color:#1abf89;line-height:2em;', 'font-family:cursor,monospace;color:#333;');
|
27
64
|
};
|
28
65
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
current[0]();
|
38
|
-
}, time);
|
66
|
+
utils.delayExec = function (fn) {
|
67
|
+
var timer = null;
|
68
|
+
return function (delay) {
|
69
|
+
clearTimeout(timer);
|
70
|
+
timer = setTimeout(function() {
|
71
|
+
fn();
|
72
|
+
}, delay || 1);
|
73
|
+
};
|
39
74
|
};
|
40
75
|
|
41
76
|
// merge: make it easy to have a fallback
|
@@ -45,19 +80,23 @@
|
|
45
80
|
var defaults = {
|
46
81
|
class: 'pen',
|
47
82
|
debug: false,
|
83
|
+
toolbar: null, // custom toolbar
|
48
84
|
stay: config.stay || !config.debug,
|
85
|
+
stayMsg: 'Are you going to leave here?',
|
49
86
|
textarea: '<textarea name="content"></textarea>',
|
50
87
|
list: [
|
51
|
-
'blockquote', 'h2', 'h3', 'p', 'insertorderedlist', 'insertunorderedlist', 'inserthorizontalrule',
|
52
|
-
'indent', 'outdent', 'bold', 'italic', 'underline', 'createlink'
|
53
|
-
]
|
88
|
+
'blockquote', 'h2', 'h3', 'p', 'code', 'insertorderedlist', 'insertunorderedlist', 'inserthorizontalrule',
|
89
|
+
'indent', 'outdent', 'bold', 'italic', 'underline', 'createlink', 'insertimage'
|
90
|
+
],
|
91
|
+
cleanAttrs: ['id', 'class', 'style', 'name'],
|
92
|
+
cleanTags: ['script']
|
54
93
|
};
|
55
94
|
|
56
95
|
// user-friendly config
|
57
|
-
if(config.nodeType === 1) {
|
96
|
+
if (config.nodeType === 1) {
|
58
97
|
defaults.editor = config;
|
59
|
-
} else if(config.match && config.match(/^#[\S]+$/)) {
|
60
|
-
defaults.editor =
|
98
|
+
} else if (config.match && config.match(/^#[\S]+$/)) {
|
99
|
+
defaults.editor = doc.getElementById(config.slice(1));
|
61
100
|
} else {
|
62
101
|
defaults = utils.copy(defaults, config);
|
63
102
|
}
|
@@ -65,279 +104,671 @@
|
|
65
104
|
return defaults;
|
66
105
|
};
|
67
106
|
|
68
|
-
|
107
|
+
function commandOverall(ctx, cmd, val) {
|
108
|
+
var message = ' to exec 「' + cmd + '」 command' + (val ? (' with value: ' + val) : '');
|
69
109
|
|
70
|
-
|
110
|
+
try {
|
111
|
+
doc.execCommand(cmd, false, val);
|
112
|
+
} catch(err) {
|
113
|
+
// TODO: there's an error when insert a image to document, bug not a bug
|
114
|
+
return utils.log('fail' + message, true);
|
115
|
+
}
|
71
116
|
|
72
|
-
|
73
|
-
|
117
|
+
utils.log('success' + message);
|
118
|
+
}
|
119
|
+
|
120
|
+
function commandInsert(ctx, name, val) {
|
121
|
+
var node = getNode(ctx);
|
122
|
+
if (!node) return;
|
123
|
+
ctx._range.selectNode(node);
|
124
|
+
ctx._range.collapse(false);
|
125
|
+
|
126
|
+
// hide menu when a image was inserted
|
127
|
+
if(name === 'insertimage' && ctx._menu) toggleNode(ctx._menu, true);
|
128
|
+
|
129
|
+
return commandOverall(ctx, name, val);
|
130
|
+
}
|
131
|
+
|
132
|
+
function commandBlock(ctx, name) {
|
133
|
+
var list = effectNode(ctx, getNode(ctx), true);
|
134
|
+
if (list.indexOf(name) !== -1) name = 'p';
|
135
|
+
return commandOverall(ctx, 'formatblock', name);
|
136
|
+
}
|
137
|
+
|
138
|
+
function commandWrap(ctx, tag, value) {
|
139
|
+
value = '<' + tag + '>' + (value||selection.toString()) + '</' + tag + '>';
|
140
|
+
return commandOverall(ctx, 'insertHTML', value);
|
141
|
+
}
|
142
|
+
|
143
|
+
function initToolbar(ctx) {
|
144
|
+
var icons = '', inputStr = '<input class="pen-input" placeholder="http://" />';
|
145
|
+
|
146
|
+
ctx._toolbar = ctx.config.toolbar;
|
147
|
+
if (!ctx._toolbar) {
|
148
|
+
var toolList = ctx.config.list;
|
149
|
+
utils.forEach(toolList, function (name) {
|
150
|
+
var klass = 'pen-icon icon-' + name;
|
151
|
+
icons += '<i class="' + klass + '" data-action="' + name + '"></i>';
|
152
|
+
}, true);
|
153
|
+
if (toolList.indexOf('createlink') >= 0 || toolList.indexOf('createlink') >= 0)
|
154
|
+
icons += inputStr;
|
155
|
+
} else if (ctx._toolbar.querySelectorAll('[data-action=createlink]').length ||
|
156
|
+
ctx._toolbar.querySelectorAll('[data-action=insertimage]').length) {
|
157
|
+
icons += inputStr;
|
158
|
+
}
|
74
159
|
|
75
|
-
if(
|
76
|
-
|
160
|
+
if (icons) {
|
161
|
+
ctx._menu = doc.createElement('div');
|
162
|
+
ctx._menu.setAttribute('class', ctx.config.class + '-menu pen-menu');
|
163
|
+
ctx._menu.innerHTML = icons;
|
164
|
+
ctx._inputBar = ctx._menu.querySelector('input');
|
165
|
+
toggleNode(ctx._menu, true);
|
166
|
+
doc.body.appendChild(ctx._menu);
|
167
|
+
}
|
168
|
+
if (ctx._toolbar && ctx._inputBar) toggleNode(ctx._inputBar);
|
169
|
+
}
|
77
170
|
|
78
|
-
|
171
|
+
function initEvents(ctx) {
|
172
|
+
var toolbar = ctx._toolbar || ctx._menu, editor = ctx.config.editor;
|
79
173
|
|
80
|
-
|
81
|
-
|
174
|
+
var toggleMenu = utils.delayExec(function() {
|
175
|
+
ctx.highlight().menu();
|
176
|
+
});
|
177
|
+
var outsideClick = function() {};
|
82
178
|
|
83
|
-
|
84
|
-
|
85
|
-
|
179
|
+
function updateStatus(delay) {
|
180
|
+
ctx._range = ctx.getRange();
|
181
|
+
toggleMenu(delay);
|
182
|
+
}
|
86
183
|
|
87
|
-
|
88
|
-
|
184
|
+
if (ctx._menu) {
|
185
|
+
var setpos = function() {
|
186
|
+
if (ctx._menu.style.display === 'block') ctx.menu();
|
187
|
+
};
|
89
188
|
|
90
|
-
|
91
|
-
|
189
|
+
// change menu offset when window resize / scroll
|
190
|
+
addListener(ctx, root, 'resize', setpos);
|
191
|
+
addListener(ctx, root, 'scroll', setpos);
|
192
|
+
|
193
|
+
// toggle toolbar on mouse select
|
194
|
+
var selecting = false;
|
195
|
+
addListener(ctx, editor, 'mousedown', function() {
|
196
|
+
selecting = true;
|
197
|
+
});
|
198
|
+
addListener(ctx, editor, 'mouseleave', function() {
|
199
|
+
if (selecting) updateStatus(800);
|
200
|
+
selecting = false;
|
201
|
+
});
|
202
|
+
addListener(ctx, editor, 'mouseup', function() {
|
203
|
+
if (selecting) updateStatus(100);
|
204
|
+
selecting = false;
|
205
|
+
});
|
206
|
+
// Hide menu when focusing outside of editor
|
207
|
+
outsideClick = function(e) {
|
208
|
+
if (ctx._menu && !containsNode(editor, e.target) && !containsNode(ctx._menu, e.target)) {
|
209
|
+
removeListener(ctx, doc, 'click', outsideClick);
|
210
|
+
toggleMenu(100);
|
211
|
+
}
|
212
|
+
};
|
213
|
+
} else {
|
214
|
+
addListener(ctx, editor, 'click', function() {
|
215
|
+
updateStatus(0);
|
216
|
+
});
|
217
|
+
}
|
92
218
|
|
93
|
-
|
94
|
-
|
219
|
+
addListener(ctx, editor, 'keyup', function(e) {
|
220
|
+
if (e.which === 8 && ctx.isEmpty()) return lineBreak(ctx, true);
|
221
|
+
// toggle toolbar on key select
|
222
|
+
if (e.which !== 13 || e.shiftKey) return updateStatus(400);
|
223
|
+
var node = getNode(ctx, true);
|
224
|
+
if (!node || !node.nextSibling || !lineBreakReg.test(node.nodeName)) return;
|
225
|
+
if (node.nodeName !== node.nextSibling.nodeName) return;
|
226
|
+
// hack for webkit, make 'enter' behavior like as firefox.
|
227
|
+
if (node.lastChild.nodeName !== 'BR') node.appendChild(doc.createElement('br'));
|
228
|
+
utils.forEach(node.nextSibling.childNodes, function(child) {
|
229
|
+
if (child) node.appendChild(child);
|
230
|
+
}, true);
|
231
|
+
node.parentNode.removeChild(node.nextSibling);
|
232
|
+
focusNode(ctx, node.lastChild, ctx.getRange());
|
233
|
+
});
|
95
234
|
|
96
|
-
//
|
97
|
-
|
235
|
+
// check line break
|
236
|
+
addListener(ctx, editor, 'keydown', function(e) {
|
237
|
+
editor.classList.remove('pen-placeholder');
|
238
|
+
if (e.which !== 13 || e.shiftKey) return;
|
239
|
+
var node = getNode(ctx, true);
|
240
|
+
if (!node || !lineBreakReg.test(node.nodeName)) return;
|
241
|
+
var lastChild = node.lastChild;
|
242
|
+
if (!lastChild || !lastChild.previousSibling) return;
|
243
|
+
if (lastChild.previousSibling.textContent || lastChild.textContent) return;
|
244
|
+
// quit block mode for 2 'enter'
|
245
|
+
e.preventDefault();
|
246
|
+
var p = doc.createElement('p');
|
247
|
+
p.innerHTML = '<br>';
|
248
|
+
node.removeChild(lastChild);
|
249
|
+
if (!node.nextSibling) node.parentNode.appendChild(p);
|
250
|
+
else node.parentNode.insertBefore(p, node.nextSibling);
|
251
|
+
focusNode(ctx, p, ctx.getRange());
|
252
|
+
});
|
98
253
|
|
99
|
-
|
100
|
-
|
254
|
+
var menuApply = function(action, value) {
|
255
|
+
ctx.execCommand(action, value);
|
256
|
+
ctx._range = ctx.getRange();
|
257
|
+
ctx.highlight().menu();
|
258
|
+
};
|
101
259
|
|
102
|
-
//
|
103
|
-
|
104
|
-
|
260
|
+
// toggle toolbar on key select
|
261
|
+
addListener(ctx, toolbar, 'click', function(e) {
|
262
|
+
var node = e.target, action;
|
263
|
+
|
264
|
+
while (node !== toolbar && !(action = node.getAttribute('data-action'))) {
|
265
|
+
node = node.parentNode;
|
266
|
+
}
|
267
|
+
|
268
|
+
if (!action) return;
|
269
|
+
if (!/(?:createlink)|(?:insertimage)/.test(action)) return menuApply(action);
|
270
|
+
if (!ctx._inputBar) return;
|
271
|
+
|
272
|
+
// create link
|
273
|
+
var input = ctx._inputBar;
|
274
|
+
if (toolbar === ctx._menu) toggleNode(input);
|
275
|
+
else {
|
276
|
+
ctx._inputActive = true;
|
277
|
+
ctx.menu();
|
278
|
+
}
|
279
|
+
if (ctx._menu.style.display === 'none') return;
|
280
|
+
|
281
|
+
setTimeout(function() { input.focus(); }, 400);
|
282
|
+
var createlink = function() {
|
283
|
+
var inputValue = input.value;
|
284
|
+
|
285
|
+
if (!inputValue) action = 'unlink';
|
286
|
+
else {
|
287
|
+
inputValue = input.value
|
288
|
+
.replace(strReg.whiteSpace, '')
|
289
|
+
.replace(strReg.mailTo, 'mailto:$1')
|
290
|
+
.replace(strReg.http, 'http://$1');
|
291
|
+
}
|
292
|
+
menuApply(action, inputValue);
|
293
|
+
if (toolbar === ctx._menu) toggleNode(input, false);
|
294
|
+
else toggleNode(ctx._menu, true);
|
295
|
+
};
|
296
|
+
|
297
|
+
input.onkeypress = function(e) {
|
298
|
+
if (e.which === 13) return createlink();
|
299
|
+
};
|
300
|
+
|
301
|
+
});
|
302
|
+
|
303
|
+
// listen for placeholder
|
304
|
+
addListener(ctx, editor, 'focus', function() {
|
305
|
+
if (ctx.isEmpty()) lineBreak(ctx, true);
|
306
|
+
addListener(ctx, doc, 'click', outsideClick);
|
307
|
+
});
|
308
|
+
|
309
|
+
addListener(ctx, editor, 'blur', function() {
|
310
|
+
checkPlaceholder(ctx);
|
311
|
+
ctx.checkContentChange();
|
312
|
+
});
|
313
|
+
|
314
|
+
// listen for paste and clear style
|
315
|
+
addListener(ctx, editor, 'paste', function() {
|
316
|
+
setTimeout(function() {
|
317
|
+
ctx.cleanContent();
|
318
|
+
});
|
319
|
+
});
|
320
|
+
}
|
321
|
+
|
322
|
+
function addListener(ctx, target, type, listener) {
|
323
|
+
if (ctx._events.hasOwnProperty(type)) {
|
324
|
+
ctx._events[type].push(listener);
|
325
|
+
} else {
|
326
|
+
ctx._eventTargets = ctx._eventTargets || [];
|
327
|
+
ctx._eventsCache = ctx._eventsCache || [];
|
328
|
+
var index = ctx._eventTargets.indexOf(target);
|
329
|
+
if (index < 0) index = ctx._eventTargets.push(target) - 1;
|
330
|
+
ctx._eventsCache[index] = ctx._eventsCache[index] || {};
|
331
|
+
ctx._eventsCache[index][type] = ctx._eventsCache[index][type] || [];
|
332
|
+
ctx._eventsCache[index][type].push(listener);
|
333
|
+
|
334
|
+
target.addEventListener(type, listener, false);
|
335
|
+
}
|
336
|
+
return ctx;
|
337
|
+
}
|
338
|
+
|
339
|
+
// trigger local events
|
340
|
+
function triggerListener(ctx, type) {
|
341
|
+
if (!ctx._events.hasOwnProperty(type)) return;
|
342
|
+
var args = slice.call(arguments, 2);
|
343
|
+
utils.forEach(ctx._events[type], function (listener) {
|
344
|
+
listener.apply(ctx, args);
|
345
|
+
});
|
346
|
+
}
|
347
|
+
|
348
|
+
function removeListener(ctx, target, type, listener) {
|
349
|
+
var events = ctx._events[type];
|
350
|
+
if (!events) {
|
351
|
+
var _index = ctx._eventTargets.indexOf(target);
|
352
|
+
if (_index >= 0) events = ctx._eventsCache[_index][type];
|
353
|
+
}
|
354
|
+
if (!events) return ctx;
|
355
|
+
var index = events.indexOf(listener);
|
356
|
+
if (index >= 0) events.splice(index, 1);
|
357
|
+
target.removeEventListener(type, listener, false);
|
358
|
+
return ctx;
|
359
|
+
}
|
360
|
+
|
361
|
+
function removeAllListeners(ctx) {
|
362
|
+
utils.forEach(this._events, function (events) {
|
363
|
+
events.length = 0;
|
364
|
+
}, false);
|
365
|
+
if (!ctx._eventsCache) return ctx;
|
366
|
+
utils.forEach(ctx._eventsCache, function (events, index) {
|
367
|
+
var target = ctx._eventTargets[index];
|
368
|
+
utils.forEach(events, function (listeners, type) {
|
369
|
+
utils.forEach(listeners, function (listener) {
|
370
|
+
target.removeEventListener(type, listener, false);
|
371
|
+
}, true);
|
372
|
+
}, false);
|
373
|
+
}, true);
|
374
|
+
ctx._eventTargets = [];
|
375
|
+
ctx._eventsCache = [];
|
376
|
+
return ctx;
|
377
|
+
}
|
378
|
+
|
379
|
+
function checkPlaceholder(ctx) {
|
380
|
+
ctx.config.editor.classList[ctx.isEmpty() ? 'add' : 'remove']('pen-placeholder');
|
381
|
+
}
|
382
|
+
|
383
|
+
function trim(str) {
|
384
|
+
return (str || '').replace(/^\s+|\s+$/g, '');
|
385
|
+
}
|
386
|
+
|
387
|
+
// node.contains is not implemented in IE10/IE11
|
388
|
+
function containsNode(parent, child) {
|
389
|
+
if (parent === child) return true;
|
390
|
+
child = child.parentNode;
|
391
|
+
while (child) {
|
392
|
+
if (child === parent) return true;
|
393
|
+
child = child.parentNode;
|
394
|
+
}
|
395
|
+
return false;
|
396
|
+
}
|
397
|
+
|
398
|
+
function getNode(ctx, byRoot) {
|
399
|
+
var node, root = ctx.config.editor;
|
400
|
+
ctx._range = ctx._range || ctx.getRange();
|
401
|
+
node = ctx._range.commonAncestorContainer;
|
402
|
+
if (!node || node === root) return null;
|
403
|
+
while (node && (node.nodeType !== 1) && (node.parentNode !== root)) node = node.parentNode;
|
404
|
+
while (node && byRoot && (node.parentNode !== root)) node = node.parentNode;
|
405
|
+
return containsNode(root, node) ? node : null;
|
406
|
+
}
|
105
407
|
|
106
408
|
// node effects
|
107
|
-
|
409
|
+
function effectNode(ctx, el, returnAsNodeName) {
|
108
410
|
var nodes = [];
|
109
|
-
|
110
|
-
|
411
|
+
el = el || ctx.config.editor;
|
412
|
+
while (el && el !== ctx.config.editor) {
|
413
|
+
if (el.nodeName.match(effectNodeReg)) {
|
111
414
|
nodes.push(returnAsNodeName ? el.nodeName.toLowerCase() : el);
|
112
415
|
}
|
113
416
|
el = el.parentNode;
|
114
417
|
}
|
115
418
|
return nodes;
|
116
|
-
}
|
117
|
-
|
118
|
-
//
|
119
|
-
|
120
|
-
var
|
121
|
-
|
122
|
-
|
419
|
+
}
|
420
|
+
|
421
|
+
// breakout from node
|
422
|
+
function lineBreak(ctx, empty) {
|
423
|
+
var range = ctx._range = ctx.getRange(), node = doc.createElement('p');
|
424
|
+
if (empty) ctx.config.editor.innerHTML = '';
|
425
|
+
node.innerHTML = '<br>';
|
426
|
+
range.insertNode(node);
|
427
|
+
focusNode(ctx, node.childNodes[0], range);
|
428
|
+
}
|
429
|
+
|
430
|
+
function focusNode(ctx, node, range) {
|
431
|
+
range.setStartAfter(node);
|
432
|
+
range.setEndBefore(node);
|
433
|
+
range.collapse(false);
|
434
|
+
ctx.setRange(range);
|
435
|
+
}
|
436
|
+
|
437
|
+
function autoLink(node) {
|
438
|
+
if (node.nodeType === 1) {
|
439
|
+
if (autoLinkReg.notLink.test(node.tagName)) return;
|
440
|
+
utils.forEach(node.childNodes, function (child) {
|
441
|
+
autoLink(child);
|
442
|
+
}, true);
|
443
|
+
} else if (node.nodeType === 3) {
|
444
|
+
var result = urlToLink(node.nodeValue || '');
|
445
|
+
if (!result.links) return;
|
446
|
+
var frag = doc.createDocumentFragment(),
|
447
|
+
div = doc.createElement('div');
|
448
|
+
div.innerHTML = result.text;
|
449
|
+
while (div.childNodes.length) frag.appendChild(div.childNodes[0]);
|
450
|
+
node.parentNode.replaceChild(frag, node);
|
451
|
+
}
|
452
|
+
}
|
453
|
+
|
454
|
+
function urlToLink(str) {
|
455
|
+
var count = 0;
|
456
|
+
str = str.replace(autoLinkReg.url, function(url) {
|
457
|
+
var realUrl = url, displayUrl = url;
|
458
|
+
count++;
|
459
|
+
if (url.length > autoLinkReg.maxLength) displayUrl = url.slice(0, autoLinkReg.maxLength) + '...';
|
460
|
+
// Add http prefix if necessary
|
461
|
+
if (!autoLinkReg.prefix.test(realUrl)) realUrl = 'http://' + realUrl;
|
462
|
+
return '<a href="' + realUrl + '">' + displayUrl + '</a>';
|
123
463
|
});
|
124
|
-
return
|
125
|
-
}
|
464
|
+
return {links: count, text: str};
|
465
|
+
}
|
126
466
|
|
127
|
-
|
467
|
+
function toggleNode(node, hide) {
|
468
|
+
node.style.display = hide ? 'none' : 'block';
|
469
|
+
}
|
128
470
|
|
129
|
-
|
471
|
+
Pen = function(config) {
|
130
472
|
|
131
|
-
|
132
|
-
var name = list[i], klass = 'pen-icon icon-' + name;
|
133
|
-
icons += '<i class="' + klass + '" data-action="' + name + '">' + (name.match(/^h[1-6]|p$/i) ? name.toUpperCase() : '') + '</i>';
|
134
|
-
if((name === 'createlink')) icons += '<input class="pen-input" placeholder="http://" />';
|
135
|
-
}
|
473
|
+
if (!config) throw new Error('Can\'t find config');
|
136
474
|
|
137
|
-
|
138
|
-
menu.setAttribute('class', this.config.class + '-menu pen-menu');
|
139
|
-
menu.innerHTML = icons;
|
140
|
-
menu.style.display = 'none';
|
475
|
+
debugMode = config.debug;
|
141
476
|
|
142
|
-
|
477
|
+
// merge user config
|
478
|
+
var defaults = utils.merge(config);
|
143
479
|
|
144
|
-
var
|
145
|
-
if(menu.style.display === 'block') that.menu();
|
146
|
-
};
|
480
|
+
var editor = defaults.editor;
|
147
481
|
|
148
|
-
|
149
|
-
window.addEventListener('resize', setpos);
|
150
|
-
window.addEventListener('scroll', setpos);
|
482
|
+
if (!editor || editor.nodeType !== 1) throw new Error('Can\'t find editor');
|
151
483
|
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
if(that._isDestroyed) return;
|
156
|
-
|
157
|
-
utils.shift('toggle_menu', function() {
|
158
|
-
var range = that._sel;
|
159
|
-
if(!range.isCollapsed) {
|
160
|
-
//show menu
|
161
|
-
that._range = range.getRangeAt(0);
|
162
|
-
that.menu().highlight();
|
163
|
-
} else {
|
164
|
-
//hide menu
|
165
|
-
that._menu.style.display = 'none';
|
166
|
-
}
|
167
|
-
}, 200);
|
168
|
-
};
|
484
|
+
// set default class
|
485
|
+
editor.classList.add(defaults.class);
|
169
486
|
|
170
|
-
//
|
171
|
-
editor.
|
487
|
+
// set contenteditable
|
488
|
+
editor.setAttribute('contenteditable', 'true');
|
172
489
|
|
173
|
-
//
|
174
|
-
|
490
|
+
// assign config
|
491
|
+
this.config = defaults;
|
175
492
|
|
176
|
-
//
|
177
|
-
|
178
|
-
|
493
|
+
// set placeholder
|
494
|
+
if (defaults.placeholder) editor.setAttribute('data-placeholder', defaults.placeholder);
|
495
|
+
checkPlaceholder(this);
|
179
496
|
|
180
|
-
|
497
|
+
// save the selection obj
|
498
|
+
this.selection = selection;
|
181
499
|
|
182
|
-
|
183
|
-
|
184
|
-
that._sel.addRange(that._range);
|
185
|
-
that._actions(action, value);
|
186
|
-
that._range = that._sel.getRangeAt(0);
|
187
|
-
that.highlight().nostyle().menu();
|
188
|
-
};
|
500
|
+
// define local events
|
501
|
+
this._events = {change: []};
|
189
502
|
|
190
|
-
|
191
|
-
|
192
|
-
var input = menu.getElementsByTagName('input')[0], createlink;
|
193
|
-
|
194
|
-
input.style.display = 'block';
|
195
|
-
input.focus();
|
196
|
-
|
197
|
-
createlink = function(input) {
|
198
|
-
input.style.display = 'none';
|
199
|
-
if(input.value) return apply(input.value.replace(/(^\s+)|(\s+$)/g, '').replace(/^(?!http:\/\/|https:\/\/)(.*)$/, 'http://$1'));
|
200
|
-
action = 'unlink';
|
201
|
-
apply();
|
202
|
-
};
|
203
|
-
|
204
|
-
return input.onkeypress = function(e) {
|
205
|
-
if(e.which === 13) return createlink(e.target);
|
206
|
-
};
|
207
|
-
}
|
503
|
+
// enable toolbar
|
504
|
+
initToolbar(this);
|
208
505
|
|
209
|
-
|
210
|
-
|
506
|
+
// init events
|
507
|
+
initEvents(this);
|
508
|
+
|
509
|
+
// to check content change
|
510
|
+
this._prevContent = this.getContent();
|
511
|
+
|
512
|
+
// enable markdown covert
|
513
|
+
if (this.markdown) this.markdown.init(this);
|
514
|
+
|
515
|
+
// stay on the page
|
516
|
+
if (this.config.stay) this.stay(this.config);
|
517
|
+
|
518
|
+
};
|
211
519
|
|
520
|
+
Pen.prototype.on = function(type, listener) {
|
521
|
+
addListener(this, this.config.editor, type, listener);
|
212
522
|
return this;
|
213
523
|
};
|
214
524
|
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
, linkInput = menu.querySelector('input')
|
221
|
-
, highlight;
|
525
|
+
Pen.prototype.isEmpty = function(node) {
|
526
|
+
node = node || this.config.editor;
|
527
|
+
return !(node.querySelector('img')) && !(node.querySelector('blockquote')) &&
|
528
|
+
!(node.querySelector('li')) && !trim(node.textContent);
|
529
|
+
};
|
222
530
|
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
});
|
531
|
+
Pen.prototype.getContent = function() {
|
532
|
+
return this.isEmpty() ? '' : trim(this.config.editor.innerHTML);
|
533
|
+
};
|
227
534
|
|
228
|
-
|
229
|
-
|
535
|
+
Pen.prototype.setContent = function(html) {
|
536
|
+
this.config.editor.innerHTML = html;
|
537
|
+
this.cleanContent();
|
538
|
+
return this;
|
539
|
+
};
|
230
540
|
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
541
|
+
Pen.prototype.checkContentChange = function () {
|
542
|
+
var prevContent = this._prevContent, currentContent = this.getContent();
|
543
|
+
if (prevContent === currentContent) return;
|
544
|
+
this._prevContent = currentContent;
|
545
|
+
triggerListener(this, 'change', currentContent, prevContent);
|
546
|
+
};
|
236
547
|
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
case 'ol': return highlight('insertorderedlist');
|
247
|
-
case 'li': return highlight('indent');
|
248
|
-
default : highlight(tag);
|
249
|
-
}
|
250
|
-
});
|
548
|
+
Pen.prototype.getRange = function() {
|
549
|
+
var editor = this.config.editor, range = selection.rangeCount && selection.getRangeAt(0);
|
550
|
+
if (!range) range = doc.createRange();
|
551
|
+
if (!containsNode(editor, range.commonAncestorContainer)) {
|
552
|
+
range.selectNodeContents(editor);
|
553
|
+
range.collapse(false);
|
554
|
+
}
|
555
|
+
return range;
|
556
|
+
};
|
251
557
|
|
558
|
+
Pen.prototype.setRange = function(range) {
|
559
|
+
range = range || this._range;
|
560
|
+
if (!range) {
|
561
|
+
range = this.getRange();
|
562
|
+
range.collapse(false); // set to end
|
563
|
+
}
|
564
|
+
try {
|
565
|
+
selection.removeAllRanges();
|
566
|
+
selection.addRange(range);
|
567
|
+
} catch (e) {/* IE throws error sometimes*/}
|
252
568
|
return this;
|
253
569
|
};
|
254
570
|
|
255
|
-
Pen.prototype.
|
256
|
-
|
571
|
+
Pen.prototype.focus = function(focusStart) {
|
572
|
+
if (!focusStart) this.setRange();
|
573
|
+
this.config.editor.focus();
|
574
|
+
return this;
|
575
|
+
};
|
257
576
|
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
}
|
577
|
+
Pen.prototype.execCommand = function(name, value) {
|
578
|
+
name = name.toLowerCase();
|
579
|
+
this.setRange();
|
580
|
+
|
581
|
+
if (commandsReg.block.test(name)) {
|
582
|
+
commandBlock(this, name);
|
583
|
+
} else if (commandsReg.inline.test(name) || commandsReg.source.test(name)) {
|
584
|
+
commandOverall(this, name, value);
|
585
|
+
} else if (commandsReg.insert.test(name)) {
|
586
|
+
commandInsert(this, name, value);
|
587
|
+
} else if (commandsReg.wrap.test(name)) {
|
588
|
+
commandWrap(this, name, value);
|
589
|
+
} else {
|
590
|
+
utils.log('can not find command function for name: ' + name + (value ? (', value: ' + value) : ''), true);
|
591
|
+
}
|
592
|
+
if (name === 'indent') this.checkContentChange();
|
593
|
+
else this.cleanContent({cleanAttrs: ['style']});
|
594
|
+
};
|
265
595
|
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
596
|
+
// remove attrs and tags
|
597
|
+
// pen.cleanContent({cleanAttrs: ['style'], cleanTags: ['id']})
|
598
|
+
Pen.prototype.cleanContent = function(options) {
|
599
|
+
var editor = this.config.editor;
|
600
|
+
|
601
|
+
if (!options) options = this.config;
|
602
|
+
utils.forEach(options.cleanAttrs, function (attr) {
|
603
|
+
utils.forEach(editor.querySelectorAll('[' + attr + ']'), function(item) {
|
604
|
+
item.removeAttribute(attr);
|
605
|
+
}, true);
|
606
|
+
}, true);
|
607
|
+
utils.forEach(options.cleanTags, function (tag) {
|
608
|
+
utils.forEach(editor.querySelectorAll(tag), function(item) {
|
609
|
+
item.parentNode.removeChild(item);
|
610
|
+
}, true);
|
611
|
+
}, true);
|
612
|
+
|
613
|
+
checkPlaceholder(this);
|
614
|
+
this.checkContentChange();
|
615
|
+
return this;
|
616
|
+
};
|
274
617
|
|
275
|
-
|
276
|
-
|
277
|
-
|
618
|
+
// auto link content, return content
|
619
|
+
Pen.prototype.autoLink = function() {
|
620
|
+
autoLink(this.config.editor);
|
621
|
+
return this.getContent();
|
622
|
+
};
|
278
623
|
|
279
|
-
|
280
|
-
|
281
|
-
|
624
|
+
// highlight menu
|
625
|
+
Pen.prototype.highlight = function() {
|
626
|
+
var toolbar = this._toolbar || this._menu
|
627
|
+
, node = getNode(this);
|
628
|
+
// remove all highlights
|
629
|
+
utils.forEach(toolbar.querySelectorAll('.active'), function(el) {
|
630
|
+
el.classList.remove('active');
|
631
|
+
}, true);
|
282
632
|
|
283
|
-
|
284
|
-
range.collapse(false);
|
285
|
-
return overall(name);
|
286
|
-
};
|
633
|
+
if (!node) return this;
|
287
634
|
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
name = 'p';
|
292
|
-
}
|
293
|
-
return overall('formatblock', name);
|
294
|
-
};
|
635
|
+
var effects = effectNode(this, node)
|
636
|
+
, inputBar = this._inputBar
|
637
|
+
, highlight;
|
295
638
|
|
296
|
-
|
297
|
-
if
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
639
|
+
if (inputBar && toolbar === this._menu) {
|
640
|
+
// display link input if createlink enabled
|
641
|
+
inputBar.style.display = 'none';
|
642
|
+
// reset link input value
|
643
|
+
inputBar.value = '';
|
644
|
+
}
|
645
|
+
|
646
|
+
highlight = function(str) {
|
647
|
+
if (!str) return;
|
648
|
+
var el = toolbar.querySelector('[data-action=' + str + ']');
|
649
|
+
return el && el.classList.add('active');
|
306
650
|
};
|
651
|
+
utils.forEach(effects, function(item) {
|
652
|
+
var tag = item.nodeName.toLowerCase();
|
653
|
+
switch(tag) {
|
654
|
+
case 'a':
|
655
|
+
if (inputBar) inputBar.value = item.getAttribute('href');
|
656
|
+
tag = 'createlink';
|
657
|
+
break;
|
658
|
+
case 'img':
|
659
|
+
if (inputBar) inputBar.value = item.getAttribute('src');
|
660
|
+
tag = 'insertimage';
|
661
|
+
break;
|
662
|
+
case 'i':
|
663
|
+
tag = 'italic';
|
664
|
+
break;
|
665
|
+
case 'u':
|
666
|
+
tag = 'underline';
|
667
|
+
break;
|
668
|
+
case 'b':
|
669
|
+
tag = 'bold';
|
670
|
+
break;
|
671
|
+
case 'pre':
|
672
|
+
case 'code':
|
673
|
+
tag = 'code';
|
674
|
+
break;
|
675
|
+
case 'ul':
|
676
|
+
tag = 'insertunorderedlist';
|
677
|
+
break;
|
678
|
+
case 'ol':
|
679
|
+
tag = 'insertorderedlist';
|
680
|
+
break;
|
681
|
+
case 'li':
|
682
|
+
tag = 'indent';
|
683
|
+
break;
|
684
|
+
}
|
685
|
+
highlight(tag);
|
686
|
+
}, true);
|
307
687
|
|
308
688
|
return this;
|
309
689
|
};
|
310
690
|
|
311
691
|
// show menu
|
312
692
|
Pen.prototype.menu = function() {
|
313
|
-
|
693
|
+
if (!this._menu) return this;
|
694
|
+
if (selection.isCollapsed) {
|
695
|
+
this._menu.style.display = 'none'; //hide menu
|
696
|
+
this._inputActive = false;
|
697
|
+
return this;
|
698
|
+
}
|
699
|
+
if (this._toolbar) {
|
700
|
+
if (!this._inputBar || !this._inputActive) return this;
|
701
|
+
}
|
314
702
|
var offset = this._range.getBoundingClientRect()
|
315
|
-
,
|
703
|
+
, menuPadding = 10
|
704
|
+
, top = offset.top - menuPadding
|
316
705
|
, left = offset.left + (offset.width / 2)
|
317
|
-
, menu = this._menu
|
318
|
-
|
319
|
-
|
706
|
+
, menu = this._menu
|
707
|
+
, menuOffset = {x: 0, y: 0}
|
708
|
+
, stylesheet = this._stylesheet;
|
709
|
+
|
710
|
+
// fixes some browser double click visual discontinuity
|
711
|
+
// if the offset has no width or height it should not be used
|
712
|
+
if (offset.width === 0 && offset.height === 0) return this;
|
713
|
+
|
714
|
+
// store the stylesheet used for positioning the menu horizontally
|
715
|
+
if (this._stylesheet === undefined) {
|
716
|
+
var style = document.createElement("style");
|
717
|
+
document.head.appendChild(style);
|
718
|
+
this._stylesheet = stylesheet = style.sheet;
|
719
|
+
}
|
720
|
+
// display block to caculate its width & height
|
320
721
|
menu.style.display = 'block';
|
321
|
-
menu.style.top = top - menu.clientHeight + 'px';
|
322
|
-
menu.style.left = left - (menu.clientWidth/2) + 'px';
|
323
722
|
|
723
|
+
menuOffset.x = left - (menu.clientWidth / 2);
|
724
|
+
menuOffset.y = top - menu.clientHeight;
|
725
|
+
|
726
|
+
// check to see if menu has over-extended its bounding box. if it has,
|
727
|
+
// 1) apply a new class if overflowed on top;
|
728
|
+
// 2) apply a new rule if overflowed on the left
|
729
|
+
if (stylesheet.cssRules.length > 0) {
|
730
|
+
stylesheet.deleteRule(0);
|
731
|
+
}
|
732
|
+
if (menuOffset.x < 0) {
|
733
|
+
menuOffset.x = 0;
|
734
|
+
stylesheet.insertRule('.pen-menu:after {left: ' + left + 'px;}', 0);
|
735
|
+
} else {
|
736
|
+
stylesheet.insertRule('.pen-menu:after {left: 50%; }', 0);
|
737
|
+
}
|
738
|
+
if (menuOffset.y < 0) {
|
739
|
+
menu.classList.add('pen-menu-below');
|
740
|
+
menuOffset.y = offset.top + offset.height + menuPadding;
|
741
|
+
} else {
|
742
|
+
menu.classList.remove('pen-menu-below');
|
743
|
+
}
|
744
|
+
|
745
|
+
menu.style.top = menuOffset.y + 'px';
|
746
|
+
menu.style.left = menuOffset.x + 'px';
|
324
747
|
return this;
|
325
748
|
};
|
326
749
|
|
327
|
-
Pen.prototype.stay = function() {
|
328
|
-
var
|
329
|
-
|
330
|
-
|
331
|
-
|
750
|
+
Pen.prototype.stay = function(config) {
|
751
|
+
var ctx = this;
|
752
|
+
if (!window.onbeforeunload) {
|
753
|
+
window.onbeforeunload = function() {
|
754
|
+
if (!ctx._isDestroyed) return config.stayMsg;
|
755
|
+
};
|
756
|
+
}
|
332
757
|
};
|
333
758
|
|
334
759
|
Pen.prototype.destroy = function(isAJoke) {
|
335
760
|
var destroy = isAJoke ? false : true
|
336
761
|
, attr = isAJoke ? 'setAttribute' : 'removeAttribute';
|
337
762
|
|
338
|
-
if(!isAJoke) {
|
339
|
-
this
|
340
|
-
|
763
|
+
if (!isAJoke) {
|
764
|
+
removeAllListeners(this);
|
765
|
+
try {
|
766
|
+
selection.removeAllRanges();
|
767
|
+
if (this._menu) this._menu.parentNode.removeChild(this._menu);
|
768
|
+
} catch (e) {/* IE throws error sometimes*/}
|
769
|
+
} else {
|
770
|
+
initToolbar(this);
|
771
|
+
initEvents(this);
|
341
772
|
}
|
342
773
|
this._isDestroyed = destroy;
|
343
774
|
this.config.editor[attr]('contenteditable', '');
|
@@ -350,8 +781,8 @@
|
|
350
781
|
};
|
351
782
|
|
352
783
|
// a fallback for old browers
|
353
|
-
|
354
|
-
if(!config) return utils.log('can\'t find config', true);
|
784
|
+
root.Pen = function(config) {
|
785
|
+
if (!config) return utils.log('can\'t find config', true);
|
355
786
|
|
356
787
|
var defaults = utils.merge(config)
|
357
788
|
, klass = defaults.editor.getAttribute('class');
|
@@ -362,7 +793,38 @@
|
|
362
793
|
return defaults.editor;
|
363
794
|
};
|
364
795
|
|
796
|
+
// export content as markdown
|
797
|
+
var regs = {
|
798
|
+
a: [/<a\b[^>]*href=["']([^"]+|[^']+)\b[^>]*>(.*?)<\/a>/ig, '[$2]($1)'],
|
799
|
+
img: [/<img\b[^>]*src=["']([^\"+|[^']+)[^>]*>/ig, ''],
|
800
|
+
b: [/<b\b[^>]*>(.*?)<\/b>/ig, '**$1**'],
|
801
|
+
i: [/<i\b[^>]*>(.*?)<\/i>/ig, '***$1***'],
|
802
|
+
h: [/<h([1-6])\b[^>]*>(.*?)<\/h\1>/ig, function(a, b, c) {
|
803
|
+
return '\n' + ('######'.slice(0, b)) + ' ' + c + '\n';
|
804
|
+
}],
|
805
|
+
li: [/<(li)\b[^>]*>(.*?)<\/\1>/ig, '* $2\n'],
|
806
|
+
blockquote: [/<(blockquote)\b[^>]*>(.*?)<\/\1>/ig, '\n> $2\n'],
|
807
|
+
pre: [/<pre\b[^>]*>(.*?)<\/pre>/ig, '\n```\n$1\n```\n'],
|
808
|
+
p: [/<p\b[^>]*>(.*?)<\/p>/ig, '\n$1\n'],
|
809
|
+
hr: [/<hr\b[^>]*>/ig, '\n---\n']
|
810
|
+
};
|
811
|
+
|
812
|
+
Pen.prototype.toMd = function() {
|
813
|
+
var html = this.getContent()
|
814
|
+
.replace(/\n+/g, '') // remove line break
|
815
|
+
.replace(/<([uo])l\b[^>]*>(.*?)<\/\1l>/ig, '$2'); // remove ul/ol
|
816
|
+
|
817
|
+
for(var p in regs) {
|
818
|
+
if (regs.hasOwnProperty(p))
|
819
|
+
html = html.replace.apply(html, regs[p]);
|
820
|
+
}
|
821
|
+
return html.replace(/\*{5}/g, '**');
|
822
|
+
};
|
823
|
+
|
365
824
|
// make it accessible
|
366
|
-
|
825
|
+
if (doc.getSelection) {
|
826
|
+
selection = doc.getSelection();
|
827
|
+
root.Pen = Pen;
|
828
|
+
}
|
367
829
|
|
368
|
-
}(document));
|
830
|
+
}(window, document));
|