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,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>