red 4.1.0 → 4.1.1

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.
@@ -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