nitro 0.26.0 → 0.27.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.
Files changed (78) hide show
  1. data/CHANGELOG +312 -0
  2. data/INSTALL +3 -1
  3. data/ProjectInfo +6 -9
  4. data/README +32 -5
  5. data/Rakefile +5 -1
  6. data/bin/nitrogen +3 -60
  7. data/doc/MIGRATION +24 -0
  8. data/doc/RELEASES +141 -0
  9. data/doc/lhttpd.txt +3 -0
  10. data/lib/glue/magick.rb +38 -0
  11. data/lib/glue/thumbnails.rb +3 -0
  12. data/lib/glue/webfile.rb +137 -0
  13. data/lib/nitro.rb +1 -1
  14. data/lib/nitro/adapter/acgi.rb +235 -0
  15. data/lib/nitro/adapter/cgi.rb +16 -17
  16. data/lib/nitro/adapter/scgi.rb +4 -4
  17. data/lib/nitro/adapter/webrick.rb +9 -2
  18. data/lib/nitro/cgi.rb +49 -49
  19. data/lib/nitro/cgi/response.rb +4 -0
  20. data/lib/nitro/cgi/stream.rb +7 -7
  21. data/lib/nitro/cgi/utils.rb +2 -1
  22. data/lib/nitro/compiler.rb +47 -4
  23. data/lib/nitro/compiler/elements.rb +40 -20
  24. data/lib/nitro/compiler/layout.rb +21 -0
  25. data/lib/nitro/compiler/localization.rb +3 -1
  26. data/lib/nitro/compiler/markup.rb +2 -0
  27. data/lib/nitro/compiler/morphing.rb +16 -4
  28. data/lib/nitro/compiler/script.rb +109 -0
  29. data/lib/nitro/context.rb +10 -10
  30. data/lib/nitro/dispatcher.rb +4 -2
  31. data/lib/nitro/element.rb +107 -26
  32. data/lib/nitro/element/{java_script.rb → javascript.rb} +7 -1
  33. data/lib/nitro/flash.rb +4 -1
  34. data/lib/nitro/helper.rb +15 -0
  35. data/lib/nitro/helper/benchmark.rb +8 -2
  36. data/lib/nitro/helper/form.rb +3 -3
  37. data/lib/nitro/helper/form/controls.rb +131 -29
  38. data/lib/nitro/helper/{dojo.rb → form/test.xhtml} +0 -0
  39. data/lib/nitro/helper/javascript.rb +69 -59
  40. data/lib/nitro/helper/{scriptaculous.rb → javascript/dojo.rb} +0 -0
  41. data/lib/nitro/helper/javascript/morphing.rb +163 -0
  42. data/lib/nitro/helper/javascript/prototype.rb +96 -0
  43. data/lib/nitro/helper/javascript/scriptaculous.rb +18 -0
  44. data/lib/nitro/helper/layout.rb +42 -0
  45. data/lib/nitro/helper/table.rb +190 -27
  46. data/lib/nitro/{adapter → helper}/wee.rb +9 -3
  47. data/lib/nitro/render.rb +23 -17
  48. data/lib/nitro/scaffolding.rb +19 -2
  49. data/lib/nitro/server.rb +4 -8
  50. data/lib/nitro/server/runner.rb +28 -6
  51. data/lib/nitro/session.rb +7 -7
  52. data/lib/nitro_and_og.rb +2 -0
  53. data/proto/public/Makefile.acgi +40 -0
  54. data/proto/public/acgi.c +138 -0
  55. data/proto/public/js/builder.js +7 -3
  56. data/proto/public/js/controls.js +32 -12
  57. data/proto/public/js/dragdrop.js +4 -3
  58. data/proto/public/js/effects.js +111 -62
  59. data/proto/public/js/scriptaculous.js +10 -13
  60. data/proto/public/js/slider.js +88 -31
  61. data/proto/public/scaffold/new.xhtml +2 -2
  62. data/setup.rb +1585 -0
  63. data/src/part/admin.rb +6 -0
  64. data/src/part/admin/controller.rb +3 -3
  65. data/src/part/admin/skin.rb +1 -8
  66. data/test/nitro/adapter/tc_webrick.rb +2 -0
  67. data/test/nitro/tc_controller_aspect.rb +1 -1
  68. data/test/nitro/tc_element.rb +5 -6
  69. data/test/nitro/tc_table.rb +66 -0
  70. metadata +277 -271
  71. data/doc/architecture.txt +0 -2
  72. data/doc/bugs.txt +0 -15
  73. data/doc/tutorial.txt +0 -26
  74. data/install.rb +0 -37
  75. data/lib/nitro/compiler/script_generator.rb +0 -14
  76. data/lib/nitro/compiler/shaders.rb +0 -206
  77. data/lib/nitro/helper/prototype.rb +0 -49
  78. data/lib/nitro/scaffold/relations.rb +0 -54
