red 4.1.0 → 4.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Manifest.txt +25 -0
- data/lib/red/version.rb +1 -1
- data/lib/source/redshift/accessors.rb +580 -0
- data/lib/source/redshift/browser.rb +150 -0
- data/lib/source/redshift/chainable.rb +75 -0
- data/lib/source/redshift/code_events.rb +204 -0
- data/lib/source/redshift/cookie.rb +142 -0
- data/lib/source/redshift/document.rb +216 -0
- data/lib/source/redshift/element.rb +417 -0
- data/lib/source/redshift/event.rb +312 -0
- data/lib/source/redshift/redshift.red +5 -0
- data/lib/source/redshift/request.rb +276 -0
- data/lib/source/redshift/selectors.rb +374 -0
- data/lib/source/redshift/situated.rb +328 -0
- data/lib/source/redshift/store.rb +48 -0
- data/lib/source/redshift/transform.rb +199 -0
- data/lib/source/redshift/tween.rb +66 -0
- data/lib/source/redshift/user_events.rb +215 -0
- data/lib/source/redshift/validator.rb +21 -0
- data/lib/source/redshift/window.rb +11 -0
- data/lib/source/redspec/index.html +11 -0
- data/lib/source/redspec/lib/red_spec/red_spec.red +525 -0
- data/lib/source/redspec/lib/stylesheets/specs.sass +290 -0
- metadata +27 -2
@@ -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
|
+
<!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>
|