/** * asRange v0.3.4 * https://github.com/amazingSurge/jquery-asRange * * Copyright (c) amazingSurge * Released under the LGPL-3.0 license */ import $ from 'jquery'; var DEFAULTS = { namespace: 'asRange', skin: null, max: 100, min: 0, value: null, step: 10, limit: true, range: false, direction: 'h', // 'v' or 'h' keyboard: true, replaceFirst: false, // false, 'inherit', {'inherit': 'default'} tip: true, scale: true, format(value) { return value; } }; function getEventObject (event) { let e = event.originalEvent; if (e.touches && e.touches.length && e.touches[0]) { e = e.touches[0]; } return e; } class Pointer { constructor ($element, id, parent) { this.$element = $element; this.uid = id; this.parent = parent; this.options = $.extend(true, {}, this.parent.options); this.direction = this.options.direction; this.value = null; this.classes = { active: `${this.parent.namespace}-pointer_active` }; } mousedown(event) { const axis = this.parent.direction.axis; const position = this.parent.direction.position; const offset = this.parent.$wrap.offset(); this.$element.trigger(`${this.parent.namespace}::moveStart`, this); this.data = {}; this.data.start = event[axis]; this.data.position = event[axis] - offset[position]; const value = this.parent.getValueFromPosition(this.data.position); this.set(value); $.each(this.parent.pointer, (i, p) => { p.deactive(); }); this.active(); this.mousemove = function(event) { const eventObj = getEventObject(event); const value = this.parent.getValueFromPosition(this.data.position + (eventObj[axis] || this.data.start) - this.data.start); this.set(value); event.preventDefault(); return false; }; this.mouseup = function() { $(document).off('.asRange mousemove.asRange touchend.asRange mouseup.asRange touchcancel.asRange'); this.$element.trigger(`${this.parent.namespace}::moveEnd`, this); return false; }; $(document).on('touchmove.asRange mousemove.asRange', $.proxy(this.mousemove, this)) .on('touchend.asRange mouseup.asRange', $.proxy(this.mouseup, this)); return false; } active() { this.$element.addClass(this.classes.active); } deactive() { this.$element.removeClass(this.classes.active); } set(value) { if (this.value === value) { return; } if (this.parent.step) { value = this.matchStep(value); } if (this.options.limit === true) { value = this.matchLimit(value); } else { if (value <= this.parent.min) { value = this.parent.min; } if (value >= this.parent.max) { value = this.parent.max; } } this.value = value; this.updatePosition(); this.$element.focus(); this.$element.trigger(`${this.parent.namespace}::move`, this); } updatePosition() { const position = {}; position[this.parent.direction.position] = `${this.getPercent()}%`; this.$element.css(position); } getPercent() { return ((this.value - this.parent.min) / this.parent.interval) * 100; } get() { return this.value; } matchStep(value) { const step = this.parent.step; const decimal = step.toString().split('.')[1]; value = Math.round(value / step) * step; if (decimal) { value = value.toFixed(decimal.length); } return parseFloat(value); } matchLimit(value) { let left; let right; const pointer = this.parent.pointer; if (this.uid === 1) { left = this.parent.min; } else { left = pointer[this.uid - 2].value; } if (pointer[this.uid] && pointer[this.uid].value !== null) { right = pointer[this.uid].value; } else { right = this.parent.max; } if (value <= left) { value = left; } if (value >= right) { value = right; } return value; } destroy() { this.$element.off('.asRange'); this.$element.remove(); } } var scale = { defaults: { scale: { valuesNumber: 3, gap: 1, grid: 5 } }, init(instance) { const opts = $.extend({}, this.defaults, instance.options.scale); const scale = opts.scale; scale.values = []; scale.values.push(instance.min); const part = (instance.max - instance.min) / (scale.valuesNumber - 1); for (let j = 1; j <= (scale.valuesNumber - 2); j++) { scale.values.push(part * j); } scale.values.push(instance.max); const classes = { scale: `${instance.namespace}-scale`, lines: `${instance.namespace}-scale-lines`, grid: `${instance.namespace}-scale-grid`, inlineGrid: `${instance.namespace}-scale-inlineGrid`, values: `${instance.namespace}-scale-values` }; const len = scale.values.length; const num = ((scale.grid - 1) * (scale.gap + 1) + scale.gap) * (len - 1) + len; const perOfGrid = 100 / (num - 1); const perOfValue = 100 / (len - 1); this.$scale = $('
').addClass(classes.scale); this.$lines = $('').addClass(classes.lines); this.$values = $('').addClass(classes.values); for (let i = 0; i < num; i++) { let $list; if (i === 0 || i === num || i % ((num - 1) / (len - 1)) === 0) { $list = $(`
  • `); } else if (i % scale.grid === 0) { $list = $(`
  • `); } else { $list = $('
  • '); } // position scale $list.css({ left: `${perOfGrid * i}%` }).appendTo(this.$lines); } for (let v = 0; v < len; v++) { // position value $(`
  • ${scale.values[v]}
  • `).css({ left: `${perOfValue * v}%` }).appendTo(this.$values); } this.$lines.add(this.$values).appendTo(this.$scale); this.$scale.appendTo(instance.$wrap); }, update(instance) { this.$scale.remove(); this.init(instance); } }; var selected = { defaults: {}, init(instance) { this.$arrow = $('').appendTo(instance.$wrap); this.$arrow.addClass(`${instance.namespace}-selected`); if (instance.options.range === false) { instance.p1.$element.on(`${instance.namespace}::move`, (e, pointer) => { this.$arrow.css({ left: 0, width: `${pointer.getPercent()}%` }); }); } if (instance.options.range === true) { const onUpdate = () => { let width = instance.p2.getPercent() - instance.p1.getPercent(); let left; if (width >= 0) { left = instance.p1.getPercent(); } else { width = -width; left = instance.p2.getPercent(); } this.$arrow.css({ left: `${left}%`, width: `${width}%` }); }; instance.p1.$element.on(`${instance.namespace}::move`, onUpdate); instance.p2.$element.on(`${instance.namespace}::move`, onUpdate); } } }; var tip = { defaults: { active: 'always' // 'always' 'onMove' }, init(instance) { const that = this; const opts = $.extend({}, this.defaults, instance.options.tip); this.opts = opts; this.classes = { tip: `${instance.namespace}-tip`, show: `${instance.namespace}-tip-show` }; $.each(instance.pointer, (i, p) => { const $tip = $('').appendTo(instance.pointer[i].$element); $tip.addClass(that.classes.tip); if (that.opts.active === 'onMove') { $tip.css({ display: 'none' }); p.$element.on(`${instance.namespace}::moveEnd`, () => { that.hide($tip); return false; }).on(`${instance.namespace}::moveStart`, () => { that.show($tip); return false; }); } p.$element.on(`${instance.namespace}::move`, () => { let value; if (instance.options.range) { value = instance.get()[i]; } else { value = instance.get(); } if (typeof instance.options.format === 'function') { if (instance.options.replaceFirst && typeof value !== 'number') { if (typeof instance.options.replaceFirst === 'string') { value = instance.options.replaceFirst; } if (typeof instance.options.replaceFirst === 'object') { for (const key in instance.options.replaceFirst) { if(Object.hasOwnProperty(instance.options.replaceFirst, key)){ value = instance.options.replaceFirst[key]; } } } } else { value = instance.options.format(value); } } $tip.text(value); return false; }); }); }, show($tip) { $tip.addClass(this.classes.show); $tip.css({ display: 'block' }); }, hide($tip) { $tip.removeClass(this.classes.show); $tip.css({ display: 'none' }); } }; var keyboard = function() { const $doc = $(document); $doc.on('asRange::ready', (event, instance) => { let step; const keyboard = { keys: { 'UP': 38, 'DOWN': 40, 'LEFT': 37, 'RIGHT': 39, 'RETURN': 13, 'ESCAPE': 27, 'BACKSPACE': 8, 'SPACE': 32 }, map: {}, bound: false, press(e) { /*eslint consistent-return: "off"*/ const key = e.keyCode || e.which; if (key in keyboard.map && typeof keyboard.map[key] === 'function') { keyboard.map[key](e); return false; } }, attach(map) { let key; let up; for (key in map) { if (map.hasOwnProperty(key)) { up = key.toUpperCase(); if (up in keyboard.keys) { keyboard.map[keyboard.keys[up]] = map[key]; } else { keyboard.map[up] = map[key]; } } } if (!keyboard.bound) { keyboard.bound = true; $doc.bind('keydown', keyboard.press); } }, detach() { keyboard.bound = false; keyboard.map = {}; $doc.unbind('keydown', keyboard.press); } }; if (instance.options.keyboard === true) { $.each(instance.pointer, (i, p) => { if (instance.options.step) { step = instance.options.step; } else { step = 1; } const left = () => { const value = p.value; p.set(value - step); }; const right = () => { const value = p.value; p.set(value + step); }; p.$element.attr('tabindex', '0').on('focus', () => { keyboard.attach({ left, right }); return false; }).on('blur', () => { keyboard.detach(); return false; }); }); } }); }; let components = {}; /** * Plugin constructor **/ class asRange { constructor(element, options) { const metas = {}; this.element = element; this.$element = $(element); if (this.$element.is('input')) { const value = this.$element.val(); if (typeof value === 'string') { metas.value = value.split(','); } $.each(['min', 'max', 'step'], (index, key) => { const val = parseFloat(this.$element.attr(key)); if (!isNaN(val)) { metas[key] = val; } }); this.$element.css({ display: 'none' }); this.$wrap = $("
    "); this.$element.after(this.$wrap); } else { this.$wrap = this.$element; } this.options = $.extend({}, DEFAULTS, options, this.$element.data(), metas); this.namespace = this.options.namespace; this.components = $.extend(true, {}, components); if (this.options.range) { this.options.replaceFirst = false; } // public properties this.value = this.options.value; if (this.value === null) { this.value = this.options.min; } if (!this.options.range) { if ($.isArray(this.value)) { this.value = this.value[0]; } } else if (!$.isArray(this.value)) { this.value = [this.value, this.value]; } else if (this.value.length === 1) { this.value[1] = this.value[0]; } this.min = this.options.min; this.max = this.options.max; this.step = this.options.step; this.interval = this.max - this.min; // flag this.initialized = false; this.updating = false; this.disabled = false; if (this.options.direction === 'v') { this.direction = { axis: 'pageY', position: 'top' }; } else { this.direction = { axis: 'pageX', position: 'left' }; } this.$wrap.addClass(this.namespace); if (this.options.skin) { this.$wrap.addClass(`${this.namespace}_${this.options.skin}`); } if (this.max < this.min || this.step >= this.interval) { throw new Error('error options about max min step'); } this.init(); } init() { this.$wrap.append(`
    `); // build pointers this.buildPointers(); // initial components this.components.selected.init(this); if (this.options.tip !== false) { this.components.tip.init(this); } if (this.options.scale !== false) { this.components.scale.init(this); } // initial pointer value this.set(this.value); // Bind events this.bindEvents(); this._trigger('ready'); this.initialized = true; } _trigger(eventType, ...params) { let data = [this].concat(params); // event this.$element.trigger(this.namespace + `::${eventType}`, data); // callback eventType = eventType.replace(/\b\w+\b/g, (word) => { return word.substring(0, 1).toUpperCase() + word.substring(1); }); let onFunction = `on${eventType}`; if (typeof this.options[onFunction] === 'function') { this.options[onFunction].apply(this, params); } } buildPointers() { this.pointer = []; let pointerCount = 1; if (this.options.range) { pointerCount = 2; } for (let i = 1; i <= pointerCount; i++) { const $pointer = $(`
    `).appendTo(this.$wrap); const p = new Pointer($pointer, i, this); this.pointer.push(p); } // alias of pointer this.p1 = this.pointer[0]; if (this.options.range) { this.p2 = this.pointer[1]; } } bindEvents() { const that = this; this.$wrap.on('touchstart.asRange mousedown.asRange', event => { /*eslint consistent-return: "off"*/ if (that.disabled === true) { return; } event = getEventObject(event); const rightclick = (event.which) ? (event.which === 3) : (event.button === 2); if (rightclick) { return false; } const offset = that.$wrap.offset(); const start = event[that.direction.axis] - offset[that.direction.position]; const p = that.getAdjacentPointer(start); p.mousedown(event); return false; }); if (this.$element.is('input')) { this.$element.on(this.namespace + `::change`, () => { const value = this.get(); this.$element.val(value); }); } $.each(this.pointer, (i, p) => { p.$element.on(this.namespace + `::move`, () => { that.value = that.get(); if (!that.initialized || that.updating) { return false; } that._trigger('change', that.value); return false; }); }); } getValueFromPosition(px) { if (px > 0) { return this.min + (px / this.getLength()) * this.interval; } return 0; } getAdjacentPointer(start) { const value = this.getValueFromPosition(start); if (this.options.range) { const p1 = this.p1.value; const p2 = this.p2.value; const diff = Math.abs(p1 - p2); if (p1 <= p2) { if (value > p1 + diff / 2) { return this.p2; } return this.p1; } if (value > p2 + diff / 2) { return this.p1; } return this.p2; } return this.p1; } getLength() { if (this.options.direction === 'v') { return this.$wrap.height(); } return this.$wrap.width(); } update(options) { this.updating = true; $.each(['max', 'min', 'step', 'limit', 'value'], (key, value) => { if (options[value]) { this[value] = options[value]; } }); if (options.max || options.min) { this.setInterval(options.min, options.max); } if (!options.value) { this.value = options.min; } $.each(this.components, (key, value) => { if (typeof value.update === "function") { value.update(this); } }); this.set(this.value); this._trigger('update'); this.updating = false; } get() { const value = []; $.each(this.pointer, (i, p) => { value[i] = p.get(); }); if (this.options.range) { return value; } if (value[0] === this.options.min) { if (typeof this.options.replaceFirst === 'string') { value[0] = this.options.replaceFirst; } if (typeof this.options.replaceFirst === 'object') { for (const key in this.options.replaceFirst) { if(Object.hasOwnProperty(this.options.replaceFirst, key)){ value[0] = key; } } } } return value[0]; } set(value) { if (this.options.range) { if (typeof value === 'number') { value = [value]; } if (!$.isArray(value)) { return; } $.each(this.pointer, (i, p) => { p.set(value[i]); }); } else { this.p1.set(value); } this.value = value; } val(value) { if (value) { this.set(value); return this; } return this.get(); } setInterval(start, end) { this.min = start; this.max = end; this.interval = end - start; } enable() { this.disabled = false; this.$wrap.removeClass(`${this.namespace}_disabled`); this._trigger('enable'); return this; } disable() { this.disabled = true; this.$wrap.addClass(`${this.namespace}_disabled`); this._trigger('disable'); return this; } destroy() { $.each(this.pointer, (i, p) => { p.destroy(); }); this.$wrap.destroy(); this._trigger('destroy'); } static registerComponent(component, methods) { components[component] = methods; } static setDefaults(options) { $.extend(DEFAULTS, $.isPlainObject(options) && options); } } asRange.registerComponent('scale', scale); asRange.registerComponent('selected', selected); asRange.registerComponent('tip', tip); keyboard(); var info = { version:'0.3.4' }; const NAMESPACE = 'asRange'; const OtherAsRange = $.fn.asRange; const jQueryAsRange = function(options, ...args) { if (typeof options === 'string') { const method = options; if (/^_/.test(method)) { return false; } else if ((/^(get)$/.test(method)) || (method === 'val' && args.length === 0)) { const instance = this.first().data(NAMESPACE); if (instance && typeof instance[method] === 'function') { return instance[method](...args); } } else { return this.each(function() { const instance = $.data(this, NAMESPACE); if (instance && typeof instance[method] === 'function') { instance[method](...args); } }); } } return this.each(function() { if (!$(this).data(NAMESPACE)) { $(this).data(NAMESPACE, new asRange(this, options)); } }); }; $.fn.asRange = jQueryAsRange; $.asRange = $.extend({ setDefaults: asRange.setDefaults, noConflict: function() { $.fn.asRange = OtherAsRange; return jQueryAsRange; } }, info);