epic-editor-rails 0.2.3 → 0.2.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +6 -2
- data/Rakefile +50 -2
- data/lib/epic-editor-rails/version.rb +1 -1
- data/vendor/assets/javascripts/epiceditor.js.erb +534 -262
- data/vendor/assets/stylesheets/preview/github.css +0 -1
- metadata +13 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5a5a65f525f4a14946a36e5abbc6998b6bf0733b
|
4
|
+
data.tar.gz: d4d27f27b14f36b91e5ad7faa55594cd838c2ab2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d089e2dda58ac142bcd69e4381a825a0a9535a421c7434fc2446a2df0318fd21ad71b928b4528c477967b1bdca3855330f1d1d1522fb47c9a8d778c2c821b3b8
|
7
|
+
data.tar.gz: b278a969811441a20079250424d8b7cf128030a16d73faa9eced5cdc1364c472b208055c530e9de710e5ec4ccd8ba445067d8a2f6878938dbc4f9f0e69d3d88f
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
epic-editor-rails v0.2.
|
1
|
+
epic-editor-rails v0.2.4
|
2
2
|
=================
|
3
3
|
Gemfile: ```gem 'epic-editor-rails'```
|
4
4
|
Install: ```$ bundle install ```
|
@@ -18,5 +18,9 @@ application.css.(scss):
|
|
18
18
|
@import 'editor/epic-light';
|
19
19
|
```
|
20
20
|
|
21
|
-
EpicEditor v0.2.
|
21
|
+
EpicEditor v0.2.3
|
22
22
|
http://epiceditor.com/
|
23
|
+
|
24
|
+
|
25
|
+
[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/zethussuen/epic-editor-rails/trend.png)](https://bitdeli.com/free "Bitdeli Badge")
|
26
|
+
|
data/Rakefile
CHANGED
@@ -1,3 +1,51 @@
|
|
1
|
-
require
|
1
|
+
require 'open-uri'
|
2
2
|
|
3
|
-
|
3
|
+
begin
|
4
|
+
require File.expand_path('../config/application', __FILE__)
|
5
|
+
EpicEditorRails::Application.load_tasks
|
6
|
+
rescue LoadError
|
7
|
+
end
|
8
|
+
|
9
|
+
# rake update[param]
|
10
|
+
# param can be a tag name, a branch name, or a commit hash on https://github.com/OscarGodson/EpicEditor
|
11
|
+
# It defaults to 'develop' (ie, the develop branch)
|
12
|
+
task :update, [:arg1] do |t, args|
|
13
|
+
version = args[:arg1] || 'develop'
|
14
|
+
puts "Updating to EpicEditor #{version}"
|
15
|
+
|
16
|
+
# version can be a commit hash, a branch name, or a tag name - github will handle them all the same.
|
17
|
+
base_url = "https://raw.githubusercontent.com/OscarGodson/EpicEditor/#{version}/"
|
18
|
+
|
19
|
+
js_url = "#{base_url}epiceditor/js/epiceditor.js"
|
20
|
+
puts "copying #{js_url}"
|
21
|
+
js_content = open(js_url).read
|
22
|
+
|
23
|
+
puts 'Replacing base path'
|
24
|
+
js_content.gsub!(/basePath: 'epiceditor'/, "basePath: ''")
|
25
|
+
|
26
|
+
# Use asset pipeline version of css files.
|
27
|
+
puts 'Substituting asset pipeline paths'
|
28
|
+
js_content.gsub!(/'\/themes\/(\w.*)\/(\w.*)\.css'/, "'<%= asset_path(\"\\1/\\2.css\") %>'")
|
29
|
+
|
30
|
+
open('./vendor/assets/javascripts/epiceditor.js.erb', 'wb') { |f| f << js_content }
|
31
|
+
|
32
|
+
remote_css_base = "#{base_url}epiceditor/themes/"
|
33
|
+
local_css_base = './vendor/assets/stylesheets/'
|
34
|
+
css_files = [
|
35
|
+
'base/epiceditor.css',
|
36
|
+
'editor/epic-dark.css',
|
37
|
+
'editor/epic-light.css',
|
38
|
+
'preview/bartik.css',
|
39
|
+
'preview/github.css',
|
40
|
+
'preview/preview-dark.css',
|
41
|
+
]
|
42
|
+
|
43
|
+
css_files.each do |css_file|
|
44
|
+
remote_path = remote_css_base + css_file
|
45
|
+
local_path = local_css_base + css_file
|
46
|
+
css_content = open(remote_path).read
|
47
|
+
|
48
|
+
puts "copying #{remote_path}"
|
49
|
+
open(local_path, 'wb') { |f| f << css_content }
|
50
|
+
end
|
51
|
+
end
|
@@ -13,7 +13,7 @@
|
|
13
13
|
function _applyAttrs(context, attrs) {
|
14
14
|
for (var attr in attrs) {
|
15
15
|
if (attrs.hasOwnProperty(attr)) {
|
16
|
-
context
|
16
|
+
context.setAttribute(attr, attrs[attr]);
|
17
17
|
}
|
18
18
|
}
|
19
19
|
}
|
@@ -337,7 +337,7 @@
|
|
337
337
|
}
|
338
338
|
, theme: { base: '<%= asset_path("base/epiceditor.css") %>'
|
339
339
|
, preview: '<%= asset_path("preview/github.css") %>'
|
340
|
-
, editor: '<%= asset_path("editor/epic-
|
340
|
+
, editor: '<%= asset_path("editor/epic-dark.css") %>'
|
341
341
|
}
|
342
342
|
, focusOnLoad: false
|
343
343
|
, shortcut: { modifier: 18 // alt keycode
|
@@ -407,6 +407,16 @@
|
|
407
407
|
else if (typeof self.settings.container == 'object') {
|
408
408
|
self.element = self.settings.container;
|
409
409
|
}
|
410
|
+
|
411
|
+
if (typeof self.settings.textarea == 'undefined' && typeof self.element != 'undefined') {
|
412
|
+
var textareas = self.element.getElementsByTagName('textarea');
|
413
|
+
if (textareas.length > 0) {
|
414
|
+
self.settings.textarea = textareas[0];
|
415
|
+
_applyStyles(self.settings.textarea, {
|
416
|
+
display: 'none'
|
417
|
+
});
|
418
|
+
}
|
419
|
+
}
|
410
420
|
|
411
421
|
// Figure out the file name. If no file name is given we'll use the ID.
|
412
422
|
// If there's no ID either we'll use a namespaced file name that's incremented
|
@@ -567,15 +577,21 @@
|
|
567
577
|
};
|
568
578
|
|
569
579
|
// Write an iframe and then select it for the editor
|
570
|
-
|
580
|
+
iframeElement = document.createElement('iframe');
|
581
|
+
_applyAttrs(iframeElement, {
|
582
|
+
scrolling: 'no',
|
583
|
+
frameborder: 0,
|
584
|
+
id: self._instanceId
|
585
|
+
});
|
586
|
+
|
587
|
+
|
588
|
+
self.element.appendChild(iframeElement);
|
571
589
|
|
572
590
|
// Because browsers add things like invisible padding and margins and stuff
|
573
591
|
// to iframes, we need to set manually set the height so that the height
|
574
592
|
// doesn't keep increasing (by 2px?) every time reflow() is called.
|
575
593
|
// FIXME: Figure out how to fix this without setting this
|
576
594
|
self.element.style.height = self.element.offsetHeight + 'px';
|
577
|
-
|
578
|
-
iframeElement = document.getElementById(self._instanceId);
|
579
595
|
|
580
596
|
// Store a reference to the iframeElement itself
|
581
597
|
self.iframeElement = iframeElement;
|
@@ -684,17 +700,20 @@
|
|
684
700
|
|
685
701
|
// TODO: Move into fullscreen setup function (_setupFullscreen)
|
686
702
|
_elementStates = {}
|
687
|
-
self._goFullscreen = function (el) {
|
703
|
+
self._goFullscreen = function (el, callback) {
|
704
|
+
callback = callback || function () {};
|
705
|
+
var wait = 0;
|
688
706
|
this._fixScrollbars('auto');
|
689
707
|
|
690
708
|
if (self.is('fullscreen')) {
|
691
|
-
self._exitFullscreen(el);
|
709
|
+
self._exitFullscreen(el, callback);
|
692
710
|
return;
|
693
711
|
}
|
694
712
|
|
695
713
|
if (nativeFs) {
|
696
714
|
if (nativeFsWebkit) {
|
697
715
|
el.webkitRequestFullScreen();
|
716
|
+
wait = 750;
|
698
717
|
}
|
699
718
|
else if (nativeFsMoz) {
|
700
719
|
el.mozRequestFullScreen();
|
@@ -706,85 +725,99 @@
|
|
706
725
|
|
707
726
|
_isInEdit = self.is('edit');
|
708
727
|
|
709
|
-
// Set the state of EE in fullscreen
|
710
|
-
// We set edit and preview to true also because they're visible
|
711
|
-
// we might want to allow fullscreen edit mode without preview (like a "zen" mode)
|
712
|
-
self._eeState.fullscreen = true;
|
713
|
-
self._eeState.edit = true;
|
714
|
-
self._eeState.preview = true;
|
715
728
|
|
716
|
-
//
|
717
|
-
|
718
|
-
|
719
|
-
|
720
|
-
|
729
|
+
// Why does this need to be in a randomly "750"ms setTimeout? WebKit's
|
730
|
+
// implementation of fullscreen seem to trigger the webkitfullscreenchange
|
731
|
+
// event _after_ everything is done. Instead, it triggers _during_ the
|
732
|
+
// transition. This means calculations of what's half, 100%, etc are wrong
|
733
|
+
// so to combat this we throw down the hammer with a setTimeout and wait
|
734
|
+
// to trigger our calculation code.
|
735
|
+
// See: https://code.google.com/p/chromium/issues/detail?id=181116
|
736
|
+
setTimeout(function () {
|
737
|
+
// Set the state of EE in fullscreen
|
738
|
+
// We set edit and preview to true also because they're visible
|
739
|
+
// we might want to allow fullscreen edit mode without preview (like a "zen" mode)
|
740
|
+
self._eeState.fullscreen = true;
|
741
|
+
self._eeState.edit = true;
|
742
|
+
self._eeState.preview = true;
|
743
|
+
|
744
|
+
// Cache calculations
|
745
|
+
var windowInnerWidth = window.innerWidth
|
746
|
+
, windowInnerHeight = window.innerHeight
|
747
|
+
, windowOuterWidth = window.outerWidth
|
748
|
+
, windowOuterHeight = window.outerHeight;
|
749
|
+
|
750
|
+
// Without this the scrollbars will get hidden when scrolled to the bottom in faux fullscreen (see #66)
|
751
|
+
if (!nativeFs) {
|
752
|
+
windowOuterHeight = window.innerHeight;
|
753
|
+
}
|
721
754
|
|
722
|
-
|
723
|
-
|
724
|
-
|
725
|
-
|
726
|
-
|
727
|
-
|
728
|
-
|
729
|
-
|
730
|
-
'
|
731
|
-
|
732
|
-
|
733
|
-
|
734
|
-
, 'styleFloat': 'left' // Older IEs
|
735
|
-
, 'display': 'block'
|
736
|
-
, 'position': 'static'
|
737
|
-
, 'left': ''
|
738
|
-
});
|
755
|
+
// This MUST come first because the editor is 100% width so if we change the width of the iframe or wrapper
|
756
|
+
// the editor's width wont be the same as before
|
757
|
+
_elementStates.editorIframe = _saveStyleState(self.editorIframe, 'save', {
|
758
|
+
'width': windowOuterWidth / 2 + 'px'
|
759
|
+
, 'height': windowOuterHeight + 'px'
|
760
|
+
, 'float': 'left' // Most browsers
|
761
|
+
, 'cssFloat': 'left' // FF
|
762
|
+
, 'styleFloat': 'left' // Older IEs
|
763
|
+
, 'display': 'block'
|
764
|
+
, 'position': 'static'
|
765
|
+
, 'left': ''
|
766
|
+
});
|
739
767
|
|
740
|
-
|
741
|
-
|
742
|
-
|
743
|
-
|
744
|
-
|
745
|
-
|
746
|
-
|
747
|
-
|
748
|
-
|
749
|
-
|
750
|
-
|
768
|
+
// the previewer
|
769
|
+
_elementStates.previewerIframe = _saveStyleState(self.previewerIframe, 'save', {
|
770
|
+
'width': windowOuterWidth / 2 + 'px'
|
771
|
+
, 'height': windowOuterHeight + 'px'
|
772
|
+
, 'float': 'right' // Most browsers
|
773
|
+
, 'cssFloat': 'right' // FF
|
774
|
+
, 'styleFloat': 'right' // Older IEs
|
775
|
+
, 'display': 'block'
|
776
|
+
, 'position': 'static'
|
777
|
+
, 'left': ''
|
778
|
+
});
|
751
779
|
|
752
|
-
|
753
|
-
|
754
|
-
|
755
|
-
|
756
|
-
|
757
|
-
|
758
|
-
|
759
|
-
|
760
|
-
|
761
|
-
|
762
|
-
|
763
|
-
|
764
|
-
|
765
|
-
|
780
|
+
// Setup the containing element CSS for fullscreen
|
781
|
+
_elementStates.element = _saveStyleState(self.element, 'save', {
|
782
|
+
'position': 'fixed'
|
783
|
+
, 'top': '0'
|
784
|
+
, 'left': '0'
|
785
|
+
, 'width': '100%'
|
786
|
+
, 'z-index': '9999' // Most browsers
|
787
|
+
, 'zIndex': '9999' // Firefox
|
788
|
+
, 'border': 'none'
|
789
|
+
, 'margin': '0'
|
790
|
+
// Should use the base styles background!
|
791
|
+
, 'background': _getStyle(self.editor, 'background-color') // Try to hide the site below
|
792
|
+
, 'height': windowInnerHeight + 'px'
|
793
|
+
});
|
766
794
|
|
767
|
-
|
768
|
-
|
769
|
-
|
770
|
-
|
771
|
-
|
795
|
+
// The iframe element
|
796
|
+
_elementStates.iframeElement = _saveStyleState(self.iframeElement, 'save', {
|
797
|
+
'width': windowOuterWidth + 'px'
|
798
|
+
, 'height': windowInnerHeight + 'px'
|
799
|
+
});
|
772
800
|
|
773
|
-
|
774
|
-
|
801
|
+
// ...Oh, and hide the buttons and prevent scrolling
|
802
|
+
utilBtns.style.visibility = 'hidden';
|
775
803
|
|
776
|
-
|
777
|
-
|
778
|
-
|
804
|
+
if (!nativeFs) {
|
805
|
+
document.body.style.overflow = 'hidden';
|
806
|
+
}
|
779
807
|
|
780
|
-
|
808
|
+
self.preview();
|
781
809
|
|
782
|
-
|
810
|
+
self.focus();
|
811
|
+
|
812
|
+
self.emit('fullscreenenter');
|
813
|
+
|
814
|
+
callback.call(self);
|
815
|
+
}, wait);
|
783
816
|
|
784
|
-
self.emit('fullscreenenter');
|
785
817
|
};
|
786
818
|
|
787
|
-
self._exitFullscreen = function (el) {
|
819
|
+
self._exitFullscreen = function (el, callback) {
|
820
|
+
callback = callback || function () {};
|
788
821
|
this._fixScrollbars();
|
789
822
|
|
790
823
|
_saveStyleState(self.element, 'apply', _elementStates.element);
|
@@ -831,6 +864,8 @@
|
|
831
864
|
self.reflow();
|
832
865
|
|
833
866
|
self.emit('fullscreenexit');
|
867
|
+
|
868
|
+
callback.call(self);
|
834
869
|
};
|
835
870
|
|
836
871
|
// This setups up live previews by triggering preview() IF in fullscreen on keyup
|
@@ -926,6 +961,7 @@
|
|
926
961
|
function shortcutHandler(e) {
|
927
962
|
if (e.keyCode == self.settings.shortcut.modifier) { isMod = true } // check for modifier press(default is alt key), save to var
|
928
963
|
if (e.keyCode == 17) { isCtrl = true } // check for ctrl/cmnd press, in order to catch ctrl/cmnd + s
|
964
|
+
if (e.keyCode == 18) { isCtrl = false }
|
929
965
|
|
930
966
|
// Check for alt+p and make sure were not in fullscreen - default shortcut to switch to preview
|
931
967
|
if (isMod === true && e.keyCode == self.settings.shortcut.preview && !self.is('fullscreen')) {
|
@@ -1113,7 +1149,6 @@
|
|
1113
1149
|
|
1114
1150
|
EpicEditor.prototype._setupTextareaSync = function () {
|
1115
1151
|
var self = this
|
1116
|
-
, textareaFileName = self.settings.file.name
|
1117
1152
|
, _syncTextarea;
|
1118
1153
|
|
1119
1154
|
// Even if autoSave is false, we want to make sure to keep the textarea synced
|
@@ -1132,7 +1167,10 @@
|
|
1132
1167
|
// This only happens for draft files. Probably has something to do with
|
1133
1168
|
// the fact draft files haven't been saved by the time this is called.
|
1134
1169
|
// TODO: Add test for this case.
|
1135
|
-
|
1170
|
+
// Get the file.name each time as it can change. DO NOT save this to a
|
1171
|
+
// var outside of this closure or the editor will stop syncing when the
|
1172
|
+
// file is changed with importFile or open.
|
1173
|
+
self._textareaElement.value = self.exportFile(self.settings.file.name, 'text', true) || self.settings.file.defaultContent;
|
1136
1174
|
}
|
1137
1175
|
|
1138
1176
|
if (typeof self.settings.textarea == 'string') {
|
@@ -1156,7 +1194,7 @@
|
|
1156
1194
|
// If the developer wants drafts to be recoverable they should check if
|
1157
1195
|
// the local file in localStorage's modified date is newer than the server.
|
1158
1196
|
if (self._textareaElement.value !== '') {
|
1159
|
-
self.importFile(
|
1197
|
+
self.importFile(self.settings.file.name, self._textareaElement.value);
|
1160
1198
|
|
1161
1199
|
// manually save draft after import so there is no delay between the
|
1162
1200
|
// import and exporting in _syncTextarea. Without this, _syncTextarea
|
@@ -1169,6 +1207,8 @@
|
|
1169
1207
|
|
1170
1208
|
// Make sure to keep it updated
|
1171
1209
|
self.on('__update', _syncTextarea);
|
1210
|
+
self.on('__create', _syncTextarea);
|
1211
|
+
self.on('__save', _syncTextarea);
|
1172
1212
|
}
|
1173
1213
|
|
1174
1214
|
/**
|
@@ -1208,7 +1248,6 @@
|
|
1208
1248
|
callback = callback || function () {};
|
1209
1249
|
|
1210
1250
|
if (self.settings.textarea) {
|
1211
|
-
self._textareaElement.value = "";
|
1212
1251
|
self.removeListener('__update');
|
1213
1252
|
}
|
1214
1253
|
|
@@ -1340,9 +1379,13 @@
|
|
1340
1379
|
* Puts the editor into fullscreen mode
|
1341
1380
|
* @returns {object} EpicEditor will be returned
|
1342
1381
|
*/
|
1343
|
-
EpicEditor.prototype.enterFullscreen = function () {
|
1344
|
-
|
1345
|
-
this.
|
1382
|
+
EpicEditor.prototype.enterFullscreen = function (callback) {
|
1383
|
+
callback = callback || function () {};
|
1384
|
+
if (this.is('fullscreen')) {
|
1385
|
+
callback.call(this);
|
1386
|
+
return this;
|
1387
|
+
}
|
1388
|
+
this._goFullscreen(this.iframeElement, callback);
|
1346
1389
|
return this;
|
1347
1390
|
}
|
1348
1391
|
|
@@ -1350,9 +1393,13 @@
|
|
1350
1393
|
* Closes fullscreen mode if opened
|
1351
1394
|
* @returns {object} EpicEditor will be returned
|
1352
1395
|
*/
|
1353
|
-
EpicEditor.prototype.exitFullscreen = function () {
|
1354
|
-
|
1355
|
-
this.
|
1396
|
+
EpicEditor.prototype.exitFullscreen = function (callback) {
|
1397
|
+
callback = callback || function () {};
|
1398
|
+
if (!this.is('fullscreen')) {
|
1399
|
+
callback.call(this);
|
1400
|
+
return this;
|
1401
|
+
}
|
1402
|
+
this._exitFullscreen(this.iframeElement, callback);
|
1356
1403
|
return this;
|
1357
1404
|
}
|
1358
1405
|
|
@@ -1461,6 +1508,7 @@
|
|
1461
1508
|
var self = this
|
1462
1509
|
, storage
|
1463
1510
|
, isUpdate = false
|
1511
|
+
, isNew = false
|
1464
1512
|
, file = self.settings.file.name
|
1465
1513
|
, previewDraftName = ''
|
1466
1514
|
, data = this._storage[previewDraftName + self.settings.localStorageName]
|
@@ -1482,6 +1530,7 @@
|
|
1482
1530
|
// If the file doesn't exist we need to create it
|
1483
1531
|
if (storage[file] === undefined) {
|
1484
1532
|
storage[file] = self._defaultFileSchema();
|
1533
|
+
isNew = true;
|
1485
1534
|
}
|
1486
1535
|
|
1487
1536
|
// If it does, we need to check if the content is different and
|
@@ -1498,10 +1547,17 @@
|
|
1498
1547
|
storage[file].content = content;
|
1499
1548
|
this._storage[previewDraftName + self.settings.localStorageName] = JSON.stringify(storage);
|
1500
1549
|
|
1501
|
-
//
|
1550
|
+
// If it's a new file, send a create event as well as a private one for
|
1551
|
+
// use internally.
|
1552
|
+
if (isNew) {
|
1553
|
+
self.emit('create');
|
1554
|
+
self.emit('__create');
|
1555
|
+
}
|
1556
|
+
|
1557
|
+
// After the content is actually changed, emit update so it emits the
|
1558
|
+
// updated content. Also send a private event for interal use.
|
1502
1559
|
if (isUpdate) {
|
1503
1560
|
self.emit('update');
|
1504
|
-
// Emit a private update event so it can't get accidentally removed
|
1505
1561
|
self.emit('__update');
|
1506
1562
|
}
|
1507
1563
|
|
@@ -1510,6 +1566,7 @@
|
|
1510
1566
|
}
|
1511
1567
|
else if (!_isPreviewDraft) {
|
1512
1568
|
this.emit('save');
|
1569
|
+
self.emit('__save');
|
1513
1570
|
}
|
1514
1571
|
}
|
1515
1572
|
|
@@ -1563,26 +1620,17 @@
|
|
1563
1620
|
* @returns {object} EpicEditor will be returned
|
1564
1621
|
*/
|
1565
1622
|
EpicEditor.prototype.importFile = function (name, content, kind, meta) {
|
1566
|
-
var self = this
|
1567
|
-
, isNew = false;
|
1623
|
+
var self = this;
|
1568
1624
|
|
1569
1625
|
name = name || self.settings.file.name;
|
1570
1626
|
content = content || '';
|
1571
1627
|
kind = kind || 'md';
|
1572
1628
|
meta = meta || {};
|
1573
1629
|
|
1574
|
-
if (JSON.parse(this._storage[self.settings.localStorageName])[name] === undefined) {
|
1575
|
-
isNew = true;
|
1576
|
-
}
|
1577
|
-
|
1578
1630
|
// Set our current file to the new file and update the content
|
1579
1631
|
self.settings.file.name = name;
|
1580
1632
|
_setText(self.editor, content);
|
1581
1633
|
|
1582
|
-
if (isNew) {
|
1583
|
-
self.emit('create');
|
1584
|
-
}
|
1585
|
-
|
1586
1634
|
self.save();
|
1587
1635
|
|
1588
1636
|
if (self.is('fullscreen')) {
|
@@ -1843,12 +1891,15 @@
|
|
1843
1891
|
// Used to store information to be shared across editors
|
1844
1892
|
EpicEditor._data = {};
|
1845
1893
|
|
1846
|
-
window.
|
1894
|
+
if (typeof window.define === 'function' && window.define.amd) {
|
1895
|
+
window.define(function () { return EpicEditor; });
|
1896
|
+
} else {
|
1897
|
+
window.EpicEditor = EpicEditor;
|
1898
|
+
}
|
1847
1899
|
})(window);
|
1848
|
-
|
1849
1900
|
/**
|
1850
1901
|
* marked - a markdown parser
|
1851
|
-
* Copyright (c) 2011-
|
1902
|
+
* Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
|
1852
1903
|
* https://github.com/chjj/marked
|
1853
1904
|
*/
|
1854
1905
|
|
@@ -1865,13 +1916,13 @@ var block = {
|
|
1865
1916
|
hr: /^( *[-*_]){3,} *(?:\n+|$)/,
|
1866
1917
|
heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
|
1867
1918
|
nptable: noop,
|
1868
|
-
lheading: /^([^\n]+)\n *(=|-){
|
1869
|
-
blockquote: /^( *>[^\n]+(\n[^\n]+)*\n*)+/,
|
1870
|
-
list: /^( *)(bull) [\s\S]+?(?:hr|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
|
1871
|
-
html: /^ *(?:comment|closed|closing
|
1872
|
-
def: /^ *\[([^\]]+)\]:
|
1919
|
+
lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
|
1920
|
+
blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
|
1921
|
+
list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
|
1922
|
+
html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
|
1923
|
+
def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
|
1873
1924
|
table: noop,
|
1874
|
-
paragraph: /^([^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))
|
1925
|
+
paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
|
1875
1926
|
text: /^[^\n]+/
|
1876
1927
|
};
|
1877
1928
|
|
@@ -1883,13 +1934,18 @@ block.item = replace(block.item, 'gm')
|
|
1883
1934
|
|
1884
1935
|
block.list = replace(block.list)
|
1885
1936
|
(/bull/g, block.bullet)
|
1886
|
-
('hr',
|
1937
|
+
('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
|
1938
|
+
('def', '\\n+(?=' + block.def.source + ')')
|
1939
|
+
();
|
1940
|
+
|
1941
|
+
block.blockquote = replace(block.blockquote)
|
1942
|
+
('def', block.def)
|
1887
1943
|
();
|
1888
1944
|
|
1889
1945
|
block._tag = '(?!(?:'
|
1890
1946
|
+ 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
|
1891
1947
|
+ '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
|
1892
|
-
+ '|span|br|wbr|ins|del|img)\\b)\\w+(
|
1948
|
+
+ '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
|
1893
1949
|
|
1894
1950
|
block.html = replace(block.html)
|
1895
1951
|
('comment', /<!--[\s\S]*?-->/)
|
@@ -1918,12 +1974,14 @@ block.normal = merge({}, block);
|
|
1918
1974
|
*/
|
1919
1975
|
|
1920
1976
|
block.gfm = merge({}, block.normal, {
|
1921
|
-
fences: /^ *(`{3,}|~{3,}) *(\
|
1977
|
+
fences: /^ *(`{3,}|~{3,}) *(\S+)? *\n([\s\S]+?)\s*\1 *(?:\n+|$)/,
|
1922
1978
|
paragraph: /^/
|
1923
1979
|
});
|
1924
1980
|
|
1925
1981
|
block.gfm.paragraph = replace(block.paragraph)
|
1926
|
-
('(?!', '(?!'
|
1982
|
+
('(?!', '(?!'
|
1983
|
+
+ block.gfm.fences.source.replace('\\1', '\\2') + '|'
|
1984
|
+
+ block.list.source.replace('\\1', '\\3') + '|')
|
1927
1985
|
();
|
1928
1986
|
|
1929
1987
|
/**
|
@@ -1987,11 +2045,13 @@ Lexer.prototype.lex = function(src) {
|
|
1987
2045
|
* Lexing
|
1988
2046
|
*/
|
1989
2047
|
|
1990
|
-
Lexer.prototype.token = function(src, top) {
|
2048
|
+
Lexer.prototype.token = function(src, top, bq) {
|
1991
2049
|
var src = src.replace(/^ +$/gm, '')
|
1992
2050
|
, next
|
1993
2051
|
, loose
|
1994
2052
|
, cap
|
2053
|
+
, bull
|
2054
|
+
, b
|
1995
2055
|
, item
|
1996
2056
|
, space
|
1997
2057
|
, i
|
@@ -2108,7 +2168,7 @@ Lexer.prototype.token = function(src, top) {
|
|
2108
2168
|
// Pass `top` to keep the current
|
2109
2169
|
// "toplevel" state. This is exactly
|
2110
2170
|
// how markdown.pl works.
|
2111
|
-
this.token(cap, top);
|
2171
|
+
this.token(cap, top, true);
|
2112
2172
|
|
2113
2173
|
this.tokens.push({
|
2114
2174
|
type: 'blockquote_end'
|
@@ -2120,10 +2180,11 @@ Lexer.prototype.token = function(src, top) {
|
|
2120
2180
|
// list
|
2121
2181
|
if (cap = this.rules.list.exec(src)) {
|
2122
2182
|
src = src.substring(cap[0].length);
|
2183
|
+
bull = cap[2];
|
2123
2184
|
|
2124
2185
|
this.tokens.push({
|
2125
2186
|
type: 'list_start',
|
2126
|
-
ordered:
|
2187
|
+
ordered: bull.length > 1
|
2127
2188
|
});
|
2128
2189
|
|
2129
2190
|
// Get each top-level item.
|
@@ -2150,12 +2211,22 @@ Lexer.prototype.token = function(src, top) {
|
|
2150
2211
|
: item.replace(/^ {1,4}/gm, '');
|
2151
2212
|
}
|
2152
2213
|
|
2214
|
+
// Determine whether the next list item belongs here.
|
2215
|
+
// Backpedal if it does not belong in this list.
|
2216
|
+
if (this.options.smartLists && i !== l - 1) {
|
2217
|
+
b = block.bullet.exec(cap[i + 1])[0];
|
2218
|
+
if (bull !== b && !(bull.length > 1 && b.length > 1)) {
|
2219
|
+
src = cap.slice(i + 1).join('\n') + src;
|
2220
|
+
i = l - 1;
|
2221
|
+
}
|
2222
|
+
}
|
2223
|
+
|
2153
2224
|
// Determine whether item is loose or not.
|
2154
2225
|
// Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
|
2155
2226
|
// for discount behavior.
|
2156
2227
|
loose = next || /\n\n(?!\s*$)/.test(item);
|
2157
2228
|
if (i !== l - 1) {
|
2158
|
-
next = item
|
2229
|
+
next = item.charAt(item.length - 1) === '\n';
|
2159
2230
|
if (!loose) loose = next;
|
2160
2231
|
}
|
2161
2232
|
|
@@ -2166,7 +2237,7 @@ Lexer.prototype.token = function(src, top) {
|
|
2166
2237
|
});
|
2167
2238
|
|
2168
2239
|
// Recurse.
|
2169
|
-
this.token(item, false);
|
2240
|
+
this.token(item, false, bq);
|
2170
2241
|
|
2171
2242
|
this.tokens.push({
|
2172
2243
|
type: 'list_item_end'
|
@@ -2187,14 +2258,14 @@ Lexer.prototype.token = function(src, top) {
|
|
2187
2258
|
type: this.options.sanitize
|
2188
2259
|
? 'paragraph'
|
2189
2260
|
: 'html',
|
2190
|
-
pre: cap[1] === 'pre',
|
2261
|
+
pre: cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style',
|
2191
2262
|
text: cap[0]
|
2192
2263
|
});
|
2193
2264
|
continue;
|
2194
2265
|
}
|
2195
2266
|
|
2196
2267
|
// def
|
2197
|
-
if (top && (cap = this.rules.def.exec(src))) {
|
2268
|
+
if ((!bq && top) && (cap = this.rules.def.exec(src))) {
|
2198
2269
|
src = src.substring(cap[0].length);
|
2199
2270
|
this.tokens.links[cap[1].toLowerCase()] = {
|
2200
2271
|
href: cap[2],
|
@@ -2242,7 +2313,9 @@ Lexer.prototype.token = function(src, top) {
|
|
2242
2313
|
src = src.substring(cap[0].length);
|
2243
2314
|
this.tokens.push({
|
2244
2315
|
type: 'paragraph',
|
2245
|
-
text: cap[
|
2316
|
+
text: cap[1].charAt(cap[1].length - 1) === '\n'
|
2317
|
+
? cap[1].slice(0, -1)
|
2318
|
+
: cap[1]
|
2246
2319
|
});
|
2247
2320
|
continue;
|
2248
2321
|
}
|
@@ -2272,7 +2345,7 @@ Lexer.prototype.token = function(src, top) {
|
|
2272
2345
|
*/
|
2273
2346
|
|
2274
2347
|
var inline = {
|
2275
|
-
escape: /^\\([\\`*{}\[\]()#+\-.!_
|
2348
|
+
escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
|
2276
2349
|
autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
|
2277
2350
|
url: noop,
|
2278
2351
|
tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
|
@@ -2281,14 +2354,14 @@ var inline = {
|
|
2281
2354
|
nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
|
2282
2355
|
strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
|
2283
2356
|
em: /^\b_((?:__|[\s\S])+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
|
2284
|
-
code: /^(`+)([\s\S]*?[^`])\1(?!`)/,
|
2357
|
+
code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
|
2285
2358
|
br: /^ {2,}\n(?!\s*$)/,
|
2286
2359
|
del: noop,
|
2287
2360
|
text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
|
2288
2361
|
};
|
2289
2362
|
|
2290
|
-
inline._inside = /(?:\[[^\]]*\]|[^\]]|\](?=[^\[]*\]))*/;
|
2291
|
-
inline._href = /\s*<?([
|
2363
|
+
inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
|
2364
|
+
inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
|
2292
2365
|
|
2293
2366
|
inline.link = replace(inline.link)
|
2294
2367
|
('inside', inline._inside)
|
@@ -2319,9 +2392,9 @@ inline.pedantic = merge({}, inline.normal, {
|
|
2319
2392
|
*/
|
2320
2393
|
|
2321
2394
|
inline.gfm = merge({}, inline.normal, {
|
2322
|
-
escape: replace(inline.escape)('])', '
|
2323
|
-
url: /^(https?:\/\/[^\s]+[
|
2324
|
-
del:
|
2395
|
+
escape: replace(inline.escape)('])', '~|])')(),
|
2396
|
+
url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
|
2397
|
+
del: /^~~(?=\S)([\s\S]*?\S)~~/,
|
2325
2398
|
text: replace(inline.text)
|
2326
2399
|
(']|', '~]|')
|
2327
2400
|
('|', '|https?://|')
|
@@ -2345,6 +2418,8 @@ function InlineLexer(links, options) {
|
|
2345
2418
|
this.options = options || marked.defaults;
|
2346
2419
|
this.links = links;
|
2347
2420
|
this.rules = inline.normal;
|
2421
|
+
this.renderer = this.options.renderer || new Renderer;
|
2422
|
+
this.renderer.options = this.options;
|
2348
2423
|
|
2349
2424
|
if (!this.links) {
|
2350
2425
|
throw new
|
@@ -2372,8 +2447,8 @@ InlineLexer.rules = inline;
|
|
2372
2447
|
* Static Lexing/Compiling Method
|
2373
2448
|
*/
|
2374
2449
|
|
2375
|
-
InlineLexer.output = function(src, links,
|
2376
|
-
var inline = new InlineLexer(links,
|
2450
|
+
InlineLexer.output = function(src, links, options) {
|
2451
|
+
var inline = new InlineLexer(links, options);
|
2377
2452
|
return inline.output(src);
|
2378
2453
|
};
|
2379
2454
|
|
@@ -2400,7 +2475,7 @@ InlineLexer.prototype.output = function(src) {
|
|
2400
2475
|
if (cap = this.rules.autolink.exec(src)) {
|
2401
2476
|
src = src.substring(cap[0].length);
|
2402
2477
|
if (cap[2] === '@') {
|
2403
|
-
text = cap[1]
|
2478
|
+
text = cap[1].charAt(6) === ':'
|
2404
2479
|
? this.mangle(cap[1].substring(7))
|
2405
2480
|
: this.mangle(cap[1]);
|
2406
2481
|
href = this.mangle('mailto:') + text;
|
@@ -2408,29 +2483,26 @@ InlineLexer.prototype.output = function(src) {
|
|
2408
2483
|
text = escape(cap[1]);
|
2409
2484
|
href = text;
|
2410
2485
|
}
|
2411
|
-
out +=
|
2412
|
-
+ href
|
2413
|
-
+ '">'
|
2414
|
-
+ text
|
2415
|
-
+ '</a>';
|
2486
|
+
out += this.renderer.link(href, null, text);
|
2416
2487
|
continue;
|
2417
2488
|
}
|
2418
2489
|
|
2419
2490
|
// url (gfm)
|
2420
|
-
if (cap = this.rules.url.exec(src)) {
|
2491
|
+
if (!this.inLink && (cap = this.rules.url.exec(src))) {
|
2421
2492
|
src = src.substring(cap[0].length);
|
2422
2493
|
text = escape(cap[1]);
|
2423
2494
|
href = text;
|
2424
|
-
out +=
|
2425
|
-
+ href
|
2426
|
-
+ '">'
|
2427
|
-
+ text
|
2428
|
-
+ '</a>';
|
2495
|
+
out += this.renderer.link(href, null, text);
|
2429
2496
|
continue;
|
2430
2497
|
}
|
2431
2498
|
|
2432
2499
|
// tag
|
2433
2500
|
if (cap = this.rules.tag.exec(src)) {
|
2501
|
+
if (!this.inLink && /^<a /i.test(cap[0])) {
|
2502
|
+
this.inLink = true;
|
2503
|
+
} else if (this.inLink && /^<\/a>/i.test(cap[0])) {
|
2504
|
+
this.inLink = false;
|
2505
|
+
}
|
2434
2506
|
src = src.substring(cap[0].length);
|
2435
2507
|
out += this.options.sanitize
|
2436
2508
|
? escape(cap[0])
|
@@ -2441,10 +2513,12 @@ InlineLexer.prototype.output = function(src) {
|
|
2441
2513
|
// link
|
2442
2514
|
if (cap = this.rules.link.exec(src)) {
|
2443
2515
|
src = src.substring(cap[0].length);
|
2516
|
+
this.inLink = true;
|
2444
2517
|
out += this.outputLink(cap, {
|
2445
2518
|
href: cap[2],
|
2446
2519
|
title: cap[3]
|
2447
2520
|
});
|
2521
|
+
this.inLink = false;
|
2448
2522
|
continue;
|
2449
2523
|
}
|
2450
2524
|
|
@@ -2455,61 +2529,55 @@ InlineLexer.prototype.output = function(src) {
|
|
2455
2529
|
link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
|
2456
2530
|
link = this.links[link.toLowerCase()];
|
2457
2531
|
if (!link || !link.href) {
|
2458
|
-
out += cap[0]
|
2532
|
+
out += cap[0].charAt(0);
|
2459
2533
|
src = cap[0].substring(1) + src;
|
2460
2534
|
continue;
|
2461
2535
|
}
|
2536
|
+
this.inLink = true;
|
2462
2537
|
out += this.outputLink(cap, link);
|
2538
|
+
this.inLink = false;
|
2463
2539
|
continue;
|
2464
2540
|
}
|
2465
2541
|
|
2466
2542
|
// strong
|
2467
2543
|
if (cap = this.rules.strong.exec(src)) {
|
2468
2544
|
src = src.substring(cap[0].length);
|
2469
|
-
out +=
|
2470
|
-
+ this.output(cap[2] || cap[1])
|
2471
|
-
+ '</strong>';
|
2545
|
+
out += this.renderer.strong(this.output(cap[2] || cap[1]));
|
2472
2546
|
continue;
|
2473
2547
|
}
|
2474
2548
|
|
2475
2549
|
// em
|
2476
2550
|
if (cap = this.rules.em.exec(src)) {
|
2477
2551
|
src = src.substring(cap[0].length);
|
2478
|
-
out +=
|
2479
|
-
+ this.output(cap[2] || cap[1])
|
2480
|
-
+ '</em>';
|
2552
|
+
out += this.renderer.em(this.output(cap[2] || cap[1]));
|
2481
2553
|
continue;
|
2482
2554
|
}
|
2483
2555
|
|
2484
2556
|
// code
|
2485
2557
|
if (cap = this.rules.code.exec(src)) {
|
2486
2558
|
src = src.substring(cap[0].length);
|
2487
|
-
out +=
|
2488
|
-
+ escape(cap[2], true)
|
2489
|
-
+ '</code>';
|
2559
|
+
out += this.renderer.codespan(escape(cap[2], true));
|
2490
2560
|
continue;
|
2491
2561
|
}
|
2492
2562
|
|
2493
2563
|
// br
|
2494
2564
|
if (cap = this.rules.br.exec(src)) {
|
2495
2565
|
src = src.substring(cap[0].length);
|
2496
|
-
out +=
|
2566
|
+
out += this.renderer.br();
|
2497
2567
|
continue;
|
2498
2568
|
}
|
2499
2569
|
|
2500
2570
|
// del (gfm)
|
2501
2571
|
if (cap = this.rules.del.exec(src)) {
|
2502
2572
|
src = src.substring(cap[0].length);
|
2503
|
-
out +=
|
2504
|
-
+ this.output(cap[1])
|
2505
|
-
+ '</del>';
|
2573
|
+
out += this.renderer.del(this.output(cap[1]));
|
2506
2574
|
continue;
|
2507
2575
|
}
|
2508
2576
|
|
2509
2577
|
// text
|
2510
2578
|
if (cap = this.rules.text.exec(src)) {
|
2511
2579
|
src = src.substring(cap[0].length);
|
2512
|
-
out += escape(cap[0]);
|
2580
|
+
out += escape(this.smartypants(cap[0]));
|
2513
2581
|
continue;
|
2514
2582
|
}
|
2515
2583
|
|
@@ -2527,31 +2595,33 @@ InlineLexer.prototype.output = function(src) {
|
|
2527
2595
|
*/
|
2528
2596
|
|
2529
2597
|
InlineLexer.prototype.outputLink = function(cap, link) {
|
2530
|
-
|
2531
|
-
|
2532
|
-
|
2533
|
-
|
2534
|
-
|
2535
|
-
|
2536
|
-
|
2537
|
-
|
2538
|
-
|
2539
|
-
|
2540
|
-
|
2541
|
-
|
2542
|
-
|
2543
|
-
|
2544
|
-
|
2545
|
-
|
2546
|
-
|
2547
|
-
|
2548
|
-
|
2549
|
-
|
2550
|
-
|
2551
|
-
|
2552
|
-
|
2553
|
-
|
2554
|
-
|
2598
|
+
var href = escape(link.href)
|
2599
|
+
, title = link.title ? escape(link.title) : null;
|
2600
|
+
|
2601
|
+
return cap[0].charAt(0) !== '!'
|
2602
|
+
? this.renderer.link(href, title, this.output(cap[1]))
|
2603
|
+
: this.renderer.image(href, title, escape(cap[1]));
|
2604
|
+
};
|
2605
|
+
|
2606
|
+
/**
|
2607
|
+
* Smartypants Transformations
|
2608
|
+
*/
|
2609
|
+
|
2610
|
+
InlineLexer.prototype.smartypants = function(text) {
|
2611
|
+
if (!this.options.smartypants) return text;
|
2612
|
+
return text
|
2613
|
+
// em-dashes
|
2614
|
+
.replace(/--/g, '\u2014')
|
2615
|
+
// opening singles
|
2616
|
+
.replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
|
2617
|
+
// closing singles & apostrophes
|
2618
|
+
.replace(/'/g, '\u2019')
|
2619
|
+
// opening doubles
|
2620
|
+
.replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
|
2621
|
+
// closing doubles
|
2622
|
+
.replace(/"/g, '\u201d')
|
2623
|
+
// ellipses
|
2624
|
+
.replace(/\.{3}/g, '\u2026');
|
2555
2625
|
};
|
2556
2626
|
|
2557
2627
|
/**
|
@@ -2575,6 +2645,149 @@ InlineLexer.prototype.mangle = function(text) {
|
|
2575
2645
|
return out;
|
2576
2646
|
};
|
2577
2647
|
|
2648
|
+
/**
|
2649
|
+
* Renderer
|
2650
|
+
*/
|
2651
|
+
|
2652
|
+
function Renderer(options) {
|
2653
|
+
this.options = options || {};
|
2654
|
+
}
|
2655
|
+
|
2656
|
+
Renderer.prototype.code = function(code, lang, escaped) {
|
2657
|
+
if (this.options.highlight) {
|
2658
|
+
var out = this.options.highlight(code, lang);
|
2659
|
+
if (out != null && out !== code) {
|
2660
|
+
escaped = true;
|
2661
|
+
code = out;
|
2662
|
+
}
|
2663
|
+
}
|
2664
|
+
|
2665
|
+
if (!lang) {
|
2666
|
+
return '<pre><code>'
|
2667
|
+
+ (escaped ? code : escape(code, true))
|
2668
|
+
+ '\n</code></pre>';
|
2669
|
+
}
|
2670
|
+
|
2671
|
+
return '<pre><code class="'
|
2672
|
+
+ this.options.langPrefix
|
2673
|
+
+ escape(lang, true)
|
2674
|
+
+ '">'
|
2675
|
+
+ (escaped ? code : escape(code, true))
|
2676
|
+
+ '\n</code></pre>\n';
|
2677
|
+
};
|
2678
|
+
|
2679
|
+
Renderer.prototype.blockquote = function(quote) {
|
2680
|
+
return '<blockquote>\n' + quote + '</blockquote>\n';
|
2681
|
+
};
|
2682
|
+
|
2683
|
+
Renderer.prototype.html = function(html) {
|
2684
|
+
return html;
|
2685
|
+
};
|
2686
|
+
|
2687
|
+
Renderer.prototype.heading = function(text, level, raw) {
|
2688
|
+
return '<h'
|
2689
|
+
+ level
|
2690
|
+
+ ' id="'
|
2691
|
+
+ this.options.headerPrefix
|
2692
|
+
+ raw.toLowerCase().replace(/[^\w]+/g, '-')
|
2693
|
+
+ '">'
|
2694
|
+
+ text
|
2695
|
+
+ '</h'
|
2696
|
+
+ level
|
2697
|
+
+ '>\n';
|
2698
|
+
};
|
2699
|
+
|
2700
|
+
Renderer.prototype.hr = function() {
|
2701
|
+
return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
|
2702
|
+
};
|
2703
|
+
|
2704
|
+
Renderer.prototype.list = function(body, ordered) {
|
2705
|
+
var type = ordered ? 'ol' : 'ul';
|
2706
|
+
return '<' + type + '>\n' + body + '</' + type + '>\n';
|
2707
|
+
};
|
2708
|
+
|
2709
|
+
Renderer.prototype.listitem = function(text) {
|
2710
|
+
return '<li>' + text + '</li>\n';
|
2711
|
+
};
|
2712
|
+
|
2713
|
+
Renderer.prototype.paragraph = function(text) {
|
2714
|
+
return '<p>' + text + '</p>\n';
|
2715
|
+
};
|
2716
|
+
|
2717
|
+
Renderer.prototype.table = function(header, body) {
|
2718
|
+
return '<table>\n'
|
2719
|
+
+ '<thead>\n'
|
2720
|
+
+ header
|
2721
|
+
+ '</thead>\n'
|
2722
|
+
+ '<tbody>\n'
|
2723
|
+
+ body
|
2724
|
+
+ '</tbody>\n'
|
2725
|
+
+ '</table>\n';
|
2726
|
+
};
|
2727
|
+
|
2728
|
+
Renderer.prototype.tablerow = function(content) {
|
2729
|
+
return '<tr>\n' + content + '</tr>\n';
|
2730
|
+
};
|
2731
|
+
|
2732
|
+
Renderer.prototype.tablecell = function(content, flags) {
|
2733
|
+
var type = flags.header ? 'th' : 'td';
|
2734
|
+
var tag = flags.align
|
2735
|
+
? '<' + type + ' style="text-align:' + flags.align + '">'
|
2736
|
+
: '<' + type + '>';
|
2737
|
+
return tag + content + '</' + type + '>\n';
|
2738
|
+
};
|
2739
|
+
|
2740
|
+
// span level renderer
|
2741
|
+
Renderer.prototype.strong = function(text) {
|
2742
|
+
return '<strong>' + text + '</strong>';
|
2743
|
+
};
|
2744
|
+
|
2745
|
+
Renderer.prototype.em = function(text) {
|
2746
|
+
return '<em>' + text + '</em>';
|
2747
|
+
};
|
2748
|
+
|
2749
|
+
Renderer.prototype.codespan = function(text) {
|
2750
|
+
return '<code>' + text + '</code>';
|
2751
|
+
};
|
2752
|
+
|
2753
|
+
Renderer.prototype.br = function() {
|
2754
|
+
return this.options.xhtml ? '<br/>' : '<br>';
|
2755
|
+
};
|
2756
|
+
|
2757
|
+
Renderer.prototype.del = function(text) {
|
2758
|
+
return '<del>' + text + '</del>';
|
2759
|
+
};
|
2760
|
+
|
2761
|
+
Renderer.prototype.link = function(href, title, text) {
|
2762
|
+
if (this.options.sanitize) {
|
2763
|
+
try {
|
2764
|
+
var prot = decodeURIComponent(unescape(href))
|
2765
|
+
.replace(/[^\w:]/g, '')
|
2766
|
+
.toLowerCase();
|
2767
|
+
} catch (e) {
|
2768
|
+
return '';
|
2769
|
+
}
|
2770
|
+
if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
|
2771
|
+
return '';
|
2772
|
+
}
|
2773
|
+
}
|
2774
|
+
var out = '<a href="' + href + '"';
|
2775
|
+
if (title) {
|
2776
|
+
out += ' title="' + title + '"';
|
2777
|
+
}
|
2778
|
+
out += '>' + text + '</a>';
|
2779
|
+
return out;
|
2780
|
+
};
|
2781
|
+
|
2782
|
+
Renderer.prototype.image = function(href, title, text) {
|
2783
|
+
var out = '<img src="' + href + '" alt="' + text + '"';
|
2784
|
+
if (title) {
|
2785
|
+
out += ' title="' + title + '"';
|
2786
|
+
}
|
2787
|
+
out += this.options.xhtml ? '/>' : '>';
|
2788
|
+
return out;
|
2789
|
+
};
|
2790
|
+
|
2578
2791
|
/**
|
2579
2792
|
* Parsing & Compiling
|
2580
2793
|
*/
|
@@ -2583,14 +2796,17 @@ function Parser(options) {
|
|
2583
2796
|
this.tokens = [];
|
2584
2797
|
this.token = null;
|
2585
2798
|
this.options = options || marked.defaults;
|
2799
|
+
this.options.renderer = this.options.renderer || new Renderer;
|
2800
|
+
this.renderer = this.options.renderer;
|
2801
|
+
this.renderer.options = this.options;
|
2586
2802
|
}
|
2587
2803
|
|
2588
2804
|
/**
|
2589
2805
|
* Static Parse Method
|
2590
2806
|
*/
|
2591
2807
|
|
2592
|
-
Parser.parse = function(src, options) {
|
2593
|
-
var parser = new Parser(options);
|
2808
|
+
Parser.parse = function(src, options, renderer) {
|
2809
|
+
var parser = new Parser(options, renderer);
|
2594
2810
|
return parser.parse(src);
|
2595
2811
|
};
|
2596
2812
|
|
@@ -2599,7 +2815,7 @@ Parser.parse = function(src, options) {
|
|
2599
2815
|
*/
|
2600
2816
|
|
2601
2817
|
Parser.prototype.parse = function(src) {
|
2602
|
-
this.inline = new InlineLexer(src.links, this.options);
|
2818
|
+
this.inline = new InlineLexer(src.links, this.options, this.renderer);
|
2603
2819
|
this.tokens = src.reverse();
|
2604
2820
|
|
2605
2821
|
var out = '';
|
@@ -2623,7 +2839,7 @@ Parser.prototype.next = function() {
|
|
2623
2839
|
*/
|
2624
2840
|
|
2625
2841
|
Parser.prototype.peek = function() {
|
2626
|
-
return this.tokens[this.tokens.length-1] || 0;
|
2842
|
+
return this.tokens[this.tokens.length - 1] || 0;
|
2627
2843
|
};
|
2628
2844
|
|
2629
2845
|
/**
|
@@ -2650,76 +2866,53 @@ Parser.prototype.tok = function() {
|
|
2650
2866
|
return '';
|
2651
2867
|
}
|
2652
2868
|
case 'hr': {
|
2653
|
-
return
|
2869
|
+
return this.renderer.hr();
|
2654
2870
|
}
|
2655
2871
|
case 'heading': {
|
2656
|
-
return
|
2657
|
-
|
2658
|
-
|
2659
|
-
|
2660
|
-
+ '</h'
|
2661
|
-
+ this.token.depth
|
2662
|
-
+ '>\n';
|
2872
|
+
return this.renderer.heading(
|
2873
|
+
this.inline.output(this.token.text),
|
2874
|
+
this.token.depth,
|
2875
|
+
this.token.text);
|
2663
2876
|
}
|
2664
2877
|
case 'code': {
|
2665
|
-
|
2666
|
-
|
2667
|
-
|
2668
|
-
this.token.escaped = true;
|
2669
|
-
this.token.text = code;
|
2670
|
-
}
|
2671
|
-
}
|
2672
|
-
|
2673
|
-
if (!this.token.escaped) {
|
2674
|
-
this.token.text = escape(this.token.text, true);
|
2675
|
-
}
|
2676
|
-
|
2677
|
-
return '<pre><code'
|
2678
|
-
+ (this.token.lang
|
2679
|
-
? ' class="lang-'
|
2680
|
-
+ this.token.lang
|
2681
|
-
+ '"'
|
2682
|
-
: '')
|
2683
|
-
+ '>'
|
2684
|
-
+ this.token.text
|
2685
|
-
+ '</code></pre>\n';
|
2878
|
+
return this.renderer.code(this.token.text,
|
2879
|
+
this.token.lang,
|
2880
|
+
this.token.escaped);
|
2686
2881
|
}
|
2687
2882
|
case 'table': {
|
2688
|
-
var
|
2689
|
-
,
|
2883
|
+
var header = ''
|
2884
|
+
, body = ''
|
2690
2885
|
, i
|
2691
2886
|
, row
|
2692
2887
|
, cell
|
2888
|
+
, flags
|
2693
2889
|
, j;
|
2694
2890
|
|
2695
2891
|
// header
|
2696
|
-
|
2892
|
+
cell = '';
|
2697
2893
|
for (i = 0; i < this.token.header.length; i++) {
|
2698
|
-
|
2699
|
-
|
2700
|
-
|
2701
|
-
:
|
2894
|
+
flags = { header: true, align: this.token.align[i] };
|
2895
|
+
cell += this.renderer.tablecell(
|
2896
|
+
this.inline.output(this.token.header[i]),
|
2897
|
+
{ header: true, align: this.token.align[i] }
|
2898
|
+
);
|
2702
2899
|
}
|
2703
|
-
|
2900
|
+
header += this.renderer.tablerow(cell);
|
2704
2901
|
|
2705
|
-
// body
|
2706
|
-
body += '<tbody>\n'
|
2707
2902
|
for (i = 0; i < this.token.cells.length; i++) {
|
2708
2903
|
row = this.token.cells[i];
|
2709
|
-
|
2904
|
+
|
2905
|
+
cell = '';
|
2710
2906
|
for (j = 0; j < row.length; j++) {
|
2711
|
-
cell
|
2712
|
-
|
2713
|
-
|
2714
|
-
|
2907
|
+
cell += this.renderer.tablecell(
|
2908
|
+
this.inline.output(row[j]),
|
2909
|
+
{ header: false, align: this.token.align[j] }
|
2910
|
+
);
|
2715
2911
|
}
|
2716
|
-
body += '</tr>\n';
|
2717
|
-
}
|
2718
|
-
body += '</tbody>\n';
|
2719
2912
|
|
2720
|
-
|
2721
|
-
|
2722
|
-
|
2913
|
+
body += this.renderer.tablerow(cell);
|
2914
|
+
}
|
2915
|
+
return this.renderer.table(header, body);
|
2723
2916
|
}
|
2724
2917
|
case 'blockquote_start': {
|
2725
2918
|
var body = '';
|
@@ -2728,25 +2921,17 @@ Parser.prototype.tok = function() {
|
|
2728
2921
|
body += this.tok();
|
2729
2922
|
}
|
2730
2923
|
|
2731
|
-
return
|
2732
|
-
+ body
|
2733
|
-
+ '</blockquote>\n';
|
2924
|
+
return this.renderer.blockquote(body);
|
2734
2925
|
}
|
2735
2926
|
case 'list_start': {
|
2736
|
-
var
|
2737
|
-
,
|
2927
|
+
var body = ''
|
2928
|
+
, ordered = this.token.ordered;
|
2738
2929
|
|
2739
2930
|
while (this.next().type !== 'list_end') {
|
2740
2931
|
body += this.tok();
|
2741
2932
|
}
|
2742
2933
|
|
2743
|
-
return
|
2744
|
-
+ type
|
2745
|
-
+ '>\n'
|
2746
|
-
+ body
|
2747
|
-
+ '</'
|
2748
|
-
+ type
|
2749
|
-
+ '>\n';
|
2934
|
+
return this.renderer.list(body, ordered);
|
2750
2935
|
}
|
2751
2936
|
case 'list_item_start': {
|
2752
2937
|
var body = '';
|
@@ -2757,9 +2942,7 @@ Parser.prototype.tok = function() {
|
|
2757
2942
|
: this.tok();
|
2758
2943
|
}
|
2759
2944
|
|
2760
|
-
return
|
2761
|
-
+ body
|
2762
|
-
+ '</li>\n';
|
2945
|
+
return this.renderer.listitem(body);
|
2763
2946
|
}
|
2764
2947
|
case 'loose_item_start': {
|
2765
2948
|
var body = '';
|
@@ -2768,24 +2951,19 @@ Parser.prototype.tok = function() {
|
|
2768
2951
|
body += this.tok();
|
2769
2952
|
}
|
2770
2953
|
|
2771
|
-
return
|
2772
|
-
+ body
|
2773
|
-
+ '</li>\n';
|
2954
|
+
return this.renderer.listitem(body);
|
2774
2955
|
}
|
2775
2956
|
case 'html': {
|
2776
|
-
|
2957
|
+
var html = !this.token.pre && !this.options.pedantic
|
2777
2958
|
? this.inline.output(this.token.text)
|
2778
2959
|
: this.token.text;
|
2960
|
+
return this.renderer.html(html);
|
2779
2961
|
}
|
2780
2962
|
case 'paragraph': {
|
2781
|
-
return
|
2782
|
-
+ this.inline.output(this.token.text)
|
2783
|
-
+ '</p>\n';
|
2963
|
+
return this.renderer.paragraph(this.inline.output(this.token.text));
|
2784
2964
|
}
|
2785
2965
|
case 'text': {
|
2786
|
-
return
|
2787
|
-
+ this.parseText()
|
2788
|
-
+ '</p>\n';
|
2966
|
+
return this.renderer.paragraph(this.parseText());
|
2789
2967
|
}
|
2790
2968
|
}
|
2791
2969
|
};
|
@@ -2803,6 +2981,19 @@ function escape(html, encode) {
|
|
2803
2981
|
.replace(/'/g, ''');
|
2804
2982
|
}
|
2805
2983
|
|
2984
|
+
function unescape(html) {
|
2985
|
+
return html.replace(/&([#\w]+);/g, function(_, n) {
|
2986
|
+
n = n.toLowerCase();
|
2987
|
+
if (n === 'colon') return ':';
|
2988
|
+
if (n.charAt(0) === '#') {
|
2989
|
+
return n.charAt(1) === 'x'
|
2990
|
+
? String.fromCharCode(parseInt(n.substring(2), 16))
|
2991
|
+
: String.fromCharCode(+n.substring(1));
|
2992
|
+
}
|
2993
|
+
return '';
|
2994
|
+
});
|
2995
|
+
}
|
2996
|
+
|
2806
2997
|
function replace(regex, opt) {
|
2807
2998
|
regex = regex.source;
|
2808
2999
|
opt = opt || '';
|
@@ -2835,17 +3026,90 @@ function merge(obj) {
|
|
2835
3026
|
return obj;
|
2836
3027
|
}
|
2837
3028
|
|
3029
|
+
|
2838
3030
|
/**
|
2839
3031
|
* Marked
|
2840
3032
|
*/
|
2841
3033
|
|
2842
|
-
function marked(src, opt) {
|
3034
|
+
function marked(src, opt, callback) {
|
3035
|
+
if (callback || typeof opt === 'function') {
|
3036
|
+
if (!callback) {
|
3037
|
+
callback = opt;
|
3038
|
+
opt = null;
|
3039
|
+
}
|
3040
|
+
|
3041
|
+
opt = merge({}, marked.defaults, opt || {});
|
3042
|
+
|
3043
|
+
var highlight = opt.highlight
|
3044
|
+
, tokens
|
3045
|
+
, pending
|
3046
|
+
, i = 0;
|
3047
|
+
|
3048
|
+
try {
|
3049
|
+
tokens = Lexer.lex(src, opt)
|
3050
|
+
} catch (e) {
|
3051
|
+
return callback(e);
|
3052
|
+
}
|
3053
|
+
|
3054
|
+
pending = tokens.length;
|
3055
|
+
|
3056
|
+
var done = function(err) {
|
3057
|
+
if (err) {
|
3058
|
+
opt.highlight = highlight;
|
3059
|
+
return callback(err);
|
3060
|
+
}
|
3061
|
+
|
3062
|
+
var out;
|
3063
|
+
|
3064
|
+
try {
|
3065
|
+
out = Parser.parse(tokens, opt);
|
3066
|
+
} catch (e) {
|
3067
|
+
err = e;
|
3068
|
+
}
|
3069
|
+
|
3070
|
+
opt.highlight = highlight;
|
3071
|
+
|
3072
|
+
return err
|
3073
|
+
? callback(err)
|
3074
|
+
: callback(null, out);
|
3075
|
+
};
|
3076
|
+
|
3077
|
+
if (!highlight || highlight.length < 3) {
|
3078
|
+
return done();
|
3079
|
+
}
|
3080
|
+
|
3081
|
+
delete opt.highlight;
|
3082
|
+
|
3083
|
+
if (!pending) return done();
|
3084
|
+
|
3085
|
+
for (; i < tokens.length; i++) {
|
3086
|
+
(function(token) {
|
3087
|
+
if (token.type !== 'code') {
|
3088
|
+
return --pending || done();
|
3089
|
+
}
|
3090
|
+
return highlight(token.text, token.lang, function(err, code) {
|
3091
|
+
if (err) return done(err);
|
3092
|
+
if (code == null || code === token.text) {
|
3093
|
+
return --pending || done();
|
3094
|
+
}
|
3095
|
+
token.text = code;
|
3096
|
+
token.escaped = true;
|
3097
|
+
--pending || done();
|
3098
|
+
});
|
3099
|
+
})(tokens[i]);
|
3100
|
+
}
|
3101
|
+
|
3102
|
+
return;
|
3103
|
+
}
|
2843
3104
|
try {
|
3105
|
+
if (opt) opt = merge({}, marked.defaults, opt);
|
2844
3106
|
return Parser.parse(Lexer.lex(src, opt), opt);
|
2845
3107
|
} catch (e) {
|
2846
3108
|
e.message += '\nPlease report this to https://github.com/chjj/marked.';
|
2847
3109
|
if ((opt || marked.defaults).silent) {
|
2848
|
-
return 'An error occured
|
3110
|
+
return '<p>An error occured:</p><pre>'
|
3111
|
+
+ escape(e.message + '', true)
|
3112
|
+
+ '</pre>';
|
2849
3113
|
}
|
2850
3114
|
throw e;
|
2851
3115
|
}
|
@@ -2857,7 +3121,7 @@ function marked(src, opt) {
|
|
2857
3121
|
|
2858
3122
|
marked.options =
|
2859
3123
|
marked.setOptions = function(opt) {
|
2860
|
-
marked.defaults
|
3124
|
+
merge(marked.defaults, opt);
|
2861
3125
|
return marked;
|
2862
3126
|
};
|
2863
3127
|
|
@@ -2867,8 +3131,14 @@ marked.defaults = {
|
|
2867
3131
|
breaks: false,
|
2868
3132
|
pedantic: false,
|
2869
3133
|
sanitize: false,
|
3134
|
+
smartLists: false,
|
2870
3135
|
silent: false,
|
2871
|
-
highlight: null
|
3136
|
+
highlight: null,
|
3137
|
+
langPrefix: 'lang-',
|
3138
|
+
smartypants: false,
|
3139
|
+
headerPrefix: '',
|
3140
|
+
renderer: new Renderer,
|
3141
|
+
xhtml: false
|
2872
3142
|
};
|
2873
3143
|
|
2874
3144
|
/**
|
@@ -2878,6 +3148,8 @@ marked.defaults = {
|
|
2878
3148
|
marked.Parser = Parser;
|
2879
3149
|
marked.parser = Parser.parse;
|
2880
3150
|
|
3151
|
+
marked.Renderer = Renderer;
|
3152
|
+
|
2881
3153
|
marked.Lexer = Lexer;
|
2882
3154
|
marked.lexer = Lexer.lex;
|
2883
3155
|
|
@@ -2886,7 +3158,7 @@ marked.inlineLexer = InlineLexer.output;
|
|
2886
3158
|
|
2887
3159
|
marked.parse = marked;
|
2888
3160
|
|
2889
|
-
if (typeof module !== 'undefined') {
|
3161
|
+
if (typeof module !== 'undefined' && typeof exports === 'object') {
|
2890
3162
|
module.exports = marked;
|
2891
3163
|
} else if (typeof define === 'function' && define.amd) {
|
2892
3164
|
define(function() { return marked; });
|