typeout 1.4.5 → 1.4.6
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/typeout.js +60 -60
- data/lib/typeout.rb +34 -33
- metadata +5 -5
data/lib/typeout.js
CHANGED
@@ -5,39 +5,39 @@ var regexpEscape = function(text) {
|
|
5
5
|
var Typeout = new Class({
|
6
6
|
initialize: function(content) {
|
7
7
|
var self = this;
|
8
|
-
|
8
|
+
|
9
9
|
self.content = content;
|
10
10
|
},
|
11
|
-
|
11
|
+
|
12
12
|
toHTML: function() {
|
13
13
|
var self = this;
|
14
|
-
|
14
|
+
|
15
15
|
var text = self.content;
|
16
|
-
|
16
|
+
|
17
17
|
text = self.cleanWhitespace(text);
|
18
18
|
text = '\n\n' + text + '\n\n'; // a few guaranteed newlines
|
19
|
-
|
19
|
+
|
20
20
|
self.archive = [];
|
21
|
-
|
21
|
+
|
22
22
|
text = self.archiveHtml(text);
|
23
|
-
|
23
|
+
|
24
24
|
text = self.htmlEscape(text);
|
25
|
-
|
25
|
+
|
26
26
|
text = self.archiveCode(text);
|
27
|
-
|
27
|
+
|
28
28
|
text = self.makeBlockquotes(text);
|
29
29
|
text = self.makeHeadings(text);
|
30
30
|
text = self.makeLists(text);
|
31
31
|
text = self.makeParagraphs(text);
|
32
32
|
text = self.makeInlines(text);
|
33
|
-
|
33
|
+
|
34
34
|
text = self.removeExcessNewlines(text);
|
35
|
-
|
35
|
+
|
36
36
|
text = self.retrieveArchive(text);
|
37
|
-
|
37
|
+
|
38
38
|
return text;
|
39
39
|
},
|
40
|
-
|
40
|
+
|
41
41
|
htmlEscape: function(text) {
|
42
42
|
return text.
|
43
43
|
replace(/&/g, '&').
|
@@ -45,107 +45,107 @@ var Typeout = new Class({
|
|
45
45
|
replace(/>/g, '>').
|
46
46
|
replace(/"/g, '"')
|
47
47
|
},
|
48
|
-
|
48
|
+
|
49
49
|
cleanWhitespace: function(text) {
|
50
50
|
return text.
|
51
51
|
replace(/\r\n/g, '\n').
|
52
52
|
replace(/\r/g, '\n').
|
53
53
|
replace(/^ +$/gm, '');
|
54
54
|
},
|
55
|
-
|
55
|
+
|
56
56
|
removeExcessNewlines: function(text) {
|
57
57
|
return text.replace(/\n{3,}/g, '\n\n');
|
58
58
|
},
|
59
|
-
|
59
|
+
|
60
60
|
archiveHtml: function(text) {
|
61
61
|
var self = this;
|
62
|
-
|
62
|
+
|
63
63
|
return text.replace(/\[html\]([\s\S]*?)\[\/html\]/gmi, function(wholeMatch, m1) {
|
64
64
|
return self.addToArchive(m1);
|
65
65
|
});
|
66
66
|
},
|
67
|
-
|
67
|
+
|
68
68
|
archiveCode: function(text) {
|
69
69
|
var self = this;
|
70
|
-
|
70
|
+
|
71
71
|
text = text.replace(/^-{3,}\n([\s\S]+?)\n-{3,}$/gm, function(wholeMatch, m1) {
|
72
72
|
return self.addToArchive('<pre><code>' + m1 +'</code></pre>');
|
73
73
|
});
|
74
|
-
|
74
|
+
|
75
75
|
return text.replace(/`(?!\s)((?:\s*\S)+?)`/g, function(wholeMatch, m1) {
|
76
76
|
return self.addToArchive('<code>' + m1 + '</code>');
|
77
77
|
});
|
78
78
|
},
|
79
|
-
|
79
|
+
|
80
80
|
addToArchive: function(text) {
|
81
81
|
var self = this;
|
82
|
-
|
82
|
+
|
83
83
|
var index = self.archive.push(text) - 1;
|
84
84
|
return '!a!r!c!h!i!v!e!' + index + '!a!r!c!h!i!v!e!'; // nobody's going to type that!
|
85
85
|
},
|
86
|
-
|
86
|
+
|
87
87
|
retrieveArchive: function(text) {
|
88
88
|
var self = this;
|
89
|
-
|
89
|
+
|
90
90
|
self.archive.each(function(content, index) {
|
91
91
|
text = text.replace(new RegExp('!a!r!c!h!i!v!e!' + index + '!a!r!c!h!i!v!e!'), content); // the block is so gsub won't substitute \&
|
92
92
|
});
|
93
93
|
return text;
|
94
94
|
},
|
95
|
-
|
95
|
+
|
96
96
|
makeBlockquotes: function(text) {
|
97
97
|
return text.replace(/^~{3,}\n([\s\S]+?)\n~{3,}$/gm, '\n\n<blockquote>\n\n$1\n\n</blockquote>\n\n');
|
98
98
|
},
|
99
|
-
|
99
|
+
|
100
100
|
makeHeadings: function(text) {
|
101
101
|
return text.replace(/^(={1,6})(.+?)=*?$/gm, function(wholeMatch, m1, m2) {
|
102
102
|
var depth = m1.length;
|
103
103
|
return '\n\n<h' + depth + '>' + m2.trim() + '</h' + depth + '>\n\n';
|
104
104
|
});
|
105
105
|
},
|
106
|
-
|
106
|
+
|
107
107
|
makeLists: function(text, baseLevel) {
|
108
108
|
var self = this;
|
109
|
-
|
109
|
+
|
110
110
|
if (baseLevel == undefined) {
|
111
111
|
baseLevel = '';
|
112
112
|
}
|
113
|
-
|
113
|
+
|
114
114
|
return text.replace(new RegExp('^(' + regexpEscape(baseLevel) + '[\\*\\#])[\\*\\#]* [^\n]+?\n(?:\\1[\\*\\#]* (?:[^\n])+?\n)*', 'gm'), function(content, level) {
|
115
115
|
content = self.makeLists(content, level);
|
116
116
|
content = content.replace(new RegExp('^' + regexpEscape(level) + ' (.+)$', 'gm'), '<li>$1</li>');
|
117
117
|
content = content.replace(/<\/li>\n<([uo])l>/gm, '<$1l>');
|
118
|
-
|
118
|
+
|
119
119
|
if (level.slice(-1) == '*') { // its an unordered list
|
120
120
|
content = '<ul>\n' + content + '</ul>';
|
121
121
|
} else {
|
122
122
|
content = '<ol>\n' + content + '</ol>';
|
123
123
|
}
|
124
|
-
|
124
|
+
|
125
125
|
if (baseLevel) {
|
126
126
|
content += '</li>\n';
|
127
127
|
} else {
|
128
128
|
content += '\n';
|
129
129
|
}
|
130
130
|
return content;
|
131
|
-
});
|
131
|
+
});
|
132
132
|
},
|
133
|
-
|
133
|
+
|
134
134
|
makeParagraphs: function(text) {
|
135
135
|
var self = this;
|
136
|
-
|
136
|
+
|
137
137
|
return text.replace(/^(?!<|!a!r!c!h!i!v!e!)([^\n]+\n)+\n/gm, function(content) {
|
138
138
|
return '\n\n<p>\n' + self.makeBreaks(content.trim()) + '</p>\n\n';
|
139
139
|
});
|
140
140
|
},
|
141
|
-
|
141
|
+
|
142
142
|
makeBreaks: function(text) {
|
143
143
|
return text.replace(/([^\n])\n(?!\n)/gm, '$1<br />\n');
|
144
144
|
},
|
145
|
-
|
145
|
+
|
146
146
|
makeInlines: function(text) {
|
147
147
|
var self = this;
|
148
|
-
|
148
|
+
|
149
149
|
text = text.
|
150
150
|
replace(/^((?: ?[\w\(\)']){3,}:) (?!\s*$)/gm, '<b>$1</b> '). // spaces are not allowed directly in front of the colon, there must be a space after it, and the rest of the line can't be blank
|
151
151
|
replace(/!\((.+?)\)(?:\:([a-zA-Z0-9\_]+))?/g, function(wholeMatch, m1, m2) {
|
@@ -165,7 +165,7 @@ var Typeout = new Class({
|
|
165
165
|
replace(/([a-z0-9\.\-\_\+]+)@((?:[a-z0-9\-]{2,}\.)*)([a-z0-9\-]+\.)(com|org|net|biz|edu|info|gov|co\.uk|co\.us)/gi, function(wholeMatch, address, subdomain, domain, tld) {
|
166
166
|
return '<a href="mailto:' + self.addToArchive(address + '@' + subdomain + domain + tld) + '">' + self.addToArchive(address + '@' + subdomain + domain + tld) + '</a>';
|
167
167
|
}).
|
168
|
-
replace(/(https?:\/\/)?((?:[a-z0-9\-]{2,}\.)*)([a-z0-9\-]+\.)(com|org|net|biz|edu|info|gov|co\.uk|co\.us)((?:\/[a-z0-9
|
168
|
+
replace(/(https?:\/\/)?((?:[a-z0-9\-]{2,}\.)*)([a-z0-9\-]+\.)(com|org|net|biz|edu|info|gov|co\.uk|co\.us)((?:\/(?:\.?[a-z0-9\-_=\?&;\%#]+)+)*\/?)/gi, function(wholeMatch, protocol, subdomain, domain, tld, file) {
|
169
169
|
if (!protocol) {
|
170
170
|
protocol = 'http://';
|
171
171
|
}
|
@@ -179,77 +179,77 @@ var Typeout = new Class({
|
|
179
179
|
['^', 'sup'],
|
180
180
|
['~', 'sub']
|
181
181
|
];
|
182
|
-
|
182
|
+
|
183
183
|
inlines.each(function(pair) {
|
184
184
|
var char = regexpEscape(pair[0]);
|
185
185
|
var tag = pair[1];
|
186
186
|
text = text.replace(new RegExp(char + '(?!\\s)((?:\\s*\\S)+?)' + char, 'g'), '<' + tag + '>$1</' + tag + '>');
|
187
187
|
});
|
188
|
-
|
188
|
+
|
189
189
|
return text;
|
190
190
|
}
|
191
191
|
});
|
192
192
|
|
193
193
|
var TypeoutPreview = new Class({
|
194
194
|
Implements: Options,
|
195
|
-
|
195
|
+
|
196
196
|
options: {
|
197
197
|
frequency: 500
|
198
198
|
},
|
199
|
-
|
199
|
+
|
200
200
|
initialize: function(textArea, previewDiv, options) {
|
201
201
|
var self = this;
|
202
|
-
|
202
|
+
|
203
203
|
self.textArea = $(textArea);
|
204
204
|
self.previewDiv = $(previewDiv);
|
205
|
-
|
205
|
+
|
206
206
|
self.setOptions(options);
|
207
|
-
|
207
|
+
|
208
208
|
self.typeOut = new Typeout;
|
209
209
|
self.dirty = true;
|
210
210
|
self.ready = false;
|
211
|
-
|
211
|
+
|
212
212
|
self.textArea.addEvent('keyup', self.setDirty.bind(self));
|
213
213
|
self.setReady();
|
214
214
|
},
|
215
|
-
|
215
|
+
|
216
216
|
setDirty: function() {
|
217
217
|
var self = this;
|
218
|
-
|
218
|
+
|
219
219
|
self.dirty = true;
|
220
220
|
if (self.ready) {
|
221
221
|
self.setUnReady();
|
222
222
|
self.preview();
|
223
223
|
}
|
224
224
|
},
|
225
|
-
|
225
|
+
|
226
226
|
setUnReady: function() {
|
227
227
|
var self = this;
|
228
|
-
|
228
|
+
|
229
229
|
self.ready = false;
|
230
230
|
self.setReady.delay(self.options.frequency, self);
|
231
231
|
},
|
232
|
-
|
232
|
+
|
233
233
|
setReady: function() {
|
234
234
|
var self = this;
|
235
|
-
|
235
|
+
|
236
236
|
self.ready = true;
|
237
237
|
if (self.dirty) {
|
238
238
|
self.setUnReady();
|
239
239
|
self.preview();
|
240
240
|
}
|
241
241
|
},
|
242
|
-
|
242
|
+
|
243
243
|
render: function(text) {
|
244
244
|
var self = this;
|
245
|
-
|
245
|
+
|
246
246
|
self.typeOut.content = text;
|
247
247
|
return self.typeOut.toHTML();
|
248
248
|
},
|
249
|
-
|
249
|
+
|
250
250
|
preview: function() {
|
251
251
|
var self = this;
|
252
|
-
|
252
|
+
|
253
253
|
self.previewDiv.set('html', self.render(self.textArea.value));
|
254
254
|
self.dirty = false;
|
255
255
|
}
|
@@ -257,17 +257,17 @@ var TypeoutPreview = new Class({
|
|
257
257
|
|
258
258
|
TypeoutTemplatePreview = new Class({
|
259
259
|
Extends: TypeoutPreview,
|
260
|
-
|
260
|
+
|
261
261
|
initialize: function(textArea, previewDiv, template, options) {
|
262
262
|
var self = this;
|
263
|
-
|
263
|
+
|
264
264
|
self.template = template;
|
265
265
|
self.parent(textArea, previewDiv, options);
|
266
266
|
},
|
267
|
-
|
267
|
+
|
268
268
|
render: function(content) {
|
269
269
|
var self = this;
|
270
|
-
|
270
|
+
|
271
271
|
self.template.tags.each(function(tag) {
|
272
272
|
content = content.replace(RegExp(regexpEscape(tag.name), 'gi'), tag.example);
|
273
273
|
});
|
data/lib/typeout.rb
CHANGED
@@ -1,43 +1,44 @@
|
|
1
1
|
require 'sanitize'
|
2
|
+
require 'erb'
|
2
3
|
|
3
4
|
class Typeout < String
|
4
5
|
VERSION = '1.4.5'
|
5
|
-
|
6
|
+
|
6
7
|
include ERB::Util
|
7
|
-
|
8
|
+
|
8
9
|
def self.convert(text, sanitize = true)
|
9
10
|
self.new(text.to_s).to_html(sanitize)
|
10
11
|
end
|
11
|
-
|
12
|
+
|
12
13
|
def to_html(sanitize = true)
|
13
14
|
@sanitize = sanitize
|
14
|
-
|
15
|
+
|
15
16
|
text = self.dup
|
16
|
-
|
17
|
+
|
17
18
|
text = clean_whitespace text
|
18
19
|
text = "\n\n#{text}\n\n" # a few guaranteed newlines
|
19
|
-
|
20
|
+
|
20
21
|
@archive = []
|
21
|
-
|
22
|
+
|
22
23
|
text = archive_html text
|
23
|
-
|
24
|
+
|
24
25
|
text = html_escape text
|
25
|
-
|
26
|
+
|
26
27
|
text = archive_code text
|
27
28
|
text = archive_blockquotes text
|
28
|
-
|
29
|
+
|
29
30
|
text = make_headings text
|
30
31
|
text = make_lists text
|
31
32
|
text = make_paragraphs text
|
32
33
|
text = make_inlines text
|
33
|
-
|
34
|
+
|
34
35
|
text = remove_excess_newlines text
|
35
|
-
|
36
|
+
|
36
37
|
text = retrieve_archive text
|
37
|
-
|
38
|
+
|
38
39
|
text
|
39
40
|
end
|
40
|
-
|
41
|
+
|
41
42
|
def sanitize_html(text)
|
42
43
|
if @sanitize
|
43
44
|
Sanitize.clean(text, Sanitize::Config::RELAXED)
|
@@ -45,66 +46,66 @@ class Typeout < String
|
|
45
46
|
text
|
46
47
|
end
|
47
48
|
end
|
48
|
-
|
49
|
+
|
49
50
|
def clean_whitespace(text)
|
50
51
|
text.gsub(/\r\n/, "\n").
|
51
52
|
gsub(/\r/, "\n").
|
52
53
|
gsub(/^ +$/, '')
|
53
54
|
end
|
54
|
-
|
55
|
+
|
55
56
|
def remove_excess_newlines(text)
|
56
57
|
text.gsub(/\n{3,}/, "\n\n")
|
57
58
|
end
|
58
|
-
|
59
|
+
|
59
60
|
def archive_html(text)
|
60
61
|
text.gsub(/\[html\](.*?)\[\/html\]/mi) { archive(sanitize_html($1)) }
|
61
62
|
end
|
62
|
-
|
63
|
+
|
63
64
|
def archive_code(text)
|
64
65
|
text = text.gsub(/^-{3,}\n(.+?)\n-{3,}$/m) { archive("<pre><code>#{$1}</code></pre>") }
|
65
66
|
text.gsub(/`(?!\s)((?:\s*\S)+?)`/) { archive("<code>#{$1}</code>") }
|
66
67
|
end
|
67
|
-
|
68
|
+
|
68
69
|
def archive_blockquotes(text)
|
69
70
|
text.gsub(/^~{3,}\n(.+?)\n~{3,}$/m) do
|
70
71
|
content = remove_excess_newlines(make_paragraphs(make_lists("\n\n#{$1}\n\n"))) # blockquotes support paragraphs and lists
|
71
72
|
archive("<blockquote>#{content}</blockquote>")
|
72
73
|
end
|
73
74
|
end
|
74
|
-
|
75
|
+
|
75
76
|
def archive(text)
|
76
77
|
@archive << text
|
77
78
|
"!a!r!c!h!i!v!e!#{@archive.length-1}!a!r!c!h!i!v!e!" # nobody's going to type that!
|
78
79
|
end
|
79
|
-
|
80
|
+
|
80
81
|
def retrieve_archive(text)
|
81
82
|
@archive.each_with_index do |content, index|
|
82
83
|
text = text.sub("!a!r!c!h!i!v!e!#{index}!a!r!c!h!i!v!e!") { content } # the block is so gsub won't substitute \&
|
83
84
|
end
|
84
85
|
text
|
85
86
|
end
|
86
|
-
|
87
|
+
|
87
88
|
def make_headings(text)
|
88
89
|
text.gsub(/^(={1,6})(.+?)=*?$/) do
|
89
90
|
depth = $1.length
|
90
91
|
"\n\n<h#{depth}>#{$2.strip}</h#{depth}>\n\n"
|
91
92
|
end
|
92
93
|
end
|
93
|
-
|
94
|
+
|
94
95
|
def make_lists(text, base_level = nil)
|
95
96
|
text.gsub(/^(#{Regexp.escape(base_level.to_s)}[\*\#])[\*\#]* [^\n]+?\n(?:\1[\*\#]* (?:[^\n])+?\n)*/m) do |content|
|
96
97
|
level = $1
|
97
|
-
|
98
|
+
|
98
99
|
content = make_lists(content, level)
|
99
100
|
content = content.gsub(/^#{Regexp.escape(level)} (.+)$/, '<li>\1</li>')
|
100
101
|
content = content.gsub(/<\/li>\n<([uo])l>/m, '<\1l>')
|
101
|
-
|
102
|
+
|
102
103
|
if level.last == "*" # its an unordered list
|
103
104
|
content = "<ul>\n#{content}</ul>"
|
104
105
|
else
|
105
106
|
content = "<ol>\n#{content}</ol>"
|
106
107
|
end
|
107
|
-
|
108
|
+
|
108
109
|
if base_level
|
109
110
|
content += "</li>\n"
|
110
111
|
else
|
@@ -113,15 +114,15 @@ class Typeout < String
|
|
113
114
|
content
|
114
115
|
end
|
115
116
|
end
|
116
|
-
|
117
|
+
|
117
118
|
def make_paragraphs(text)
|
118
119
|
text.gsub(/^(?!<|!a!r!c!h!i!v!e!)([^\n]+\n)+\n/m) { |content| "\n\n<p>\n#{make_breaks(content.strip)}</p>\n\n" }
|
119
120
|
end
|
120
|
-
|
121
|
+
|
121
122
|
def make_breaks(text)
|
122
123
|
text.gsub(/([^\n])\n(?!\n)/m, "\\1<br />\n")
|
123
124
|
end
|
124
|
-
|
125
|
+
|
125
126
|
def make_inlines(text)
|
126
127
|
text = text.
|
127
128
|
gsub(/^((?: ?[\w\(\)']){3,}:) (?!\s*$)/, '<b>\1</b> '). # spaces are not allowed directly in front of the colon, there must be a space after it, and the rest of the line can't be blank
|
@@ -142,7 +143,7 @@ class Typeout < String
|
|
142
143
|
gsub(/([a-z0-9\.\-\_\+]+)@((?:[a-z0-9\-]{2,}\.)*)([a-z0-9\-]+\.)(com|org|net|biz|edu|info|gov|co\.uk|co\.us)/i) do
|
143
144
|
"<a href=\"mailto:#{archive($1 + '@' + $2 + $3 + $4)}\">#{archive($1 + '@' + $2 + $3 + $4)}</a>"
|
144
145
|
end.
|
145
|
-
gsub(/(https?:\/\/)?((?:[a-z0-9\-]{2,}\.)*)([a-z0-9\-]+\.)(com|org|net|biz|edu|info|gov|co\.uk|co\.us)((?:\/[a-z0-9
|
146
|
+
gsub(/(https?:\/\/)?((?:[a-z0-9\-]{2,}\.)*)([a-z0-9\-]+\.)(com|org|net|biz|edu|info|gov|co\.uk|co\.us)((?:\/(?:\.?[a-z0-9\-_=\?&;\%#]+)+)*\/?)/i) do # some of the magic in here is to not grab periods at the end of URLs
|
146
147
|
if $1.nil?
|
147
148
|
protocol = 'http://'
|
148
149
|
else
|
@@ -150,7 +151,7 @@ class Typeout < String
|
|
150
151
|
end
|
151
152
|
"<a href=\"#{archive(protocol + $2 + $3 + $4 + $5)}\">#{archive(protocol + $2 + $3 + $4 + $5)}</a>"
|
152
153
|
end
|
153
|
-
|
154
|
+
|
154
155
|
inlines = [
|
155
156
|
['*', 'strong'],
|
156
157
|
['_', 'em'],
|
@@ -158,7 +159,7 @@ class Typeout < String
|
|
158
159
|
['^', 'sup'],
|
159
160
|
['~', 'sub']
|
160
161
|
]
|
161
|
-
|
162
|
+
|
162
163
|
inlines.each do |char, tag|
|
163
164
|
char = Regexp.escape(char)
|
164
165
|
re = /#{char} # the opening character
|
@@ -170,7 +171,7 @@ class Typeout < String
|
|
170
171
|
#re = /#{char}(.+?)#{char}/
|
171
172
|
text.gsub!(re, "<#{tag}>\\1</#{tag}>")
|
172
173
|
end
|
173
|
-
|
174
|
+
|
174
175
|
text
|
175
176
|
end
|
176
177
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: typeout
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 11
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 1
|
8
8
|
- 4
|
9
|
-
-
|
10
|
-
version: 1.4.
|
9
|
+
- 6
|
10
|
+
version: 1.4.6
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Connor McKay
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date:
|
18
|
+
date: 2013-09-15 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
21
|
name: sanitize
|
@@ -76,7 +76,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
76
76
|
requirements: []
|
77
77
|
|
78
78
|
rubyforge_project: typeout
|
79
|
-
rubygems_version: 1.8.
|
79
|
+
rubygems_version: 1.8.24
|
80
80
|
signing_key:
|
81
81
|
specification_version: 3
|
82
82
|
summary: Dead simple plain text to HTML converter
|