wee 0.7.0 → 0.8.0

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/ChangeLog CHANGED
@@ -1,3 +1,99 @@
1
+ Mon Apr 4 18:45:28 CET 2005 Michael Neumann <mneumann@ntecs.de>
2
+
3
+ * examples/ajax/ajax.rb, examples/ajax/ajax.js,
4
+ lib/wee/core/presenter.rb, lib/wee/renderer/html/brushes.rb: Added minimal
5
+ Ajax update support.
6
+
7
+ Mon Apr 4 14:32:34 CET 2005 Michael Neumann <mneumann@ntecs.de>
8
+
9
+ * lib/wee/abstractsession.rb, lib/wee/session.rb,
10
+ lib/wee/pageless/session.rb, examples/live-update.rb: refactored
11
+ Session classes.
12
+
13
+ * lib/wee/core/presenter.rb: added send_response method, to send a
14
+ premature response.
15
+
16
+ Mon Apr 4 13:03:08 CET 2005 Michael Neumann <mneumann@ntecs.de>
17
+
18
+ * lib/wee/pageless/session.rb, lib/wee/pageless/application.rb: Remove
19
+ dependency of WEBrick::Cookie, use CGI::Cookie instead.
20
+
21
+ * lib/wee/adaptors/fastcgi.rb: Added preliminary FastCGI adaptor
22
+ (does not work yet with Pageless applications).
23
+
24
+ * lib/wee/adaptors/webrick.rb: reordered line
25
+
26
+ Sun Apr 3 13:24:04 CET 2005 Michael Neumann <mneumann@ntecs.de>
27
+
28
+ * lib/wee/adaptors/webrick.rb: doc fix
29
+
30
+ Sun Apr 3 13:11:07 CET 2005 Michael Neumann <mneumann@ntecs.de>
31
+
32
+ * lib/wee/core/presenter.rb, lib/wee/core/component.rb,
33
+ lib/wee/core/decoration.rb, lib/wee/session.rb,
34
+ lib/wee/pageless/session.rb, examples/window.rb, README:
35
+ Fixed a bug in processing the callbacks. It was not guaranteed that
36
+ all input callbacks are invoked before the final action callback.
37
+ This is now guaranteed! I've introduced two separate tree traversals
38
+ of process_callbacks to make this work. As a side effect, this also
39
+ has removed the dependency of Presenter/Component/Decoration classes
40
+ from the CallbackStream class. Instead of a CallbackStream, a code
41
+ block is now passed through the
42
+ process_callbacks/process_callbacks_chain traversal. The Session
43
+ class specifies how to invoke the callbacks.
44
+
45
+ Thu Mar 3 12:31:15 CET 2005 Michael Neumann <mneumann@ntecs.de>
46
+
47
+ * wee/session.rb, wee/pageless/session.rb: new methods
48
+ pre_respond_hook, post_callbacks_hook, especially to make pretty-URLs
49
+ as suggested by Joao Pedrosa working.
50
+
51
+ Thu Mar 3 12:11:05 CET 2005 Michael Neumann <mneumann@ntecs.de>
52
+
53
+ * wee/response.rb (Wee::Response): Use the passed mime_type as
54
+ content-type (bug fix).
55
+
56
+ * wee/request.rb: Made 'info' attribute writeable.
57
+
58
+ * wee/core/callback.rb: Added reader method for 'obj'
59
+
60
+ * wee/core/component.rb: Added #parent method, to retrieve the parent
61
+ component from a child if existent. The parent attribute will be set
62
+ during #add_child.
63
+
64
+ Thu Mar 3 11:38:21 CET 2005 Michael Neumann <mneumann@ntecs.de>
65
+
66
+ * wee/application.rb, wee/pageless/application.rb
67
+ (request_handler_expired): Forward to an URL that includes the info
68
+ part but neither the request_handler_id nor the page_id.
69
+
70
+ Sat Feb 26 17:12:35 CET 2005 Michael Neumann <mneumann@ntecs.de>
71
+
72
+ * wee/request.rb, test/test_request.rb, wee/pageless/request.rb,
73
+ wee/session.rb, wee/pageless/session.rb,
74
+ wee/renderer/html/canvas.rb, wee/renderer/html/brushes.rb:
75
+
76
+ - request delimeter changed to /___/ (looks nicer)
77
+
78
+ - refactored Request#build_url, which now takes a hash and
79
+ remembers already specified values
80
+
81
+ - A Request knows now itself whether it's a action or a render
82
+ request.
83
+
84
+ - added Request#info attribute. This is a part of the URL that you
85
+ can use for your own purposes. It is remembered across requests
86
+ unless you overwrite its value.
87
+
88
+ Sat Feb 26 14:22:34 CET 2005 Michael Neumann <mneumann@ntecs.de>
89
+
90
+ * wee/core/presenter.rb, wee/session.rb, wee/application.rb: renamed @properties to @__properties
91
+
92
+ * wee/core/component.rb: rename @children to @__children
93
+
94
+ * wee/core/component.rb, wee/core/valueholder.rb: rename @decoration
95
+ to @__decoration
96
+
1
97
  --------------------------------------------------------------------
