連載第7回は、
スライダーの各部の名称
コードの解説に入る前に、

実際の動作例もご覧ください
Control.Slider
それではコードを見ていきましょう。
0001:// script.aculo.us slider.js v1.8.1, Thu Jan 03 22:07:12 -0500 2008
0002:
0003:// Copyright (c) 2005-2007 Marty Haught, Thomas Fuchs
0004://
0005:// script.aculo.us is freely distributable under the terms of an MIT-style license.
0006:// For details, see the script.aculo.us web site: http://script.aculo.us/
0007:
1~7行目は、
0008:if (!Control) var Control = { };
0009:
8行目で、
0010:// options:
0011:// axis: 'vertical', or 'horizontal' (default)
0012://
0013:// callbacks:
0014:// onChange(value)
0015:// onSlide(value)
10~15行目で、
0016:Control.Slider = Class.create({
0017: initialize: function(handle, track, options) {
0018: var slider = this;
0019:
0020: if (Object.isArray(handle)) {
0021: this.handles = handle.collect( function(e) { return $(e) });
0022: } else {
0023: this.handles = [$(handle)];
0024: }
0025:
0026: this.track = $(track);
0027: this.options = options || { };
0028:
0029: this.axis = this.options.axis || 'horizontal';
0030: this.increment = this.options.increment || 1;
0031: this.step = parseInt(this.options.step || '1');
0032: this.range = this.options.range || $R(0,1);
0033:
0034: this.value = 0; // assure backwards compat
0035: this.values = this.handles.map( function() { return 0 });
0036: this.spans = this.options.spans ? this.options.spans.map(function(s){ return $(s) }) : false;
0037: this.options.startSpan = $(this.options.startSpan || null);
0038: this.options.endSpan = $(this.options.endSpan || null);
0039:
0040: this.restricted = this.options.restricted || false;
0041:
0042: this.maximum = this.options.maximum || this.range.end;
0043: this.minimum = this.options.minimum || this.range.start;
0044:
0045: // Will be used to align the handle onto the track, if necessary
0046: this.alignX = parseInt(this.options.alignX || '0');
0047: this.alignY = parseInt(this.options.alignY || '0');
0048:
0049: this.trackLength = this.maximumOffset() - this.minimumOffset();
0050:
0051: this.handleLength = this.isVertical() ?
0052: (this.handles[0].offsetHeight != 0 ?
0053: this.handles[0].offsetHeight : this.handles[0].style.height.replace(/px$/,"")) :
0054: (this.handles[0].offsetWidth != 0 ? this.handles[0].offsetWidth :
0055: this.handles[0].style.width.replace(/px$/,""));
0056:
0057: this.active = false;
0058: this.dragging = false;
0059: this.disabled = false;
0060:
0061: if (this.options.disabled) this.setDisabled();
0062:
0063: // Allowed values array
0064: this.allowedValues = this.options.values ? this.options.values.sortBy(Prototype.K) : false;
0065: if (this.allowedValues) {
0066: this.minimum = this.allowedValues.min();
0067: this.maximum = this.allowedValues.max();
0068: }
0069:
0070: this.eventMouseDown = this.startDrag.bindAsEventListener(this);
0071: this.eventMouseUp = this.endDrag.bindAsEventListener(this);
0072: this.eventMouseMove = this.update.bindAsEventListener(this);
0073:
0074: // Initialize handles in reverse (make sure first handle is active)
0075: this.handles.each( function(h,i) {
0076: i = slider.handles.length-1-i;
0077: slider.setValue(parseFloat(
0078: (Object.isArray(slider.options.sliderValue) ?
0079: slider.options.sliderValue[i] : slider.options.sliderValue) ||
0080: slider.range.start), i);
0081: h.makePositioned().observe("mousedown", slider.eventMouseDown);
0082: });
0083:
0084: this.track.observe("mousedown", this.eventMouseDown);
0085: document.observe("mouseup", this.eventMouseUp);
0086: document.observe("mousemove", this.eventMouseMove);
0087:
0088: this.initialized = true;
0089: },
16~89行目の、
20行目で、
23行目で、
26行目で、
29行目で、
30行目で、
31行目で、
32行目で、
34行目で、
35行目で、
36行目で、
37行目で、
38行目で、
40行目で、
42行目で、
43行目で、
46行目で、
47行目で、
49行目で、
51行目で、
57行目で、
58行目で、
59行目で、
61行目で、
64行目で、
66、
70~72行目で、
75~83行目で、
76行目で、
77行目で、
81行目で、
84行目で、
85行目で、
86行目で、
88行目で、
0090: dispose: function() {
0091: var slider = this;
0092: Event.stopObserving(this.track, "mousedown", this.eventMouseDown);
0093: Event.stopObserving(document, "mouseup", this.eventMouseUp);
0094: Event.stopObserving(document, "mousemove", this.eventMouseMove);
0095: this.handles.each( function(h) {
0096: Event.stopObserving(h, "mousedown", slider.eventMouseDown);
0097: });
0098: },
90~98行目の、
0099: setDisabled: function(){
0100: this.disabled = true;
0101: },
0102: setEnabled: function(){
0103: this.disabled = false;
0104: },
99~104行目の、
0105: getNearestValue: function(value){
0106: if (this.allowedValues){
0107: if (value >= this.allowedValues.max()) return(this.allowedValues.max());
0108: if (value <= this.allowedValues.min()) return(this.allowedValues.min());
0109:
0110: var offset = Math.abs(this.allowedValues[0] - value);
0111: var newValue = this.allowedValues[0];
0112: this.allowedValues.each( function(v) {
0113: var currentOffset = Math.abs(v - value);
0114: if (currentOffset <= offset){
0115: newValue = v;
0116: offset = currentOffset;
0117: }
0118: });
0119: return newValue;
0120: }
0121: if (value > this.range.end) return this.range.end;
0122: if (value < this.range.start) return this.range.start;
0123: return value;
0124: },
105~124行目の、
106行目で、
107行目で、
108行目で、
110~119行目で、
121~123行目で、
0125: setValue: function(sliderValue, handleIdx){
0126: if (!this.active) {
0127: this.activeHandleIdx = handleIdx || 0;
0128: this.activeHandle = this.handles[this.activeHandleIdx];
0129: this.updateStyles();
0130: }
0131: handleIdx = handleIdx || this.activeHandleIdx || 0;
0132: if (this.initialized && this.restricted) {
0133: if ((handleIdx>0) && (sliderValue<this.values[handleIdx-1]))
0134: sliderValue = this.values[handleIdx-1];
0135: if ((handleIdx < (this.handles.length-1)) && (sliderValue>this.values[handleIdx+1]))
0136: sliderValue = this.values[handleIdx+1];
0137: }
0138: sliderValue = this.getNearestValue(sliderValue);
0139: this.values[handleIdx] = sliderValue;
0140: this.value = this.values[0]; // assure backwards compat
0141:
0142: this.handles[handleIdx].style[this.isVertical() ? 'top' : 'left'] =
0143: this.translateToPx(sliderValue);
0144:
0145: this.drawSpans();
0146: if (!this.dragging || !this.event) this.updateFinished();
0147: },
125~147行目の、
126行目で、
127行目で、
128行目で、
129行目で、
132~137行目で、
138行目で、
139行目で、
140行目で、
142行目で、
145行目で、
146行目で、
0148: setValueBy: function(delta, handleIdx) {
0149: this.setValue(this.values[handleIdx || this.activeHandleIdx || 0] + delta,
0150: handleIdx || this.activeHandleIdx || 0);
0151: },
148~151行目の、
149行目で、
0152: translateToPx: function(value) {
0153: return Math.round(
0154: ((this.trackLength-this.handleLength)/(this.range.end-this.range.start)) *
0155: (value - this.range.start)) + "px";
0156: },
152~156行目の、
154行目の、
155行目で、
0157: translateToValue: function(offset) {
0158: return ((offset/(this.trackLength-this.handleLength) *
0159: (this.range.end-this.range.start)) + this.range.start);
0160: },
157~160行目の、
158行目の、
159行目で、
0161: getRange: function(range) {
0162: var v = this.values.sortBy(Prototype.K);
0163: range = range || 0;
0164: return $R(v[range],v[range+1]);
0165: },
161~165行目の、
162行目で、
164行目で、
0166: minimumOffset: function(){
0167: return(this.isVertical() ? this.alignY : this.alignX);
0168: },
166~168行目の、
0169: maximumOffset: function(){
0170: return(this.isVertical() ?
0171: (this.track.offsetHeight != 0 ? this.track.offsetHeight :
0172: this.track.style.height.replace(/px$/,"")) - this.alignY :
0173: (this.track.offsetWidth != 0 ? this.track.offsetWidth :
0174: this.track.style.width.replace(/px$/,"")) - this.alignX);
0175: },
169~175行目の、
170行目で、
173行目で、
0176: isVertical: function(){
0177: return (this.axis == 'vertical');
0178: },
176~178行目の、
0179: drawSpans: function() {
0180: var slider = this;
0181: if (this.spans)
0182: $R(0, this.spans.length-1).each(function(r) { slider.setSpan(slider.spans[r], slider.getRange(r)) });
0183: if (this.options.startSpan)
0184: this.setSpan(this.options.startSpan,
0185: $R(0, this.values.length>1 ? this.getRange(0).min() : this.value ));
0186: if (this.options.endSpan)
0187: this.setSpan(this.options.endSpan,
0188: $R(this.values.length>1 ? this.getRange(this.spans.length-1).max() : this.value, this.maximum));
0189: },
179~189行目の、
182行目で、
183行目で、
184行目で、
186行目で、
187行目で、
0190: setSpan: function(span, range) {
0191: if (this.isVertical()) {
0192: span.style.top = this.translateToPx(range.start);
0193: span.style.height = this.translateToPx(range.end - range.start + this.range.start);
0194: } else {
0195: span.style.left = this.translateToPx(range.start);
0196: span.style.width = this.translateToPx(range.end - range.start + this.range.start);
0197: }
0198: },
190~198行目の、
191行目で、
194行目で、
0199: updateStyles: function() {
0200: this.handles.each( function(h){ Element.removeClassName(h, 'selected') });
0201: Element.addClassName(this.activeHandle, 'selected');
0202: },
199~202行目の、
200行目で、
201行目で、
0203: startDrag: function(event) {
0204: if (Event.isLeftClick(event)) {
0205: if (!this.disabled){
0206: this.active = true;
0207:
0208: var handle = Event.element(event);
0209: var pointer = [Event.pointerX(event), Event.pointerY(event)];
0210: var track = handle;
0211: if (track==this.track) {
0212: var offsets = Position.cumulativeOffset(this.track);
0213: this.event = event;
0214: this.setValue(this.translateToValue(
0215: (this.isVertical() ? pointer[1]-offsets[1] : pointer[0]-offsets[0])-(this.handleLength/2)
0216: ));
0217: var offsets = Position.cumulativeOffset(this.activeHandle);
0218: this.offsetX = (pointer[0] - offsets[0]);
0219: this.offsetY = (pointer[1] - offsets[1]);
0220: } else {
0221: // find the handle (prevents issues with Safari)
0222: while((this.handles.indexOf(handle) == -1) && handle.parentNode)
0223: handle = handle.parentNode;
0224:
0225: if (this.handles.indexOf(handle)!=-1) {
0226: this.activeHandle = handle;
0227: this.activeHandleIdx = this.handles.indexOf(this.activeHandle);
0228: this.updateStyles();
0229:
0230: var offsets = Position.cumulativeOffset(this.activeHandle);
0231: this.offsetX = (pointer[0] - offsets[0]);
0232: this.offsetY = (pointer[1] - offsets[1]);
0233: }
0234: }
0235: }
0236: Event.stop(event);
0237: }
0238: },
203~238行目の、
205行目で、
206行目で、
208行目で、
209行目で、
211行目で、
212行目で、
214行目で、
217行目で、
218、

220行目で、
222行目で、
225行目で、
226、
228行目で、
230~232行目で、
0239: update: function(event) {
0240: if (this.active) {
0241: if (!this.dragging) this.dragging = true;
0242: this.draw(event);
0243: if (Prototype.Browser.WebKit) window.scrollBy(0,0);
0244: Event.stop(event);
0245: }
0246: },
239~246行目の、
240行目で、
241行目で、
242行目で、
243行目で、
0247: draw: function(event) {
0248: var pointer = [Event.pointerX(event), Event.pointerY(event)];
0249: var offsets = Position.cumulativeOffset(this.track);
0250: pointer[0] -= this.offsetX + offsets[0];
0251: pointer[1] -= this.offsetY + offsets[1];
0252: this.event = event;
0253: this.setValue(this.translateToValue( this.isVertical() ? pointer[1] : pointer[0] ));
0254: if (this.initialized && this.options.onSlide)
0255: this.options.onSlide(this.values.length>1 ? this.values : this.value, this)
0256: },
247~256行目の、
248行目で、
249行目で、
250、
253行目で、
254行目で、
0257: endDrag: function(event) {
0258: if (this.active && this.dragging) {
0259: this.finishDrag(event, true);
0260: Event.stop(event);
0261: }
0262: this.active = false;
0263: this.dragging = false;
0264: },
257~264行目の、
258行目で、
262、
0265: finishDrag: function(event, success) {
0266: this.active = false;
0267: this.dragging = false;
0268: this.updateFinished();
0269: },
265~269行目の、
0270: updateFinished: function() {
0271: if (this.initialized && this.options.onChange)
0272: this.options.onChange(this.values.length>1 ? this.values : this.value, this);
0273: this.event = null;
0274: }
0275:});
0276:
270~276行目の、
271行目で、
273行目で、