markaby 0.8.1 → 0.9.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|