2
98
  TAGGED 0.7.0
3
99
  --------------------------------------------------------------------
data/README CHANGED
@@ -237,11 +237,8 @@ called for the component itself. As such, <i>process_callbacks_chain</i>
237
237
  is important to avoid entering an infinite loop (a method calling itself). What
238
238
  decorations are, is discussed elsewhere.
239
239
 
240
- Method <i>process_callbacks</i> of class Component first invokes all input
241
- callbacks specified for this component, then calls
242
- <i>process_callbacks_chain</i> for all of it's child components. This ensures,
243
- that all input callbacks are triggered before the first action callback is
244
- run. Finally, it invokes all of it's action callbacks.
240
+ There are two "process_callbacks" tree traversals. The first invokes all input
241
+ callbacks, the second the action callback.
245
242
 
246
243
  === Rendering Phase
247
244
 
@@ -1,9 +1,8 @@
1
1
  $LOAD_PATH.unshift << "../lib"
2
2
  require 'wee'
3
- require 'wee/webrick'
4
- require 'wee/utils/cache'
3
+ require 'wee/adaptors/webrick'
4
+ require 'wee/utils'
5
5
  require 'cgi'
6
-
7
6
  require 'enumerator'
8
7
 
9
8
  module ObjectSpaceBrowser
@@ -15,7 +14,7 @@ module ObjectSpaceBrowser
15
14
  end
16
15
 
17
16
  def choose(klass)
18
- call(Klass.new(klass))
17
+ call Klass.new(klass)
19
18
  end
20
19
 
21
20
  def render
@@ -23,7 +22,7 @@ module ObjectSpaceBrowser
23
22
 
24
23
  r.ul {
25
24
  klasses.each do |klass|
26
- r.li { r.anchor.action(:choose, klass).with(klass.name) }
25
+ r.li { r.anchor.callback(:choose, klass).with(klass.name) }
27
26
  end
28
27
  }
29
28
  end
@@ -63,7 +62,7 @@ module ObjectSpaceBrowser
63
62
 
64
63
  r.ul {
65
64
  @instances.each do |instance|
66
- r.li { r.anchor.action(:choose, instance).with("0x%x" % instance.object_id) }
65
+ r.li { r.anchor.callback(:choose, instance).with("0x%x" % instance.object_id) }
67
66
  end
68
67
  }
69
68
  end
@@ -86,7 +85,7 @@ module ObjectSpaceBrowser
86
85
  end
87
86
 
88
87
  def render
89
- r.anchor.action(:back).with("back")
88
+ r.anchor.callback(:back).with("back")
90
89
 
91
90
  r.break
92
91
  r.h1 "Instance 0x%x of #{@instance.class.name}" % @instance.object_id
@@ -97,7 +96,7 @@ module ObjectSpaceBrowser
97
96
  r.break
98
97
  r.ul do
99
98
  @instance.each do |obj|
100
- r.li { render_obj(obj, r) }
99
+ r.li { render_obj(obj) }
101
100
  end
102
101
  end
103
102
  when Hash
@@ -111,8 +110,8 @@ module ObjectSpaceBrowser
111
110
 
112
111
  @instance.each_pair do |k, v|
113
112
  r.table_row do
114
- r.table_data { render_obj(k, r) }
115
- r.table_data { render_obj(v, r) }
113
+ r.table_data { render_obj(k) }
114
+ r.table_data { render_obj(v) }
116
115
  end
117
116
  end
118
117
  end