@@ -146,6 +146,7 @@ var Draggables = {
146
146
  if(!this.activeDraggable) return;
147
147
  this._lastPointer = null;
148
148
  this.activeDraggable.endDrag(event);
149
+ this.activeDraggable = null;
149
150
  },
150
151
 
151
152
  keyPress: function(event) {
@@ -191,7 +192,7 @@ Draggable.prototype = {
191
192
  },
192
193
  reverteffect: function(element, top_offset, left_offset) {
193
194
  var dur = Math.sqrt(Math.abs(top_offset^2)+Math.abs(left_offset^2))*0.02;
194
- element._revert = new Effect.MoveBy(element, -top_offset, -left_offset, {duration:dur});
195
+ element._revert = new Effect.Move(element, { x: -left_offset, y: -top_offset, duration: dur});
195
196
  },
196
197
  endeffect: function(element) {
197
198
  new Effect.Opacity(element, {duration:0.2, from:0.7, to:1.0});
@@ -227,8 +228,8 @@ Draggable.prototype = {
227
228
 
228
229
  currentDelta: function() {
229
230
  return([
230
- parseInt(this.element.style.left || '0'),
231
- parseInt(this.element.style.top || '0')]);
231
+ parseInt(Element.getStyle(this.element,'left') || '0'),
232
+ parseInt(Element.getStyle(this.element,'top') || '0')]);
232
233
  },
233
234
 
234
235
  initDrag: function(event) {
@@ -22,23 +22,21 @@ String.prototype.parseColor = function() {
22
22
  }
23
23
  }
24
24
  return(color.length==7 ? color : (arguments[0] || this));
25
- }
25
+ }
26
26
 
