www_app 1.3.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +4 -0
  3. data/README.md +42 -24
  4. data/VERSION +1 -1
  5. data/bin/www_app +15 -4
  6. data/lib/public/vendor/hogan-3.0.2.min.js +5 -0
  7. data/lib/public/vendor/instruct_instruct_instruct.js +247 -0
  8. data/lib/public/vendor/jquery-2.1.3.min.js +4 -0
  9. data/lib/public/vendor/lodash.min.js +89 -0
  10. data/lib/public/www_app.js +885 -625
  11. data/lib/www_app/CSS.rb +310 -0
  12. data/lib/www_app/HTML.rb +219 -0
  13. data/lib/www_app/JavaScript.rb +51 -0
  14. data/lib/www_app/TO.rb +897 -0
  15. data/lib/www_app.rb +324 -945
  16. data/playground/config.ru +102 -0
  17. data/specs/client-side/index.html +1 -1
  18. data/specs/client-side/index.js +31 -379
  19. data/specs/lib/config.ru +60 -31
  20. data/specs/lib/helpers.rb +24 -2
  21. data/specs/server-side/0000-new.rb +1 -1
  22. data/specs/server-side/0001-underscore-double.rb +38 -0
  23. data/specs/server-side/0001-underscore.rb +73 -0
  24. data/specs/server-side/0010-attrs.rb +3 -4
  25. data/specs/server-side/0011-id.rb +5 -5
  26. data/specs/server-side/0020-tag.rb +2 -2
  27. data/specs/server-side/0020-tag_content.rb +1 -1
  28. data/specs/server-side/0021-body.rb +1 -1
  29. data/specs/server-side/0021-script.rb +66 -20
  30. data/specs/server-side/{0021-page_title.rb → 0021-title.rb} +5 -3
  31. data/specs/server-side/0030-mustache.rb +27 -20
  32. data/specs/server-side/0030-style.rb +64 -21
  33. data/specs/server-side/0040-css.rb +4 -4
  34. data/specs/server-side/0041-pseudo.rb +55 -0
  35. data/specs/server-side/0042-slash.rb +20 -0
  36. data/specs/server-side/0060-text.rb +26 -0
  37. metadata +18 -13
  38. data/lib/public/jquery-2.1.1.js +0 -4
  39. data/lib/public/underscore-1.7.0.js +0 -6
  40. data/lib/public/underscore-min.map +0 -1
  41. data/lib/public/underscore.string-2.3.0.js +0 -1
  42. data/lib/www_app/Clean.rb +0 -169
  43. data/lib/www_app/dsl.rb +0 -86
  44. data/lib/www_app/source.rb +0 -53
  45. data/specs/server-side/0050-on.rb +0 -64
  46. data/specs/server-side/0060-string.rb +0 -32
  47. /data/lib/public/{jquery.serialize-object.min.js → vendor/jquery.serialize-object.min.js} +0 -0
