typeout 1.4.1
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/LICENSE +19 -0
- data/README.rdoc +133 -0
- data/lib/typeout.js +276 -0
- data/lib/typeout.rb +170 -0
- metadata +86 -0
data/LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2010 Connor McKay
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,133 @@
|
|
1
|
+
= Typeout
|
2
|
+
|
3
|
+
Typeout is a plain text formatting language designed around two goals:
|
4
|
+
|
5
|
+
* Syntax simple enough for computer illiterate users
|
6
|
+
* Easily typed into an HTML textbox, meaning no tabs or extra spaces.
|
7
|
+
|
8
|
+
It is also a set of libraries in different programming languages for converting
|
9
|
+
Typeout formatted text into HTML. The primary implementation is in Ruby, which
|
10
|
+
has been more or less directly translated into Javascript.
|
11
|
+
|
12
|
+
Typeout is inspired by:
|
13
|
+
|
14
|
+
* Markdown http://daringfireball.net/projects/markdown
|
15
|
+
* Textile http://textism.com/tools/textile
|
16
|
+
* Creole http://www.wikicreole.org
|
17
|
+
|
18
|
+
*Author*:: Connor McKay (mailto:connor@verticalforest.com)
|
19
|
+
*Version*:: 1.4.1 (2010-7-8)
|
20
|
+
*Copyright*:: Copyright (c) 2007-2010 Connor McKay. All rights reserved.
|
21
|
+
*License*:: MIT License (http://opensource.org/licenses/mit-license.php)
|
22
|
+
*Website*:: http://github.com/greneholt/typeout
|
23
|
+
|
24
|
+
== Requires
|
25
|
+
|
26
|
+
Ruby version:
|
27
|
+
|
28
|
+
* Sanitize >= 1.2.1
|
29
|
+
|
30
|
+
Javascript version:
|
31
|
+
|
32
|
+
* MooTools Core >= 1.2.4
|
33
|
+
|
34
|
+
== Installation
|
35
|
+
|
36
|
+
Gem:
|
37
|
+
|
38
|
+
gem install typeout
|
39
|
+
|
40
|
+
Rails plugin:
|
41
|
+
|
42
|
+
./script/plugin install git://github.com/greneholt/typeout.git
|
43
|
+
|
44
|
+
== Usage
|
45
|
+
|
46
|
+
Ruby:
|
47
|
+
|
48
|
+
Typeout.convert(text)
|
49
|
+
|
50
|
+
Javascript:
|
51
|
+
|
52
|
+
var text = new TypeOut(text);
|
53
|
+
text.toHTML();
|
54
|
+
|
55
|
+
== Syntax
|
56
|
+
|
57
|
+
---
|
58
|
+
Code block
|
59
|
+
---
|
60
|
+
|
61
|
+
= Level 1 Header
|
62
|
+
== Level 2 Header, etc.
|
63
|
+
|
64
|
+
* Unordered
|
65
|
+
* List
|
66
|
+
|
67
|
+
# Ordered
|
68
|
+
# List
|
69
|
+
|
70
|
+
* Multilevel
|
71
|
+
* Unordered
|
72
|
+
** List
|
73
|
+
** With
|
74
|
+
*** Multiple
|
75
|
+
* Asterisks
|
76
|
+
|
77
|
+
* Mixed
|
78
|
+
* Unordered
|
79
|
+
*# And
|
80
|
+
*# Ordered
|
81
|
+
*# Nested
|
82
|
+
* List
|
83
|
+
|
84
|
+
~~~
|
85
|
+
Four score and seven years ago, blockquotes use at least three tildes at
|
86
|
+
beginning and end.
|
87
|
+
~~~
|
88
|
+
|
89
|
+
Average paragraph containing *bold text*, _italic text_, and a
|
90
|
+
[link to google](http://google.com) and some `inline code`.
|
91
|
+
|
92
|
+
Another paragraph with a [link](http://google.com):button with a class of "button".
|
93
|
+
|
94
|
+
Images
|
95
|
+
|
96
|
+
!(http://example.com/an_image.png)
|
97
|
+
|
98
|
+
!(http://example.com/logo.png)[An image with optional alt text]
|
99
|
+
|
100
|
+
|
101
|
+
Name: Jane Alice
|
102
|
+
Age: 25
|
103
|
+
Favorite food: Pizza
|
104
|
+
|
105
|
+
Text at the beginning of a line followed by a colon will be automatically bolded.
|
106
|
+
|
107
|
+
Newlines in the middle of a paragraph are preserved in Typeout, meaning
|
108
|
+
that a paragraph cannot be split across multiple lines.
|
109
|
+
|
110
|
+
Plain html can be preserved intact by surrounding it in [html], [/html]
|
111
|
+
pairs. It will be sanitized using the Sanitize library in the Ruby version.
|
112
|
+
|
113
|
+
== License
|
114
|
+
|
115
|
+
Copyright (c) 2010 Connor McKay
|
116
|
+
|
117
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
118
|
+
of this software and associated documentation files (the "Software"), to deal
|
119
|
+
in the Software without restriction, including without limitation the rights
|
120
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
121
|
+
copies of the Software, and to permit persons to whom the Software is
|
122
|
+
furnished to do so, subject to the following conditions:
|
123
|
+
|
124
|
+
The above copyright notice and this permission notice shall be included in
|
125
|
+
all copies or substantial portions of the Software.
|
126
|
+
|
127
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
128
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
129
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
130
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
131
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
132
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
133
|
+
THE SOFTWARE.
|
data/lib/typeout.js
ADDED
@@ -0,0 +1,276 @@
|
|
1
|
+
var Typeout = new Class({
|
2
|
+
initialize: function(content) {
|
3
|
+
var self = this;
|
4
|
+
|
5
|
+
self.content = content;
|
6
|
+
},
|
7
|
+
|
8
|
+
toHTML: function() {
|
9
|
+
var self = this;
|
10
|
+
|
11
|
+
var text = self.content;
|
12
|
+
|
13
|
+
text = self.cleanWhitespace(text);
|
14
|
+
text = '\n\n' + text + '\n\n'; // a few guaranteed newlines
|
15
|
+
|
16
|
+
self.archive = [];
|
17
|
+
|
18
|
+
text = self.archiveHtml(text);
|
19
|
+
|
20
|
+
text = self.htmlEscape(text);
|
21
|
+
|
22
|
+
text = self.archiveCode(text);
|
23
|
+
|
24
|
+
text = self.makeBlockquotes(text);
|
25
|
+
text = self.makeHeadings(text);
|
26
|
+
text = self.makeLists(text);
|
27
|
+
text = self.makeParagraphs(text);
|
28
|
+
text = self.makeInlines(text);
|
29
|
+
|
30
|
+
text = self.removeExcessNewlines(text);
|
31
|
+
|
32
|
+
text = self.retrieveArchive(text);
|
33
|
+
|
34
|
+
return text;
|
35
|
+
},
|
36
|
+
|
37
|
+
htmlEscape: function(text) {
|
38
|
+
return text.
|
39
|
+
replace(/&/g, '&').
|
40
|
+
replace(/</g, '<').
|
41
|
+
replace(/>/g, '>').
|
42
|
+
replace(/"/g, '"')
|
43
|
+
},
|
44
|
+
|
45
|
+
regexpEscape: function(text) {
|
46
|
+
return text.replace(/([\{\}\(\)\[\]\*\+\?\$\^\.\#\\])/g, '\\$1')
|
47
|
+
},
|
48
|
+
|
49
|
+
cleanWhitespace: function(text) {
|
50
|
+
return text.
|
51
|
+
replace(/\r\n/g, '\n').
|
52
|
+
replace(/\r/g, '\n').
|
53
|
+
replace(/^ +$/gm, '');
|
54
|
+
},
|
55
|
+
|
56
|
+
removeExcessNewlines: function(text) {
|
57
|
+
return text.replace(/\n{3,}/g, '\n\n');
|
58
|
+
},
|
59
|
+
|
60
|
+
archiveHtml: function(text) {
|
61
|
+
var self = this;
|
62
|
+
|
63
|
+
return text.replace(/\[html\]([\s\S]*?)\[\/html\]/gmi, function(wholeMatch, m1) {
|
64
|
+
return self.addToArchive(m1);
|
65
|
+
});
|
66
|
+
},
|
67
|
+
|
68
|
+
archiveCode: function(text) {
|
69
|
+
var self = this;
|
70
|
+
|
71
|
+
text = text.replace(/^-{3,}\n([\s\S]+?)\n-{3,}$/gm, function(wholeMatch, m1) {
|
72
|
+
return self.addToArchive('<pre><code>' + m1 +'</code></pre>');
|
73
|
+
});
|
74
|
+
|
75
|
+
return text.replace(/`(?!\s)((?:\s*\S)+?)`/g, function(wholeMatch, m1) {
|
76
|
+
return self.addToArchive('<code>' + m1 + '</code>');
|
77
|
+
});
|
78
|
+
},
|
79
|
+
|
80
|
+
addToArchive: function(text) {
|
81
|
+
var self = this;
|
82
|
+
|
83
|
+
var index = self.archive.push(text) - 1;
|
84
|
+
return '!a!r!c!h!i!v!e!' + index + '!a!r!c!h!i!v!e!'; // nobody's going to type that!
|
85
|
+
},
|
86
|
+
|
87
|
+
retrieveArchive: function(text) {
|
88
|
+
var self = this;
|
89
|
+
|
90
|
+
self.archive.each(function(content, index) {
|
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
|
+
});
|
93
|
+
return text;
|
94
|
+
},
|
95
|
+
|
96
|
+
makeBlockquotes: function(text) {
|
97
|
+
return text.replace(/^~{3,}\n([\s\S]+?)\n~{3,}$/gm, '\n\n<blockquote>\n\n$1\n\n</blockquote>\n\n');
|
98
|
+
},
|
99
|
+
|
100
|
+
makeHeadings: function(text) {
|
101
|
+
return text.replace(/^(={1,6})(.+?)=*?$/gm, function(wholeMatch, m1, m2) {
|
102
|
+
var depth = m1.length;
|
103
|
+
return '\n\n<h' + depth + '>' + m2.trim() + '</h' + depth + '>\n\n';
|
104
|
+
});
|
105
|
+
},
|
106
|
+
|
107
|
+
makeLists: function(text, baseLevel) {
|
108
|
+
var self = this;
|
109
|
+
|
110
|
+
if (baseLevel == undefined) {
|
111
|
+
baseLevel = '';
|
112
|
+
}
|
113
|
+
|
114
|
+
return text.replace(new RegExp('^(' + self.regexpEscape(baseLevel) + '[\\*\\#])[\\*\\#]* [^\n]+?\n(?:\\1[\\*\\#]* (?:[^\n])+?\n)*', 'gm'), function(content, level) {
|
115
|
+
content = self.makeLists(content, level);
|
116
|
+
content = content.replace(new RegExp('^' + self.regexpEscape(level) + ' (.+)$', 'gm'), '<li>$1</li>');
|
117
|
+
content = content.replace(/<\/li>\n<([uo])l>/gm, '<$1l>');
|
118
|
+
|
119
|
+
if (level.slice(-1) == '*') { // its an unordered list
|
120
|
+
content = '<ul>\n' + content + '</ul>';
|
121
|
+
} else {
|
122
|
+
content = '<ol>\n' + content + '</ol>';
|
123
|
+
}
|
124
|
+
|
125
|
+
if (baseLevel) {
|
126
|
+
content += '</li>\n';
|
127
|
+
} else {
|
128
|
+
content += '\n';
|
129
|
+
}
|
130
|
+
return content;
|
131
|
+
});
|
132
|
+
},
|
133
|
+
|
134
|
+
makeParagraphs: function(text) {
|
135
|
+
var self = this;
|
136
|
+
|
137
|
+
return text.replace(/^(?!<|!a!r!c!h!i!v!e!)([^\n]+\n)+\n/gm, function(content) {
|
138
|
+
return '\n\n<p>\n' + self.makeBreaks(content.trim()) + '</p>\n\n';
|
139
|
+
});
|
140
|
+
},
|
141
|
+
|
142
|
+
makeBreaks: function(text) {
|
143
|
+
return text.replace(/([^\n])\n(?!\n)/gm, '$1<br />\n');
|
144
|
+
},
|
145
|
+
|
146
|
+
makeInlines: function(text) {
|
147
|
+
var self = this;
|
148
|
+
|
149
|
+
text = text.
|
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
|
+
replace(/!\((.+?)\)(?:\:([a-zA-Z0-9\_]+))?/g, function(wholeMatch, m1, m2) {
|
152
|
+
if (m2) {
|
153
|
+
return '<img src="' + self.addToArchive(m1.trim()) + '" class="' + m2 + '" />';
|
154
|
+
} else {
|
155
|
+
return '<img src="' + self.addToArchive(m1.trim()) + '" />';
|
156
|
+
}
|
157
|
+
}).
|
158
|
+
replace(/\[(.+?)\]\((.+?)\)(?:\:([a-zA-Z0-9\_]+))?/g, function(wholeMatch, m1, m2, m3) {
|
159
|
+
if (m3) {
|
160
|
+
return '<a href="' + self.addToArchive(m2.trim()) + '" class="' + m3 + '">' + self.addToArchive(m1) + '</a>';
|
161
|
+
} else {
|
162
|
+
return '<a href="' + self.addToArchive(m2.trim()) + '">' + self.addToArchive(m1) + '</a>';
|
163
|
+
}
|
164
|
+
}).
|
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
|
+
return '<a href="mailto:' + self.addToArchive(address + '@' + subdomain + domain + tld) + '">' + self.addToArchive(address + '@' + subdomain + domain + tld) + '</a>';
|
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\-\._=\?&;\%#]+)*\/?)/gi, function(wholeMatch, protocol, subdomain, domain, tld, file) {
|
169
|
+
if (!protocol) {
|
170
|
+
protocol = 'http://';
|
171
|
+
}
|
172
|
+
return '<a href="' + self.addToArchive(protocol + subdomain + domain + tld + file) + '">' + self.addToArchive(protocol + subdomain + domain + tld + file) + '</a>';
|
173
|
+
});
|
174
|
+
|
175
|
+
var inlines = [
|
176
|
+
['*', 'strong'],
|
177
|
+
['_', 'em'],
|
178
|
+
['`', 'code'],
|
179
|
+
['^', 'sup'],
|
180
|
+
['~', 'sub']
|
181
|
+
];
|
182
|
+
|
183
|
+
inlines.each(function(pair) {
|
184
|
+
var char = self.regexpEscape(pair[0]);
|
185
|
+
var tag = pair[1];
|
186
|
+
text = text.replace(new RegExp(char + '(?!\\s)((?:\\s*\\S)+?)' + char, 'g'), '<' + tag + '>$1</' + tag + '>');
|
187
|
+
});
|
188
|
+
|
189
|
+
return text;
|
190
|
+
}
|
191
|
+
});
|
192
|
+
|
193
|
+
var TypeOutPreview = new Class({
|
194
|
+
Implements: Options,
|
195
|
+
|
196
|
+
options: {
|
197
|
+
frequency: 500
|
198
|
+
},
|
199
|
+
|
200
|
+
initialize: function(textArea, previewDiv, options) {
|
201
|
+
var self = this;
|
202
|
+
|
203
|
+
self.textArea = $(textArea);
|
204
|
+
self.previewDiv = $(previewDiv);
|
205
|
+
|
206
|
+
self.setOptions(options);
|
207
|
+
|
208
|
+
self.typeOut = new TypeOut;
|
209
|
+
self.dirty = true;
|
210
|
+
self.ready = false;
|
211
|
+
|
212
|
+
self.textArea.addEvent('keyup', self.setDirty.bind(self));
|
213
|
+
self.setReady();
|
214
|
+
},
|
215
|
+
|
216
|
+
setDirty: function() {
|
217
|
+
var self = this;
|
218
|
+
|
219
|
+
self.dirty = true;
|
220
|
+
if (self.ready) {
|
221
|
+
self.setUnReady();
|
222
|
+
self.preview();
|
223
|
+
}
|
224
|
+
},
|
225
|
+
|
226
|
+
setUnReady: function() {
|
227
|
+
var self = this;
|
228
|
+
|
229
|
+
self.ready = false;
|
230
|
+
self.setReady.delay(self.options.frequency, self);
|
231
|
+
},
|
232
|
+
|
233
|
+
setReady: function() {
|
234
|
+
var self = this;
|
235
|
+
|
236
|
+
self.ready = true;
|
237
|
+
if (self.dirty) {
|
238
|
+
self.setUnReady();
|
239
|
+
self.preview();
|
240
|
+
}
|
241
|
+
},
|
242
|
+
|
243
|
+
render: function(text) {
|
244
|
+
var self = this;
|
245
|
+
|
246
|
+
self.typeOut.content = text;
|
247
|
+
return self.typeOut.toHTML();
|
248
|
+
},
|
249
|
+
|
250
|
+
preview: function() {
|
251
|
+
var self = this;
|
252
|
+
|
253
|
+
self.previewDiv.set('html', self.render(self.textArea.value));
|
254
|
+
self.dirty = false;
|
255
|
+
}
|
256
|
+
});
|
257
|
+
|
258
|
+
TypeOutTemplatePreview = new Class({
|
259
|
+
Extends: TypeOutPreview,
|
260
|
+
|
261
|
+
initialize: function(textArea, previewDiv, template, options) {
|
262
|
+
var self = this;
|
263
|
+
|
264
|
+
self.template = template;
|
265
|
+
self.parent(textArea, previewDiv, options);
|
266
|
+
},
|
267
|
+
|
268
|
+
render: function(content) {
|
269
|
+
var self = this;
|
270
|
+
|
271
|
+
self.template.tags.each(function(tag) {
|
272
|
+
content = content.replace(RegExp(self.regexpEscape(tag.name), 'gi'), tag.example);
|
273
|
+
});
|
274
|
+
return self.parent(content);
|
275
|
+
}
|
276
|
+
});
|
data/lib/typeout.rb
ADDED
@@ -0,0 +1,170 @@
|
|
1
|
+
require 'sanitize'
|
2
|
+
|
3
|
+
class Typeout < String
|
4
|
+
VERSION = '1.4.1'
|
5
|
+
|
6
|
+
include ERB::Util
|
7
|
+
|
8
|
+
def self.convert(text)
|
9
|
+
self.new(text.to_s).to_html
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_html
|
13
|
+
text = self.dup
|
14
|
+
|
15
|
+
text = clean_whitespace text
|
16
|
+
text = "\n\n#{text}\n\n" # a few guaranteed newlines
|
17
|
+
|
18
|
+
@archive = []
|
19
|
+
|
20
|
+
text = archive_html text
|
21
|
+
|
22
|
+
text = html_escape text
|
23
|
+
|
24
|
+
text = archive_code text
|
25
|
+
text = archive_blockquotes text
|
26
|
+
|
27
|
+
text = make_headings text
|
28
|
+
text = make_lists text
|
29
|
+
text = make_paragraphs text
|
30
|
+
text = make_inlines text
|
31
|
+
|
32
|
+
text = remove_excess_newlines text
|
33
|
+
|
34
|
+
text = retrieve_archive text
|
35
|
+
|
36
|
+
text
|
37
|
+
end
|
38
|
+
|
39
|
+
def sanitize_html(text)
|
40
|
+
Sanitize.clean(text, Sanitize::Config::RELAXED)
|
41
|
+
end
|
42
|
+
|
43
|
+
def clean_whitespace(text)
|
44
|
+
text.gsub(/\r\n/, "\n").
|
45
|
+
gsub(/\r/, "\n").
|
46
|
+
gsub(/^ +$/, '')
|
47
|
+
end
|
48
|
+
|
49
|
+
def remove_excess_newlines(text)
|
50
|
+
text.gsub(/\n{3,}/, "\n\n")
|
51
|
+
end
|
52
|
+
|
53
|
+
def archive_html(text)
|
54
|
+
text.gsub(/\[html\](.*?)\[\/html\]/mi) { archive(sanitize_html($1)) }
|
55
|
+
end
|
56
|
+
|
57
|
+
def archive_code(text)
|
58
|
+
text = text.gsub(/^-{3,}\n(.+?)\n-{3,}$/m) { archive("<pre><code>#{$1}</code></pre>") }
|
59
|
+
text.gsub(/`(?!\s)((?:\s*\S)+?)`/) { archive("<code>#{$1}</code>") }
|
60
|
+
end
|
61
|
+
|
62
|
+
def archive_blockquotes(text)
|
63
|
+
text.gsub(/^~{3,}\n(.+?)\n~{3,}$/m) do
|
64
|
+
content = remove_excess_newlines(make_paragraphs(make_lists("\n\n#{$1}\n\n"))) # blockquotes support paragraphs and lists
|
65
|
+
archive("<blockquote>#{content}</blockquote>")
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def archive(text)
|
70
|
+
@archive << text
|
71
|
+
"!a!r!c!h!i!v!e!#{@archive.length-1}!a!r!c!h!i!v!e!" # nobody's going to type that!
|
72
|
+
end
|
73
|
+
|
74
|
+
def retrieve_archive(text)
|
75
|
+
@archive.each_with_index do |content, index|
|
76
|
+
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 \&
|
77
|
+
end
|
78
|
+
text
|
79
|
+
end
|
80
|
+
|
81
|
+
def make_headings(text)
|
82
|
+
text.gsub(/^(={1,6})(.+?)=*?$/) do
|
83
|
+
depth = $1.length
|
84
|
+
"\n\n<h#{depth}>#{$2.strip}</h#{depth}>\n\n"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def make_lists(text, base_level = nil)
|
89
|
+
text.gsub(/^(#{Regexp.escape(base_level.to_s)}[\*\#])[\*\#]* [^\n]+?\n(?:\1[\*\#]* (?:[^\n])+?\n)*/m) do |content|
|
90
|
+
level = $1
|
91
|
+
|
92
|
+
content = make_lists(content, level)
|
93
|
+
content = content.gsub(/^#{Regexp.escape(level)} (.+)$/, '<li>\1</li>')
|
94
|
+
content = content.gsub(/<\/li>\n<([uo])l>/m, '<\1l>')
|
95
|
+
|
96
|
+
if level.last == "*" # its an unordered list
|
97
|
+
content = "<ul>\n#{content}</ul>"
|
98
|
+
else
|
99
|
+
content = "<ol>\n#{content}</ol>"
|
100
|
+
end
|
101
|
+
|
102
|
+
if base_level
|
103
|
+
content += "</li>\n"
|
104
|
+
else
|
105
|
+
content += "\n"
|
106
|
+
end
|
107
|
+
content
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def make_paragraphs(text)
|
112
|
+
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" }
|
113
|
+
end
|
114
|
+
|
115
|
+
def make_breaks(text)
|
116
|
+
text.gsub(/([^\n])\n(?!\n)/m, "\\1<br />\n")
|
117
|
+
end
|
118
|
+
|
119
|
+
def make_inlines(text)
|
120
|
+
text = text.
|
121
|
+
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
|
122
|
+
gsub(/!\((.+?)\)(?:\:([a-zA-Z0-9\_]+))?/) do
|
123
|
+
if $2
|
124
|
+
"<img src=\"#{archive($1)}\" class=\"#{$2}\" />"
|
125
|
+
else
|
126
|
+
"<img src=\"#{archive($1)}\" />"
|
127
|
+
end
|
128
|
+
end.
|
129
|
+
gsub(/\[(.+?)\]\((.+?)\)(?:\:([a-zA-Z0-9\_]+))?/) do
|
130
|
+
if $3
|
131
|
+
"<a href=\"#{archive($2)}\" class=\"#{$3}\">#{archive($1)}</a>"
|
132
|
+
else
|
133
|
+
"<a href=\"#{archive($2)}\">#{archive($1)}</a>"
|
134
|
+
end
|
135
|
+
end.
|
136
|
+
gsub(/([a-z0-9\.\-\_\+]+)@((?:[a-z0-9\-]{2,}\.)*)([a-z0-9\-]+\.)(com|org|net|biz|edu|info|gov|co\.uk|co\.us)/i) do
|
137
|
+
"<a href=\"mailto:#{archive($1 + '@' + $2 + $3 + $4)}\">#{archive($1 + '@' + $2 + $3 + $4)}</a>"
|
138
|
+
end.
|
139
|
+
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
|
140
|
+
if $1.nil?
|
141
|
+
protocol = 'http://'
|
142
|
+
else
|
143
|
+
protocol = $1
|
144
|
+
end
|
145
|
+
"<a href=\"#{archive(protocol + $2 + $3 + $4 + $5)}\">#{archive(protocol + $2 + $3 + $4 + $5)}</a>"
|
146
|
+
end
|
147
|
+
|
148
|
+
inlines = [
|
149
|
+
['*', 'strong'],
|
150
|
+
['_', 'em'],
|
151
|
+
['`', 'code'],
|
152
|
+
['^', 'sup'],
|
153
|
+
['~', 'sub']
|
154
|
+
]
|
155
|
+
|
156
|
+
inlines.each do |char, tag|
|
157
|
+
char = Regexp.escape(char)
|
158
|
+
re = /#{char} # the opening character
|
159
|
+
(?!\s) # no spaces directly after the char
|
160
|
+
( # main capture group
|
161
|
+
(?:\s*\S)+? # because of no lookbehinds, every space must be followed by a non-space
|
162
|
+
) # with lookbehinds, the whole regex is: \*(?! )(.+?)(?<! )\*
|
163
|
+
#{char}/x # closing character, no spaces directly before it
|
164
|
+
#re = /#{char}(.+?)#{char}/
|
165
|
+
text.gsub!(re, "<#{tag}>\\1</#{tag}>")
|
166
|
+
end
|
167
|
+
|
168
|
+
text
|
169
|
+
end
|
170
|
+
end
|
metadata
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: typeout
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 5
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 1
|
8
|
+
- 4
|
9
|
+
- 1
|
10
|
+
version: 1.4.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Connor McKay
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-07-08 00:00:00 -07:00
|
19
|
+
default_executable:
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: sanitize
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
30
|
+
segments:
|
31
|
+
- 0
|
32
|
+
version: "0"
|
33
|
+
type: :runtime
|
34
|
+
version_requirements: *id001
|
35
|
+
description:
|
36
|
+
email:
|
37
|
+
- connor@verticalforest.com
|
38
|
+
executables: []
|
39
|
+
|
40
|
+
extensions: []
|
41
|
+
|
42
|
+
extra_rdoc_files: []
|
43
|
+
|
44
|
+
files:
|
45
|
+
- README.rdoc
|
46
|
+
- LICENSE
|
47
|
+
- lib/typeout.js
|
48
|
+
- lib/typeout.rb
|
49
|
+
has_rdoc: true
|
50
|
+
homepage: http://github.com/greneholt/typeout
|
51
|
+
licenses: []
|
52
|
+
|
53
|
+
post_install_message:
|
54
|
+
rdoc_options: []
|
55
|
+
|
56
|
+
require_paths:
|
57
|
+
- lib
|
58
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
59
|
+
none: false
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
hash: 3
|
64
|
+
segments:
|
65
|
+
- 0
|
66
|
+
version: "0"
|
67
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
68
|
+
none: false
|
69
|
+
requirements:
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
hash: 17
|
73
|
+
segments:
|
74
|
+
- 1
|
75
|
+
- 3
|
76
|
+
- 5
|
77
|
+
version: 1.3.5
|
78
|
+
requirements: []
|
79
|
+
|
80
|
+
rubyforge_project: typeout
|
81
|
+
rubygems_version: 1.3.7
|
82
|
+
signing_key:
|
83
|
+
specification_version: 3
|
84
|
+
summary: Dead simple plain text to HTML converter
|
85
|
+
test_files: []
|
86
|
+
|