typeout 1.4.5 → 1.4.6
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.
- 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
|