@@ -0,0 +1,310 @@
1
+
2
+ class WWW_App
3
+
4
+ module CSS
5
+ COMMA = ", ".freeze
6
+
7
+ AT_RULES = [ 'font-face', 'media' ]
8
+
9
+ # === From:
10
+ # https://developer.mozilla.org/en-US/docs/Web/CSS/Pseudo-classes
11
+ PSEUDO = %w[
12
+ active checked default dir() disabled
13
+ empty enabled
14
+ first first-child first-of-type fullscreen focus
15
+ hover
16
+ indeterminate in-range invalid
17
+ lang() last-child last-of-type left link
18
+ not() nth-child() nth-last-child() nth-last-of-type() nth-of-type()
19
+ only-child only-of-type optional out-of-range
20
+ read-only read-write required right root
21
+ scope
22
+ target
23
+ valid visited
24
+ ].select { |name| name[/\A[a-z0-9\-]+\Z/] }.map { |name| name.gsub('-', '_').to_sym }
25
+
26
+ # From: Sanitize::Config::RELAXED[:css][:properties]
27
+ PROPERTIES = %w[
28
+ background bottom font_variant_position quotes
29
+ background_attachment box_decoration_break font_weight resize
30
+ background_clip box_shadow height right
31
+ background_color box_sizing hyphens tab_size
32
+ background_image clear icon table_layout
33
+ background_origin clip image_orientation text_align
34
+ background_position clip_path image_rendering text_align_last
35
+ background_repeat color image_resolution text_combine_horizontal
36
+ background_size column_count ime_mode text_decoration
37
+ border column_fill justify_content text_decoration_color
38
+ border_bottom column_gap left text_decoration_line
39
+ border_bottom_color column_rule letter_spacing text_decoration_style
40
+ border_bottom_left_radius column_rule_color line_height text_indent
41
+ border_bottom_right_radius column_rule_style list_style text_orientation
42
+ border_bottom_style column_rule_width list_style_image text_overflow
43
+ border_bottom_width column_span list_style_position text_rendering
44
+ border_collapse column_width list_style_type text_shadow
45
+ border_color columns margin text_size_adjust
46
+ border_image counter_increment margin_bottom text_transform
47
+ border_image_outset counter_reset margin_left text_underline_position
48
+ border_image_repeat cursor margin_right top
49
+ border_image_slice direction margin_top touch_action
50
+ border_image_source display marks transform
51
+ border_image_width empty_cells mask transform_origin
52
+ border_left filter mask_type transform_style
53
+ border_left_color float max_height transition
54
+ border_left_style font max_width transition_delay
55
+ border_left_width font_family min_height transition_duration
56
+ border_radius font_feature_settings min_width transition_property
57
+ border_right font_kerning opacity transition_timing_function
58
+ border_right_color font_language_override order unicode_bidi
59
+ border_right_style font_size orphans unicode_range
60
+ border_right_width font_size_adjust overflow vertical_align
61
+ border_spacing font_stretch overflow_wrap visibility
62
+ border_style font_style overflow_x white_space
63
+ border_top font_synthesis overflow_y widows
64
+ border_top_color font_variant padding width
65
+ border_top_left_radius font_variant_alternates padding_bottom word_break
66
+ border_top_right_radius font_variant_caps padding_left word_spacing
67
+ border_top_style font_variant_east_asian padding_right word_wrap
68
+ border_top_width font_variant_ligatures padding_top z_index
69
+ border_width font_variant_numeric position
70
+ ].map(&:to_sym)
71
+
72
+ PROPERTIES.each { |name|
73
+ eval <<-EOF, nil, __FILE__, __LINE__ + 1
74
+ def #{name} *args
75
+ alter_css_property(:#{name}, *args)
76
+ block_given? ?
77
+ close { yield } :
78
+ self
79
+ end
80
+ EOF
81
+ }
82
+
83
+ PSEUDO.each { |name|
84
+ eval <<-EOF, nil, __FILE__, __LINE__ + 1
85
+ def _#{name} *args
86
+ pseudo :#{name}, *args
87
+ block_given? ?
88
+ close { yield } :
89
+ self
90
+ end
91
+ EOF
92
+ }
93
+
94
+ #
95
+ # Ex:
96
+ # style {
97
+ # div.^(:bad).__.div {
98
+ # }
99
+ #
100
+ def __
101
+ fail "Can only be used inside :style tag" unless ancestor?(:style)
102
+
103
+ if !@tag || (@tag[:tag_name] == :group || @tag[:tag_name] == :groups)
104
+ fail "Can only be used after an HTML element is created: #{@tag[:tag_name].inspect}"
105
+ end
106
+
107
+ @tag[:__] = true
108
+ go_up
109
+ self
110
+ end
111
+
112
+ #
113
+ # Example:
114
+ # style {
115
+ # a._link / a._hover {
116
+ # ...
117
+ # }
118
+ # }
119
+ #
120
+ # link.href('/file')./
121
+ #
122
+ def / *args
123
+ fail "No block allowed here." if block_given?
124
+
125
+ case args.size
126
+ when 0
127
+ close
128
+ when 1
129
+ self
130
+ else
131
+ fail ::ArgumentError, "Unknown args: #{args.inspect[0,50]}"
132
+ end
133
+ end
134
+
135
+ #
136
+ # Example:
137
+ # css_selector
138
+ # css_selector tag
139
+ # css_selector :full || :tag || :ancestor
140
+ # css_selector tag, :full || :tag || :ancestor
141
+ #
142
+ def css_selector *args
143
+ tag = @tag
144
+ type = :full
145
+ args.each { |a|
146
+ case
147
+ when a.is_a?(Symbol)
148
+ type = a
149
+ else
150
+ tag = a
151
+ end
152
+ }
153
+
154
+ metaphor = (de_ref(tag) || {}.freeze)
155
+
156
+ final = case
157
+
158
+ when type == :full && tag?(metaphor, :group)
159
+ css = metaphor[:children].inject([]) { |memo, c|
160
+ if !(tag?(c, :group)) && !c[:__parent]
161
+ memo << css_selector(c, :full)
162
+ end
163
+ memo
164
+ }
165
+
166
+ if css
167
+ css.join COMMA
168
+ else
169
+ nil
170
+ end
171
+
172
+ when tag?(metaphor, :style)
173
+ p = metaphor[:parent]
174
+ if p
175
+ css_selector p, type
176
+ end
177
+
178
+ when type == :full && parent?(metaphor, :group)
179
+ grand_parent = metaphor[:parent][:parent]
180
+ grand_css = grand_parent && css_selector(grand_parent, :full)
181
+ use_grand = !(metaphor[:__] && metaphor[:__children].detect { |e| tag?(e, :_) })
182
+
183
+ if grand_css && use_grand
184
+ grand_css.split(COMMA).map { |css|
185
+ css << SPACE << css_selector(metaphor, :tag)
186
+ }.join COMMA
187
+ else
188
+ css_selector metaphor, :tag
189
+ end
190
+
191
+ when type == :tag
192
+ id = metaphor[:id]
193
+ name = if id
194
+ '#' << Clean.html_id(id).to_s
195
+ else
196
+ metaphor[:tag_name].to_s
197
+ end
198
+
199
+ if metaphor[:class]
200
+ name << '.'.freeze
201
+ name.<<(
202
+ metaphor[:class].map { |name|
203
+ Clean.css_class_name(name.to_s)
204
+ }.join('.'.freeze)
205
+ )
206
+ end
207
+
208
+ if metaphor[:pseudo]
209
+ name << ":#{metaphor[:pseudo]}"
210
+ end
211
+
212
+ if tag[:__]
213
+ name << SPACE << tag[:__children].map { |c|
214
+ css_selector(c, :tag)
215
+ }.join(SPACE)
216
+ end
217
+
218
+ name = if name.empty?
219
+ nil
220
+ else
221
+ name
222
+ end
223
+
224
+ when type == :ancestor
225
+ if metaphor[:id]
226
+ nil
227
+ else
228
+ selectors = []
229
+ p = metaphor[:parent]
230
+ while p
231
+ selectors.unshift(css_selector(p, :tag)) unless [:style, :group].freeze.include?(p[:tag_name])
232
+ p = p[:id] ? nil : p[:parent]
233
+ end # === while
234
+
235
+ selectors.compact.join(SPACE)
236
+ end
237
+
238
+ else
239
+ [css_selector(metaphor, :ancestor), css_selector(metaphor, :tag)].compact.join SPACE
240
+ end
241
+
242
+ return nil if !final || final.empty?
243
+ final.gsub(' _!:'.freeze, ':'.freeze)
244
+ end
245
+
246
+ private # ==================================
247
+
248
+ def pseudo name
249
+ origin = @tag
250
+ case
251
+
252
+ when tag?(:style)
253
+ #
254
+ # WHEN:
255
+ #
256
+ # style {
257
+ # _link {
258
+ #
259
+ create :group
260
+ create :_
261
+
262
+ when ancestor?(:groups) && @tag[:closed]
263
+ #
264
+ # WHEN:
265
+ #
266
+ # style {
267
+ # div { ... }
268
+ # _link { ... }
269
+ #
270
+ create :group
271
+ create :_!
272
+
273
+ when (tag?(:_) || tag?(:_!)) && @tag[:pseudo]
274
+ # WHEN:
275
+ # style {
276
+ # _link / _visited
277
+ tag_name = @tag[:tag_name]
278
+ go_up
279
+ create tag_name
280
+
281
+ when @tag[:pseudo] && !@tag[:closed]
282
+ puts @tag
283
+ # Ex:
284
+ # a._link._visited { ... }
285
+ fail "Applying two pseudos at the same element: #{@tag[:pseudo].inspect} #{name.inspect}"
286
+
287
+ end # === case
288
+
289
+ @tag[:pseudo] = name
290
+ block_given? ?
291
+ close { yield } :
292
+ self
293
+ end
294
+
295
+ def alter_css_property name, *args
296
+ if !@tag
297
+ self._
298
+ end
299
+ @tag[:css] ||= {}
300
+ @tag[:css][name] = args.size == 1 ? args.first : args
301
+ self
302
+ end
303
+
304
+ def style
305
+ create :style, :type=>'text/css', :groups=>true
306
+ close { yield }
307
+ end
308
+
309
+ end # === module CSS ==============
310
+ end # === class WWW_App =============
@@ -0,0 +1,219 @@
1
+
2
+ class WWW_App
3
+ module HTML
4
+
5
+ SELF_CLOSING_TAGS = [:br, :input, :link, :meta, :hr, :img]
6
+ NO_NEW_LINES = [:p, :code, :span].freeze
7
+
8
+ TAGS = %w[
9
+ title
10
+ body div span
11
+
12
+ img
13
+ b em i strong u a
14
+ abbr blockquote cite
15
+ br cite code
16
+ ul ol li p pre q
17
+ sup sub
18
+ form input button
19
+
20
+ link
21
+
22
+ script
23
+ ].map(&:to_sym)
24
+
25
+ TAGS_TO_ATTRIBUTES = {
26
+ :all => [:id, :class],
27
+ :a => [:href, :rel],
28
+ :form => [:action, :method, :accept_charset],
29
+ :input => [:type, :name, :value],
30
+ :style => [:type],
31
+ :script => [:type, :src, :language],
32
+ :link => [:rel, :type, :sizes, :href, :title],
33
+ :meta => [:name, :http_equiv, :property, :content, :charset],
34
+ :img => [:src], # :width, :height will be used by CSS only.
35
+ :html => [:lang]
36
+ }
37
+
38
+ ATTRIBUTES_TO_TAGS = TAGS_TO_ATTRIBUTES.inject({}) { |memo, (tag, attrs)|
39
+ attrs.each { |a|
40
+ memo[a] ||= []
41
+ memo[a] << tag
42
+ }
43
+ memo
44
+ }
45
+
46
+ ATTRIBUTES = ATTRIBUTES_TO_TAGS.keys
47
+
48
+ ATTRIBUTES_TO_TAGS.each { |name, tags|
49
+ eval <<-EOF, nil, __FILE__, __LINE__ + 1
50
+ def #{name} val
51
+ alter_attribute :#{name}, val
52
+ block_given? ?
53
+ close { yield } :
54
+ self
55
+ end
56
+ EOF
57
+ }
58
+
59
+ TAGS.each { |name|
60
+ eval <<-EOF, nil, __FILE__, __LINE__ + 1
61
+ def #{name}
62
+ create(:#{name})
63
+ block_given? ?
64
+ close { yield } :
65
+ self
66
+ end
67
+ EOF
68
+ }
69
+
70
+ def alter_attribute name, val
71
+ allowed = @tag &&
72
+ ATTRIBUTES_TO_TAGS[name] &&
73
+ ATTRIBUTES_TO_TAGS[name].include?(@tag[:tag_name])
74
+
75
+ fail "#{name.inspect} not allowed to be set here." unless allowed
76
+
77
+ @tag[name] = val
78
+
79
+ block_given? ?
80
+ close { yield } :
81
+ self
82
+ end
83
+
84
+ def meta *args
85
+ fail "No block allowed." if block_given?
86
+ fail "Not allowed here." if parent
87
+ create(:meta, *args)
88
+ end
89
+
90
+ def title
91
+ fail ":title not allowed here" if parent
92
+ create :title do
93
+ yield
94
+ end
95
+ end
96
+
97
+ def id new_id
98
+ if !@tag
99
+ fail "No HTML tag found. Try using _.id(#{new_id.inspect})"
100
+ end
101
+
102
+ if !ancestor?(:group)
103
+ old_id = @tag[:id]
104
+ if old_id && old_id != new_id
105
+ fail("Id already set: #{old_id} new: #{new_id}")
106
+ end
107
+
108
+ if @html_ids[new_id]
109
+ fail(HTML_ID_Duplicate, "Id already used: #{new_id.inspect}, tag index: #{@html_ids[new_id]}")
110
+ end
111
+ @html_ids[ new_id ] = new_id
112
+ end
113
+
114
+ @tag[:id] = new_id
115
+
116
+ if block_given?
117
+ close { yield }
118
+ else
119
+ self
120
+ end
121
+ end # === def id
122
+
123
+ def is_fragment?
124
+ !is_doc?
125
+ end
126
+
127
+ def is_doc?
128
+ @is_doc ||= begin
129
+ found = false
130
+ tags = @tags.dup
131
+ while !found && !tags.empty?
132
+ t = tags.shift
133
+ found = begin
134
+ (t[:tag_name] == :body && (t[:id] || t[:css]) ) ||
135
+ t[:tag_name] == :style ||
136
+ t[:tag_name] == :script ||
137
+ t[:tag_name] == :meta ||
138
+ t[:css] ||
139
+ (t[:tag_name] == :title && t[:parent] && t[:parent][:tag_name] == :body)
140
+ end
141
+ if !found && t[:children]
142
+ tags = t[:children].concat(tags)
143
+ end
144
+ end
145
+
146
+ found
147
+ end
148
+ end # === def is_doc?
149
+
150
+ def lang name
151
+ fail "Tag has to be placed tomost of the page." if parent
152
+ fail "Block not allowed here." if block_given?
153
+ create :html_tag_attr do
154
+ @tag[:lang] = name.to_s.downcase.gsub(/[^a-z0-9\_\-]+/, ''.freeze)
155
+ @tag[:lang] = 'en' if @tag[:lang].empty?
156
+ end
157
+
158
+ self
159
+ end
160
+
161
+ #
162
+ # Example:
163
+ # div.^(:alert, :red_hot) { 'my content' }
164
+ #
165
+ def ^ *names
166
+ @tag[:class] ||= []
167
+ @tag[:class].concat(names)
168
+
169
+ if block_given?
170
+ close { yield }
171
+ else
172
+ self
173
+ end
174
+ end
175
+
176
+ def render_if name
177
+ fail ::ArgumentError, "Not a symbol: #{name.inspect}" unless name.is_a?(Symbol)
178
+ raw_text %^{{#coll.#{name}}}^
179
+ yield
180
+ raw_text %^{{/coll.#{name}}}^
181
+ nil
182
+ end
183
+
184
+ def render_unless name
185
+ fail ::ArgumentError, "Not a symbol: #{name.inspect}" unless name.is_a?(Symbol)
186
+ raw_text %!{{^coll.#{name}}}!
187
+ yield
188
+ raw_text %!{{/coll.#{name}}}!
189
+ nil
190
+ end
191
+
192
+ def input *args
193
+ case
194
+ when args.size === 3
195
+ create(:input, :type=>args[0].to_s, :name=>args[1].to_s, :value=>args[2], :closed=>true)
196
+ go_up
197
+ else
198
+ super
199
+ end
200
+ end
201
+
202
+ def script src = nil
203
+
204
+ if src.is_a?(String) && src['.js'.freeze]
205
+ return create(:script, :src=>src) { }
206
+ end
207
+
208
+ attrs = {
209
+ :type => src || "text/mustache"
210
+ }
211
+
212
+ create :script, attrs
213
+ close { yield } if block_given?
214
+ self
215
+ end
216
+
217
+ end # === module HTML
218
+ end # === class WWW_App
219
+
@@ -0,0 +1,51 @@
1
+
2
+ class WWW_App
3
+ module JavaScript
4
+
5
+ class DSL < BasicObject
6
+
7
+ include ::Kernel
8
+
9
+ def initialize &blok
10
+ @js = []
11
+ instance_eval &blok
12
+ end
13
+
14
+ %w[ add_class ].each { |name|
15
+ eval <<-EOF, nil, __FILE__, __LINE__ + 1
16
+ def #{name} *args
17
+ run_method :#{name}, args
18
+ end
19
+ EOF
20
+ }
21
+ def run_method name, args
22
+ self << name
23
+ self << args
24
+ self
25
+ end
26
+
27
+ def raw_code
28
+ @js
29
+ end
30
+
31
+ def concat arr
32
+ @js.concat(arr)
33
+ end
34
+
35
+ def << *args
36
+ @js.<<(*args)
37
+ end
38
+
39
+ def capture &blok
40
+ orig = @js
41
+ new = []
42
+ @js = new
43
+ instance_eval &blok
44
+ @js = orig
45
+ new
46
+ end
47
+
48
+ end # === class DSL
49
+
50
+ end # === module JavaScript
51
+ end # === class WWW_App