-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathShortcode.js
More file actions
189 lines (154 loc) · 5.25 KB
/
Shortcode.js
File metadata and controls
189 lines (154 loc) · 5.25 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
/**
* achoi@hearst.com
* inspired by: https://github.com/nicinabox/shortcode.js
* but sans dom
*/
var Shortcode = function(text, tags) {
if (!text) return;
this.text = text;
this.tags = tags;
this.now = Date.now(); // used to generate a non-orthogonal key
this.matches = [];
this.regex = '\\[{name}(\\s[\\s\\S]*?)?\\]' +
'(?:((?!\\s*?(?:\\[{name}|\\[\\/(?!{name})))[\\s\\S]*?)' +
'(\\[\/{name}\\]))?';
};
Shortcode.prototype.get = function() {
this.matchTags();
this.convertMatchesToNodes();
return this.replaceNodes();
};
Shortcode.prototype.matchTags = function() {
var html = this.text;
var instances;
var match;
var re;
var contents;
var regex;
var tag;
var options;
for (var key in this.tags) {
re = this.template(this.regex, {
name: key
});
instances = html.match(new RegExp(re, 'g')) || [];
for (var i = 0, len = instances.length; i < len; i++) {
match = instances[i].match(new RegExp(re));
contents = match[3] ? '' : undefined;
tag = match[0];
regex = this.escapeTagRegExp(tag);
options = this.parseOptions(instances[i]);
if (match[2]) {
contents = match[2].trim();
tag = tag.replace(contents, '').replace(/\n\s*/g, '');
regex = this.escapeTagRegExp(tag).replace('\\]\\[', '\\]([\\s\\S]*?)\\[');
}
var instanceId = key + this.now + '_' + i;
this.matches.push({
name: key,
tag: tag,
source: instances[i],
regex: regex,
options: options,
contents: contents,
id: instanceId, // every instance has a unique idenitifer
placeholderToken: '==SHORTCODE.' + instanceId + '=='
});
}
}
return this.matches;
};
/**
* Convert the Matches to replaceable elements
*/
Shortcode.prototype.convertMatchesToNodes = function() {
var html = this.text;
var replacer;
for (var i = 0, len = this.matches.length; i < len; i++) {
var match = this.matches[i];
html = html.replace(match.source, match.placeholderToken);
}
this.text = html;
return this.text;
};
/**
* replaceNodes
*/
Shortcode.prototype.replaceNodes = function() {
var self = this;
var match;
var result;
var done;
var fn;
var replacer = function(result) {
var re = new RegExp(replaceToken, 'g');
self.text = self.text.replace(re, result);
};
for (var i = 0, len = this.matches.length; i < len; i++) {
match = this.matches[i];
var replaceToken = match.placeholderToken;
fn = this.tags[match.name].bind(match);
done = replacer.bind(match);
result = fn(done);
if (result !== undefined) {
done(result);
}
}
return this.text;
};
Shortcode.prototype.parseOptions = function(instanceString) {
if (instanceString) {
// first, strip brackets
var openingTag = instanceString.match(/\[[a-zA-Z](.*?[^?])?\]/); // http://stackoverflow.com/a/8038932
// lets get the opening tag only
var options;
if (openingTag){
var openingTagText = openingTag[0];
if (openingTagText){
// robustificate this in order to handle special characters
// that broke the previous regexp
// this thing does two passes to get all attrnames
// this is necessary to capture attributes that contain no value
// why bother? because that's how it was received
var attrNames = openingTagText.match(/(\s(\-?\:?[a-zA-Z0-9._]*))/g);
if (!attrNames){
// none found? GTFO
return;
}
options = {};
for( var j = 0; j < attrNames.length; j++) {
if (!attrNames[i] || true) {
var singleAttrName = attrNames[j].trim();
options[singleAttrName] = null; // preappoint the attribute
}
}
//http://stackoverflow.com/a/2482127
var attrList = openingTagText.match(/([\w\-.:]+)\s*=\s*("[^"]*"|'[^']*'|[\w\-.:]+)/g);
if (attrList) {
for (var i = 0; i < attrList.length; i++) {
// time ti interate thru the list...
var attrSplit = attrList[i].split(/\=(.+)?/);
var prop = attrSplit[0];
var value = attrSplit[1];
if (value){
value = value.replace(/^"(.*)"$/, '$1');
}
options[prop] = value;
}
return options;
}
}
}
return options;
}
};
Shortcode.prototype.escapeTagRegExp = function(regex) {
return regex.replace(/[\[\]\/]/g, '\\$&');
};
Shortcode.prototype.template = function(s, d) {
for (var p in d) {
s = s.replace(new RegExp('{' + p + '}', 'g'), d[p]);
}
return s;
};
module.exports = Shortcode;