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,48 @@
1
+ # Classes mixing in <tt>Store</tt> gain the ability to store
2
+ # properties related to DOM objects without causing memory-sapping circular
3
+ # references.
4
+ #
5
+ module Store
6
+ `#{Store}.__table__={}`
7
+
8
+ # call-seq:
9
+ # obj.delete(sym) -> obj
10
+ #
11
+ # Deletes the property _sym_, then returns _obj_.
12
+ #
13
+ def delete(property)
14
+ `var stringId=''+this.__id__`
15
+ storage = `#{Store}.__table__[stringId]`
16
+ storage = `#{Store}.__table__[stringId]=#{{}}` unless storage
17
+ value = storage[property]
18
+ value = nil if `value==null`
19
+ `delete storage.__contents__[property.m$hash()]`
20
+ return value
21
+ end
22
+
23
+ # call-seq:
24
+ # obj.retrieve(sym, default = nil) -> object or default
25
+ #
26
+ # Returns the property _sym_, or _default_ if the property is not defined.
27
+ #
28
+ def fetch(property, deflt = nil)
29
+ `var stringId=''+this.__id__`
30
+ storage = `#{Store}.__table__[stringId]`
31
+ storage = `#{Store}.__table__[stringId]=#{{}}` unless storage
32
+ value = storage[property.to_sym]
33
+ value = storage[property.to_sym] = deflt unless `$T(value)||value==false`
34
+ return value
35
+ end
36
+
37
+ # call-seq:
38
+ # obj.store(hash) -> object
39
+ #
40
+ # Stores the key-value pairs as properties of _obj_, then returns _obj_.
41
+ #
42
+ def store(hash)
43
+ `var stringId=''+this.__id__`
44
+ storage = `#{Store}.__table__[stringId]`
45
+ storage = `#{Store}.__table__[stringId]=#{{}}` unless storage
46
+ hash.each {|property,value| storage[property.to_sym] = value }
47
+ end
48
+ end
@@ -0,0 +1,199 @@
1
+ require 'code_events'
2
+
3
+ class Transform
4
+ include CodeEvents
5
+ OPTIONS = {:fps => 50,
6
+ :unit => false,
7
+ :duration => 500,
8
+ :link => 'ignore'
9
+ }
10
+
11
+ Durations = {:short => 250,
12
+ :normal => 500,
13
+ :long => 1000
14
+ }
15
+
16
+
17
+ `$clear = function(timer){clearTimeout(timer);clearInterval(timer);return nil;};`
18
+
19
+ # need periodical fot setInterval on Effect#step. Find out what
20
+ # setInterval is when there is net again.
21
+ `Function.prototype.create = function(options){
22
+ var self = this;
23
+ options = options || {};
24
+ return function(event){
25
+ var args = options.arguments;
26
+ args = (args != undefined) ? $splat(args) : Array.slice(arguments, (options.event) ? 1 : 0);
27
+ if (options.event) args = [event || window.event].extend(args);
28
+ var returns = function(){
29
+ return self.apply(options.bind || null, args);
30
+ };
31
+ if (options.delay) return setTimeout(returns, options.delay);
32
+ if (options.periodical) return setInterval(returns, options.periodical);
33
+ return returns();
34
+ };
35
+ }`
36
+ `Function.prototype.periodical = function(periodical, bind, args){
37
+ return this.create({bind: bind, arguments: args, periodical: periodical})();
38
+ };`
39
+
40
+ def self.compute(from, to, delta)
41
+ `(to - from) * delta + from`
42
+ end
43
+
44
+ def initialize(options={})
45
+ @subject = @subject || self
46
+ @options = OPTIONS.merge(options)
47
+ @options[:duration] = Transform::Durations[@options[:duration]] || @options[:duration].to_i
48
+ wait = @options[:wait]
49
+ @options[:link] = 'cancel' if wait === false
50
+ end
51
+
52
+ def step
53
+ `
54
+ var time = +new Date
55
+ if (time < this.__time__ + #{@options[:duration]}){
56
+ var delta = this.__transition__((time - this.__time__) / #{@options[:duration]});
57
+ this.m$set(this.m$compute(this.__from__, this.__to__, delta));
58
+ } else {
59
+ this.m$set(this.m$compute(this.__from__, this.__to__, 1));
60
+ this.m$complete();
61
+ }
62
+ `
63
+ return nil
64
+ end
65
+
66
+ def set(now)
67
+ return now
68
+ end
69
+
70
+ def compute(from, to, delta)
71
+ return Transform.compute(from, to, delta)
72
+ end
73
+
74
+ def check(caller)
75
+ `
76
+ if (!this.__timer__) return true;
77
+ switch (#{@options[:link]}){
78
+ case 'cancel': this.cancel(); return true;
79
+ case 'chain' : this.chain(caller.bind(this, Array.slice(arguments, 1))); return false;
80
+ }`
81
+ return false
82
+ end
83
+
84
+ def start(from,to)
85
+ `if (!this.m$check(arguments.callee, from, to)) return this`
86
+ `this.__from__ = from`
87
+ `this.__to__ = to`
88
+ `this.__time__ = 0`
89
+ `this.__transition__ = function(p){
90
+ return -(Math.cos(Math.PI * p) - 1) / 2;
91
+ }`
92
+ self.start_timer
93
+
94
+ self.fire(:start)
95
+
96
+ return self
97
+ end
98
+
99
+ def complete
100
+ self.fire(:completion)
101
+ self.stop_timer
102
+ self
103
+ end
104
+
105
+ def cancel
106
+ self.fire(:cancellation)
107
+ self.stop_timer
108
+ self
109
+ end
110
+
111
+ def pause
112
+ self.stop_timer
113
+ self
114
+ end
115
+
116
+ def resume
117
+ self.start_timer
118
+ self
119
+ end
120
+
121
+ def stop_timer
122
+ `if (!this.__timer__) return false`
123
+ `this.__time__ = (+new Date) - this.__time__`
124
+ `this.__timer__ = $clear(this.__timer__)`
125
+ return true
126
+ end
127
+
128
+ def start_timer
129
+ `if (this.__timer__) return false`
130
+ `this.__time__ = (+new Date) - this.__time__`
131
+ `this.__timer__ = this.m$step.periodical(Math.round(1000 / #{@options[:fps]}), this)`
132
+ return true
133
+ end
134
+
135
+ module Parser
136
+ class Color
137
+ def self.hex_to_array(color)
138
+ `var hex = color.match(/^#?(\\w{1,2})(\\w{1,2})(\\w{1,2})$/).slice(1)`
139
+ `var rgb = []
140
+ for(i = 0, l = hex.length; i < hex.length; i++){
141
+ value = hex[i]
142
+ if (value.length == 1) value += value;
143
+ rgb[i] = parseInt(value,16);
144
+ }`
145
+ `rgb`
146
+ end
147
+
148
+ def self.compute(from, to, delta)
149
+ rgb = []
150
+ from.each do |i|
151
+ rgb << `Math.round(#{::Transform.compute(from[i], to[i], delta)})`
152
+ end
153
+ `'rgb(' + rgb + ')'`
154
+ end
155
+
156
+ def self.parse(value)
157
+ `value = value.__value__ || String(value)`
158
+ `if (value.match(/^#[0-9a-f]{3,6}$/i)) return #{Transform::Parser::Color.hex_to_array(value)}`
159
+ `((value = value.match(/(\\d+),\\s*(\\d+),\\s*(\\d+)/))) ? #{[value[1], value[2], value[3]]} : false`
160
+ end
161
+
162
+ def self.serve(value, unit)
163
+ value
164
+ end
165
+ end
166
+
167
+ class Number
168
+ def self.compute(from,to,delta)
169
+ Transform.compute(from,to,delta)
170
+ end
171
+
172
+ def self.parse(value)
173
+ `value = value.__value__ || String(value)`
174
+ `parsed = parseFloat(value)`
175
+ `(parsed || parsed == 0) ? parsed : false`
176
+ end
177
+
178
+ def self.serve(value, unit)
179
+ return (unit) ? value + unit : value
180
+ end
181
+ end
182
+
183
+ # can't tween a string
184
+ class String
185
+ def self.parse(value)
186
+ false
187
+ end
188
+ def self.compute(from, to, delta)
189
+ return to
190
+ end
191
+
192
+ def self.serve(value, unit)
193
+ return value
194
+ end
195
+ end
196
+ end
197
+
198
+ Parsers = [Parser::Color, Parser::Number, Parser::String]
199
+ end
@@ -0,0 +1,66 @@
1
+ require 'transform'
2
+
3
+ class Tween < Transform
4
+ def initialize(element, options)
5
+ @element = @subject = element
6
+ super(options)
7
+ end
8
+
9
+ def start(property, from, to)
10
+ @property = property
11
+ parsed = self.prepare(@element, property, [from, to])
12
+ super(parsed[:from], parsed[:to])
13
+ end
14
+
15
+ def prepare(element, property, from_to)
16
+ to = from_to[1]
17
+ # set a default to, if one isn't set
18
+ if (!to)
19
+ from_to[1] = from_to[0]
20
+ from_to[0] = element.styles[property]
21
+ end
22
+ # convert from and to to numeric values from hex/string if possible
23
+ parsed_from_to = []
24
+ from_to.each do |val|
25
+ parsed_from_to << self.parse(val)
26
+ end
27
+ return {:from => parsed_from_to[0], :to => parsed_from_to[1]}
28
+ end
29
+
30
+ # parses a value by its Parser, returning a hash of the parsed value and parser
31
+ def parse(value)
32
+ value = value.to_s.split(' ')
33
+ returns = []
34
+ value.each do |val|
35
+ ::Transform::Parsers.each do |parser|
36
+ parsed = parser.parse(val)
37
+ `console.log(parsed)`
38
+ found = {:value => parsed, :parser => parser} if (parsed)
39
+ end
40
+ found = found || {:value => val, :parser => ::Transform::Parser::String}
41
+ returns << found
42
+ end
43
+ return returns
44
+ end
45
+
46
+ def set(current_value)
47
+ self.render(@element, @property, current_value[0], @options[:unit])
48
+ return self
49
+ end
50
+
51
+ def render(element, property, current_value, unit)
52
+ element.set_style(property, self.serve(current_value, unit))
53
+ end
54
+
55
+ def serve(value, unit)
56
+ value[:parser].serve(value[:value], unit)
57
+ end
58
+
59
+ def compute(from,to,delta)
60
+ computed = []
61
+ (`Math.min(from.length, to.length)`).times do |i|
62
+ computed << {:value => from[i][:parser].compute(from[i][:value], to[i][:value], delta), :parser => from[i][:parser]}
63
+ end
64
+ return computed
65
+ end
66
+ end
@@ -0,0 +1,215 @@
1
+ require 'event'
2
+
3
+ # The +UserEvents+ module mixes in methods for handling user-generated events
4
+ # such as mouse gestures and keystrokes. +UserEvents+ is also responsible for
5
+ # defining new event types based on native events.
6
+ #
7
+ # Only +Document+, +Window+, and objects of class +Element+ respond to
8
+ # +UserEvents+ methods. See module +CodeEvents+ for information on defining
9
+ # and handling custom callback events.
10
+ #
11
+ module UserEvents
12
+ `c$UserEvents.mousecheck=function(element,event){
13
+ var related=event.__related_target__,el=element.__native__;
14
+ if(related===nil||related==undefined){return true;};
15
+ if(related===false){return false;};
16
+ return((el!==document)&&(related!=el)&&(related.prefix!='xul')&&!(el.contains?el.contains(related):!!(el.compareDocumentPosition(el)&16)))
17
+ }`
18
+
19
+ NATIVE_EVENTS = {
20
+ # mouse buttons
21
+ :click => 2,
22
+ :dblclick => 2,
23
+ :mouseup => 2,
24
+ :mousedown => 2,
25
+ :contextmenu => 2,
26
+ # mouse wheel
27
+ :mousewheel => 2,
28
+ :DOMMouseScroll => 2,
29
+ # mouse movement
30
+ :mouseover => 2,
31
+ :mouseout => 2,
32
+ :mousemove => 2,
33
+ :selectstart => 2,
34
+ :selectend => 2,
35
+ # keyboard
36
+ :keydown => 2,
37
+ :keypress => 2,
38
+ :keyup => 2,
39
+ # form elements
40
+ :focus => 2,
41
+ :blur => 2,
42
+ :change => 2,
43
+ :reset => 2,
44
+ :select => 2,
45
+ :submit => 2,
46
+ # window
47
+ :load => 1,
48
+ :unload => 1,
49
+ :beforeunload => 1,
50
+ :resize => 1,
51
+ :move => 1,
52
+ :DOMContentLoaded => 1,
53
+ :readystatechange => 1,
54
+ # misc
55
+ :error => 1,
56
+ :abort => 1,
57
+ :scroll => 1
58
+ }
59
+
60
+ DEFINED_EVENTS = {
61
+ :mouse_enter => {:base => 'mouseover', :condition => proc(`c$UserEvents.mousecheck`) },
62
+ :mouse_leave => {:base => 'mouseout', :condition => proc(`c$UserEvents.mousecheck`) },
63
+ :mouse_wheel => {:base => gecko? ? 'DOMMouseScroll' : 'mousewheel' }
64
+ }
65
+
66
+ # call-seq:
67
+ # UserEvents.define(sym, { :base => symbol [, ...] }) -> true
68
+ #
69
+ # Adds a new event type _sym_ to the UserEvents::DEFINED_EVENTS hash with
70
+ # the given options. The following options can be set:
71
+ #
72
+ # *Required*
73
+ # <i>base</i>:: A symbol representing the native event type upon
74
+ # which _sym_ will be based.
75
+ #
76
+ # *Optional*
77
+ # <i>condition</i>:: A +Proc+ object with parameters
78
+ # <tt>|element, event|</tt> which, if returning +false+
79
+ # when evaluated for a given event, kills the event.
80
+ # <i>on_listen</i>:: A +Proc+ object with parameters
81
+ # <tt>|element, listener_proc|</tt> that is evaluated
82
+ # when _sym_ is passed to <tt>UserEvents#listen</tt>.
83
+ # <i>on_unlisten</i>:: A +Proc+ object with parameters
84
+ # <tt>|element, listener_proc|</tt> that is evaluated
85
+ # when _sym_ is passed to <tt>UserEvents#unlisten</tt>.
86
+ #
87
+ # --------------------------------------------------------------------------
88
+ #
89
+ # condition = proc {|element,event| event.shift? } #=> #<Proc:0x393825>
90
+ # on_listen = proc {|element,listener_proc| puts "%s responds to shift-click with %s" % [element.inspect, listener_proc] } #=> #<Proc:0x3935da>
91
+ #
92
+ # UserEvents.define(:shift_click, :base => 'click', :condition => condition, :on_listen => on_listen) #=> true
93
+ #
94
+ # Document['#example'].listen :shift_click do |element,event|
95
+ # puts "%s was shift-clicked" % element.inspect
96
+ # end
97
+ #
98
+ # produces:
99
+ #
100
+ # #<Element: DIV id="example"> responds to shift-click with #<Proc:0x3962ae>
101
+ #
102
+ # shift-clicking element '#example' produces:
103
+ #
104
+ # #<Element: DIV id="example"> was shift-clicked
105
+ #
106
+ def self.define(sym, hash = {})
107
+ DEFINED_EVENTS[sym.to_sym] = hash
108
+ return true
109
+ end
110
+
111
+ def self.included(base) # :nodoc:
112
+ raise(TypeError, 'only class Element and the singleton objects Window and Document may include UserEvents; use CodeEvents instead') unless base == `c$Element`
113
+ end
114
+
115
+ def self.extended(base) # :nodoc:
116
+ raise(TypeError, 'only Document and Window may be extended with UserEvents; use CodeEvents instead') unless [`c$Document`, `c$Window`].include?(base)
117
+ end
118
+
119
+ # call-seq:
120
+ # obj.listen(sym) { |element,event| block } -> obj
121
+ #
122
+ # Adds a listener to _obj_ for a native or defined user event type _sym_,
123
+ # then returns _obj_.
124
+ #
125
+ # Document['#example'].listen :click do |element, event|
126
+ # puts "%s was clicked" % element.inspect
127
+ # end
128
+ #
129
+ # clicking element '#example' produces:
130
+ #
131
+ # #<Element: DIV id="example"> was clicked
132
+ #
133
+ def listen(sym, &block)
134
+ type = sym.to_sym
135
+ events = @events ||= {}
136
+ events[type] ||= {}
137
+ return self if events[type][block]
138
+
139
+ custom = DEFINED_EVENTS[type]
140
+ condition = block
141
+ real_type = type
142
+
143
+ if custom
144
+ custom[:on_listen].call(self, block) if custom[:on_listen]
145
+ if custom[:condition]
146
+ condition = lambda {|element,event| custom[:condition].call(element,event) ? block.call(element,event) : true }
147
+ end
148
+ real_type = (custom[:base] || real_type).to_sym
149
+ end
150
+
151
+ listener = lambda { block.call(self,nil); }
152
+ native_event = NATIVE_EVENTS[real_type]
153
+
154
+ if native_event
155
+ if native_event == 2
156
+ listener = lambda do |native_event|
157
+ event = `$v(native_event)`
158
+ event.kill! if condition.call(self,event) == false
159
+ end
160
+ end
161
+ self.add_listener(real_type, &listener)
162
+ end
163
+
164
+ events[type][block] = listener
165
+ return self
166
+ end
167
+
168
+ def add_listener(sym, &block) # :nodoc:
169
+ `var el=this.__native__,type=sym.__value__,fn=block.__block__`
170
+ `if(type==='unload'){var old=fn,that=this;fn=function(){that.m$remove_listener($q('unload'),fn);old();};}else{var collected = {};collected[this.__id__]=this}` # TODO: put this element into "collected"
171
+ `if(el.addEventListener){el.addEventListener(type,fn,false);}else{el.attachEvent('on'+type,fn);}`
172
+ return self
173
+ end
174
+
175
+ # call-seq:
176
+ # obj.unlisten(sym, &proc) -> obj
177
+ #
178
+ # Unsets the function _proc_ as a listener for event type _sym_, then
179
+ # returns _obj_. _proc_ must be the self-same object originally assigned as
180
+ # a listener.
181
+ #
182
+ # proc_1 = proc {|element,event| puts "%s was clicked" % element.inspect }
183
+ # proc_2 = proc {|element,event| puts "%s has two listeners" % element.inspect }
184
+ # elem = Document['#example']
185
+ #
186
+ # elem.listen(:click, proc_1).listen(:click, proc_2) #=> #<Element: DIV id="example">
187
+ # elem.unlisten(:click, proc_2) #=> #<Element: DIV id="example">
188
+ #
189
+ # clicking element '#example' produces:
190
+ #
191
+ # #<Element: DIV id="example"> was clicked
192
+ #
193
+ def unlisten(sym, &block)
194
+ type = sym.to_sym
195
+ events = @events
196
+ return self unless events && events[type] && events[type][block]
197
+
198
+ listener = events[type].delete(block)
199
+ custom = DEFINED_EVENTS[type]
200
+
201
+ if custom
202
+ custom[:on_unlisten].call(self, block) if custom[:on_unlisten]
203
+ type = (custom[:base] || type).to_sym
204
+ end
205
+
206
+ self.remove_listener(type, &listener) if NATIVE_EVENTS[type]
207
+ return self
208
+ end
209
+
210
+ def remove_listener(sym, &block) # :nodoc:
211
+ `var el=this.__native__,type=sym.__value__,fn=block.__block__`
212
+ `if(this.removeEventListener){this.removeEventListener(type,fn,false);}else{this.detachEvent('on'+type,fn);}`
213
+ return self
214
+ end
215
+ end
@@ -0,0 +1,21 @@
1
+ class Element
2
+ # knows element valid attributes & element valid styles
3
+ # if you try to set or get invalid attribute or style, warns A
4
+ # if you try to set an invalid value to an attribute or style, warns B
5
+ module Validator
6
+ # attributes shared by all elements; checked last
7
+ ATTRIBUTES = {
8
+ :class => lambda {|klass| true }
9
+ :id => lambda {|id| true }
10
+ :style => lambda {|style| true }
11
+ :title => lambda {|title| true }
12
+ }
13
+
14
+ class A
15
+ ATTRIBUTES = {
16
+ :href => lambda {|href| true }
17
+ :rel => lambda {|href| true }
18
+ }
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,11 @@
1
+ module Window
2
+ `this.__native__ = window`
3
+
4
+ def self.window
5
+ self
6
+ end
7
+
8
+ def self.document
9
+ ::Document
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
2
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
3
+ <head>
4
+ <title>Redspec</title>
5
+ <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
6
+ <link rel="stylesheet" type="text/css" media="screen" href="lib/stylesheets/specs.sass" />
7
+ <script type="text/javascript" src="lib/red_spec/includes.red"></script>
8
+ </head>
9
+ <body>
10
+ </body>
11
+ </html>