// vim: et:ts=4:sw=4:sts=4:ft=javascript /*@ Ox.Range <f:Ox.Element> Range Object () -> <f> Range Object (options) -> <f> Range Object (options, self) -> <f> Range Object options <o> Options object arrows <b> if true, show arrows arrowStep <n> step when clicking arrows arrowSymbols <a> arrow symbols, like ['minus', 'plus'] max <n> maximum value min <n> minimum value orientation <s> 'horizontal' or 'vertical' step <n> step between values size <n> width or height, in px thumbSize <n> minimum width or height of thumb, in px thumbValue <b> if true, display value on thumb trackGradient <a> colors trackImages <s> or <a> one or multiple track background image URLs trackStep <n> 0 (scroll here) or step when clicking track value <n> initial value valueNames <a> value names to display on thumb self <o> 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 = $('<div>') .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]) $('<img>') .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; };