27
- Element.collectTextNodesIgnoreClass = function(element, ignoreclass) {
28
- var children = $(element).childNodes;
29
- var text = '';
30
- var classtest = new RegExp('^([^ ]+ )*' + ignoreclass+ '( [^ ]+)*$','i');
31
-
32
- for (var i = 0; i < children.length; i++) {
33
- if(children[i].nodeType==3) {
34
- text+=children[i].nodeValue;
35
- } else {
36
- if((!children[i].className.match(classtest)) && children[i].hasChildNodes())
37
- text += Element.collectTextNodesIgnoreClass(children[i], ignoreclass);
38
- }
39
- }
40
-
41
- return text;
27
+ Element.collectTextNodes = function(element) {
28
+ return $A($(element).childNodes).collect( function(node) {
29
+ return (node.nodeType==3 ? node.nodeValue :
30
+ (node.hasChildNodes() ? Element.collectTextNodes(node) : ''));
31
+ }).flatten().join('');
32
+ }
33
+
34
+ Element.collectTextNodesIgnoreClass = function(element, className) {
35
+ return $A($(element).childNodes).collect( function(node) {
36
+ return (node.nodeType==3 ? node.nodeValue :
37
+ ((node.hasChildNodes() && !Element.hasClassName(node,className)) ?
38
+ Element.collectTextNodes(node) : ''));
39
+ }).flatten().join('');
42
40
  }
43
41
 
44
42
  Element.setStyle = function(element, style) {
@@ -129,6 +127,20 @@ var Effect = {
129
127
  $A(elements).each( function(element, index) {
130
128
  new effect(element, Object.extend(options, { delay: index * options.speed + masterDelay }));
131
129
  });
130
+ },
131
+ PAIRS: {
132
+ 'slide': ['SlideDown','SlideUp'],
133
+ 'blind': ['BlindDown','BlindUp'],
134
+ 'appear': ['Appear','Fade']
135
+ },
136
+ toggle: function(element, effect) {
137
+ element = $(element);
138
+ effect = (effect || 'appear').toLowerCase();
139
+ var options = Object.extend({
140
+ queue: { position:'end', scope:(element.id || 'global') }
141
+ }, arguments[2] || {});
142
+ Effect[Element.visible(element) ?
143
+ Effect.PAIRS[effect][1] : Effect.PAIRS[effect][0]](element, options);
132
144
  }
133
145
  };
134
146
 
@@ -166,16 +178,22 @@ Effect.Transitions.full = function(pos) {
166
178
 
167
179
  /* ------------- core effects ------------- */
168
180
 
169
- Effect.Queue = {
170
- effects: [],
181
+ Effect.ScopedQueue = Class.create();
182
+ Object.extend(Object.extend(Effect.ScopedQueue.prototype, Enumerable), {
183
+ initialize: function() {
184
+ this.effects = [];
185
+ this.interval = null;
186
+ },
171
187
  _each: function(iterator) {
172
188
  this.effects._each(iterator);
173
189
  },
174
- interval: null,
175
190
  add: function(effect) {
176
191
  var timestamp = new Date().getTime();
177
192
 
178
- switch(effect.options.queue) {
193
+ var position = (typeof effect.options.queue == 'string') ?
194
+ effect.options.queue : effect.options.queue.position;
195
+
196
+ switch(position) {
179
197
  case 'front':
180
198
  // move unstarted effects after this effect
181
199
  this.effects.findAll(function(e){ return e.state=='idle' }).each( function(e) {
@@ -206,32 +224,45 @@ Effect.Queue = {
206
224
  var timePos = new Date().getTime();
207
225
  this.effects.invoke('loop', timePos);
208
226
  }
227
+ });
228
+
229
+ Effect.Queues = {
230
+ instances: $H(),
231
+ get: function(queueName) {
232
+ if(typeof queueName != 'string') return queueName;
233
+
234
+ if(!this.instances[queueName])
235
+ this.instances[queueName] = new Effect.ScopedQueue();
236
+
237
+ return this.instances[queueName];
238
+ }
239
+ }
240
+ Effect.Queue = Effect.Queues.get('global');
241
+
242
+ Effect.DefaultOptions = {
243
+ transition: Effect.Transitions.sinoidal,
244
+ duration: 1.0, // seconds
245
+ fps: 25.0, // max. 25fps due to Effect.Queue implementation
246
+ sync: false, // true for combining
247
+ from: 0.0,
248
+ to: 1.0,
249
+ delay: 0.0,
250
+ queue: 'parallel'
209
251
  }
210
- Object.extend(Effect.Queue, Enumerable);
211
252
 
212
253
  Effect.Base = function() {};
213
254
  Effect.Base.prototype = {
214
255
  position: null,
215
- setOptions: function(options) {
216
- this.options = Object.extend({
217
- transition: Effect.Transitions.sinoidal,
218
- duration: 1.0, // seconds
219
- fps: 25.0, // max. 25fps due to Effect.Queue implementation
220
- sync: false, // true for combining
221
- from: 0.0,
222
- to: 1.0,
223
- delay: 0.0,
224
- queue: 'parallel'
225
- }, options || {});
226
- },
227
256
  start: function(options) {
228
- this.setOptions(options || {});
257
+ this.options = Object.extend(Object.extend({},Effect.DefaultOptions), options || {});
229
258
  this.currentFrame = 0;
230
259
  this.state = 'idle';
231
260
  this.startOn = this.options.delay*1000;
232
261
  this.finishOn = this.startOn + (this.options.duration*1000);
233
262
  this.event('beforeStart');
234
- if(!this.options.sync) Effect.Queue.add(this);
263
+ if(!this.options.sync)
264
+ Effect.Queues.get(typeof this.options.queue == 'string' ?
265
+ 'global' : this.options.queue.scope).add(this);
235
266
  },
236
267
  loop: function(timePos) {
237
268
  if(timePos >= this.startOn) {
@@ -269,7 +300,9 @@ Effect.Base.prototype = {
269
300
  }
270
301
  },
271
302
  cancel: function() {
272
- if(!this.options.sync) Effect.Queue.remove(this);
303
+ if(!this.options.sync)
304
+ Effect.Queues.get(typeof this.options.queue == 'string' ?
305
+ 'global' : this.options.queue.scope).remove(this);
273
306
  this.state = 'finished';
274
307
  },
275
308
  event: function(eventName) {
@@ -319,13 +352,16 @@ Object.extend(Object.extend(Effect.Opacity.prototype, Effect.Base.prototype), {
319
352
  }
320
353
  });
321
354
 
322
- Effect.MoveBy = Class.create();
323
- Object.extend(Object.extend(Effect.MoveBy.prototype, Effect.Base.prototype), {
324
- initialize: function(element, toTop, toLeft) {
325
- this.element = $(element);
326
- this.toTop = toTop;
327
- this.toLeft = toLeft;
328
- this.start(arguments[3]);
355
+ Effect.Move = Class.create();
356
+ Object.extend(Object.extend(Effect.Move.prototype, Effect.Base.prototype), {
357
+ initialize: function(element) {
358
+ this.element = $(element);
359
+ var options = Object.extend({
360
+ x: 0,
361
+ y: 0,
362
+ mode: 'relative'
363
+ }, arguments[1] || {});
364
+ this.start(options);
329
365
  },
330
366
  setup: function() {
331
367
  // Bug in Opera: Opera returns the "real" position of a static element or
@@ -333,17 +369,28 @@ Object.extend(Object.extend(Effect.MoveBy.prototype, Effect.Base.prototype), {
333
369
  // ==> Always set top and left for position relative elements in your stylesheets
334
370
  // (to 0 if you do not need them)
335
371
  Element.makePositioned(this.element);
336
- this.originalTop = parseFloat(Element.getStyle(this.element,'top') || '0');
337
372
  this.originalLeft = parseFloat(Element.getStyle(this.element,'left') || '0');
373
+ this.originalTop = parseFloat(Element.getStyle(this.element,'top') || '0');
374
+ if(this.options.mode == 'absolute') {
375
+ // absolute movement, so we need to calc deltaX and deltaY
376
+ this.options.x = this.options.x - this.originalLeft;
377
+ this.options.y = this.options.y - this.originalTop;
378
+ }
338
379
  },
339
380
  update: function(position) {
340
381
  Element.setStyle(this.element, {
341
- top: this.toTop * position + this.originalTop + 'px',
342
- left: this.toLeft * position + this.originalLeft + 'px'
382
+ left: this.options.x * position + this.originalLeft + 'px',
383
+ top: this.options.y * position + this.originalTop + 'px'
343
384
  });
344
385
  }
345
386
  });
346
387
 
388
+ // for backwards compatibility
389
+ Effect.MoveBy = function(element, toTop, toLeft) {
390
+ return new Effect.Move(element,
391
+ Object.extend({ x: toLeft, y: toTop }, arguments[3] || {}));
392
+ };
393
+
347
394
  Effect.Scale = Class.create();
348
395
  Object.extend(Object.extend(Effect.Scale.prototype, Effect.Base.prototype), {
349
396
  initialize: function(element, percent) {
@@ -585,7 +632,7 @@ Effect.DropOut = function(element) {
585
632
  left: Element.getStyle(element, 'left'),
586
633
  opacity: Element.getInlineOpacity(element) };
587
634
  return new Effect.Parallel(
588
- [ new Effect.MoveBy(element, 100, 0, { sync: true }),
635
+ [ new Effect.Move(element, {x: 0, y: 100, sync: true }),
589
636
  new Effect.Opacity(element, { sync: true, to: 0.0 }) ],
590
637
  Object.extend(
591
638
  { duration: 0.5,
@@ -602,18 +649,18 @@ Effect.Shake = function(element) {
602
649
  var oldStyle = {
603
650
  top: Element.getStyle(element, 'top'),
604
651
  left: Element.getStyle(element, 'left') };
605
- return new Effect.MoveBy(element, 0, 20,
606
- { duration: 0.05, afterFinishInternal: function(effect) {
607
- new Effect.MoveBy(effect.element, 0, -40,
608
- { duration: 0.1, afterFinishInternal: function(effect) {
609
- new Effect.MoveBy(effect.element, 0, 40,
610
- { duration: 0.1, afterFinishInternal: function(effect) {
611
- new Effect.MoveBy(effect.element, 0, -40,
612
- { duration: 0.1, afterFinishInternal: function(effect) {
613
- new Effect.MoveBy(effect.element, 0, 40,
614
- { duration: 0.1, afterFinishInternal: function(effect) {
615
- new Effect.MoveBy(effect.element, 0, -20,
616
- { duration: 0.05, afterFinishInternal: function(effect) { with(Element) {
652
+ return new Effect.Move(element,
653
+ { x: 20, y: 0, duration: 0.05, afterFinishInternal: function(effect) {
654
+ new Effect.Move(effect.element,
655
+ { x: -40, y: 0, duration: 0.1, afterFinishInternal: function(effect) {
656
+ new Effect.Move(effect.element,
657
+ { x: 40, y: 0, duration: 0.1, afterFinishInternal: function(effect) {
658
+ new Effect.Move(effect.element,
659
+ { x: -40, y: 0, duration: 0.1, afterFinishInternal: function(effect) {
660
+ new Effect.Move(effect.element,
661
+ { x: 40, y: 0, duration: 0.1, afterFinishInternal: function(effect) {
662
+ new Effect.Move(effect.element,
663
+ { x: -20, y: 0, duration: 0.05, afterFinishInternal: function(effect) { with(Element) {
617
664
  undoPositioned(effect.element);
618
665
  setStyle(effect.element, oldStyle);
619
666
  }}}) }}) }}) }}) }}) }});
@@ -737,7 +784,9 @@ Effect.Grow = function(element) {
737
784
  break;
738
785
  }
739
786
 
740
- return new Effect.MoveBy(element, initialMoveY, initialMoveX, {
787
+ return new Effect.Move(element, {
788
+ x: initialMoveX,
789
+ y: initialMoveY,
741
790
  duration: 0.01,
742
791
  beforeSetup: function(effect) { with(Element) {
743
792
  hide(effect.element);
@@ -747,7 +796,7 @@ Effect.Grow = function(element) {
747
796
  afterFinishInternal: function(effect) {
748
797
  new Effect.Parallel(
749
798
  [ new Effect.Opacity(effect.element, { sync: true, to: 1.0, from: 0.0, transition: options.opacityTransition }),
750
- new Effect.MoveBy(effect.element, moveY, moveX, { sync: true, transition: options.moveTransition }),
799
+ new Effect.Move(effect.element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition }),
751
800
  new Effect.Scale(effect.element, 100, {
752
801
  scaleMode: { originalHeight: dims.height, originalWidth: dims.width },
753
802
  sync: true, scaleFrom: window.opera ? 1 : 0, transition: options.scaleTransition, restoreAfterFinish: true})
@@ -807,7 +856,7 @@ Effect.Shrink = function(element) {
807
856
  return new Effect.Parallel(
808
857
  [ new Effect.Opacity(element, { sync: true, to: 0.0, from: 1.0, transition: options.opacityTransition }),
809
858
  new Effect.Scale(element, window.opera ? 1 : 0, { sync: true, transition: options.scaleTransition, restoreAfterFinish: true}),
810
- new Effect.MoveBy(element, moveY, moveX, { sync: true, transition: options.moveTransition })
859
+ new Effect.Move(element, { x: moveX, y: moveY, sync: true, transition: options.moveTransition })
811
860
  ], Object.extend({
812
861
  beforeStartInternal: function(effect) { with(Element) {
813
862
  [makePositioned, makeClipping].call(effect.effects[0].element) }},
@@ -20,7 +20,7 @@
20
20
  // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
21
 
22
22
  var Scriptaculous = {
23
- Version: '1.5_rc4',
23
+ Version: '1.5.1',
24
24
  require: function(libraryName) {
25
25
  // inserting via DOM fails in Safari 2.0, so brute force approach
26
26
  document.write('<script type="text/javascript" src="'+libraryName+'"></script>');
@@ -30,18 +30,15 @@ var Scriptaculous = {
30
30
  parseFloat(Prototype.Version.split(".")[0] + "." +
31
31
  Prototype.Version.split(".")[1]) < 1.4)
32
32
  throw("script.aculo.us requires the Prototype JavaScript framework >= 1.4.0");
33
- var scriptTags = document.getElementsByTagName("script");
34
- for(var i=0;i<scriptTags.length;i++) {
35
- if(scriptTags[i].src && scriptTags[i].src.match(/scriptaculous\.js(\?.*)?$/)) {
36
- var path = scriptTags[i].src.replace(/scriptaculous\.js(\?.*)?$/,'');
37
- this.require(path + 'builder.js');
38
- this.require(path + 'effects.js');
39
- this.require(path + 'dragdrop.js');
40
- this.require(path + 'controls.js');
41
- this.require(path + 'slider.js');
42
- break;
43
- }
44
- }
33
+
34
+ $A(document.getElementsByTagName("script")).findAll( function(s) {
35
+ return (s.src && s.src.match(/scriptaculous\.js(\?.*)?$/))
36
+ }).each( function(s) {
37
+ var path = s.src.replace(/scriptaculous\.js(\?.*)?$/,'');
38
+ var includes = s.src.match(/\?.*load=([a-z,]*)/);
39
+ (includes ? includes[1] : 'builder,effects,dragdrop,controls,slider').split(',').each(
40
+ function(include) { Scriptaculous.require(path+include+'.js') });
41
+ });
45
42
  }
46
43
  }
47
44
 
@@ -1,6 +1,25 @@
1
- // Copyright (c) 2005 Marty Haught
1
+ // Copyright (c) 2005 Marty Haught, Thomas Fuchs
2
+ //
3
+ // See http://script.aculo.us for more info
4
+ //
5
+ // Permission is hereby granted, free of charge, to any person obtaining
6
+ // a copy of this software and associated documentation files (the
7
+ // "Software"), to deal in the Software without restriction, including
8
+ // without limitation the rights to use, copy, modify, merge, publish,
9
+ // distribute, sublicense, and/or sell copies of the Software, and to
10
+ // permit persons to whom the Software is furnished to do so, subject to
11
+ // the following conditions:
2
12
  //
3
- // See scriptaculous.js for full license.
13
+ // The above copyright notice and this permission notice shall be
14
+ // included in all copies or substantial portions of the Software.
15
+ //
16
+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
4
23
 
5
24
  if(!Control) var Control = {};
6
25
  Control.Slider = Class.create();
@@ -32,6 +51,9 @@ Control.Slider.prototype = {
32
51
  this.value = 0; // assure backwards compat
33
52
  this.values = this.handles.map( function() { return 0 });
34
53
  this.spans = this.options.spans ? this.options.spans.map(function(s){ return $(s) }) : false;
54
+ this.options.startSpan = $(this.options.startSpan || null);
55
+ this.options.endSpan = $(this.options.endSpan || null);
56
+
35
57
  this.restricted = this.options.restricted || false;
36
58
 
37
59
  this.maximum = this.options.maximum || this.range.end;
@@ -42,6 +64,7 @@ Control.Slider.prototype = {
42
64
  this.alignY = parseInt(this.options.alignY || '0');
43
65
 
44
66
  this.trackLength = this.maximumOffset() - this.minimumOffset();
67
+ this.handleLength = this.isVertical() ? this.handles[0].offsetHeight : this.handles[0].offsetWidth;
45
68
 
46
69
  this.active = false;
47
70
  this.dragging = false;
@@ -60,18 +83,26 @@ Control.Slider.prototype = {
60
83
  this.eventMouseUp = this.endDrag.bindAsEventListener(this);
61
84
  this.eventMouseMove = this.update.bindAsEventListener(this);
62
85
 
63
- // Initialize handles
86
+ // Initialize handles in reverse (make sure first handle is active)
64
87
  this.handles.each( function(h,i) {
65
- slider.setValue(parseInt(slider.options.sliderValue || slider.range.start), i);
88
+ i = slider.handles.length-1-i;
89
+ slider.setValue(parseFloat(
90
+ (slider.options.sliderValue instanceof Array ?
91
+ slider.options.sliderValue[i] : slider.options.sliderValue) ||
92
+ slider.range.start), i);
66
93
  Element.makePositioned(h); // fix IE
67
94
  Event.observe(h, "mousedown", slider.eventMouseDown);
68
95
  });
69
96
 
97
+ Event.observe(this.track, "mousedown", this.eventMouseDown);
70
98
  Event.observe(document, "mouseup", this.eventMouseUp);
71
99
  Event.observe(document, "mousemove", this.eventMouseMove);
100
+
101
+ this.initialized = true;
72
102
  },
73
103
  dispose: function() {
74
104
  var slider = this;
105
+ Event.stopObserving(this.track, "mousedown", this.eventMouseDown);
75
106
  Event.stopObserving(document, "mouseup", this.eventMouseUp);
76
107
  Event.stopObserving(document, "mousemove", this.eventMouseMove);
77
108
  this.handles.each( function(h) {
@@ -108,9 +139,10 @@ Control.Slider.prototype = {
108
139
  if(!this.active) {
109
140
  this.activeHandle = this.handles[handleIdx];
110
141
  this.activeHandleIdx = handleIdx;
142
+ this.updateStyles();
111
143
  }
112
144
  handleIdx = handleIdx || this.activeHandleIdx || 0;
113
- if(this.restricted) {
145
+ if(this.initialized && this.restricted) {
114
146
  if((handleIdx>0) && (sliderValue<this.values[handleIdx-1]))
115
147
  sliderValue = this.values[handleIdx-1];
116
148
  if((handleIdx < (this.handles.length-1)) && (sliderValue>this.values[handleIdx+1]))
@@ -120,21 +152,24 @@ Control.Slider.prototype = {
120
152
  this.values[handleIdx] = sliderValue;
121
153
  this.value = this.values[0]; // assure backwards compat
122
154
 
123
- this.handles[handleIdx].style[ this.isVertical() ? 'top' : 'left'] =
155
+ this.handles[handleIdx].style[this.isVertical() ? 'top' : 'left'] =
124
156
  this.translateToPx(sliderValue);
125
157
 
126
158
  this.drawSpans();
127
- this.updateFinished();
159
+ if(!this.dragging || !this.event) this.updateFinished();
128
160
  },
129
161
  setValueBy: function(delta, handleIdx) {
130
162
  this.setValue(this.values[handleIdx || this.activeHandleIdx || 0] + delta,
131
163
  handleIdx || this.activeHandleIdx || 0);
132
164
  },
133
165
  translateToPx: function(value) {
134
- return Math.round((this.trackLength / (this.range.end - this.range.start)) * (value - this.range.start)) + "px";
166
+ return Math.round(
167
+ ((this.trackLength-this.handleLength)/(this.range.end-this.range.start)) *
168
+ (value - this.range.start)) + "px";
135
169
  },
136
170
  translateToValue: function(offset) {
137
- return ((offset/this.trackLength) * (this.range.end - this.range.start)) + this.range.start;
171
+ return ((offset/(this.trackLength-this.handleLength) *
172
+ (this.range.end-this.range.start)) + this.range.start);
138
173
  },
139
174
  getRange: function(range) {
140
175
  var v = this.values.sortBy(Prototype.K);
@@ -154,45 +189,63 @@ Control.Slider.prototype = {
154
189
  drawSpans: function() {
155
190
  var slider = this;
156
191
  if(this.spans)
157
- $R(0, this.spans.length-1).each(function(r) { slider.setSpan(r, slider.getRange(r)) });
192
+ $R(0, this.spans.length-1).each(function(r) { slider.setSpan(slider.spans[r], slider.getRange(r)) });
193
+ if(this.options.startSpan)
194
+ this.setSpan(this.options.startSpan,
195
+ $R(0, this.values.length>1 ? this.getRange(0).min() : this.value ));
196
+ if(this.options.endSpan)
197
+ this.setSpan(this.options.endSpan,
198
+ $R(this.values.length>1 ? this.getRange(this.spans.length-1).max() : this.value, this.maximum));
158
199
  },
159
200
  setSpan: function(span, range) {
160
201
  if(this.isVertical()) {
161
- this.spans[span].style.top = this.translateToPx(range.start);
162
- this.spans[span].style.height = this.translateToPx(range.end - range.start);
202
+ span.style.top = this.translateToPx(range.start);
203
+ span.style.height = this.translateToPx(range.end - range.start);
163
204
  } else {
164
- this.spans[span].style.left = this.translateToPx(range.start);
165
- this.spans[span].style.width = this.translateToPx(range.end - range.start);
205
+ span.style.left = this.translateToPx(range.start);
206
+ span.style.width = this.translateToPx(range.end - range.start);
166
207
  }
167
208
  },
209
+ updateStyles: function() {
210
+ this.handles.each( function(h){ Element.removeClassName(h, 'selected') });
211
+ Element.addClassName(this.activeHandle, 'selected');
212
+ },
168
213
  startDrag: function(event) {
169
214
  if(Event.isLeftClick(event)) {
170
215
  if(!this.disabled){
171
216
  this.active = true;
172
217
 
173
- // find the handle (prevents issues with Safari)
174
218
  var handle = Event.element(event);
175
- while((this.handles.indexOf(handle) == -1) && handle.parentNode)
176
- handle = handle.parentNode;
177
-
178
- this.activeHandle = handle;
179
- this.activeHandleIdx = this.handles.indexOf(this.activeHandle);
180
-
181
219
  var pointer = [Event.pointerX(event), Event.pointerY(event)];
182
- var offsets = Position.cumulativeOffset(this.activeHandle);
183
- this.offsetX = (pointer[0] - offsets[0]);
184
- this.offsetY = (pointer[1] - offsets[1]);
220
+ if(handle==this.track) {
221
+ var offsets = Position.cumulativeOffset(this.track);
222
+ this.event = event;
223
+ this.setValue(this.translateToValue(
224
+ (this.isVertical() ? pointer[1]-offsets[1] : pointer[0]-offsets[0])-(this.handleLength/2)
225
+ ));
226
+ var offsets = Position.cumulativeOffset(this.activeHandle);
227
+ this.offsetX = (pointer[0] - offsets[0]);
228
+ this.offsetY = (pointer[1] - offsets[1]);
229
+ } else {
230
+ // find the handle (prevents issues with Safari)
231
+ while((this.handles.indexOf(handle) == -1) && handle.parentNode)
232
+ handle = handle.parentNode;
185
233
 
234
+ this.activeHandle = handle;
235
+ this.activeHandleIdx = this.handles.indexOf(this.activeHandle);
236
+ this.updateStyles();
237
+
238
+ var offsets = Position.cumulativeOffset(this.activeHandle);
239
+ this.offsetX = (pointer[0] - offsets[0]);
240
+ this.offsetY = (pointer[1] - offsets[1]);
241
+ }
186
242
  }
187
243
  Event.stop(event);
188
244
  }
189
245
  },
190
246
  update: function(event) {
191
247
  if(this.active) {
192
- if(!this.dragging) {
193
- this.dragging = true;
194
- if(this.activeHandle.style.position=="") style.position = "relative";
195
- }
248
+ if(!this.dragging) this.dragging = true;
196
249
  this.draw(event);
197
250
  // fix AppleWebKit rendering
198
251
  if(navigator.appVersion.indexOf('AppleWebKit')>0) window.scrollBy(0,0);
@@ -204,8 +257,10 @@ Control.Slider.prototype = {
204
257
  var offsets = Position.cumulativeOffset(this.track);
205
258
  pointer[0] -= this.offsetX + offsets[0];
206
259
  pointer[1] -= this.offsetY + offsets[1];
260
+ this.event = event;
207
261
  this.setValue(this.translateToValue( this.isVertical() ? pointer[1] : pointer[0] ));
208
- if(this.options.onSlide) this.options.onSlide(this.values.length>1 ? this.values : this.value, this);
262
+ if(this.initialized && this.options.onSlide)
263
+ this.options.onSlide(this.values.length>1 ? this.values : this.value, this);
209
264
  },
210
265
  endDrag: function(event) {
211
266
  if(this.active && this.dragging) {
@@ -221,6 +276,8 @@ Control.Slider.prototype = {
221
276
  this.updateFinished();
222
277
  },
223
278
  updateFinished: function() {
224
- if(this.options.onChange) this.options.onChange(this.values.length>1 ? this.values : this.value, this);
279
+ if(this.initialized && this.options.onChange)
280
+ this.options.onChange(this.values.length>1 ? this.values : this.value, this);
281
+ this.event = null;
225
282
  }
226
- }
283
+ }