epic-editor-rails 0.2.3 → 0.2.4
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/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
|
+
[](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; });
|