// vim: et:ts=4:sw=4:sts=4:ft=javascript /*@ Ox.Range Range Object () -> Range Object (options) -> Range Object (options, self) -> Range Object options Options object arrows if true, show arrows arrowStep step when clicking arrows arrowSymbols arrow symbols, like ['minus', 'plus'] max maximum value min minimum value orientation 'horizontal' or 'vertical' step step between values size width or height, in px thumbSize minimum width or height of thumb, in px thumbValue if true, display value on thumb trackGradient colors trackImages one or multiple track background image URLs trackStep 0 (scroll here) or step when clicking track value initial value valueNames value names to display on thumb self shared private variable change triggered on change of the range @*/ Ox.Range = function(options, self) { self = self || {}; var that = Ox.Element({}, self) .defaults({ arrows: false, arrowStep: 1, arrowSymbols: ['left', 'right'], max: 100, min: 0, orientation: 'horizontal', step: 1, size: 128, // fixme: shouldn't this be width? thumbSize: 16, thumbValue: false, trackColors: [], trackImages: [], trackStep: 0, value: 0, valueNames: null, }) .options($.extend(options, { arrowStep: options.arrowStep ? options.arrowStep : options.step, trackImages: $.makeArray(options.trackImages || []) })) .addClass('OxRange') .css({ width: self.options.size + 'px' }); $.extend(self, { trackColors: self.options.trackColors.length, trackImages: self.options.trackImages.length, values: (self.options.max - self.options.min + self.options.step) / self.options.step }); setSizes(); if (self.options.arrows) { self.$arrows = []; Ox.range(0, 2).forEach(function(i) { self.$arrows[i] = Ox.Button({ overlap: i == 0 ? 'right' : 'left', title: self.options.arrowSymbols[i], type: 'image' }) .addClass('OxArrow') .bindEvent({ mousedown: function(event, e) { clickArrow(e, i, true); }, mouserepeat: function(event, e) { clickArrow(e, i, false); } }) .appendTo(that.$element); }); } self.$track = Ox.Element() .addClass('OxTrack') .css($.extend({ width: (self.trackSize - 2) + 'px' }, self.trackImages == 1 ? { background: 'rgb(0, 0, 0)' } : {})) .bindEvent({ mousedown: clickTrack, drag: dragTrack }) .appendTo(that.$element); self.trackColors && setTrackColors(); if (self.trackImages) { self.$trackImages = $('
') .css({ width: self.trackSize + 'px', marginRight: (-self.trackSize - 1) + 'px' }) .appendTo(self.$track.$element); self.options.trackImages.forEach(function(v, i) { //Ox.print(self.trackImageWidths[i]) $('') .attr({ src: v }) .addClass(i == 0 ? 'OxFirstChild' : '') .addClass(i == self.trackImages - 1 ? 'OxLastChild' : '') .css({ width: self.trackImageWidths[i] + 'px' }) .mousedown(function(e) { e.preventDefault(); // prevent drag }) .appendTo(self.$trackImages); //left += self.trackImageWidths[i]; }); } self.$thumb = Ox.Button({ id: self.options.id + 'Thumb', title: self.options.thumbValue ? (self.options.valueNames ? self.options.valueNames[self.options.value] : self.options.value) : '', width: self.thumbSize }) .addClass('OxThumb') /* .css({ border: '1px solid rgb(255, 255, 255)', background: 'rgba(0, 0, 0, 0)' }) */ .appendTo(self.$track); setThumb(); function clickArrow(e, i, animate) { // fixme: shift doesn't work, see menu scrolling setValue(self.options.value + self.options.arrowStep * (i == 0 ? -1 : 1) * (e.shiftKey ? 2 : 1), animate); } function clickTrack(event, e) { // fixme: thumb ends up a bit too far on the right var isThumb = $(e.target).hasClass('OxThumb'); self.drag = { left: self.$track.offset().left, offset: isThumb ? e.clientX - self.$thumb.offset().left - 8 /*self.thumbSize / 2*/ : 0 }; setValue(getVal(e.clientX - self.drag.left - self.drag.offset), !isThumb); } function dragTrack(event, e) { setValue(getVal(e.clientX - self.drag.left - self.drag.offset)) } function getPx(val) { var pxPerVal = (self.trackSize - self.thumbSize) / (self.options.max - self.options.min); return Math.ceil((val - self.options.min) * pxPerVal); } /* function getTime(oldValue, newValue) { return self.animationTime * Math.abs(oldValue - newValue) / (self.options.max - self.options.min); } */ function getVal(px) { var px = self.trackSize / self.values >= 16 ? px : px - 8, valPerPx = (self.options.max - self.options.min) / (self.trackSize - self.thumbSize); return Ox.limit(self.options.min + Math.floor(px * valPerPx / self.options.step) * self.options.step, self.options.min, self.options.max); } function setSizes() { self.trackSize = self.options.size - self.options.arrows * 32; self.thumbSize = Math.max(self.trackSize / self.values, self.options.thumbSize); self.trackImageWidths = self.trackImages == 1 ? [self.trackSize - 16] : Ox.divideInt(self.trackSize - 2, self.trackImages); self.trackColorsStart = self.thumbSize / 2 / self.options.size; self.trackColorsStep = (self.options.size - self.thumbSize) / (self.trackColors - 1) / self.options.size; that.css({ width: self.options.size + 'px' }); self.$track && self.$track.css({ width: (self.trackSize - 2) + 'px' }); if (self.$thumb) { self.$thumb.options({ width: self.thumbSize }); setThumb(); } } function setThumb(animate) { self.$thumb.stop().animate({ marginLeft: (getPx(self.options.value) - 1) + 'px', //width: self.thumbSize + 'px' }, animate ? 200 : 0, function() { if (self.options.thumbValue) { self.$thumb.options({ title: self.options.valueNames ? self.options.valueNames[self.options.value] : self.options.value }); } }); } function setTrackColors() { self.$track.css({ backgroundImage: $.browser.mozilla ? ('-moz-linear-gradient(left, ' + self.options.trackColors[0] + ' 0%, ' + $.map(self.options.trackColors, function(v, i) { return v + ' ' + ((self.trackColorsStart + self.trackColorsStep * i) * 100) + '%'; }).join(', ') + ', ' + self.options.trackColors[self.trackColors - 1] + ' 100%)') : ('-webkit-gradient(linear, left top, right top, color-stop(0, ' + self.options.trackColors[0] + '), ' + $.map(self.options.trackColors, function(v, i) { return 'color-stop(' + (self.trackColorsStart + self.trackColorsStep * i) + ', ' + v + ')'; }).join(', ') + ', color-stop(1, ' + self.options.trackColors[self.trackColors - 1] + '))') }); } function setValue(value, animate) { value = Ox.limit(value, self.options.min, self.options.max); if (value != self.options.value) { //time = getTime(self.options.value, value); self.options.value = value; setThumb(animate); that.triggerEvent('change', { value: value }); } } self.setOption = function(key, value) { if (key == 'size') { setSizes(); } else if (key == 'trackColors') { setTrackColors(); } else if (key == 'value') { setThumb(); } } return that; };