red 4.1.0 → 4.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,150 @@
1
+ # The +Browser+ module contains methods for checking the current platform and
2
+ # rendering engine.
3
+ #
4
+ module Browser
5
+ Plugins = {}
6
+
7
+ # call-seq:
8
+ # Browser.engine -> hash
9
+ #
10
+ # Returns a hash containing the name and version of the current rendering
11
+ # engine.
12
+ #
13
+ # Browser.engine #=> {:name => "gecko", :version => 19}
14
+ #
15
+ def self.engine
16
+ {:name => Engine.instance_variable_get('@name'), :version => Engine.instance_variable_get('@version')}
17
+ end
18
+
19
+ # call-seq:
20
+ # Browser.platform -> string
21
+ #
22
+ # Returns the name of the current platform as a string.
23
+ #
24
+ # Browser.platform #=> "mac"
25
+ #
26
+ def self.platform
27
+ @platform ||= `$q(window.orientation==undefined?(navigator.platform.match(/mac|win|linux/i)||['other'])[0].toLowerCase():'ipod')`
28
+ end
29
+
30
+ # The +Features+ module mixes in methods to check for browser features such
31
+ # as XPath and Adobe AIR.
32
+ #
33
+ module Features
34
+ `c$Browser.c$Features.__xpath__=!!(document.evaluate)`
35
+ `c$Browser.c$Features.__air__=!!(window.runtime)`
36
+ `c$Browser.c$Features.__query__=!!(document.querySelector)`
37
+
38
+ # call-seq:
39
+ # xpath? -> true or false
40
+ #
41
+ # Returns +true+ if XPath is available, +false+ otherwise.
42
+ #
43
+ def xpath?
44
+ `c$Browser.c$Features.__xpath__`
45
+ end
46
+
47
+ # call-seq:
48
+ # air? -> true or false
49
+ #
50
+ # Returns +true+ if Adobe AIR is available, +false+ otherwise.
51
+ #
52
+ def air?
53
+ `c$Browser.c$Features.__air__`
54
+ end
55
+
56
+ # call-seq:
57
+ # query? -> true or false
58
+ #
59
+ # Returns +true+ if the W3C Selectors API is available, +false+ otherwise.
60
+ #
61
+ def query?
62
+ `c$Browser.c$Features.__query__`
63
+ end
64
+ end
65
+
66
+ # The +Engine+ module mixes in methods to check the current browser's
67
+ # rendering engine and its version.
68
+ #
69
+ module Engine
70
+ if `window.opera`
71
+ @name = 'presto'
72
+ @version = `document.getElementsByClassName` ? 950 : 925
73
+ elsif `window.ActiveXObject`
74
+ @name = 'trident'
75
+ @version = `window.XMLHttpRequest` ? 5 : 4
76
+ elsif `!navigator.taintEnabled`
77
+ @name = 'webkit'
78
+ @version = `#{Browser::Features}.__xpath__` ? (`#{Browser::Features}.__query__` ? 525 : 420) : 419
79
+ elsif `document.getBoxObjectFor != null`
80
+ @name = 'gecko'
81
+ @version = `document.getElementsByClassName` ? 19 : 18
82
+ else
83
+ @name = 'unknown'
84
+ @version = 0
85
+ end
86
+
87
+ # call-seq:
88
+ # gecko? -> true or false
89
+ # gecko?(num) -> true or false
90
+ #
91
+ # Returns +true+ if the current browser is Firefox. Optionally checks for
92
+ # version _num_.
93
+ #
94
+ # gecko? #=> true
95
+ # gecko?(18) #=> false
96
+ #
97
+ def gecko?(version)
98
+ self.browser?('gecko', version)
99
+ end
100
+
101
+ # call-seq:
102
+ # presto? -> true or false
103
+ # presto?(num) -> true or false
104
+ #
105
+ # Returns +true+ if the current browser is Opera. Optionally checks for
106
+ # version _num_.
107
+ #
108
+ # presto? #=> true
109
+ # presto?(925) #=> false
110
+ #
111
+ def presto?(version)
112
+ self.browser?('presto', version)
113
+ end
114
+
115
+ # call-seq:
116
+ # trident? -> true or false
117
+ # trident?(num) -> true or false
118
+ #
119
+ # Returns +true+ if the current browser is Internet Explorer. Optionally
120
+ # checks for version _num_.
121
+ #
122
+ # trident? #=> true
123
+ # trident?(4) #=> false
124
+ #
125
+ def trident?(version)
126
+ self.browser?('trident', version)
127
+ end
128
+
129
+ # call-seq:
130
+ # webkit? -> true or false
131
+ # webkit?(num) -> true or false
132
+ #
133
+ # Returns +true+ if the current browser is Safari, Mobile Safari, or
134
+ # Google Chrome. Optionally checks for version _num_.
135
+ #
136
+ # webkit? #=> true
137
+ # webkit?(419) #=> false
138
+ #
139
+ def webkit?(version)
140
+ self.browser?('webkit', version)
141
+ end
142
+
143
+ def browser?(name, version) # :nodoc:
144
+ Engine.instance_variable_get('@name') == name && (version ? Engine.instance_variable_get('@version') == version : true)
145
+ end
146
+ end
147
+ end
148
+
149
+ include Browser::Engine
150
+ include Browser::Features
@@ -0,0 +1,75 @@
1
+ # Classes including module +Chainable+ gain the ability to cache a stack of
2
+ # +Proc+ objects. These blocks of code can later be executed one at a time, in
3
+ # the order of their insertion.
4
+ #
5
+ # class Foo
6
+ # include Chainable
7
+ # end
8
+ #
9
+ # foo = Foo.new
10
+ # foo.chain {|x| puts "x: " + x }.chain {|y,z| puts ["y: %s","z: %s"].join("\n") % [y,z] }
11
+ #
12
+ # foo.call_chain('called 1st')
13
+ # foo.call_chain('called 2nd', 'called 2nd also')
14
+ #
15
+ # produces:
16
+ #
17
+ # x: called 1st
18
+ # y: called 2nd
19
+ # z: called 2nd also
20
+ #
21
+ module Chainable
22
+ # call-seq:
23
+ # chainable.call_chain(arg, ...) -> object or false
24
+ #
25
+ # Removes the foremost +Proc+ from _chainable_'s chain and calls it,
26
+ # returning the result. Returns +false+ if the chain was empty.
27
+ #
28
+ # chainable.chain {|x,y,z| x * (y + z) }
29
+ #
30
+ # chainable.call_chain(4,5,6) #=> 44
31
+ # chainable.call_chain #=> false
32
+ #
33
+ def call_chain(*args)
34
+ @chain ||= []
35
+ return `#{@chain.shift}.__block__.apply(this,args)` unless @chain.empty?
36
+ return false
37
+ end
38
+
39
+ # call-seq:
40
+ # chainable.chain { |arg,...| block } -> chainable
41
+ #
42
+ # Adds _block_ to the end of _chainable_'s chain, then returns _chainable_.
43
+ #
44
+ # chainable.chain { puts 1; return 'called 1' }.chain { puts 2; return 'called 2' }
45
+ #
46
+ # chainable.call_chain #=> "called 1"
47
+ # chainable.call_chain #=> "called 2"
48
+ # chainable.call_chain #=> false
49
+ #
50
+ # produces:
51
+ #
52
+ # 1
53
+ # 2
54
+ #
55
+ def chain(&block)
56
+ @chain ||= []
57
+ @chain << block
58
+ return self
59
+ end
60
+
61
+ # call-seq:
62
+ # chainable.clear_chain -> chainable
63
+ #
64
+ # Returns _chainable_ with its chain emptied.
65
+ #
66
+ # chainable.chain { 1 + 1 }
67
+ #
68
+ # chainable.clear_chain.call_chain #=> false
69
+ #
70
+ def clear_chain
71
+ @chain ||= []
72
+ @chain.clear
73
+ return self
74
+ end
75
+ end
@@ -0,0 +1,204 @@
1
+ # Module +CodeEvents+ enables event-driven programming by giving objects of
2
+ # the including class the ability to define custom observers and callbacks.
3
+ #
4
+ # class Looper
5
+ # include CodeEvents
6
+ #
7
+ # def initialize(range)
8
+ # @range = range
9
+ # end
10
+ #
11
+ # def run(min, max)
12
+ # self.fire(:start)
13
+ # @range.each do |i|
14
+ # self.fire(:minimum, 0, i) && next if i == min
15
+ # self.fire(:maximum, 0, i) && next if i == max
16
+ # puts i
17
+ # end
18
+ # return self
19
+ # end
20
+ # end
21
+ #
22
+ # looper = Looper.new(1..10) #=> #<Looper:0x34fe78>
23
+ #
24
+ # looper.upon :start do
25
+ # puts "The loop has begun"
26
+ # end
27
+ #
28
+ # min_proc = Proc.new {|i| puts "Minimum amount reached: %s" % i } #=> #<Proc:0x3e72a9>
29
+ # max_proc = Proc.new {|i| puts "Maximum amount reached: %s" % i } #=> #<Proc:0x3ea147>
30
+ # looper.upon(:minimum => min_proc, :maximum => max_proc) #=> #<Looper:0x34fe78>
31
+ #
32
+ # looper.run(3,8) #=> #<Looper:0x34fe78>
33
+ #
34
+ # produces:
35
+ #
36
+ # The loop has begun
37
+ # 1
38
+ # 2
39
+ # Minimum amount reached: 3
40
+ # 4
41
+ # 5
42
+ # 6
43
+ # 7
44
+ # Maximum amount reached: 8
45
+ # 9
46
+ # 10
47
+ #
48
+ module CodeEvents
49
+ # call-seq:
50
+ # obj.fire(sym) -> obj
51
+ # obj.fire(sym, delay, arg, ...) -> obj
52
+ #
53
+ # Instructs _obj_ to call the +Proc+ objects associated with the event
54
+ # _sym_, then returns _obj_. Optionally delays the firing of the event by
55
+ # _delay_ milliseconds. The second form allows passing of arguments to the
56
+ # event's +Proc+ objects, but if any arguments are passed, the first must be
57
+ # the _delay_ argument.
58
+ #
59
+ # obj.upon(:A) {|x| puts x } #=> obj
60
+ # obj.upon(:B) {|y| puts y } #=> obj
61
+ # obj.upon(:C) { puts 'fire 3' } #=> obj
62
+ #
63
+ # obj.fire(:A, 500, 'fire 1').fire(:B, 0, 'fire 2').fire(:C)
64
+ #
65
+ # produces:
66
+ #
67
+ # fire 2
68
+ # fire 3
69
+ # fire 1
70
+ #
71
+ def fire(sym, delay, *args)
72
+ name = sym.to_sym
73
+ return self unless @code_events && events_group = @code_events[name]
74
+ events_group.each {|proc| proc.process_event(self, delay, args) }
75
+ return self
76
+ end
77
+
78
+ # call-seq:
79
+ # obj.ignore -> obj
80
+ # obj.ignore(sym) -> obj
81
+ # obj.ignore(sym, &proc) -> obj
82
+ #
83
+ # Instructs _obj_ to ignore the specified event, then returns _obj_.
84
+ #
85
+ # In the first form, all event-related +Proc+ objects not marked
86
+ # as "unignorable" are removed from _obj_.
87
+ #
88
+ # obj.upon(:A, true) { puts '1st executed' } #=> obj
89
+ # obj.upon(:A) { puts '2nd executed' } #=> obj
90
+ # obj.upon(:B) { puts '3rd executed' } #=> obj
91
+ #
92
+ # obj.ignore #=> obj
93
+ # obj.fire(:A).fire(:B) #=> obj
94
+ #
95
+ # produces:
96
+ #
97
+ # 1st executed
98
+ #
99
+ # In the second form, only the ignorable +Proc+ objects associated with the
100
+ # event _sym_ are removed.
101
+ #
102
+ # obj.upon(:A, true) { puts '1st executed' } #=> obj
103
+ # obj.upon(:A) { puts '2nd executed' } #=> obj
104
+ # obj.upon(:B) { puts '3rd executed' } #=> obj
105
+ #
106
+ # obj.ignore(:A) #=> obj
107
+ # obj.fire(:A).fire(:B) #=> obj
108
+ #
109
+ # produces:
110
+ #
111
+ # 1st executed
112
+ # 3rd executed
113
+ #
114
+ # In the third form, only the +Proc+ object passed in as <i>&proc</i> is
115
+ # removed.
116
+ #
117
+ # proc_1 = Proc.new { puts 'Proc 1 executed' } #=> #<Proc:0x3e78ee>
118
+ # proc_2 = Proc.new { puts 'Proc 2 executed' } #=> #<Proc:0x3e888a>
119
+ #
120
+ # obj.upon(:A, &proc_1) #=> obj
121
+ # obj.upon(:A, &proc_2) #=> obj
122
+ #
123
+ # obj.ignore(:A, &proc_1) #=> obj
124
+ # obj.fire(:A) #=> obj
125
+ #
126
+ # produces:
127
+ #
128
+ # Proc 2 executed
129
+ #
130
+ def ignore(sym, &block)
131
+ if sym
132
+ name = sym.to_sym
133
+ return self unless @code_events && events_group = @code_events[name]
134
+ if block
135
+ events_group.delete(block) unless `block.__block__.__unignorable__`
136
+ else
137
+ events_group.each {|proc| self.disregard(name, &proc) }
138
+ end
139
+ else
140
+ @code_events.each_key {|name| self.disregard(name) }
141
+ end
142
+ return self
143
+ end
144
+
145
+ # call-seq:
146
+ # obj.upon(sym, unignorable = false) { |arg,...| block } -> obj
147
+ # obj.upon(hash) -> obj
148
+ #
149
+ # Stores a number of +Proc+ objects to be called when the event _sym_ is
150
+ # fired.
151
+ #
152
+ # The first form adds _block_ to the array of +Proc+ objects executed when
153
+ # the event _sym_ is fired, then returns _obj_. If _unignorable_ is set to
154
+ # +true+, the _block_ will be executed when _sym_ fires, regardless of
155
+ # whether it has been ignored.
156
+ #
157
+ # obj.upon(:A, true) { puts '1st executed' } #=> obj
158
+ # obj.upon(:A, true) { puts '2nd executed' } #=> obj
159
+ # obj.upon(:A) { puts '3rd executed' } #=> obj
160
+ #
161
+ # obj.ignore(:A) #=> obj
162
+ # obj.fire(:A) #=> obj
163
+ #
164
+ # produces:
165
+ #
166
+ # 1st executed
167
+ # 2nd executed
168
+ #
169
+ # The second form takes a hash of +Proc+ objects keyed by event name,
170
+ # running <tt>obj.upon(name, &proc)</tt> on each key-value pair, then
171
+ # returns _obj_.
172
+ #
173
+ # proc_1 = Proc.new { puts '1st executed' } #=> #<Proc:0x3e78ee>
174
+ # proc_2 = Proc.new { puts '2nd executed' } #=> #<Proc:0x3e888a>
175
+ #
176
+ # obj.upon(:A => proc_1, :B => proc_2) #=> obj
177
+ # obj.fire(:B) #=> obj
178
+ #
179
+ # produces
180
+ #
181
+ # 2nd executed
182
+ #
183
+ def upon(sym_or_hash, unignorable, &block)
184
+ if sym_or_hash.instance_of?(Hash)
185
+ sym_or_hash.each {|name,proc| self.upon(name, &proc) }
186
+ return self
187
+ else
188
+ name = sym_or_hash.to_sym
189
+ @code_events ||= {}
190
+ @code_events[name] ||= []
191
+ @code_events[name] << block
192
+ `block.__block__.__unignorable__=typeof(unignorable)=='function'?false:unignorable`
193
+ return self
194
+ end
195
+ end
196
+
197
+ class ::Proc # :nodoc:
198
+ def process_event(context, delay, args_array) # :nodoc:
199
+ `var f=this.__block__.__unbound__,event_function=function(){return f.apply(context,args_array);}`
200
+ `if(delay){return setTimeout(event_function,delay);}`
201
+ `event_function()`
202
+ end
203
+ end
204
+ end
@@ -0,0 +1,142 @@
1
+ # Class +Cookie+ governs the writing and accessing of cookies in the browser.
2
+ #
3
+ # A cookie is a key-value pair stored by your browser as text data. If you
4
+ # know a cookie's key, you can read or overwrite its value, or reassign any of
5
+ # a number of parameters.
6
+ #
7
+ # Instances of class +Cookie+ are temporary holders for browser-based cookie
8
+ # data. When you create a new +Cookie+ object using <tt>Cookie.new</tt> or
9
+ # update an existing +Cookie+ object using <tt>Cookie#update</tt>, class
10
+ # +Cookie+ writes the key-value pair and the cookie's parameters to the
11
+ # browser's cookie file. You can then read the value of the cookie immediately
12
+ # or during subsequent visits using <tt>Cookie.read</tt>.
13
+ #
14
+ # The following parameters can be set for a +Cookie+ object:
15
+ #
16
+ # *Required*
17
+ # _key_:: The unique (per domain) identifier by which you identify a cookie.
18
+ # _value_:: The string of data associated with a given key.
19
+ #
20
+ # *Optional*
21
+ # _duration_:: The amount of time (in days) before the cookie should expire.
22
+ # _domain_:: The domain to which the cookie should be sent.
23
+ # _path_:: The path, relative to the domain, where the cookie is active.
24
+ # _secure_:: If +true+, the browser will use SSL when sending the cookie.
25
+ #
26
+ # The browser can hold up to 20 cookies from a single domain.
27
+ #
28
+ class Cookie
29
+ OPTIONS = {
30
+ :duration => nil,
31
+ :domain => nil,
32
+ :path => nil,
33
+ :secure => false,
34
+ :document => Document
35
+ }
36
+
37
+ attr_accessor :key, :value, :duration, :domain, :path, :secure, :document
38
+
39
+ # call-seq:
40
+ # Cookie.new(key, value, options = {}) -> cookie
41
+ #
42
+ # Returns a new +Cookie+ object with the given parameters and stores the
43
+ # data in the browser as cookie data. If the browser already has a cookie
44
+ # that matches _key_, that cookie's parameters will be overwritten.
45
+ #
46
+ # Cookie.new(:user_jds, '2237115568') #=> #<Cookie: @key="user_jds" @value="2237115568">
47
+ # Cookie.read(:user_jds) #=> '2237115568'
48
+ #
49
+ # Cookie.new(:user_jds, '8557acb0') #=> #<Cookie: @key="user_jds" @value="8557acb0">
50
+ # Cookie.read(:user_jds) #=> '8557acb0'
51
+ #
52
+ def initialize(key, value, options = {})
53
+ self.key = key
54
+ self.update(value, OPTIONS.merge(options))
55
+ end
56
+
57
+ # call-seq:
58
+ # Cookie.read(key) -> string
59
+ #
60
+ # Returns the string value of the cookie named _key_, or +nil+ if no such
61
+ # cookie exists.
62
+ #
63
+ # c = Cookie.new(:user_jds, '2237115568', :domain => '.example.com')
64
+ #
65
+ # Cookie.read(:user_jds) #=> '2237115568'
66
+ #
67
+ # This method can be used to test whether a cookie with the name _key_
68
+ # exists in the browser.
69
+ #
70
+ # Cookie.new(:user_jds, '8557acb0') unless Cookie.read(:user_jds)
71
+ #
72
+ def self.read(key)
73
+ value = `#{OPTIONS[:document].native}.cookie.match('(?:^|;)\\s*' + #{Regexp.escape(key)}.__value__ + '=([^;]*)')`
74
+ return value ? `$q(decodeURIComponent(value[1]))` : nil
75
+ end
76
+
77
+ # call-seq:
78
+ # Cookie.store(cookie) -> cookie
79
+ #
80
+ # Writes the given cookie to the browser, then returns _cookie_. This method
81
+ # is called internally by <tt>Cookie.new</tt> and <tt>Cookie#update</tt>.
82
+ #
83
+ def self.store(cookie)
84
+ `var str = cookie.m$key().__value__ + '=' + encodeURIComponent(cookie.m$value().__value__)`
85
+ `str += '; domain=' + cookie.m$domain().__value__` if cookie.domain
86
+ `str += '; path=' + cookie.m$path().__value__` if cookie.path
87
+ if cookie.duration
88
+ `date = new Date()`
89
+ `date.setTime(date.getTime() + cookie.m$duration() * 86400000)`
90
+ `str += '; expires=' + date.toGMTString()`
91
+ end
92
+ `str += '; secure'` if cookie.secure
93
+
94
+ `#{cookie.document.native}.cookie = str`
95
+ return cookie
96
+ end
97
+
98
+ # call-seq:
99
+ # cookie.destroy -> true
100
+ #
101
+ # Expires _cookie_, then returns +true+.
102
+ #
103
+ # c = Cookie.new(:user_jds, '2237115568', :duration => 14)
104
+ #
105
+ # c.destroy #=> true
106
+ # Cookie.read(:user_jds) #=> nil
107
+ #
108
+ def destroy
109
+ self.update('',:duration => -1)
110
+ end
111
+
112
+ # call-seq:
113
+ # cookie.inspect -> string
114
+ #
115
+ # Returns a string representing _cookie_ and its key-value data.
116
+ #
117
+ # c = Cookie.new(:user_jds, '2237115568', :duration => 14)
118
+ #
119
+ # c.inspect #=> #<Cookie: @key="user_jds" @value="2237115568">
120
+ #
121
+ def inspect
122
+ "#<Cookie: @key=#{self.key.inspect} @value=#{self.value.inspect}>"
123
+ end
124
+
125
+ # call-seq:
126
+ # cookie.update(value, options = {}) -> cookie
127
+ #
128
+ # Updates _cookie_ with the given parameters, then writes the cookie data to
129
+ # the browser.
130
+ #
131
+ # c = Cookie.new(:user_jds, '2237115568', :duration => 14)
132
+ #
133
+ # Cookie.read(:user_jds) #=> '2237115568'
134
+ # c.update('8557acb0') #=> #<Cookie: @key="user_jds" @value="8557acb0">
135
+ # Cookie.read(:user_jds) #=> '8557acb0'
136
+ #
137
+ def update(value, options = {})
138
+ self.value = value
139
+ options.each {|k,v| self.send("#{k}=",v) }
140
+ Cookie.store(self)
141
+ end
142
+ end