markaby 0.8.1 → 0.9.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.
- checksums.yaml +5 -5
- data/.gitignore +2 -1
- data/.ruby-version +1 -1
- data/.travis.yml +12 -0
- data/CHANGELOG.rdoc +19 -0
- data/Gemfile +11 -5
- data/Gemfile.lock +68 -11
- data/LICENSE +19 -0
- data/Markaby.gemspec +12 -16
- data/README.rdoc +143 -25
- data/Rakefile +30 -24
- data/lib/markaby/builder.rb +58 -100
- data/lib/markaby/builder_tags.rb +8 -10
- data/lib/markaby/cssproxy.rb +5 -5
- data/lib/markaby/html5.rb +118 -0
- data/lib/markaby/kernel_method.rb +4 -3
- data/lib/markaby/rails.rb +36 -0
- data/lib/markaby/tagset.rb +89 -0
- data/lib/markaby/version.rb +5 -1
- data/lib/markaby/xhtml_frameset.rb +13 -0
- data/lib/markaby/xhtml_strict.rb +89 -0
- data/lib/markaby/xhtml_transitional.rb +61 -0
- data/lib/markaby/xml_tagset.rb +19 -0
- data/lib/markaby.rb +5 -4
- data/spec/markaby/builder_spec.rb +1 -1
- data/spec/markaby/css_proxy_spec.rb +13 -12
- data/spec/markaby/fragment_spec.rb +1 -1
- data/spec/markaby/html5_spec.rb +74 -25
- data/spec/markaby/markaby_spec.rb +71 -91
- data/spec/markaby/markaby_test_unit_spec.rb +98 -127
- data/spec/markaby/rails_spec.rb +29 -0
- data/spec/spec_helper.rb +19 -20
- metadata +21 -20
- data/lib/markaby/tags.rb +0 -288
data/lib/markaby/builder.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
require
|
2
|
-
require
|
1
|
+
require "markaby/tagset"
|
2
|
+
require "markaby/builder_tags"
|
3
3
|
|
4
4
|
module Markaby
|
5
5
|
RUBY_VERSION_ID = RUBY_VERSION.split(".").join.to_i
|
@@ -23,30 +23,13 @@ module Markaby
|
|
23
23
|
#
|
24
24
|
class Builder
|
25
25
|
include Markaby::BuilderTags
|
26
|
-
|
27
|
-
|
28
|
-
:
|
29
|
-
:output_helpers => true,
|
30
|
-
:output_xml_instruction => true,
|
31
|
-
:output_meta_tag => 'xhtml',
|
32
|
-
:auto_validation => true,
|
33
|
-
:tagset => Markaby::XHTMLTransitional,
|
34
|
-
:root_attributes => {
|
35
|
-
:xmlns => 'http://www.w3.org/1999/xhtml',
|
36
|
-
:'xml:lang' => 'en',
|
37
|
-
:lang => 'en'
|
38
|
-
}
|
26
|
+
GENERIC_OPTIONS = {
|
27
|
+
indent: 0,
|
28
|
+
auto_validation: true
|
39
29
|
}
|
40
30
|
|
41
|
-
HTML5_OPTIONS =
|
42
|
-
|
43
|
-
:output_helpers => true,
|
44
|
-
:output_xml_instruction => false,
|
45
|
-
:output_meta_tag => 'html5',
|
46
|
-
:auto_validation => true,
|
47
|
-
:tagset => Markaby::HTML5,
|
48
|
-
:root_attributes => {}
|
49
|
-
}
|
31
|
+
HTML5_OPTIONS = HTML5.default_options.dup
|
32
|
+
DEFAULT_OPTIONS = GENERIC_OPTIONS.merge(HTML5_OPTIONS)
|
50
33
|
|
51
34
|
@@options = DEFAULT_OPTIONS.dup
|
52
35
|
|
@@ -54,10 +37,6 @@ module Markaby
|
|
54
37
|
@@options = DEFAULT_OPTIONS.dup
|
55
38
|
end
|
56
39
|
|
57
|
-
def self.set_html5_options!
|
58
|
-
@@options = HTML5_OPTIONS.dup
|
59
|
-
end
|
60
|
-
|
61
40
|
def self.set(option, value)
|
62
41
|
@@options[option] = value
|
63
42
|
end
|
@@ -66,15 +45,15 @@ module Markaby
|
|
66
45
|
@@options[option]
|
67
46
|
end
|
68
47
|
|
69
|
-
|
70
|
-
@@ignored_helpers ||= []
|
71
|
-
end
|
48
|
+
attr_reader :tagset
|
72
49
|
|
73
|
-
def
|
74
|
-
|
75
|
-
end
|
50
|
+
def tagset=(tagset)
|
51
|
+
@tagset = tagset
|
76
52
|
|
77
|
-
|
53
|
+
tagset.default_options.each do |k, v|
|
54
|
+
instance_variable_set("@#{k}".to_sym, v)
|
55
|
+
end
|
56
|
+
end
|
78
57
|
|
79
58
|
# Create a Markaby builder object. Pass in a hash of variable assignments to
|
80
59
|
# +assigns+ which will be available as instance variables inside tag construction
|
@@ -105,13 +84,11 @@ module Markaby
|
|
105
84
|
instance_variable_set("@#{k}", v)
|
106
85
|
end
|
107
86
|
|
108
|
-
|
109
|
-
helper.
|
110
|
-
instance_variable_set(iv, helper.instance_variable_get(iv))
|
111
|
-
end
|
87
|
+
helper&.instance_variables&.each do |iv|
|
88
|
+
instance_variable_set(iv, helper.instance_variable_get(iv))
|
112
89
|
end
|
113
90
|
|
114
|
-
@builder = XmlMarkup.new(:
|
91
|
+
@builder = XmlMarkup.new(indent: @indent, target: @streams.last)
|
115
92
|
|
116
93
|
text(capture(&block)) if block
|
117
94
|
end
|
@@ -170,50 +147,24 @@ module Markaby
|
|
170
147
|
# Create a tag named +tag+. Other than the first argument which is the tag name,
|
171
148
|
# the arguments are the same as the tags implemented via method_missing.
|
172
149
|
def tag!(tag, *args, &block)
|
173
|
-
|
150
|
+
attributes = {}
|
174
151
|
if @auto_validation && @tagset
|
175
|
-
|
176
|
-
|
177
|
-
elsif args.last.respond_to?(:to_hash)
|
178
|
-
attrs = args.last.to_hash
|
179
|
-
|
180
|
-
if @tagset.forms.include?(tag) && attrs[:id]
|
181
|
-
attrs[:name] ||= attrs[:id]
|
182
|
-
end
|
183
|
-
|
184
|
-
attrs.each do |k, v|
|
185
|
-
atname = k.to_s.downcase.intern
|
186
|
-
unless k =~ /:/ or @tagset.tagset[tag].include? atname
|
187
|
-
raise InvalidXhtmlError, "no attribute `#{k}' on #{tag} elements"
|
188
|
-
end
|
189
|
-
if atname == :id
|
190
|
-
ele_id = v.to_s
|
191
|
-
if @used_ids.has_key? ele_id
|
192
|
-
raise InvalidXhtmlError, "id `#{ele_id}' already used (id's must be unique)."
|
193
|
-
end
|
194
|
-
end
|
195
|
-
if AttrsBoolean.include? atname
|
196
|
-
if v
|
197
|
-
attrs[k] = atname.to_s
|
198
|
-
else
|
199
|
-
attrs.delete k
|
200
|
-
end
|
201
|
-
end
|
202
|
-
end
|
203
|
-
end
|
152
|
+
attributes = @tagset.validate_and_transform_attributes!(tag, *args)
|
153
|
+
tag = @tagset.validate_and_transform_tag_name! tag
|
204
154
|
end
|
205
|
-
|
155
|
+
element_id = attributes[:id].to_s
|
156
|
+
raise InvalidXhtmlError, "id `#{element_id}' already used (id's must be unique)." if @used_ids.has_key?(element_id)
|
206
157
|
if block
|
207
158
|
str = capture(&block)
|
208
159
|
block = proc { text(str) }
|
209
160
|
end
|
210
161
|
|
211
162
|
f = fragment { @builder.tag!(tag, *args, &block) }
|
212
|
-
@used_ids[
|
163
|
+
@used_ids[element_id] = f unless element_id.empty?
|
213
164
|
f
|
214
165
|
end
|
215
166
|
|
216
|
-
|
167
|
+
private
|
217
168
|
|
218
169
|
# This method is used to intercept calls to helper methods and instance
|
219
170
|
# variables. Here is the order of interception:
|
@@ -224,37 +175,40 @@ module Markaby
|
|
224
175
|
# * If +sym+ is also the name of an instance variable, the
|
225
176
|
# value of the instance variable is returned.
|
226
177
|
# * If +sym+ has come this far and no +tagset+ is found, +sym+ and its arguments are passed to tag!
|
227
|
-
# * If a tagset is found,
|
178
|
+
# * If a tagset is found, the tagset is tole to handle +sym+
|
228
179
|
#
|
229
180
|
# method_missing used to be the lynchpin in Markaby, but it's no longer used to handle
|
230
181
|
# HTML tags. See html_tag for that.
|
231
182
|
def method_missing(sym, *args, &block)
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
# Rails' ActionView assigns hash has string keys for
|
243
|
-
# instance variables that are defined in the controller.
|
244
|
-
@assigns[stringy_key]
|
245
|
-
elsif instance_variables_for(self).include?(ivar = "@#{sym}".to_sym)
|
246
|
-
instance_variable_get(ivar)
|
247
|
-
elsif @_helper && instance_variables_for(@_helper).include?(ivar)
|
248
|
-
@_helper.instance_variable_get(ivar)
|
249
|
-
elsif instance_methods_for(::Builder::XmlMarkup).include?(sym)
|
250
|
-
@builder.__send__(sym, *args, &block)
|
251
|
-
elsif !@tagset
|
252
|
-
tag!(sym, *args, &block)
|
253
|
-
else
|
254
|
-
super
|
183
|
+
case response_for(sym)
|
184
|
+
when :helper then @_helper.send(sym, *args, &block)
|
185
|
+
when :assigns then @assigns[sym]
|
186
|
+
when :stringy_assigns then @assigns[sym.to_s]
|
187
|
+
when :ivar then instance_variable_get(ivar)
|
188
|
+
when :helper_ivar then @_helper.instance_variable_get(ivar)
|
189
|
+
when :xml_markup then @builder.__send__(sym, *args, &block)
|
190
|
+
when :tag then tag!(sym, *args, &block)
|
191
|
+
when :tagset then @tagset.handle_tag sym, self, *args, &block
|
192
|
+
else super
|
255
193
|
end
|
256
194
|
end
|
257
195
|
|
196
|
+
def response_for sym
|
197
|
+
return :helper if @_helper.respond_to?(sym, true)
|
198
|
+
return :assigns if @assigns.has_key?(sym)
|
199
|
+
return :stringy_assigns if @assigns.has_key?(sym.to_s)
|
200
|
+
return :ivar if instance_variables_for(self).include?(ivar = "@#{sym}".to_sym)
|
201
|
+
return :helper_ivar if @_helper && instance_variables_for(@_helper).include?(ivar)
|
202
|
+
return :xml_markup if instance_methods_for(::Builder::XmlMarkup).include?(sym)
|
203
|
+
return :tag if @tagset.nil?
|
204
|
+
return :tagset if @tagset.can_handle? sym
|
205
|
+
nil
|
206
|
+
end
|
207
|
+
|
208
|
+
def respond_to_missing? sym, include_private = false
|
209
|
+
!response_for(sym).nil?
|
210
|
+
end
|
211
|
+
|
258
212
|
if RUBY_VERSION_ID >= 191
|
259
213
|
def instance_variables_for(obj)
|
260
214
|
obj.instance_variables
|
@@ -301,11 +255,15 @@ module Markaby
|
|
301
255
|
undef_method method if method_defined?(method)
|
302
256
|
end
|
303
257
|
|
304
|
-
|
258
|
+
private
|
305
259
|
|
306
|
-
def method_missing(
|
260
|
+
def method_missing(...)
|
307
261
|
transform_stream unless transformed_stream?
|
308
|
-
@str.__send__(
|
262
|
+
@str.__send__(...)
|
263
|
+
end
|
264
|
+
|
265
|
+
def respond_to_missing? sym, *args
|
266
|
+
@str.respond_to? sym
|
309
267
|
end
|
310
268
|
|
311
269
|
def transform_stream
|
data/lib/markaby/builder_tags.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
|
+
require "markaby/html5"
|
2
|
+
|
1
3
|
module Markaby
|
2
4
|
module BuilderTags
|
3
5
|
(HTML5.tags - [:head]).each do |k|
|
4
|
-
class_eval <<-CODE, __FILE__, __LINE__
|
6
|
+
class_eval <<-CODE, __FILE__, __LINE__ + 1
|
5
7
|
def #{k}(*args, &block)
|
6
8
|
html_tag(#{k.inspect}, *args, &block)
|
7
9
|
end
|
@@ -28,8 +30,8 @@ module Markaby
|
|
28
30
|
# set to <tt>text/html; charset=utf-8</tt>.
|
29
31
|
def head(*args, &block)
|
30
32
|
tag!(:head, *args) do
|
31
|
-
tag!(:meta, "http-equiv" => "Content-Type", "content" => "text/html; charset=utf-8") if @output_meta_tag ==
|
32
|
-
tag!(:meta, "charset" => "utf-8") if @output_meta_tag ==
|
33
|
+
tag!(:meta, "http-equiv" => "Content-Type", "content" => "text/html; charset=utf-8") if @output_meta_tag == "xhtml"
|
34
|
+
tag!(:meta, "charset" => "utf-8") if @output_meta_tag == "html5"
|
33
35
|
instance_eval(&block)
|
34
36
|
end
|
35
37
|
end
|
@@ -56,19 +58,15 @@ module Markaby
|
|
56
58
|
|
57
59
|
# Builds an html tag with HTML5 doctype instead.
|
58
60
|
def html5(attrs = {}, &block)
|
59
|
-
|
61
|
+
self.tagset = Markaby::HTML5
|
60
62
|
html5_html(attrs, &block)
|
61
63
|
end
|
62
64
|
|
63
65
|
def enable_html5!
|
64
|
-
self.tagset = Markaby::HTML5
|
65
|
-
|
66
|
-
Markaby::Builder::HTML5_OPTIONS.each do |k, v|
|
67
|
-
self.instance_variable_set("@#{k}".to_sym, v)
|
68
|
-
end
|
66
|
+
raise NotImplementedError, "Deprecated! Call self.tagset = Markaby::HTML5"
|
69
67
|
end
|
70
68
|
|
71
|
-
|
69
|
+
private
|
72
70
|
|
73
71
|
def xhtml_html(attrs = {}, &block)
|
74
72
|
instruct! if @output_xml_instruction
|
data/lib/markaby/cssproxy.rb
CHANGED
@@ -6,9 +6,9 @@ module Markaby
|
|
6
6
|
class CssProxy
|
7
7
|
def initialize(builder, stream, sym)
|
8
8
|
@builder = builder
|
9
|
-
@stream
|
10
|
-
@sym
|
11
|
-
@attrs
|
9
|
+
@stream = stream
|
10
|
+
@sym = sym
|
11
|
+
@attrs = {}
|
12
12
|
|
13
13
|
@original_stream_length = @stream.length
|
14
14
|
|
@@ -16,10 +16,10 @@ module Markaby
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def respond_to?(sym, include_private = false)
|
19
|
-
include_private || !private_methods.map { |m| m.to_sym }.include?(sym.to_sym) ? true : false
|
19
|
+
(include_private || !private_methods.map { |m| m.to_sym }.include?(sym.to_sym)) ? true : false
|
20
20
|
end
|
21
21
|
|
22
|
-
|
22
|
+
private
|
23
23
|
|
24
24
|
# Adds attributes to an element. Bang methods set the :id attribute.
|
25
25
|
# Other methods add to the :class attribute.
|
@@ -0,0 +1,118 @@
|
|
1
|
+
require "markaby/xhtml_transitional"
|
2
|
+
module Markaby
|
3
|
+
class HTML5 < Tagset
|
4
|
+
class << self
|
5
|
+
def default_options
|
6
|
+
super.merge({
|
7
|
+
output_xml_instruction: false,
|
8
|
+
output_meta_tag: "html5",
|
9
|
+
root_attributes: {}
|
10
|
+
})
|
11
|
+
end
|
12
|
+
|
13
|
+
def custom_element? tag_name
|
14
|
+
tag_name.to_s.include? "_"
|
15
|
+
end
|
16
|
+
|
17
|
+
def can_handle? tag_name
|
18
|
+
custom_element? tag_name
|
19
|
+
end
|
20
|
+
|
21
|
+
def handle_tag tag_name, builder, *args, &block
|
22
|
+
builder.tag! tag_name, *args, &block
|
23
|
+
end
|
24
|
+
|
25
|
+
def validate_and_transform_tag_name! tag_name
|
26
|
+
custom_element?(tag_name) ? custom_element_tag_for(tag_name) : super
|
27
|
+
end
|
28
|
+
|
29
|
+
def custom_element_tag_for tag_name
|
30
|
+
tag_name.to_s.tr("_", "-").to_sym
|
31
|
+
end
|
32
|
+
|
33
|
+
def validate_attribute! tag_name, attribute_name
|
34
|
+
custom_element?(tag_name) || super
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
@doctype = ["html"]
|
39
|
+
@tagset = XHTMLTransitional.tagset.merge({
|
40
|
+
abbr: Attrs,
|
41
|
+
article: Attrs,
|
42
|
+
aside: Attrs,
|
43
|
+
audio: Attrs,
|
44
|
+
bdi: Attrs,
|
45
|
+
canvas: Attrs,
|
46
|
+
command: Attrs,
|
47
|
+
datalist: Attrs,
|
48
|
+
details: Attrs,
|
49
|
+
embed: Attrs,
|
50
|
+
figure: Attrs,
|
51
|
+
figcaption: Attrs,
|
52
|
+
footer: Attrs,
|
53
|
+
header: Attrs,
|
54
|
+
hgroup: Attrs,
|
55
|
+
keygen: Attrs,
|
56
|
+
mark: Attrs,
|
57
|
+
menu: Attrs,
|
58
|
+
meter: Attrs,
|
59
|
+
nav: Attrs,
|
60
|
+
output: Attrs,
|
61
|
+
progress: Attrs,
|
62
|
+
rp: Attrs,
|
63
|
+
rt: Attrs,
|
64
|
+
ruby: Attrs,
|
65
|
+
section: Attrs,
|
66
|
+
source: Attrs,
|
67
|
+
time: Attrs,
|
68
|
+
track: Attrs,
|
69
|
+
video: Attrs,
|
70
|
+
wbr: Attrs
|
71
|
+
})
|
72
|
+
|
73
|
+
# Additional attributes found in HTML5
|
74
|
+
additional_tags = {
|
75
|
+
a: [:media, :download, :ping],
|
76
|
+
area: [:media, :download, :ping, :hreflang, :rel, :type],
|
77
|
+
base: [:target],
|
78
|
+
button: [:autofocus, :form, :formaction, :formenctype, :formmethod,
|
79
|
+
:formnovalidate, :formtarget],
|
80
|
+
fieldset: [:form, :disabled, :name],
|
81
|
+
form: [:novalidate],
|
82
|
+
label: [:form],
|
83
|
+
html: [:manifest],
|
84
|
+
iframe: [:sandbox, :seamless, :srcdoc],
|
85
|
+
img: [:crossorigin],
|
86
|
+
input: [:autofocus, :placeholder, :form, :required, :autocomplete,
|
87
|
+
:min, :max, :multiple, :pattern, :step, :list, :width, :height,
|
88
|
+
:dirname, :formaction, :formenctype, :formmethod,
|
89
|
+
:formnovalidate, :formtarget],
|
90
|
+
link: [:sizes],
|
91
|
+
meta: [:charset],
|
92
|
+
menu: [:type, :label],
|
93
|
+
object: [:form, :typemustmatch],
|
94
|
+
ol: [:reversed],
|
95
|
+
output: [:form],
|
96
|
+
script: [:async],
|
97
|
+
select: [:autofocus, :form, :required],
|
98
|
+
style: [:scoped],
|
99
|
+
textarea: [:autofocus, :placeholder, :form, :required, :dirname,
|
100
|
+
:maxlength, :wrap]
|
101
|
+
}
|
102
|
+
|
103
|
+
AttrsHTML5 = [:contenteditable, :contextmentu, :draggable, :dropzone,
|
104
|
+
:hidden, :role, :spellcheck, :translate]
|
105
|
+
|
106
|
+
additional_tags.each do |k, v|
|
107
|
+
@tagset[k] += v
|
108
|
+
end
|
109
|
+
|
110
|
+
@tagset.each do |k, v|
|
111
|
+
@tagset[k] += AttrsHTML5
|
112
|
+
end
|
113
|
+
|
114
|
+
@tags = @tagset.keys
|
115
|
+
@forms = @tags & FORM_TAGS
|
116
|
+
@self_closing = @tags & SELF_CLOSING_TAGS
|
117
|
+
end
|
118
|
+
end
|
@@ -1,8 +1,9 @@
|
|
1
1
|
# You'll need to <tt>require 'markaby/kernel_method'</tt> for this.
|
2
|
+
require "markaby"
|
3
|
+
|
2
4
|
module Kernel
|
3
5
|
# Shortcut for creating a quick block of Markaby.
|
4
|
-
def mab(
|
5
|
-
Markaby::Builder.new(
|
6
|
+
def mab(...)
|
7
|
+
Markaby::Builder.new(...).to_s
|
6
8
|
end
|
7
9
|
end
|
8
|
-
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require "markaby"
|
2
|
+
|
3
|
+
module Markaby
|
4
|
+
module Rails
|
5
|
+
class TemplateHandler
|
6
|
+
class << self
|
7
|
+
def register!(options = {})
|
8
|
+
self.options = options
|
9
|
+
ActionView::Template.register_template_handler(:mab, new)
|
10
|
+
end
|
11
|
+
|
12
|
+
# TODO: Do we need this?
|
13
|
+
# Default format used by Markaby
|
14
|
+
# class_attribute :default_format
|
15
|
+
# self.default_format = :html
|
16
|
+
|
17
|
+
def options
|
18
|
+
@options ||= {}
|
19
|
+
end
|
20
|
+
|
21
|
+
def options=(val)
|
22
|
+
options.merge!(val)
|
23
|
+
options
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def call(template, source = template.source)
|
28
|
+
<<-CODE
|
29
|
+
Markaby::Builder.new(Markaby::Rails::TemplateHandler.options, self) do
|
30
|
+
#{source}
|
31
|
+
end.to_s
|
32
|
+
CODE
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
module Markaby
|
2
|
+
FORM_TAGS = [:form, :input, :select, :textarea]
|
3
|
+
SELF_CLOSING_TAGS = [:area, :base, :br, :col, :command, :embed, :frame, :hr,
|
4
|
+
:img, :input, :keygen, :link, :meta, :param, :source,
|
5
|
+
:track, :wbr]
|
6
|
+
|
7
|
+
# Common sets of attributes.
|
8
|
+
AttrCore = [:id, :class, :style, :title]
|
9
|
+
AttrI18n = [:lang, :"xml:lang", :dir]
|
10
|
+
AttrEvents = [:onclick,
|
11
|
+
:ondblclick,
|
12
|
+
:onmousedown,
|
13
|
+
:onmouseup,
|
14
|
+
:onmouseover,
|
15
|
+
:onmousemove,
|
16
|
+
:onmouseout,
|
17
|
+
:onkeypress,
|
18
|
+
:onkeydown,
|
19
|
+
:onkeyup]
|
20
|
+
AttrFocus = [:accesskey, :tabindex, :onfocus, :onblur]
|
21
|
+
AttrHAlign = [:align, :char, :charoff]
|
22
|
+
AttrVAlign = [:valign]
|
23
|
+
Attrs = AttrCore + AttrI18n + AttrEvents
|
24
|
+
|
25
|
+
AttrsBoolean = [
|
26
|
+
:checked, :disabled, :multiple, :readonly, :selected, # standard forms
|
27
|
+
:autofocus, :required, :novalidate, :formnovalidate, # HTML5 forms
|
28
|
+
:defer, :ismap, # <script defer>, <img ismap>
|
29
|
+
:compact, :declare, :noresize, :noshade, :nowrap # deprecated or unused
|
30
|
+
]
|
31
|
+
class Tagset
|
32
|
+
class << self
|
33
|
+
attr_accessor :tags, :tagset, :forms, :self_closing, :doctype
|
34
|
+
|
35
|
+
def default_options
|
36
|
+
{tagset: self}
|
37
|
+
end
|
38
|
+
|
39
|
+
def can_handle? tag_name
|
40
|
+
false
|
41
|
+
end
|
42
|
+
|
43
|
+
def handle_tag tag_name, builder, *args, &block
|
44
|
+
raise NoMethodError.new
|
45
|
+
end
|
46
|
+
|
47
|
+
def validate_and_transform_tag_name! tag_name
|
48
|
+
raise(InvalidXhtmlError, "no element `#{tag_name}' for #{doctype}") unless @tagset.has_key?(tag_name)
|
49
|
+
tag_name
|
50
|
+
end
|
51
|
+
|
52
|
+
def validate_and_transform_attributes! tag_name, *args
|
53
|
+
args.last.respond_to?(:to_hash) ? transform_attributes(tag_name, args.last.to_hash) : {}
|
54
|
+
end
|
55
|
+
|
56
|
+
def transform_attributes tag_name, attributes
|
57
|
+
attributes[:name] ||= attributes[:id] if forms.include?(tag_name) && attributes[:id]
|
58
|
+
attributes.transform_keys! { |name| transform_attribute_name name }
|
59
|
+
hashed_attributes = attributes.keys.select { |name| attributes[name].is_a? Hash }
|
60
|
+
hashed_attributes.each { |name| transform_attribute_hash attributes, name }
|
61
|
+
attributes.reject! { |name, value| name.nil? || (AttrsBoolean.include?(name) && value.nil?) }
|
62
|
+
attributes.keys.each { |name| validate_attribute! tag_name, name }
|
63
|
+
attributes
|
64
|
+
end
|
65
|
+
|
66
|
+
def transform_attribute_name name
|
67
|
+
name.to_s.downcase.tr("_", "-").to_sym
|
68
|
+
end
|
69
|
+
|
70
|
+
def transform_attribute_hash attributes, prefix
|
71
|
+
values = attributes[prefix]
|
72
|
+
expanded_attributes = {}
|
73
|
+
values.each do |suffix, value|
|
74
|
+
name = transform_attribute_name "#{prefix}-#{suffix}"
|
75
|
+
expanded_attributes[name] = value
|
76
|
+
end
|
77
|
+
attributes.merge!(expanded_attributes).delete(prefix)
|
78
|
+
end
|
79
|
+
|
80
|
+
def validate_attribute! tag_name, attribute_name
|
81
|
+
raise InvalidXhtmlError, "no attribute `#{attribute_name}' on #{tag_name} elements" unless valid_attribute_name? tag_name, attribute_name
|
82
|
+
end
|
83
|
+
|
84
|
+
def valid_attribute_name? tag_name, attribute_name
|
85
|
+
attribute_name.to_s.start_with?(":", "data-", "aria-") || @tagset[tag_name].include?(attribute_name)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
data/lib/markaby/version.rb
CHANGED
@@ -0,0 +1,13 @@
|
|
1
|
+
module Markaby
|
2
|
+
class XHTMLFrameset < XmlTagset
|
3
|
+
@doctype = ["-//W3C//DTD XHTML 1.0 Frameset//EN", "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd"]
|
4
|
+
@tagset = XHTMLTransitional.tagset.merge({
|
5
|
+
frameset: AttrCore + [:rows, :cols, :onload, :onunload],
|
6
|
+
frame: AttrCore + [:longdesc, :name, :src, :frameborder, :marginwidth, :marginheight, :noresize, :scrolling]
|
7
|
+
})
|
8
|
+
|
9
|
+
@tags = @tagset.keys
|
10
|
+
@forms = @tags & FORM_TAGS
|
11
|
+
@self_closing = @tags & SELF_CLOSING_TAGS
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
module Markaby
|
2
|
+
# All the tags and attributes from XHTML 1.0 Strict
|
3
|
+
class XHTMLStrict < XmlTagset
|
4
|
+
@doctype = ["-//W3C//DTD XHTML 1.0 Strict//EN", "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"]
|
5
|
+
@tagset = {
|
6
|
+
html: AttrI18n + [:id, :xmlns],
|
7
|
+
head: AttrI18n + [:id, :profile],
|
8
|
+
title: AttrI18n + [:id],
|
9
|
+
base: [:href, :id],
|
10
|
+
meta: AttrI18n + [:id, :http, :name, :content, :scheme, :"http-equiv"],
|
11
|
+
link: Attrs + [:charset, :href, :hreflang, :type, :rel, :rev, :media],
|
12
|
+
style: AttrI18n + [:id, :type, :media, :title, :"xml:space"],
|
13
|
+
script: [:id, :charset, :type, :src, :defer, :"xml:space"],
|
14
|
+
noscript: Attrs,
|
15
|
+
body: Attrs + [:onload, :onunload],
|
16
|
+
div: Attrs,
|
17
|
+
p: Attrs,
|
18
|
+
ul: Attrs,
|
19
|
+
ol: Attrs,
|
20
|
+
li: Attrs,
|
21
|
+
dl: Attrs,
|
22
|
+
dt: Attrs,
|
23
|
+
dd: Attrs,
|
24
|
+
address: Attrs,
|
25
|
+
hr: Attrs,
|
26
|
+
pre: Attrs + [:"xml:space"],
|
27
|
+
blockquote: Attrs + [:cite],
|
28
|
+
ins: Attrs + [:cite, :datetime],
|
29
|
+
del: Attrs + [:cite, :datetime],
|
30
|
+
a: Attrs + AttrFocus + [:charset, :type, :name, :href, :hreflang, :rel, :rev, :shape, :coords],
|
31
|
+
span: Attrs,
|
32
|
+
bdo: AttrCore + AttrEvents + [:lang, :"xml:lang", :dir],
|
33
|
+
br: AttrCore,
|
34
|
+
em: Attrs,
|
35
|
+
strong: Attrs,
|
36
|
+
dfn: Attrs,
|
37
|
+
code: Attrs,
|
38
|
+
samp: Attrs,
|
39
|
+
kbd: Attrs,
|
40
|
+
var: Attrs,
|
41
|
+
cite: Attrs,
|
42
|
+
abbr: Attrs,
|
43
|
+
acronym: Attrs,
|
44
|
+
q: Attrs + [:cite],
|
45
|
+
sub: Attrs,
|
46
|
+
sup: Attrs,
|
47
|
+
tt: Attrs,
|
48
|
+
i: Attrs,
|
49
|
+
b: Attrs,
|
50
|
+
big: Attrs,
|
51
|
+
small: Attrs,
|
52
|
+
object: Attrs + [:declare, :classid, :codebase, :data, :type, :codetype, :archive, :standby, :height, :width, :usemap, :name, :tabindex],
|
53
|
+
param: [:id, :name, :value, :valuetype, :type],
|
54
|
+
img: Attrs + [:src, :alt, :longdesc, :height, :width, :usemap, :ismap],
|
55
|
+
map: AttrI18n + AttrEvents + [:id, :class, :style, :title, :name],
|
56
|
+
area: Attrs + AttrFocus + [:shape, :coords, :href, :nohref, :alt],
|
57
|
+
form: Attrs + [:action, :method, :enctype, :onsubmit, :onreset, :accept, :accept],
|
58
|
+
label: Attrs + [:for, :accesskey, :onfocus, :onblur],
|
59
|
+
input: Attrs + AttrFocus + [:type, :name, :value, :checked, :disabled, :readonly, :size, :maxlength, :src, :alt, :usemap, :onselect, :onchange, :accept],
|
60
|
+
select: Attrs + [:name, :size, :multiple, :disabled, :tabindex, :onfocus, :onblur, :onchange],
|
61
|
+
optgroup: Attrs + [:disabled, :label],
|
62
|
+
option: Attrs + [:selected, :disabled, :label, :value],
|
63
|
+
textarea: Attrs + AttrFocus + [:name, :rows, :cols, :disabled, :readonly, :onselect, :onchange],
|
64
|
+
fieldset: Attrs,
|
65
|
+
legend: Attrs + [:accesskey],
|
66
|
+
button: Attrs + AttrFocus + [:name, :value, :type, :disabled],
|
67
|
+
table: Attrs + [:summary, :width, :border, :frame, :rules, :cellspacing, :cellpadding],
|
68
|
+
caption: Attrs,
|
69
|
+
colgroup: Attrs + AttrHAlign + AttrVAlign + [:span, :width],
|
70
|
+
col: Attrs + AttrHAlign + AttrVAlign + [:span, :width],
|
71
|
+
thead: Attrs + AttrHAlign + AttrVAlign,
|
72
|
+
tfoot: Attrs + AttrHAlign + AttrVAlign,
|
73
|
+
tbody: Attrs + AttrHAlign + AttrVAlign,
|
74
|
+
tr: Attrs + AttrHAlign + AttrVAlign,
|
75
|
+
th: Attrs + AttrHAlign + AttrVAlign + [:abbr, :axis, :headers, :scope, :rowspan, :colspan],
|
76
|
+
td: Attrs + AttrHAlign + AttrVAlign + [:abbr, :axis, :headers, :scope, :rowspan, :colspan],
|
77
|
+
h1: Attrs,
|
78
|
+
h2: Attrs,
|
79
|
+
h3: Attrs,
|
80
|
+
h4: Attrs,
|
81
|
+
h5: Attrs,
|
82
|
+
h6: Attrs
|
83
|
+
}
|
84
|
+
|
85
|
+
@tags = @tagset.keys
|
86
|
+
@forms = @tags & FORM_TAGS
|
87
|
+
@self_closing = @tags & SELF_CLOSING_TAGS
|
88
|
+
end
|
89
|
+
end
|