typeout 1.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (5) hide show
  1. data/LICENSE +19 -0
  2. data/README.rdoc +133 -0
  3. data/lib/typeout.js +276 -0
  4. data/lib/typeout.rb +170 -0
  5. 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, '&lt;').
41
+ replace(/>/g, '&gt;').
42
+ replace(/"/g, '&quot;')
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
+