@@ -124,31 +123,31 @@ module ObjectSpaceBrowser
124
123
  return if @instance.instance_variables.empty?
125
124
  r.break
126
125
 
127
- render_instance_variables(r)
126
+ render_instance_variables
128
127
  end
129
128
 
130
- def render_instance_variables(r)
129
+ def render_instance_variables
131
130
  r.table.border(1).with do
132
131
  r.table_row do
133
132
  r.table_data do r.bold("Instance Variable") end
134
133
  r.table_data do r.bold("Object") end
135
134
  end
136
- @instance.instance_variables.each do |var| render_ivar_row(var, r) end
135
+ @instance.instance_variables.each do |var| render_ivar_row(var) end
137
136
  end
138
137
  end
139
138
 
140
- def render_ivar_row(var, r)
139
+ def render_ivar_row(var)
141
140
  r.table_row do
142
141
  r.table_data(var)
143
142
  r.table_data do
144
143
  v = @instance.instance_variable_get(var)
145
- render_obj(v, r)
144
+ render_obj(v)
146
145
  end
147
146
  end
148
147
  end
149
148
 
150
- def render_obj(obj, r)
151
- r.anchor.action(:choose, obj).with do
149
+ def render_obj(obj)
150
+ r.anchor.callback(:choose, obj).with do
152
151
  r.bold(obj.class.name)
153
152
  r.space
154
153
  r.text("(#{ obj.object_id })")
@@ -170,30 +169,13 @@ end # module ObjectSpaceBrowser
170
169
 
171
170
  if $0 == __FILE__ then
172
171
 
173
- OBJ = {
174
- "hello" => { [1,2,3] => [5,6,7], "test" => :super },
175
- "other" => %w(a b c d e f)
176
- }
177
-
178
- class MySession < Wee::Session
179
- def initialize
180
- super do
181
- self.root_component = ObjectSpaceBrowser::Instance.new(OBJ)
182
- self.page_store = Wee::Utils::LRUCache.new(10) # backtrack up to 10 pages
183
- end
184
- end
185
- end
186
-
187
- class MyApplication < Wee::Application
188
- def shutdown
189
- end
190
- end
172
+ OBJ = {
173
+ "hello" => { [1,2,3] => [5,6,7], "test" => :super },
174
+ "other" => %w(a b c d e f)
175
+ }
191
176
 
192
- Wee::Application.new {|app|
193
- app.name = 'ObjectBrowser'
194
- app.path = '/ob'
195
- app.session_class = MySession
196
- app.session_store = Wee::Utils::LRUCache.new(10)
197
- app.dumpfile = ''
198
- }.start
177
+ app = Wee::Utils.app_for {
178
+ ObjectSpaceBrowser::Instance.new(OBJ)
179
+ }
180
+ Wee::WEBrickAdaptor.register('/ob' => app).start
199
181
  end
