nitro 0.26.0 → 0.27.0

Sign up to get free protection for your applications and to get access to all the features.
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
+ }