wieldymarkup 0.2.0
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/bin/wieldymarkup +93 -0
- data/lib/wieldymarkup.rb +625 -0
- metadata +51 -0
data/bin/wieldymarkup
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'wieldymarkup'
|
4
|
+
|
5
|
+
# Creates an HTML file by running the WieldyMarkup compiler on the contents
|
6
|
+
# of the provided WML file.
|
7
|
+
#
|
8
|
+
# @param [String] filepath The path to the WML file
|
9
|
+
# @param [Hash] options The options for compiling the input text
|
10
|
+
# @option options [String] :strict
|
11
|
+
# Whether or not to raise an error for invalid filepaths
|
12
|
+
# @option options [String] :compress
|
13
|
+
# Whether to leave whitespace between HTML tags or not
|
14
|
+
def compile_file_from_path(filepath, options={})
|
15
|
+
defaults = {:strict => true, :compress => false}
|
16
|
+
options = defaults.merge options
|
17
|
+
|
18
|
+
ext = filepath.split('/')[-1].split('.')[-1]
|
19
|
+
if ext != 'wml'
|
20
|
+
puts ext
|
21
|
+
if options[:strict]
|
22
|
+
raise Exception, "Invalid extension for (#{filepath}). Must be .wml."
|
23
|
+
else
|
24
|
+
return
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
file = File.open(filepath, 'rb')
|
29
|
+
data = file.read
|
30
|
+
|
31
|
+
html = Compiler.new(:text => data, :compress => options[:compress]).output
|
32
|
+
|
33
|
+
temp = filepath.split('/')
|
34
|
+
temp.pop()
|
35
|
+
filename = temp.join('/') << '/' << filepath.split('/')[-1].split('.')[0] << '.html'
|
36
|
+
File.open(filename, 'wb') {|f| f.write(html) }
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
# Compiles WML files into HTML files based on command line options.
|
41
|
+
#
|
42
|
+
# @param [Array] args The command line arguments
|
43
|
+
def compile_from_command_line(args)
|
44
|
+
compress = false
|
45
|
+
if args.index("-c") != nil or args.index("--compress") != nil
|
46
|
+
compress = true
|
47
|
+
args = args.keep_if { |arg| ["-c", "--compress"].index(arg) == nil }
|
48
|
+
end
|
49
|
+
|
50
|
+
if args.index("-d") != nil
|
51
|
+
d_index = args.index("-d")
|
52
|
+
if args.length < d_index + 1
|
53
|
+
raise Exception, "The -d argument must be followed immediately by a directory path in which to compiler .wml files."
|
54
|
+
end
|
55
|
+
|
56
|
+
dir_path = args[d_index + 1]
|
57
|
+
|
58
|
+
if !File.directory? dir_path
|
59
|
+
raise Exception, "Invalid directory path following -d argument."
|
60
|
+
end
|
61
|
+
|
62
|
+
Dir.chdir dir_path
|
63
|
+
if args["-r"] != nil
|
64
|
+
Dir['**/*'].each { |f|
|
65
|
+
if File.directory? f and ['.', '..'][f] == nil
|
66
|
+
puts f
|
67
|
+
compile_file_from_path(f, :strict => false, :compress => compress)
|
68
|
+
end
|
69
|
+
}
|
70
|
+
else
|
71
|
+
Dir['*'].each { |f|
|
72
|
+
if File.directory? f and ['.', '..'][f] == nil
|
73
|
+
puts f
|
74
|
+
compile_file_from_path(f, :strict => false, :compress => compress)
|
75
|
+
end
|
76
|
+
}
|
77
|
+
end
|
78
|
+
|
79
|
+
else
|
80
|
+
strict = true
|
81
|
+
if args.index("-f") != nil or args.index("--force") != nil
|
82
|
+
strict = false
|
83
|
+
args = args.keep_if { |arg| ["-f", "--force"].index(arg) == nil }
|
84
|
+
end
|
85
|
+
|
86
|
+
args.each { |filepath|
|
87
|
+
compile_file_from_path(filepath, :strict => strict, :compress => compress)
|
88
|
+
}
|
89
|
+
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
compile_from_command_line ARGV
|
data/lib/wieldymarkup.rb
ADDED
@@ -0,0 +1,625 @@
|
|
1
|
+
# Raises semantic errors in parsing markup.
|
2
|
+
class CompilerException < Exception; end
|
3
|
+
|
4
|
+
# Compiles WieldyMarkup HTML abstraction language into HTML, with each tag
|
5
|
+
# indented on its own line or without any whitespace between tags.
|
6
|
+
#
|
7
|
+
# @author Vail Gold
|
8
|
+
class Compiler
|
9
|
+
|
10
|
+
# Only available for unit tests.
|
11
|
+
attr_accessor :line_starts_with_tick,
|
12
|
+
:current_level, :tag, :indent_token, :inner_text, :open_tags,
|
13
|
+
:previous_level, :tag_id, :tag_classes, :tag_attributes, :self_closing
|
14
|
+
attr_reader :line_number
|
15
|
+
|
16
|
+
# @!attribute [rw] text
|
17
|
+
# @return [String] The text to parse and compile
|
18
|
+
attr_accessor :text
|
19
|
+
|
20
|
+
# @!attribute [rw] compress
|
21
|
+
# @return [Boolean] Whether or not the compiler should leave whitespace
|
22
|
+
# between output HTML tags
|
23
|
+
attr_accessor :compress
|
24
|
+
|
25
|
+
# @!attribute [rw] embedding_token
|
26
|
+
# @return [String] The character used to identify embedded HTML lines
|
27
|
+
attr_accessor :embedding_token
|
28
|
+
|
29
|
+
# @!attribute [r] output
|
30
|
+
# @return [String] The compiled HTML
|
31
|
+
attr_reader :output
|
32
|
+
|
33
|
+
class << self
|
34
|
+
# Removes all substrings surrounded by a grouping substring, including
|
35
|
+
# grouping substring on both sides.
|
36
|
+
#
|
37
|
+
# @param [String] text The string from which to remove grouped substrings
|
38
|
+
# @param [String] z The grouping substring
|
39
|
+
# @return [String] The string with substrings removed
|
40
|
+
def remove_grouped_text(text, z)
|
41
|
+
output = ""
|
42
|
+
text_copy = text * 1
|
43
|
+
status = true
|
44
|
+
while text_copy != '' do
|
45
|
+
grouper_index = text_copy.index z
|
46
|
+
if grouper_index == nil
|
47
|
+
if status
|
48
|
+
output << (text_copy * 1)
|
49
|
+
end
|
50
|
+
text_copy = ''
|
51
|
+
|
52
|
+
else
|
53
|
+
if status
|
54
|
+
output << text_copy[0..grouper_index-1]
|
55
|
+
end
|
56
|
+
|
57
|
+
if text_copy[grouper_index+1]
|
58
|
+
text_copy = text_copy[grouper_index+1..-1]
|
59
|
+
else
|
60
|
+
text_copy = ''
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
status = !status
|
65
|
+
end
|
66
|
+
|
67
|
+
output
|
68
|
+
end
|
69
|
+
|
70
|
+
# Gets the selector from the line of markup
|
71
|
+
#
|
72
|
+
# @param [String] line The string from which to get the selector
|
73
|
+
# @return [String] The substring from the beginning of the line until the
|
74
|
+
# first whitespace character
|
75
|
+
def get_selector_from_line(line)
|
76
|
+
first_whitespace_index = nil
|
77
|
+
line_copy = line.strip * 1
|
78
|
+
i = 0
|
79
|
+
while line_copy[i] != nil do
|
80
|
+
if " \t"[line_copy[i]] != nil
|
81
|
+
first_whitespace_index = i
|
82
|
+
break
|
83
|
+
end
|
84
|
+
i += 1
|
85
|
+
end
|
86
|
+
|
87
|
+
if first_whitespace_index == nil
|
88
|
+
return line
|
89
|
+
else
|
90
|
+
return line[0..first_whitespace_index-1]
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# Determines the level of nesting in a string
|
95
|
+
#
|
96
|
+
# @param [String] text The string in which to determine the nest level
|
97
|
+
# @param [Hash] delimiters The delimiters for determining nest level
|
98
|
+
# @option delimiters [String] :open_string
|
99
|
+
# The substring that denotes an increase in nesting level.
|
100
|
+
# @option delimiters [String] :close_string
|
101
|
+
# The substring that denotes a decrase in nesting level.
|
102
|
+
# @return [String] The substring from the beginning of the line until the
|
103
|
+
# first whitespace character
|
104
|
+
def get_tag_nest_level(text, delimiters={})
|
105
|
+
defaults = {:open_string => '<', :close_string => '>'}
|
106
|
+
delimiters = defaults.merge delimiters
|
107
|
+
open_string = delimiters[:open_string] != nil ? delimiters[:open_string] : '<'
|
108
|
+
close_string = delimiters[:close_string] != nil ? delimiters[:close_string] : '>'
|
109
|
+
|
110
|
+
text = text * 1
|
111
|
+
nest_level = 0
|
112
|
+
while true do
|
113
|
+
open_string_index = text.index(open_string)
|
114
|
+
close_string_index = text.index(close_string)
|
115
|
+
open_string_first = false
|
116
|
+
close_string_first = false
|
117
|
+
|
118
|
+
# Only same if both nil
|
119
|
+
if open_string_index == close_string_index
|
120
|
+
break
|
121
|
+
elsif open_string_index != nil
|
122
|
+
open_string_first = true
|
123
|
+
elsif close_string_index != nil
|
124
|
+
close_string_first = true
|
125
|
+
else
|
126
|
+
if open_string_index < close_string_index
|
127
|
+
open_string_first = true
|
128
|
+
else
|
129
|
+
close_string_first = true
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
if open_string_first
|
134
|
+
nest_level += 1
|
135
|
+
if text.length == open_string_index + open_string.length
|
136
|
+
break
|
137
|
+
else
|
138
|
+
text = text[open_string_index + open_string.length..-1]
|
139
|
+
end
|
140
|
+
elsif close_string_first
|
141
|
+
nest_level -= 1
|
142
|
+
if text.length == close_string_index + close_string.length
|
143
|
+
break
|
144
|
+
else
|
145
|
+
text = text[close_string_index + close_string.length..-1]
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
nest_level
|
151
|
+
end
|
152
|
+
|
153
|
+
# Gets the string of leading spaces and tabs in some text.
|
154
|
+
#
|
155
|
+
# @param [String] text The string from which to get the leading whitespace
|
156
|
+
# @return [String] The leading whitespace in the string
|
157
|
+
def get_leading_whitespace_from_text(text)
|
158
|
+
leading_whitespace = ""
|
159
|
+
text_copy = text * 1
|
160
|
+
i = 0
|
161
|
+
while text_copy[i] != nil do
|
162
|
+
if " \t"[text_copy[i]] == nil
|
163
|
+
if i > 0
|
164
|
+
leading_whitespace = text[0..i-1]
|
165
|
+
end
|
166
|
+
break
|
167
|
+
end
|
168
|
+
|
169
|
+
i += 1
|
170
|
+
end
|
171
|
+
|
172
|
+
leading_whitespace
|
173
|
+
end
|
174
|
+
|
175
|
+
end
|
176
|
+
|
177
|
+
# Instantiate a new Compiler instance. Automatically compiles text if passed
|
178
|
+
# in via parameters.
|
179
|
+
#
|
180
|
+
# @param [Hash] options The options for compiling the input text
|
181
|
+
# @option options [String] :text
|
182
|
+
# The input text to compile
|
183
|
+
# @option options [String] :compress
|
184
|
+
# Whether to leave whitespace between HTML tags or not
|
185
|
+
def initialize(options={})
|
186
|
+
defaults = {:text => "", :compress => false}
|
187
|
+
options = defaults.merge options
|
188
|
+
@text = options[:text] != nil ? options[:text] : ''
|
189
|
+
@compress = options[:compress] != nil ? !!options[:compress] : false
|
190
|
+
|
191
|
+
@output = ""
|
192
|
+
@open_tags = []
|
193
|
+
@indent_token = ""
|
194
|
+
@current_level = 0
|
195
|
+
@previous_level = nil
|
196
|
+
@line_number = 0
|
197
|
+
@embedding_token = '`'
|
198
|
+
|
199
|
+
if @text != ""
|
200
|
+
self.compile
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
# Compiles input markup into HTML.
|
205
|
+
#
|
206
|
+
# @param [Hash] options The options for compiling the input text
|
207
|
+
# @option options [String] :text
|
208
|
+
# The input text to compile
|
209
|
+
# @option options [String] :compress
|
210
|
+
# Whether to leave whitespace between HTML tags or not
|
211
|
+
# @return [String] The compiled HTML
|
212
|
+
def compile(options={})
|
213
|
+
defaults = {:text => nil, :compress => nil}
|
214
|
+
options = defaults.merge options
|
215
|
+
@text = options[:text] if options[:text] != nil
|
216
|
+
@compress = !!options[:compress] if options[:compress] != nil
|
217
|
+
|
218
|
+
while @text != "" do
|
219
|
+
self.process_current_level
|
220
|
+
self.close_lower_level_tags
|
221
|
+
self.process_next_line
|
222
|
+
end
|
223
|
+
|
224
|
+
self.close_tag while @open_tags.length > 0
|
225
|
+
|
226
|
+
@output
|
227
|
+
end
|
228
|
+
|
229
|
+
# Determines current nesting level for HTML output.
|
230
|
+
#
|
231
|
+
# @return [Object] The reference to this instance object.
|
232
|
+
def process_current_level
|
233
|
+
@previous_level = @current_level * 1
|
234
|
+
leading_whitespace = self.class.get_leading_whitespace_from_text @text
|
235
|
+
if leading_whitespace == ""
|
236
|
+
@current_level = 0
|
237
|
+
|
238
|
+
# If there is leading whitespace but indent_token is still empty string
|
239
|
+
elsif @indent_token == ""
|
240
|
+
@indent_token = leading_whitespace
|
241
|
+
@current_level = 1
|
242
|
+
|
243
|
+
# Else, set current_level to number of repetitions of index_token in leading_whitespace
|
244
|
+
else
|
245
|
+
i = 0
|
246
|
+
while leading_whitespace.index(@indent_token) == 0 do
|
247
|
+
leading_whitespace = leading_whitespace[@indent_token.length..-1]
|
248
|
+
i += 1
|
249
|
+
end
|
250
|
+
@current_level = i
|
251
|
+
end
|
252
|
+
|
253
|
+
self
|
254
|
+
end
|
255
|
+
|
256
|
+
# Iterates through nesting levels that have been closed.
|
257
|
+
#
|
258
|
+
# @return [Object] The reference to this instance object.
|
259
|
+
def close_lower_level_tags
|
260
|
+
# If indentation level is less than or equal to previous level
|
261
|
+
if @current_level <= @previous_level
|
262
|
+
# Close all indentations greater than or equal to indentation level of this line
|
263
|
+
while @open_tags.length > 0 and @open_tags[@open_tags.length - 1][0] >= @current_level do
|
264
|
+
self.close_tag
|
265
|
+
end
|
266
|
+
end
|
267
|
+
self
|
268
|
+
end
|
269
|
+
|
270
|
+
# Adds closing HTML tags to output and removes entry from @open_tags.
|
271
|
+
#
|
272
|
+
# @return [Object] The reference to this instance object.
|
273
|
+
def close_tag
|
274
|
+
closing_tag_array = @open_tags.pop
|
275
|
+
if !@compress
|
276
|
+
@output << (@indent_token * closing_tag_array[0])
|
277
|
+
end
|
278
|
+
@output << "</" << closing_tag_array[1] << ">"
|
279
|
+
if !@compress
|
280
|
+
@output << "\n"
|
281
|
+
end
|
282
|
+
self
|
283
|
+
end
|
284
|
+
|
285
|
+
# Gets the next line of text, splits it into relevant pieces, and sends them
|
286
|
+
# to respective methods for parsing.
|
287
|
+
#
|
288
|
+
# @return [Object] The reference to this instance object.
|
289
|
+
def process_next_line
|
290
|
+
@line_starts_with_tick = false
|
291
|
+
@self_closing = false
|
292
|
+
@inner_text = nil
|
293
|
+
|
294
|
+
line = ""
|
295
|
+
|
296
|
+
if @text["\n"] != nil
|
297
|
+
line_break_index = @text.index "\n"
|
298
|
+
line = @text[0..line_break_index].strip
|
299
|
+
@text = @text[line_break_index+1..-1]
|
300
|
+
else
|
301
|
+
line = @text.strip()
|
302
|
+
@text = ""
|
303
|
+
end
|
304
|
+
|
305
|
+
@line_number += 1
|
306
|
+
if line.length == 0
|
307
|
+
return self
|
308
|
+
end
|
309
|
+
|
310
|
+
# Whole line embedded HTML, starting with back ticks:
|
311
|
+
if line[0] == @embedding_token
|
312
|
+
self.process_embedded_line line
|
313
|
+
|
314
|
+
else
|
315
|
+
# Support multiple tags on one line via "\-\" delimiter
|
316
|
+
while true do
|
317
|
+
line_split_list = line.split '\\-\\'
|
318
|
+
lines = [line_split_list[0]]
|
319
|
+
|
320
|
+
if line_split_list.length == 1
|
321
|
+
line = line_split_list[0].strip
|
322
|
+
break
|
323
|
+
else
|
324
|
+
lines << line_split_list[1..-1].join('\\-\\')
|
325
|
+
end
|
326
|
+
|
327
|
+
lines[0] = lines[0].strip
|
328
|
+
selector = self.class.get_selector_from_line lines[0]
|
329
|
+
self.process_selector((selector*1))
|
330
|
+
rest_of_line = lines[0][selector.length..-1].strip
|
331
|
+
rest_of_line = self.process_attributes rest_of_line
|
332
|
+
self.add_html_to_output
|
333
|
+
|
334
|
+
@tag = nil
|
335
|
+
@tag_id = nil
|
336
|
+
@tag_classes = []
|
337
|
+
@tag_attributes = []
|
338
|
+
@previous_level = @current_level * 1
|
339
|
+
@current_level += 1
|
340
|
+
line = lines[1..-1].join '\\-\\'
|
341
|
+
end
|
342
|
+
|
343
|
+
selector = self.class.get_selector_from_line line
|
344
|
+
self.process_selector((selector*1))
|
345
|
+
rest_of_line = line[selector.length..-1].strip
|
346
|
+
rest_of_line = self.process_attributes rest_of_line
|
347
|
+
|
348
|
+
if rest_of_line.index('<') == 0
|
349
|
+
@inner_text = rest_of_line
|
350
|
+
if self.class.get_tag_nest_level(@inner_text) < 0
|
351
|
+
raise CompilerException, "Too many '>' found on line #{@line_number}"
|
352
|
+
end
|
353
|
+
|
354
|
+
while self.class.get_tag_nest_level(@inner_text) > 0 do
|
355
|
+
if @text == ""
|
356
|
+
raise CompilerException, "Unmatched '<' found on line #{@line_number}"
|
357
|
+
|
358
|
+
elsif @text["\n"] != nil
|
359
|
+
line_break_index = @text.index "\n"
|
360
|
+
# Guarantee only one space between text between lines.
|
361
|
+
@inner_text << ' ' + @text[0..line_break_index].strip
|
362
|
+
if @text.length == line_break_index + 1
|
363
|
+
@text = ""
|
364
|
+
else
|
365
|
+
@text = @text[line_break_index+1..-1]
|
366
|
+
end
|
367
|
+
|
368
|
+
else
|
369
|
+
@inner_text << @text
|
370
|
+
@text = ""
|
371
|
+
end
|
372
|
+
end
|
373
|
+
|
374
|
+
@inner_text = @inner_text.strip()[1..-2]
|
375
|
+
|
376
|
+
elsif rest_of_line.index('/') == 0
|
377
|
+
if rest_of_line.length > 0 and rest_of_line[-1] == '/'
|
378
|
+
@self_closing = true
|
379
|
+
end
|
380
|
+
end
|
381
|
+
|
382
|
+
self.add_html_to_output
|
383
|
+
end
|
384
|
+
|
385
|
+
self
|
386
|
+
end
|
387
|
+
|
388
|
+
# Adds an embedded line to output, removing @embedding_token
|
389
|
+
# and not compiling.
|
390
|
+
#
|
391
|
+
# @param [String] line
|
392
|
+
# The line of text with @embedding_token
|
393
|
+
# @return [Object] The reference to this instance object.
|
394
|
+
def process_embedded_line(line)
|
395
|
+
@line_starts_with_tick = true
|
396
|
+
if !@compress
|
397
|
+
@output << (@indent_token * @current_level)
|
398
|
+
end
|
399
|
+
@output << line[1..-1]
|
400
|
+
if !@compress
|
401
|
+
@output << "\n"
|
402
|
+
end
|
403
|
+
self
|
404
|
+
end
|
405
|
+
|
406
|
+
# Parses a selector into tag, ID, and classes.
|
407
|
+
#
|
408
|
+
# @param [String] selector
|
409
|
+
# The unparsed selector string
|
410
|
+
# @return [Object] The reference to this instance object.
|
411
|
+
def process_selector(selector)
|
412
|
+
# Parse the first piece as a selector, defaulting to DIV tag if none is specified
|
413
|
+
if selector.length > 0 and ['#', '.'].count(selector[0]) > 0
|
414
|
+
@tag = 'div'
|
415
|
+
else
|
416
|
+
delimiter_index = nil
|
417
|
+
i = 0
|
418
|
+
for char in selector.split("") do
|
419
|
+
if ['#', '.'].count(char) > 0
|
420
|
+
delimiter_index = i
|
421
|
+
break
|
422
|
+
end
|
423
|
+
i += 1
|
424
|
+
end
|
425
|
+
|
426
|
+
if delimiter_index == nil
|
427
|
+
@tag = selector * 1
|
428
|
+
selector = ""
|
429
|
+
else
|
430
|
+
@tag = selector[0..delimiter_index-1]
|
431
|
+
selector = selector[@tag.length..-1]
|
432
|
+
end
|
433
|
+
end
|
434
|
+
|
435
|
+
@tag_id = nil
|
436
|
+
@tag_classes = []
|
437
|
+
while true do
|
438
|
+
next_delimiter_index = nil
|
439
|
+
if selector == ""
|
440
|
+
break
|
441
|
+
|
442
|
+
else
|
443
|
+
i = 0
|
444
|
+
for char in selector.split("") do
|
445
|
+
if i > 0 and ['#', '.'].count(char) > 0
|
446
|
+
next_delimiter_index = i
|
447
|
+
break
|
448
|
+
end
|
449
|
+
i += 1
|
450
|
+
end
|
451
|
+
|
452
|
+
if next_delimiter_index == nil
|
453
|
+
if selector[0] == '#'
|
454
|
+
@tag_id = selector[1..-1]
|
455
|
+
elsif selector[0] == "."
|
456
|
+
@tag_classes << selector[1..-1]
|
457
|
+
end
|
458
|
+
|
459
|
+
selector = ""
|
460
|
+
|
461
|
+
else
|
462
|
+
if selector[0] == '#'
|
463
|
+
@tag_id = selector[1..next_delimiter_index-1]
|
464
|
+
elsif selector[0] == "."
|
465
|
+
@tag_classes << selector[1..next_delimiter_index-1]
|
466
|
+
end
|
467
|
+
|
468
|
+
selector = selector[next_delimiter_index..-1]
|
469
|
+
end
|
470
|
+
end
|
471
|
+
end
|
472
|
+
|
473
|
+
self
|
474
|
+
end
|
475
|
+
|
476
|
+
# Parses attribute string off of the beginning of a line of text after
|
477
|
+
# the selector was removed, and returns everything after the attribute
|
478
|
+
# string.
|
479
|
+
#
|
480
|
+
# @param [String] rest_of_line
|
481
|
+
# The line of text after leading whitespace and selector have been removed
|
482
|
+
# @return [String] The input text after all attributes have been removed
|
483
|
+
def process_attributes(rest_of_line)
|
484
|
+
@tag_attributes = []
|
485
|
+
while rest_of_line != "" do
|
486
|
+
# If '=' doesn't exist, empty attribute string and break from loop
|
487
|
+
if rest_of_line.index('=') == nil
|
488
|
+
break
|
489
|
+
elsif rest_of_line.index('=') != nil and rest_of_line.index('<') != nil and rest_of_line.index('<') < rest_of_line.index('=')
|
490
|
+
break
|
491
|
+
end
|
492
|
+
|
493
|
+
first_equals_index = rest_of_line.index '='
|
494
|
+
embedded_attribute = false
|
495
|
+
|
496
|
+
if rest_of_line[first_equals_index+1..first_equals_index+2] == '{{'
|
497
|
+
embedded_attribute = true
|
498
|
+
close_index = rest_of_line.index '}}'
|
499
|
+
if close_index == nil
|
500
|
+
raise CompilerException, "Unmatched '{{' found in line #{@line_number}"
|
501
|
+
end
|
502
|
+
elsif rest_of_line[first_equals_index+1..first_equals_index+2] == '<%'
|
503
|
+
embedded_attribute = true
|
504
|
+
close_index = rest_of_line.index '%>'
|
505
|
+
if close_index == nil
|
506
|
+
raise CompilerException, "Unmatched '<%' found in line #{@line_number}"
|
507
|
+
end
|
508
|
+
end
|
509
|
+
|
510
|
+
if embedded_attribute
|
511
|
+
current_attribute = rest_of_line[0..close_index+1]
|
512
|
+
if rest_of_line.length == close_index + 2
|
513
|
+
rest_of_line = ""
|
514
|
+
else
|
515
|
+
rest_of_line = rest_of_line[close_index+2..-1]
|
516
|
+
end
|
517
|
+
|
518
|
+
elsif rest_of_line.length == first_equals_index
|
519
|
+
current_attribute = rest_of_line.strip
|
520
|
+
rest_of_line = ""
|
521
|
+
|
522
|
+
elsif rest_of_line[first_equals_index + 1..-1].index('=') == nil
|
523
|
+
open_inner_text_index = rest_of_line.index('<')
|
524
|
+
if open_inner_text_index != nil
|
525
|
+
current_attribute = rest_of_line[0..open_inner_text_index-1].strip
|
526
|
+
rest_of_line = rest_of_line[open_inner_text_index..-1]
|
527
|
+
else
|
528
|
+
current_attribute = rest_of_line * 1
|
529
|
+
rest_of_line = ""
|
530
|
+
end
|
531
|
+
|
532
|
+
else
|
533
|
+
second_equals_index = rest_of_line[first_equals_index + 1..-1].index '='
|
534
|
+
reversed_letters_between_equals = rest_of_line[first_equals_index+1..first_equals_index + 1 + second_equals_index - 1].split("").reverse
|
535
|
+
|
536
|
+
whitespace_index = nil
|
537
|
+
i = 0
|
538
|
+
for char in reversed_letters_between_equals do
|
539
|
+
if " \t".index(char) != nil
|
540
|
+
whitespace_index = first_equals_index + 1 + second_equals_index - i
|
541
|
+
break
|
542
|
+
end
|
543
|
+
i += 1
|
544
|
+
end
|
545
|
+
|
546
|
+
if whitespace_index == nil
|
547
|
+
# TODO: Do some error reporting here
|
548
|
+
break
|
549
|
+
end
|
550
|
+
|
551
|
+
current_attribute = rest_of_line[0..whitespace_index-1].strip
|
552
|
+
rest_of_line = rest_of_line[whitespace_index..-1]
|
553
|
+
end
|
554
|
+
|
555
|
+
if current_attribute != nil
|
556
|
+
equals_index = current_attribute.index '='
|
557
|
+
@tag_attributes << ' ' + current_attribute[0..equals_index-1] + '="' + current_attribute[equals_index+1..-1] + '"'
|
558
|
+
end
|
559
|
+
end
|
560
|
+
|
561
|
+
rest_of_line.strip
|
562
|
+
end
|
563
|
+
|
564
|
+
# Adds HTML to output for a given line.
|
565
|
+
#
|
566
|
+
# @return [Object] The reference to this instance object.
|
567
|
+
def add_html_to_output
|
568
|
+
if !@line_starts_with_tick
|
569
|
+
tag_html = "<" << @tag
|
570
|
+
|
571
|
+
if @tag_id != nil
|
572
|
+
tag_html << ' id="' << @tag_id << '"'
|
573
|
+
end
|
574
|
+
|
575
|
+
if @tag_classes.length > 0
|
576
|
+
tag_html << ' class="' << @tag_classes.join(' ') << '"'
|
577
|
+
end
|
578
|
+
|
579
|
+
if @tag_attributes.length > 0
|
580
|
+
tag_html << @tag_attributes.join('')
|
581
|
+
end
|
582
|
+
|
583
|
+
if @self_closing
|
584
|
+
tag_html << " />"
|
585
|
+
if !@compress
|
586
|
+
@output << (@indent_token * @current_level)
|
587
|
+
end
|
588
|
+
@output << tag_html
|
589
|
+
if !@compress
|
590
|
+
@output << "\n"
|
591
|
+
end
|
592
|
+
|
593
|
+
else
|
594
|
+
tag_html << ">"
|
595
|
+
|
596
|
+
if @inner_text != nil
|
597
|
+
tag_html << @inner_text
|
598
|
+
end
|
599
|
+
|
600
|
+
if !@compress
|
601
|
+
@output << (@indent_token * @current_level)
|
602
|
+
end
|
603
|
+
|
604
|
+
@output << tag_html
|
605
|
+
|
606
|
+
if @inner_text == nil
|
607
|
+
if !@compress
|
608
|
+
@output << "\n"
|
609
|
+
end
|
610
|
+
# Add tag data to open_tags list
|
611
|
+
@open_tags << [@current_level, @tag]
|
612
|
+
|
613
|
+
else
|
614
|
+
@output <<"</" << @tag << ">"
|
615
|
+
if !@compress
|
616
|
+
@output << "\n"
|
617
|
+
end
|
618
|
+
end
|
619
|
+
end
|
620
|
+
end
|
621
|
+
|
622
|
+
self
|
623
|
+
end
|
624
|
+
|
625
|
+
end
|
metadata
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: wieldymarkup
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Vail Gold
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-01-11 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: ! " The WieldyMarkup compiler allows you to write more concise HTML
|
15
|
+
templates\n for your modern web applications. It works with other templating
|
16
|
+
engines\n as well, like Underscore, Mustache, et cetera.\n See http://www.github.com/vail130/wieldymarkup-ruby
|
17
|
+
for more information.\n"
|
18
|
+
email: vail@vailgold.com
|
19
|
+
executables:
|
20
|
+
- wieldymarkup
|
21
|
+
extensions: []
|
22
|
+
extra_rdoc_files: []
|
23
|
+
files:
|
24
|
+
- lib/wieldymarkup.rb
|
25
|
+
- bin/wieldymarkup
|
26
|
+
homepage: http://www.github.com/vail130/wieldymarkup-ruby
|
27
|
+
licenses: []
|
28
|
+
post_install_message:
|
29
|
+
rdoc_options: []
|
30
|
+
require_paths:
|
31
|
+
- lib
|
32
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ! '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
requirements: []
|
45
|
+
rubyforge_project:
|
46
|
+
rubygems_version: 1.8.23
|
47
|
+
signing_key:
|
48
|
+
specification_version: 3
|
49
|
+
summary: WieldyMarkup HTML Abstraction Markup Language Compiler
|
50
|
+
test_files: []
|
51
|
+
has_rdoc:
|