/** * RightJS-UI Slider v2.2.3 * http://rightjs.org/ui/slider * * Copyright (C) 2009-2011 Nikolay Nemshilov */ var Slider = RightJS.Slider = (function(document, Math, RightJS) { /** * This module defines the basic widgets constructor * it creates an abstract proxy with the common functionality * which then we reuse and override in the actual widgets * * Copyright (C) 2010-2011 Nikolay Nemshilov */ /** * The widget units constructor * * @param String tag-name or Object methods * @param Object methods * @return Widget wrapper */ function Widget(tag_name, methods) { if (!methods) { methods = tag_name; tag_name = 'DIV'; } /** * An Abstract Widget Unit * * Copyright (C) 2010 Nikolay Nemshilov */ var AbstractWidget = new RightJS.Class(RightJS.Element.Wrappers[tag_name] || RightJS.Element, { /** * The common constructor * * @param Object options * @param String optional tag name * @return void */ initialize: function(key, options) { this.key = key; var args = [{'class': 'rui-' + key}]; // those two have different constructors if (!(this instanceof RightJS.Input || this instanceof RightJS.Form)) { args.unshift(tag_name); } this.$super.apply(this, args); if (RightJS.isString(options)) { options = RightJS.$(options); } // if the options is another element then // try to dynamically rewrap it with our widget if (options instanceof RightJS.Element) { this._ = options._; if ('$listeners' in options) { options.$listeners = options.$listeners; } options = {}; } this.setOptions(options, this); return (RightJS.Wrapper.Cache[RightJS.$uid(this._)] = this); }, // protected /** * Catches the options * * @param Object user-options * @param Element element with contextual options * @return void */ setOptions: function(options, element) { if (element) { options = RightJS.Object.merge(options, new Function("return "+( element.get('data-'+ this.key) || '{}' ))()); } if (options) { RightJS.Options.setOptions.call(this, RightJS.Object.merge(this.options, options)); } return this; } }); /** * Creating the actual widget class * */ var Klass = new RightJS.Class(AbstractWidget, methods); // creating the widget related shortcuts RightJS.Observer.createShortcuts(Klass.prototype, Klass.EVENTS || RightJS([])); return Klass; } /** * Same as the assignable, only it doesn't work with popups * instead it simply updates the assigned unit value/content * * Copyright (C) 2010 Nikolay Nemshilov */ var Updater = { /** * Assigns the unit to work with an input element * * @param mixed element reference * @return Rater this */ assignTo: function(element) { var assign = R(function(element, event) { if ((element = $(element))) { element[element.setValue ? 'setValue' : 'update'](event.target.getValue()); } }).curry(element); var connect = R(function(element, object) { element = $(element); if (element && element.onChange) { element.onChange(R(function() { this.setValue(element.value()); }).bind(object)); } }).curry(element); if ($(element)) { assign({target: this}); connect(this); } else { $(document).onReady(R(function() { assign({target: this}); connect(this); }.bind(this))); } return this.onChange(assign); } }; /** * The filenames to include * * Copyright (C) 2010 Nikolay Nemshilov */ var R = RightJS, $ = RightJS.$, $$ = RightJS.$$, $w = RightJS.$w, $E = RightJS.$E, $A = RightJS.$A, isHash = RightJS.isHash, Element = RightJS.Element; /** * RightJS UI Slider unit * * Copyright (C) 2009-2011 Nikolay Nemshilov */ var Slider = new Widget({ include: Updater, extend: { version: '2.2.3', EVENTS: $w('change'), Options: { min: 0, // the min value max: 100, // the max value snap: 0, // the values threshold value: null, // start value, if null then the min value will be used direction: 'x', // slider direction 'x', 'y' update: null, // reference to an element to update round: 0 // the number of symbols after the decimal pointer }, current: false }, /** * basic constructor * USAGE: * new Slider('element-id'[, {options}]); * new Slider({options}); * * @param mixed slider element reference or options * @param Object options */ initialize: function() { var args = $A(arguments).compact(), options = args.pop(), element = args.pop(); // figuring out the arguments if (!isHash(options) || options instanceof Element) { element = $(element || options); options = {}; } this.$super('slider', element).setOptions(options) .on('selectstart', 'stopEvent'); // disable select under IE this.level = this.first('.level') || $E('div', {'class': 'level'}).insertTo(this); this.handle = this.first('.handle') || $E('div', {'class': 'handle'}).insertTo(this); options = this.options; this.value = options.value === null ? options.min : options.value; if (options.update) { this.assignTo(options.update); } if (options.direction === 'y') { this.addClass('rui-slider-vertical'); } else if (this.hasClass('rui-slider-vertical')) { options.direction = 'y'; } this.setValue(this.value); }, /** * The value setter * * NOTE: will get snapped according to the options * * @param mixed string or number value * @return Slider this */ setValue: function(value) { return this.precalc().shiftTo(value); }, /** * Returns the value * * @return Float number */ getValue: function() { return this.value; }, /** * Inserts the widget into the element * * @param mixed element reference * @param String optional position * @return Slider this */ insertTo: function(element, position) { return this.$super(element, position).setValue(this.value); }, // protected // precalculates dimensions, direction and offset for further use precalc: function() { var horizontal = this.options.direction === 'x', handle = this.handle.setStyle(horizontal ? {left: 0} : {bottom: 0}).dimensions(), handle_size = this.hSize = horizontal ? handle.width : handle.height, dims = this.dims = this.dimensions(); this.offset = horizontal ? handle.left - dims.left : dims.top + dims.height - handle.top - handle_size; this.space = (horizontal ? dims.width : dims.height) - handle_size - this.offset * 2; return this; }, // initializes the slider drag start: function(event) { return this.precalc().e2val(event); }, // processes the slider-drag move: function(event) { return this.e2val(event); }, // shifts the slider to the value shiftTo: function(value) { var options = this.options, base = Math.pow(10, options.round), horizontal = options.direction === 'x'; // rounding the value up value = Math.round(value * base) / base; // checking the value constraings if (value < options.min) { value = options.min; } if (value > options.max) { value = options.max; } if (options.snap) { var snap = options.snap; var diff = (value - options.min) % snap; value = diff < snap/2 ? value - diff : value - diff + snap; } // calculating and setting the actual position var position = this.space / (options.max - options.min) * (value - options.min); this.handle._.style[horizontal ? 'left' : 'bottom'] = position + 'px'; this.level._.style[horizontal ? 'width': 'height'] = ((position > 0 ? position : 0) + 2) + 'px'; // checking the change status if (value !== this.value) { this.value = value; this.fire('change', {value: value}); } return this; }, // converts the event position into the actual value in terms of the slider measures e2val: function(event) { var options = this.options, horizontal = options.direction === 'x', dims = this.dims, offset = this.offset, space = this.space, cur_pos = event.position()[horizontal ? 'x' : 'y'] - offset - this.hSize/2, min_pos = horizontal ? dims.left + offset : dims.top + offset, value = (options.max - options.min) / space * (cur_pos - min_pos); return this.shiftTo(horizontal ? options.min + value : options.max - value); } }); /** * Document onReady hook for sliders * * Copyright (C) 2009-2010 Nikolay Nemshilov */ $(document).on({ // preinitializing the sliders ready: function() { $$('.rui-slider').each(function(element) { if (!(element instanceof Slider)) { element = new Slider(element); } }); }, // initiates the slider move mousedown: function(event) { var slider = event.find('.rui-slider'); if (slider) { event.stop(); if (!(slider instanceof Slider)) { slider = new Slider(slider); } Slider.current = slider.start(event); } }, // handles the slider move mousemove: function(event) { if (Slider.current) { Slider.current.move(event); } }, // handles the slider release mouseup: function(event) { if (Slider.current) { Slider.current = false; } } }); $(window).onBlur(function() { if (Slider.current) { Slider.current = false; } }); var embed_style = document.createElement('style'), embed_rules = document.createTextNode("div.rui-slider,div.rui-slider .handle div.rui-slider .level{margin:0;padding:0;border:none;background:none}div.rui-slider{height:0.4em;width:20em;border:1px solid #bbb;background:#F8F8F8;border-radius:.2em;-moz-border-radius:.2em;-webkit-border-radius:.2em;position:relative;margin:.6em 0;display:inline-block; *display:inline; *zoom:1;vertical-align:middle;user-select:none;-moz-user-select:none;-webkit-user-select:none;cursor:pointer}div.rui-slider .handle{font-size:25%;position:absolute;left:0;top:0;width:4pt;height:10pt;margin-top:-4pt;margin-left:0.4em;background:#BBB;border:1px solid #999;border-radius:.8em;-moz-border-radius:.8em;-webkit-border-radius:.8em;z-index:20}div.rui-slider .level{font-size:25%;position:absolute;top:0;left:0;width:0;height:100%;background:#ddd;z-index:1}div.rui-slider-vertical{height:10em;width:0.4em;margin:0 .3em}div.rui-slider-vertical .handle{top:auto;bottom:0;margin:0;margin-left:-4pt;margin-bottom:0.4em;height:5pt;width:10pt}div.rui-slider-vertical .level{height:0;width:100%;top:auto;bottom:0}"); embed_style.type = 'text/css'; document.getElementsByTagName('head')[0].appendChild(embed_style); if(embed_style.styleSheet) { embed_style.styleSheet.cssText = embed_rules.nodeValue; } else { embed_style.appendChild(embed_rules); } return Slider; })(document, Math, RightJS);