@@ -0,0 +1,444 @@
1
+ /* Prototype: an object-oriented Javascript library, version 1.1.0
2
+ * (c) 2005 Sam Stephenson <sam@conio.net>
3
+ *
4
+ * Prototype is freely distributable under the terms of an MIT-style license.
5
+ * For details, see http://prototype.conio.net/
6
+ *
7
+ * Changes by Michael Neumann.
8
+ */
9
+
10
+ var Prototype = {
11
+ Version: '1.1.0'
12
+ }
13
+
14
+ var Class = {
15
+ create: function() {
16
+ return function() {
17
+ this.initialize.apply(this, arguments);
18
+ }
19
+ }
20
+ }
21
+
22
+ var Abstract = new Object();
23
+
24
+ Object.prototype.extend = function(object) {
25
+ for (property in object) {
26
+ this[property] = object[property];
27
+ }
28
+ return this;
29
+ }
30
+
31
+ Function.prototype.bind = function(object) {
32
+ var method = this;
33
+ return function() {
34
+ method.apply(object, arguments);
35
+ }
36
+ }
37
+
38
+ Function.prototype.bindAsEventListener = function(object) {
39
+ var method = this;
40
+ return function(event) {
41
+ method.call(object, event || window.event);
42
+ }
43
+ }
44
+
45
+ Number.prototype.toColorPart = function() {
46
+ var digits = this.toString(16);
47
+ if (this < 16) return '0' + digits;
48
+ return digits;
49
+ }
50
+
51
+ var Try = {
52
+ these: function() {
53
+ var returnValue;
54
+
55
+ for (var i = 0; i < arguments.length; i++) {
56
+ var lambda = arguments[i];
57
+ try {
58
+ returnValue = lambda();
59
+ break;
60
+ } catch (e) {}
61
+ }
62
+
63
+ return returnValue;
64
+ }
65
+ }
66
+
67
+ var Toggle = {
68
+ display: function() {
69
+ for (var i = 0; i < arguments.length; i++) {
70
+ var element = $(arguments[i]);
71
+ element.style.display =
72
+ (element.style.display == 'none' ? '' : 'none');
73
+ }
74
+ }
75
+ }
76
+
77
+ /*--------------------------------------------------------------------------*/
78
+
79
+ function $() {
80
+ var elements = new Array();
81
+
82
+ for (var i = 0; i < arguments.length; i++) {
83
+ var element = arguments[i];
84
+ if (typeof element == 'string')
85
+ element = document.getElementById(element);
86
+
87
+ if (arguments.length == 1)
88
+ return element;
89
+
90
+ elements.push(element);
91
+ }
92
+
93
+ return elements;
94
+ }
95
+
96
+ function getElementsByClassName(className) {
97
+ var children = document.getElementsByTagName('*') || document.all;
98
+ var elements = new Array();
99
+
100
+ for (var i = 0; i < children.length; i++) {
101
+ var child = children[i];
102
+ var classNames = child.className.split(' ');
103
+ for (var j = 0; j < classNames.length; j++) {
104
+ if (classNames[j] == className) {
105
+ elements.push(child);
106
+ break;
107
+ }
108
+ }
109
+ }
110
+
111
+ return elements;
112
+ }
113
+ var Ajax = {
114
+ getTransport: function() {
115
+ return Try.these(
116
+ function() {return new ActiveXObject('Msxml2.XMLHTTP')},
117
+ function() {return new ActiveXObject('Microsoft.XMLHTTP')},
118
+ function() {return new XMLHttpRequest()}
119
+ ) || false;
120
+ },
121
+
122
+ emptyFunction: function() {}
123
+ }
124
+
125
+ Ajax.Base = function() {};
126
+ Ajax.Base.prototype = {
127
+ setOptions: function(options) {
128
+ this.options = {
129
+ method: 'post',
130
+ asynchronous: true,
131
+ parameters: ''
132
+ }.extend(options || {});
133
+ }
134
+ }
135
+
136
+ Ajax.Request = Class.create();
137
+ Ajax.Request.Events =
138
+ ['Uninitialized', 'Loading', 'Loaded', 'Interactive', 'Complete'];
139
+
140
+ Ajax.Request.prototype = (new Ajax.Base()).extend({
141
+ initialize: function(url, options) {
142
+ this.transport = Ajax.getTransport();
143
+ this.setOptions(options);
144
+
145
+ try {
146
+ /*if (this.options.method == 'get')
147
+ url += '?' + this.options.parameters; */
148
+
149
+ this.transport.open(this.options.method, url, true);
150
+
151
+ if (this.options.asynchronous) {
152
+ this.transport.onreadystatechange = this.onStateChange.bind(this);
153
+ setTimeout((function() {this.respondToReadyState(1)}).bind(this), 10);
154
+ }
155
+
156
+ if (this.options.method == 'post') {
157
+ this.transport.setRequestHeader('Connection', 'close');
158
+ this.transport.setRequestHeader('Content-type',
159
+ 'application/x-www-form-urlencoded');
160
+ }
161
+
162
+ this.transport.send(this.options.method == 'post' ?
163
+ this.options.parameters : null);
164
+
165
+ } catch (e) {
166
+ }
167
+ },
168
+
169
+ onStateChange: function() {
170
+ var readyState = this.transport.readyState;
171
+ if (readyState != 1)
172
+ this.respondToReadyState(this.transport.readyState);
173
+ },
174
+
175
+ respondToReadyState: function(readyState) {
176
+ var event = Ajax.Request.Events[readyState];
177
+ (this.options['on' + event] || Ajax.emptyFunction)(this.transport);
178
+ }
179
+ });
180
+
181
+ Ajax.Updater = Class.create();
182
+ Ajax.Updater.prototype = (new Ajax.Base()).extend({
183
+ initialize: function(container, url, options) {
184
+ this.container = $(container);
185
+ this.setOptions(options);
186
+
187
+ if (this.options.asynchronous) {
188
+ this.onComplete = this.options.onComplete;
189
+ this.options.onComplete = this.updateContent.bind(this);
190
+ }
191
+
192
+ this.request = new Ajax.Request(url, this.options);
193
+
194
+ if (!this.options.asynchronous)
195
+ this.updateContent();
196
+ },
197
+
198
+ updateContent: function() {
199
+ if (this.options.insertion) {
200
+ new this.options.insertion(this.container,
201
+ this.request.transport.responseText);
202
+ } else {
203
+ this.container.innerHTML = this.request.transport.responseText;
204
+ }
205
+
206
+ if (this.onComplete) {
207
+ setTimeout((function() {this.onComplete(this.request)}).bind(this), 10);
208
+ }
209
+ }
210
+ });
211
+ var Field = {
212
+ clear: function() {
213
+ for (var i = 0; i < arguments.length; i++)
214
+ $(arguments[i]).value = '';
215
+ },
216
+
217
+ focus: function(element) {
218
+ $(element).focus();
219
+ },
220
+
221
+ present: function() {
222
+ for (var i = 0; i < arguments.length; i++)
223
+ if ($(arguments[i]).value == '') return false;
224
+ return true;
225
+ }
226
+ }
227
+
228
+ /*--------------------------------------------------------------------------*/
229
+
230
+ var Form = {
231
+ serialize: function(form) {
232
+ var elements = Form.getElements($(form));
233
+ var queryComponents = new Array();
234
+
235
+ for (var i = 0; i < elements.length; i++) {
236
+ var queryComponent = Form.Element.serialize(elements[i]);
237
+ if (queryComponent)
238
+ queryComponents.push(queryComponent);
239
+ }
240
+
241
+ return queryComponents.join('&');
242
+ },
243
+
244
+ getElements: function(form) {
245
+ form = $(form);
246
+ var elements = new Array();
247
+
248
+ for (tagName in Form.Element.Serializers) {
249
+ var tagElements = form.getElementsByTagName(tagName);
250
+ for (var j = 0; j < tagElements.length; j++)
251
+ elements.push(tagElements[j]);
252
+ }
253
+ return elements;
254
+ }
255
+ }
256
+
257
+ Form.Element = {
258
+ serialize: function(element) {
259
+ element = $(element);
260
+ var method = element.tagName.toLowerCase();
261
+ var parameter = Form.Element.Serializers[method](element);
262
+
263
+ if (parameter)
264
+ return encodeURIComponent(parameter[0]) + '=' +
265
+ encodeURIComponent(parameter[1]);
266
+ },
267
+
268
+ getValue: function(element) {
269
+ element = $(element);
270
+ var method = element.tagName.toLowerCase();
271
+ var parameter = Form.Element.Serializers[method](element);
272
+
273
+ if (parameter)
274
+ return parameter[1];
275
+ }
276
+ }
277
+
278
+ Form.Element.Serializers = {
279
+ input: function(element) {
280
+ switch (element.type.toLowerCase()) {
281
+ case 'hidden':
282
+ case 'password':
283
+ case 'text':
284
+ return Form.Element.Serializers.textarea(element);
285
+ case 'checkbox':
286
+ case 'radio':
287
+ return Form.Element.Serializers.inputSelector(element);
288
+ }
289
+ return false;
290
+ },
291
+
292
+ inputSelector: function(element) {
293
+ if (element.checked)
294
+ return [element.name, element.value];
295
+ },
296
+
297
+ textarea: function(element) {
298
+ return [element.name, element.value];
299
+ },
300
+
301
+ select: function(element) {
302
+ var index = element.selectedIndex;
303
+ return [element.name, (index >= 0) ? element.options[index].value : ''];
304
+ }
305
+ }
306
+
307
+ /*--------------------------------------------------------------------------*/
308
+
309
+ Abstract.TimedObserver = function() {}
310
+ Abstract.TimedObserver.prototype = {
311
+ initialize: function(element, frequency, callback) {
312
+ this.frequency = frequency;
313
+ this.element = $(element);
314
+ this.callback = callback;
315
+
316
+ this.lastValue = this.getValue();
317
+ this.registerCallback();
318
+ },
319
+
320
+ registerCallback: function() {
321
+ setTimeout(this.onTimerEvent.bind(this), this.frequency * 1000);
322
+ },
323
+
324
+ onTimerEvent: function() {
325
+ var value = this.getValue();
326
+ if (this.lastValue != value) {
327
+ this.callback(this.element, value);
328
+ this.lastValue = value;
329
+ }
330
+
331
+ this.registerCallback();
332
+ }
333
+ }
334
+
335
+ Form.Element.Observer = Class.create();
336
+ Form.Element.Observer.prototype = (new Abstract.TimedObserver()).extend({
337
+ getValue: function() {
338
+ return Form.Element.getValue(this.element);
339
+ }
340
+ });
341
+
342
+ Form.Observer = Class.create();
343
+ Form.Observer.prototype = (new Abstract.TimedObserver()).extend({
344
+ getValue: function() {
345
+ return Form.serialize(this.element);
346
+ }
347
+ });
348
+
349
+ Abstract.Insertion = function(adjacency) {
350
+ this.adjacency = adjacency;
351
+ }
352
+
353
+ Abstract.Insertion.prototype = {
354
+ initialize: function(element, content) {
355
+ this.element = $(element);
356
+ this.content = content;
357
+
358
+ if (this.adjacency && this.element.insertAdjacentHTML) {
359
+ this.element.insertAdjacentHTML(this.adjacency, this.content);
360
+ } else {
361
+ this.range = this.element.ownerDocument.createRange();
362
+ if (this.initializeRange) this.initializeRange();
363
+ this.fragment = this.range.createContextualFragment(this.content);
364
+ this.insertContent();
365
+ }
366
+ }
367
+ }
368
+
369
+ var Insertion = new Object();
370
+
371
+ Insertion.Before = Class.create();
372
+ Insertion.Before.prototype = (new Abstract.Insertion('beforeBegin')).extend({
373
+ initializeRange: function() {
374
+ this.range.setStartBefore(this.element);
375
+ },
376
+
377
+ insertContent: function() {
378
+ this.element.parentNode.insertBefore(this.fragment, this.element);
379
+ }
380
+ });
381
+
382
+ Insertion.Top = Class.create();
383
+ Insertion.Top.prototype = (new Abstract.Insertion('afterBegin')).extend({
384
+ initializeRange: function() {
385
+ this.range.selectNodeContents(this.element);
386
+ this.range.collapse(true);
387
+ },
388
+
389
+ insertContent: function() {
390
+ this.element.insertBefore(this.fragment, this.element.firstChild);
391
+ }
392
+ });
393
+
394
+ Insertion.Bottom = Class.create();
395
+ Insertion.Bottom.prototype = (new Abstract.Insertion('beforeEnd')).extend({
396
+ initializeRange: function() {
397
+ this.range.selectNodeContents(this.element);
398
+ this.range.collapse(this.element);
399
+ },
400
+
401
+ insertContent: function() {
402
+ this.element.appendChild(this.fragment);
403
+ }
404
+ });
405
+
406
+ Insertion.After = Class.create();
407
+ Insertion.After.prototype = (new Abstract.Insertion('afterEnd')).extend({
408
+ initializeRange: function() {
409
+ this.range.setStartAfter(this.element);
410
+ },
411
+
412
+ insertContent: function() {
413
+ this.element.parentNode.insertBefore(this.fragment,
414
+ this.element.nextSibling);
415
+ }
416
+ });
417
+ var Effect = new Object();
418
+
419
+ Effect.Highlight = Class.create();
420
+ Effect.Highlight.prototype = {
421
+ initialize: function(element) {
422
+ this.element = $(element);
423
+ this.start = 153;
424
+ this.finish = 255;
425
+ this.current = this.start;
426
+ this.fade();
427
+ },
428
+
429
+ fade: function() {
430
+ if (this.isFinished()) return;
431
+ if (this.timer) clearTimeout(this.timer);
432
+ this.highlight(this.element, this.current);
433
+ this.current += 17;
434
+ this.timer = setTimeout(this.fade.bind(this), 250);
435
+ },
436
+
437
+ isFinished: function() {
438
+ return this.current > this.finish;
439
+ },
440
+
441
+ highlight: function(element, current) {
442
+ element.style.backgroundColor = "#ffff" + current.toColorPart();
443
+ }
444
+ }