pen 0.1.2 → 0.2.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
-
[![Gem Version](https://badge.fury.io/rb/pen.png)](http://badge.fury.io/rb/pen) [![Code Climate](https://codeclimate.com/
|
2
|
+
[![Gem Version](https://badge.fury.io/rb/pen.png)](http://badge.fury.io/rb/pen) [![Code Climate](https://codeclimate.com/github/neowork/pen/badges/gpa.svg)](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, '![]($1)'],
|
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));
|