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