wedge 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,92 @@
1
+ module Wedge
2
+ module Plugins
3
+ class History < Component
4
+ config.name :history_plugin
5
+ end
6
+ end
7
+ end
8
+
9
+ if RUBY_ENGINE == 'opal'
10
+ require 'wedge/plugins/location'
11
+
12
+ module Browser
13
+ # {Window} instances are {Native} objects used to wrap native window instances.
14
+ #
15
+ # Generally, you will want to use the top level {::Window} instance, which
16
+ # wraps `window` from the main page.
17
+ class Window
18
+ include Native
19
+
20
+ # @!attribute [r] history
21
+ # @return [History] the history for this window
22
+ def history
23
+ History.new(`#@native.history`) if `#@native.history`
24
+ end
25
+ end
26
+
27
+ # {History} allows manipulation of the session history.
28
+ #
29
+ # @see https://developer.mozilla.org/en-US/docs/Web/API/History
30
+ class History
31
+ include Native
32
+
33
+ # @!attribute [r] length
34
+ # @return [Integer] how many items are in the history
35
+ alias_native :length
36
+
37
+ # Go back in the history.
38
+ #
39
+ # @param number [Integer] how many items to go back
40
+ def back(number = 1)
41
+ `History.go(-number)`
42
+ end
43
+
44
+ # Go forward in the history.
45
+ #
46
+ # @param number [Integer] how many items to go forward
47
+ def forward(number = 1)
48
+ `History.go(number)`
49
+ end
50
+
51
+ # Push an item in the history.
52
+ #
53
+ # @param item [String] the item to push in the history
54
+ # @param data [Object] additional state to push
55
+ def push(item, data = nil)
56
+ data = `null` if data.nil?
57
+
58
+ `History.pushState(jQuery.parseJSON(data.$to_json()), null, item)`
59
+ end
60
+
61
+ # Replace the current history item with another.
62
+ #
63
+ # @param item [String] the item to replace with
64
+ # @param data [Object] additional state to replace
65
+ def replace(item, data = nil)
66
+ data = `null` if data.nil?
67
+
68
+ `History.replaceState(data, null, item)`
69
+ end
70
+
71
+ def get_state
72
+ Native(`History.getState()`)
73
+ end
74
+
75
+ # @!attribute [r] current
76
+ # @return [String] the current item
77
+ def current
78
+ $window.location.path
79
+ end
80
+
81
+ def change &block
82
+ %x{
83
+ History.Adapter.bind(window,'statechange',function(e){
84
+ var state = History.getState();
85
+ state = #{Native(`state`)}
86
+ return #{block.call(`state`)}
87
+ });
88
+ }
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,78 @@
1
+ module Browser
2
+
3
+ # Allows manipulation of a location, usually from {Window} and {DOM::Document}.
4
+ #
5
+ # @see https://developer.mozilla.org/en-US/docs/Web/API/Location
6
+ class Location
7
+ include Native
8
+
9
+ # Change the location.
10
+ #
11
+ # @param url [String, #to_s] the URL to go to
12
+ def assign(url)
13
+ `#@native.assign(#{url.to_s})`
14
+ end
15
+
16
+ # Replace the current URL.
17
+ #
18
+ # @param url [String, #to_s] the URL to go to
19
+ def replace(url)
20
+ `#@native.replace(#{url.to_s})`
21
+ end
22
+
23
+ # Reload the page.
24
+ #
25
+ # @param force [Boolean] whether to force the reload
26
+ def reload(force = false)
27
+ `#@native.reload(force)`
28
+ end
29
+
30
+ # Convert the location to a string.
31
+ def to_s
32
+ `#@native.toString()`
33
+ end
34
+
35
+ # @!attribute fragment
36
+ # @return [String] the hash fragment of the location URI
37
+ alias_native :fragment, :hash
38
+ alias_native :fragment=, :hash=
39
+
40
+ # @!attribute host
41
+ # @return [String] the host part of the location URI
42
+ alias_native :host
43
+ alias_native :host=
44
+
45
+ # @!attribute uri
46
+ # @return [String] the whole location URI
47
+ alias_native :uri, :href
48
+ alias_native :uri=, :href=
49
+
50
+ # @!attribute path
51
+ # @return [String] the path part of the location URI
52
+ alias_native :path, :pathname
53
+ alias_native :path=, :pathname=
54
+
55
+ # @!attribute port
56
+ # @return [Integer] the port part of the location URI
57
+ alias_native :port
58
+ alias_native :port=
59
+
60
+ # @!attribute scheme
61
+ # @return [String] the scheme part of the location URI
62
+ alias_native :scheme, :protocol
63
+ alias_native :scheme=, :protocol=
64
+
65
+ # @!attribute query
66
+ # @return [String] the query part of the location URI
67
+ alias_native :query, :search
68
+ alias_native :query=, :search=
69
+ end
70
+
71
+ class Window
72
+ # @!attribute [r] location
73
+ # @return [Location] the location for the window
74
+ def location
75
+ Location.new(`#@native.location`) if `#@native.location`
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,65 @@
1
+ module Wedge
2
+ module Plugins
3
+ class Pjax < Component
4
+ config.name :pjax, :pjax_plugin
5
+ config.requires :history_plugin
6
+
7
+ class Nanobar
8
+ include Native
9
+
10
+ alias_native :go
11
+ alias_native :start
12
+ alias_native :finish
13
+
14
+ def initialize options = {}
15
+ `var Nanobar=function(){"use strict";var t,i,e,s,h,n,o={width:"100%",height:"4px",zIndex:9999,top:"0"},a={width:0,height:"100%",clear:"both",transition:"height .3s"};return t=function(t,i){var e;for(e in i)t.style[e]=i[e];t.style["float"]="left"},s=function(){var t=this,i=this.width-this.here;.1>i&&i>-.1?(h.call(this,this.here),this.moving=!1,100==this.width&&(this.el.style.height=0,setTimeout(function(){t.cont.el.removeChild(t.el)},300))):(h.call(this,this.width-i/4),setTimeout(function(){t.go()},16))},h=function(t){this.width=t,this.el.style.width=this.width+"%"},n=function(){var t=new i(this);this.bars.unshift(t)},i=function(i){this.el=document.createElement("div"),this.el.style.backgroundColor=i.opts.bg,this.width=0,this.here=0,this.moving=!1,this.cont=i,t(this.el,a),i.el.appendChild(this.el)},i.prototype.go=function(t){t?(this.here=t,this.moving||(this.moving=!0,s.call(this))):this.moving&&s.call(this)},e=function(i){var e,s,h=this.opts=i||{};h.bg=h.bg||"#000",this.bars=[],e=this.el=document.createElement("div"),t(this.el,o),h.id&&(e.id=h.id),h.className&&(e.className=h.className),e.style.position=h.target?"relative":"fixed",h.target?(s=h.target,s.insertBefore(e,h.target.firstChild)):(s=document.getElementsByTagName("body")[0],s.appendChild(e)),s.className="nanobar-custom-parent",n.call(this)},e.prototype.go=function(t){this.bars[0].go(t),100==t&&n.call(this)},e.prototype.start=function(){(function(){var t=this.bars[0],i=function(){setTimeout(function(){var e=t.here+Math.round(10*Math.random());t.here>=99||(e>99&&(e=99),t.go(e),i())},500)};t.go(10),i()}).call(this)},e.prototype.finish=function(){this.go(100)},e}();`
16
+ super `new Nanobar(options)`
17
+ end
18
+ end if client?
19
+
20
+ def progress_bar
21
+ $pjax_progress_bar
22
+ end
23
+
24
+ def get href = false
25
+ $pjax_progress_bar = Nanobar.new({bg: '#f99f22'}.to_n)
26
+ progress_bar.start
27
+ `$(document).trigger('page:click')`
28
+ $window.history.push href, pjax: true
29
+ end
30
+
31
+ on :click, 'a' do |el, evt|
32
+ href = el.attr 'href'
33
+
34
+ unless href =~ /^#/i || href =~ /^javascript:/i || el.attr('target') == '_blank'
35
+ evt.prevent_default
36
+ get href
37
+ end
38
+ end
39
+
40
+ on :history_change do |e|
41
+ if e.data.pjax
42
+ progress_bar.start
43
+ `$(document).trigger('page:request')`
44
+ HTTP.get(e.url) do |response|
45
+ res = Native(response.xhr)
46
+ html = res.responseText
47
+ # grab and add the body
48
+ matches = html.match(/<body[^>]*>((.|[\n\r])*)<\/body>/im)
49
+ dom.find('body').html matches[1]
50
+ # grab and eval the scripts
51
+ matches = html.match(/<script>((.|[\n\r])*)<\/script>/im)
52
+ # `eval(#{matches[0]})`
53
+ (matches[1] || '').split('</script>').each do |script|
54
+ # script = script.sub('<script>', '')
55
+ script = script.strip.sub('</html>', '').sub('<script>', '')
56
+ `jQuery.globalEval(script);`
57
+ end
58
+ progress_bar.finish
59
+ `$('html, body').animate({ scrollTop: 0 }, 0); $(document).trigger('page:load');`
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,251 @@
1
+ module Wedge
2
+ module Plugins
3
+ class Form < Component
4
+ # Provides a base implementation for extensible validation routines.
5
+ # {Scrivener::Validations} currently only provides the following assertions:
6
+ #
7
+ # * assert
8
+ # * assert_present
9
+ # * assert_format
10
+ # * assert_numeric
11
+ # * assert_url
12
+ # * assert_email
13
+ # * assert_member
14
+ # * assert_length
15
+ # * assert_decimal
16
+ # * assert_equal
17
+ #
18
+ # The core tenets that Scrivener::Validations advocates can be summed up in a
19
+ # few bullet points:
20
+ #
21
+ # 1. Validations are much simpler and better done using composition rather
22
+ # than macros.
23
+ # 2. Error messages should be kept separate and possibly in the view or
24
+ # presenter layer.
25
+ # 3. It should be easy to write your own validation routine.
26
+ #
27
+ # Other validations are simply added on a per-model or per-project basis.
28
+ #
29
+ # @example
30
+ #
31
+ # class Quote
32
+ # attr_accessor :title
33
+ # attr_accessor :price
34
+ # attr_accessor :date
35
+ #
36
+ # def validate
37
+ # assert_present :title
38
+ # assert_numeric :price
39
+ # assert_format :date, /\A[\d]{4}-[\d]{1,2}-[\d]{1,2}\z
40
+ # end
41
+ # end
42
+ #
43
+ # s = Quote.new
44
+ # s.valid?
45
+ # # => false
46
+ #
47
+ # s.errors
48
+ # # => { :title => [:not_present],
49
+ # :price => [:not_numeric],
50
+ # :date => [:format] }
51
+ #
52
+ module Validations
53
+ def server? &block
54
+ RUBY_ENGINE == 'ruby'
55
+ end
56
+ alias :server :server?
57
+
58
+ def client?
59
+ RUBY_ENGINE == 'opal'
60
+ end
61
+ alias :client :client?
62
+
63
+ def self.server? &block
64
+ RUBY_ENGINE == 'ruby'
65
+ end
66
+ alias :server :server?
67
+
68
+ def self.client?
69
+ RUBY_ENGINE == 'opal'
70
+ end
71
+ alias :client :client?
72
+
73
+ # Check if the current model state is valid. Each call to {#valid?} will
74
+ # reset the {#errors} array.
75
+ #
76
+ # All validations should be declared in a `validate` method.
77
+ #
78
+ # @example
79
+ #
80
+ # class Login
81
+ # attr_accessor :username
82
+ # attr_accessor :password
83
+ #
84
+ # def validate
85
+ # assert_present :user
86
+ # assert_present :password
87
+ # end
88
+ # end
89
+ #
90
+ def valid?
91
+ errors.clear
92
+ validate
93
+ errors.empty?
94
+ end
95
+
96
+ # Base validate implementation. Override this method in subclasses.
97
+ def validate
98
+ end
99
+
100
+ # Hash of errors for each attribute in this model.
101
+ def errors
102
+ @errors ||= Hash.new { |hash, key| hash[key] = [] }
103
+ end
104
+
105
+ protected
106
+
107
+ # Allows you to do a validation check against a regular expression.
108
+ # It's important to note that this internally calls {#assert_present},
109
+ # therefore you need not structure your regular expression to check
110
+ # for a non-empty value.
111
+ #
112
+ # @param [Symbol] att The attribute you want to verify the format of.
113
+ # @param [Regexp] format The regular expression with which to compare
114
+ # the value of att with.
115
+ # @param [Array<Symbol, Symbol>] error The error that should be returned
116
+ # when the validation fails.
117
+ def assert_format(att, format, error = [att, :format])
118
+ if assert_present(att, error)
119
+ assert(_attributes.send(att).to_s.match(format), error)
120
+ end
121
+ end
122
+
123
+ # The most basic and highly useful assertion. Simply checks if the
124
+ # value of the attribute is empty.
125
+ #
126
+ # @param [Symbol] att The attribute you wish to verify the presence of.
127
+ # @param [Array<Symbol, Symbol>] error The error that should be returned
128
+ # when the validation fails.
129
+ def assert_present(att, error = [att, :not_present])
130
+ if att.is_a? Array
131
+ att.each { |a| assert_present(a, error = [a, :not_present])}
132
+ else
133
+ if klass = _form[att]
134
+ options = {}
135
+ options[:key] = _options[:key] if _options.key? :key
136
+
137
+ f = klass.new(_attributes.send(att).attributes, options)
138
+ assert(f.valid?, [att, f.errors])
139
+ else
140
+ assert(!_attributes.send(att).to_s.empty?, error)
141
+ end
142
+ end
143
+ end
144
+
145
+ # Checks if all the characters of an attribute is a digit.
146
+ #
147
+ # @param [Symbol] att The attribute you wish to verify the numeric format.
148
+ # @param [Array<Symbol, Symbol>] error The error that should be returned
149
+ # when the validation fails.
150
+ def assert_numeric(att, error = [att, :not_numeric])
151
+ if assert_present(att, error)
152
+ if client?
153
+ assert_format(att, /^\-?\d+$/, error)
154
+ else
155
+ assert_format(att, /\A\-?\d+\z/, error)
156
+ end
157
+ end
158
+ end
159
+
160
+ if client?
161
+ URL = /^(http|https):\/\/([a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}|(2 5[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3} |localhost)(:[0-9]{1,5})?(\/.*)?$/i
162
+ else
163
+ URL = /\A(http|https):\/\/([a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}|(2 5[0-5]|2[0-4]\d|[0-1]?\d?\d)(\.(25[0-5]|2[0-4]\d|[0-1]?\d?\d)){3} |localhost)(:[0-9]{1,5})?(\/.*)?\z/i
164
+ end
165
+
166
+ def assert_url(att, error = [att, :not_url])
167
+ if assert_present(att, error)
168
+ assert_format(att, URL, error)
169
+ end
170
+ end
171
+
172
+ if client?
173
+ EMAIL = /^[a-z0-9!\#$%&'*\/=\?^{|}+_-]+(?:\.[a-z0-9!\#$%&'*\/=\?^{|}+_-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i
174
+ else
175
+ EMAIL = /\A[a-z0-9!\#$%&'*\/=\?^{|}+_-]+(?:\.[a-z0-9!\#$%&'*\/=\?^{|}+_-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\z/i
176
+ end
177
+
178
+ def assert_email(att, error = [att, :not_email])
179
+ if assert_present(att, error)
180
+ assert_format(att, EMAIL, error)
181
+ end
182
+ end
183
+
184
+ def assert_member(att, set, err = [att, :not_valid])
185
+ assert(set.include?(_attributes.send(att)), err)
186
+ end
187
+
188
+ def assert_length(att, range, error = [att, :not_in_range])
189
+ if assert_present(att, error)
190
+ val = _attributes.send(att).to_s
191
+ assert range.include?(val.length), error
192
+ end
193
+ end
194
+
195
+ if client?
196
+ DECIMAL = /^\-?(\d+)?(\.\d+)?$/
197
+ else
198
+ DECIMAL = /\A\-?(\d+)?(\.\d+)?\z/
199
+ end
200
+
201
+ def assert_decimal(att, error = [att, :not_decimal])
202
+ assert_format att, DECIMAL, error
203
+ end
204
+
205
+ # Check that the attribute has the expected value. It uses === for
206
+ # comparison, so type checks are possible too. Note that in order
207
+ # to make the case equality work, the check inverts the order of
208
+ # the arguments: `assert_equal :foo, Bar` is translated to the
209
+ # expression `Bar === send(:foo)`.
210
+ #
211
+ # @example
212
+ #
213
+ # def validate
214
+ # assert_equal :status, "pending"
215
+ # assert_equal :quantity, Fixnum
216
+ # end
217
+ #
218
+ # @param [Symbol] att The attribute you wish to verify for equality.
219
+ # @param [Object] value The value you want to test against.
220
+ # @param [Array<Symbol, Symbol>] error The error that should be returned
221
+ # when the validation fails.
222
+ def assert_equal(att, value, error = [att, :not_equal])
223
+ assert value === _attributes.send(att), error
224
+ end
225
+
226
+ # The grand daddy of all assertions. If you want to build custom
227
+ # assertions, or even quick and dirty ones, you can simply use this method.
228
+ #
229
+ # @example
230
+ #
231
+ # class CreatePost
232
+ # attr_accessor :slug
233
+ # attr_accessor :votes
234
+ #
235
+ # def validate
236
+ # assert_slug :slug
237
+ # assert votes.to_i > 0, [:votes, :not_valid]
238
+ # end
239
+ #
240
+ # protected
241
+ # def assert_slug(att, error = [att, :not_slug])
242
+ # assert send(att).to_s =~ /\A[a-z\-0-9]+\z/, error
243
+ # end
244
+ # end
245
+ def assert(value, error)
246
+ value or errors[error.first].push(error.last) && false
247
+ end
248
+ end
249
+ end
250
+ end
251
+ end