www_app 1.3.0 → 2.0.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.
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