mirror of
https://github.com/daniviga/django-ram.git
synced 2025-08-04 13:17:50 +02:00
420 lines
12 KiB
JavaScript
420 lines
12 KiB
JavaScript
/*
|
|
Version 1.0.1
|
|
|
|
Copyright 2014 Red White Silver GmbH
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
See the License for the specific language governing permissions and
|
|
limitations under the License.
|
|
*/
|
|
|
|
(function($, window, document, undefined) {
|
|
|
|
var pluginName = 'rotaryswitch',
|
|
defaults = {
|
|
minimum: 0, // Minimal value
|
|
maximum: 12, // Maximum value
|
|
step: 1, // Step size
|
|
snapInMotion: true, // Snap to steps in motion
|
|
beginDeg: 0, // Start point in deg
|
|
lengthDeg: 360, // Length in deg
|
|
minimumOverMaximum: true, // Which value will used, if the the start and the end point at the same deg.
|
|
showInput: false, // Show input element
|
|
showMarks: false, // Show deg marks
|
|
themeClass: 'defaultTheme' // Theme class
|
|
};
|
|
|
|
function Plugin(element, options) {
|
|
this.element = $(element);
|
|
this.domElements = {};
|
|
this.htmlStructure = {
|
|
wrap: '<div class="rotaryswitchPlugin"></div>',
|
|
switchButton: '<div class="switch"></div>',
|
|
overlay: '<div class="overlay"></div>',
|
|
marks: '<div class="marks"></div>',
|
|
mark: '<div class="mark"></div>'
|
|
};
|
|
this.mousePosition = {x: -1, y: -1};
|
|
this.switchDeg = 0;
|
|
this.valueInPercent = 0;
|
|
this.value = 0;
|
|
this.steps = 0;
|
|
this.totalDeg = 0;
|
|
this.degPerStep = 0;
|
|
this.lastTriggeredValue = -1;
|
|
|
|
this.options = $.extend({}, defaults, options);
|
|
this._defaults = defaults;
|
|
this._name = pluginName;
|
|
|
|
this.initialize();
|
|
}
|
|
|
|
Plugin.prototype = {
|
|
|
|
/**
|
|
* Initialze this plugin
|
|
* See inline comments for more details
|
|
*
|
|
* @param none
|
|
* @return none
|
|
*/
|
|
|
|
initialize: function() {
|
|
|
|
// Save the needed jquery DOM elements
|
|
this.domElements = {
|
|
// Wrap the input element and save the parent as main element and add theme class:
|
|
main: this.element.wrap(this.htmlStructure.wrap).parent().addClass(this.options.themeClass),
|
|
switchButton: $(this.htmlStructure.switchButton),
|
|
overlay: $(this.htmlStructure.overlay)
|
|
};
|
|
|
|
// Append addional dom elements:
|
|
this.domElements.main.append([this.domElements.switchButton, this.domElements.overlay]);
|
|
|
|
// Calculate the length (Maximum - minimum)
|
|
this.steps = Math.abs(this.options.maximum - this.options.minimum);
|
|
|
|
// Calculate the total deg length
|
|
this.totalDeg = this.options.lengthDeg;
|
|
|
|
// Calculate deg per step
|
|
this.degPerStep = this.totalDeg / this.steps;
|
|
|
|
// Listen to some necessary events
|
|
this.domElements.main.on('mousedown', $.proxy(this.onMouseDown, this));
|
|
this.domElements.main.on('touchstart', $.proxy(this.onTouchStart, this));
|
|
this.element.on('change', $.proxy(this.onChangeElementValue, this));
|
|
|
|
this.readValueFromInput(); // Get the value from the input element
|
|
|
|
this.rotateSwitch(); // Rotate the switch
|
|
|
|
// Show marks if wished
|
|
if (this.options.showMarks === true) {
|
|
this.renderMarks();
|
|
}
|
|
|
|
// Show the input element if wished
|
|
if (this.options.showInput === false) {
|
|
this.element.hide();
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Adds marks for each step
|
|
*
|
|
* @param none
|
|
* @return none
|
|
*/
|
|
|
|
renderMarks: function() {
|
|
var i=0,
|
|
len = this.steps / this.options.step,
|
|
deg = this.options.beginDeg,
|
|
degPerStep = this.degPerStep * this.options.step,
|
|
marks = $(this.htmlStructure.marks);
|
|
|
|
for (; i < len; i += 1) {
|
|
deg += degPerStep;
|
|
var mark = $(this.htmlStructure.mark).css({'transform': 'rotate('+deg+'deg) translate(0, -'+ (this.domElements.main.width()/2 + (this.domElements.main.width()*0.1)) +'px)'});
|
|
marks.append(mark);
|
|
}
|
|
|
|
this.domElements.main.append(marks);
|
|
},
|
|
|
|
|
|
/**
|
|
* On mouse down event handler
|
|
* Save the mouse position (x, y) to the object this.mousePosition
|
|
* call the method this.startHandling
|
|
*
|
|
* @param event jquery mouse event
|
|
* @return none
|
|
*/
|
|
onMouseDown: function(event) {
|
|
event.preventDefault();
|
|
|
|
this.mousePosition.x = event.pageX;
|
|
this.mousePosition.y = event.pageY;
|
|
|
|
this.startHandling();
|
|
},
|
|
|
|
/**
|
|
* On mouse up event handler
|
|
* call the method this.stopHandling
|
|
*
|
|
* @param event jquery mouse event
|
|
* @return none
|
|
*/
|
|
onMouseUp: function(event) {
|
|
this.stopHandling();
|
|
},
|
|
|
|
/**
|
|
* On mouse move event handler
|
|
* Save the mouse position (x, y) to the object this.mousePosition
|
|
* call the method this.calculateSwitchDeg
|
|
* call the method this.calculateValueByDeg
|
|
* call the method this.rotateSwitch
|
|
* call the method this.setValueToInput
|
|
*
|
|
* @param event jquery mouse event
|
|
* @return none
|
|
*/
|
|
onMousemove: function(event) {
|
|
event.preventDefault();
|
|
this.mousePosition.x = event.pageX;
|
|
this.mousePosition.y = event.pageY;
|
|
this.calculateSwitchDeg();
|
|
this.calculateValueByDeg();
|
|
this.rotateSwitch();
|
|
this.setValueToInput();
|
|
},
|
|
|
|
/**
|
|
* On touch start event handler
|
|
* Identical width this.onMouseDown
|
|
*
|
|
* @param event jquery touch event
|
|
* @return none
|
|
* @see this.onMouseDown
|
|
*/
|
|
onTouchStart: function(event) {
|
|
event.preventDefault();
|
|
this.mousePosition.x = event.originalEvent.targetTouches[0].pageX;
|
|
this.mousePosition.y = event.originalEvent.targetTouches[0].pageY;
|
|
this.startHandling();
|
|
},
|
|
|
|
/**
|
|
* On touch end event handler
|
|
* Identical width this.onMouseUp
|
|
*
|
|
* @param event jquery touch event
|
|
* @return none
|
|
* @see this.onMouseUp
|
|
*/
|
|
onTouchEnd: function(event) {
|
|
this.stopHandling();
|
|
},
|
|
|
|
/**
|
|
* On touch move event handler
|
|
* Identical width this.onMousemove
|
|
*
|
|
* @param event jquery touch event
|
|
* @return none
|
|
* @see this.onMousemove
|
|
*/
|
|
onTouchMove: function(event) {
|
|
event.preventDefault();
|
|
|
|
this.mousePosition.x = event.originalEvent.targetTouches[0].pageX;
|
|
this.mousePosition.y = event.originalEvent.targetTouches[0].pageY;
|
|
|
|
this.calculateSwitchDeg();
|
|
this.calculateValueByDeg();
|
|
this.rotateSwitch();
|
|
this.setValueToInput();
|
|
},
|
|
|
|
/**
|
|
* On change input element event handler
|
|
* If event not triggered by plugin:
|
|
* Call the method this.readValueFromInput
|
|
* Call the method this.rotateSwitch
|
|
*
|
|
* @param event jquery event
|
|
* @return none
|
|
*/
|
|
onChangeElementValue: function(event) {
|
|
if (!event.plugin || event.plugin !== this) {
|
|
this.readValueFromInput();
|
|
this.rotateSwitch();
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Triggered by mouse or touch begin event
|
|
* Calls some methods
|
|
* Listen to some events
|
|
* Add class 'active' to the main DOM element (this.domElements.main)
|
|
*
|
|
* @param none
|
|
* @return none
|
|
*/
|
|
startHandling: function() {
|
|
this.calculateSwitchDeg();
|
|
this.calculateValueByDeg();
|
|
this.rotateSwitch();
|
|
this.setValueToInput();
|
|
|
|
$(document).on('mouseup', $.proxy(this.onMouseUp, this));
|
|
$(document).on('mousemove', $.proxy(this.onMousemove, this));
|
|
$(document).on('touchend', $.proxy(this.onTouchEnd, this));
|
|
$(document).on('touchmove', $.proxy(this.onTouchMove, this));
|
|
|
|
this.domElements.main.addClass('active');
|
|
},
|
|
|
|
/**
|
|
* Triggered by mouseup or touchend event
|
|
* Stop listen to some events
|
|
* Call method this.rotateSwitch
|
|
* Remove class 'active' from the main DOM element (this.domElements.main)
|
|
*
|
|
* @param none
|
|
* @return none
|
|
*/
|
|
stopHandling: function() {
|
|
$(document).off('mouseup', $.proxy(this.onMouseUp, this))
|
|
.off('mousemove', $.proxy(this.onMousemove, this))
|
|
.off('touchend', $.proxy(this.onTouchEnd, this))
|
|
.off('touchmove', $.proxy(this.onTouchMove, this));
|
|
|
|
this.rotateSwitch(true);
|
|
this.domElements.main.removeClass('active');
|
|
},
|
|
|
|
/**
|
|
* Calculate the switch deg by the element position and the mouse position
|
|
* Stores the switch deg in this.switchDeg
|
|
* @param none
|
|
* @return none
|
|
*/
|
|
calculateSwitchDeg: function() {
|
|
var offset = this.domElements.main.offset(),
|
|
radians = Math.atan2(this.mousePosition.x - (offset.left + (this.domElements.main.width()/2)), this.mousePosition.y - (offset.top + (this.domElements.main.height()/2)));
|
|
|
|
if (this.mousePosition.x !== -1) {
|
|
this.switchDeg = (radians * (180 / Math.PI) * -1) + 180;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Calculate the value by deg
|
|
* Stores the in percent in this.valueInPercent
|
|
* Stores the value in this.value
|
|
* @param none
|
|
* @return none
|
|
*/
|
|
calculateValueByDeg: function() {
|
|
var range = this.options.maximum - this.options.minimum;
|
|
|
|
if (this.switchDeg - this.options.beginDeg > 0) {
|
|
this.valueInPercent = (this.switchDeg - this.options.beginDeg) / this.totalDeg;
|
|
} else {
|
|
this.valueInPercent = (this.switchDeg - this.options.beginDeg + 360) / this.totalDeg;
|
|
}
|
|
|
|
if (this.valueInPercent > 1) {
|
|
if (this.valueInPercent > (((360 / this.totalDeg)-1) / 2)+1 ) {
|
|
this.valueInPercent = 0;
|
|
} else {
|
|
this.valueInPercent = 1;
|
|
}
|
|
}
|
|
|
|
this.value = ~~ (((((range * this.valueInPercent) < 0) ? -0.5 : 0.5) + ((range * this.valueInPercent) / this.options.step))) * this.options.step;
|
|
this.value += this.options.minimum;
|
|
|
|
if (this.options.lengthDeg === 360 && (this.value === this.options.minimum || this.value === this.options.maximum)) {
|
|
if (this.options.minimumOverMaximum === true) {
|
|
this.value = this.options.minimum;
|
|
} else {
|
|
this.value = this.options.maximum;
|
|
}
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Rotate the switch with css transform
|
|
* snap to the the next rounded value if the parameter snap is true
|
|
* @param snap boolean
|
|
* @return none
|
|
*/
|
|
rotateSwitch: function(snap) {
|
|
var deg = 0,
|
|
exactDeg = (this.valueInPercent * this.totalDeg),
|
|
roundedDeg = ((this.value / this.steps) * this.totalDeg) - (this.options.minimum * this.degPerStep),
|
|
difference = Math.abs(Math.abs(exactDeg) - Math.abs(roundedDeg)),
|
|
rotateString = '';
|
|
|
|
if (snap === true || (this.options.snapInMotion === true && difference < this.degPerStep / 6)) {
|
|
if (roundedDeg + this.options.beginDeg < 360) {
|
|
deg = (roundedDeg + this.options.beginDeg);
|
|
} else {
|
|
deg = roundedDeg + this.options.beginDeg - 360;
|
|
}
|
|
} else {
|
|
if (exactDeg + this.options.beginDeg < 360) {
|
|
deg = (exactDeg + this.options.beginDeg);
|
|
} else {
|
|
deg = exactDeg + this.options.beginDeg - 360;
|
|
}
|
|
}
|
|
|
|
rotateString = ['rotate(', deg, 'deg)'].join('');
|
|
this.domElements.switchButton.css({
|
|
'transform': rotateString,
|
|
'-webkit-transform': rotateString,
|
|
'-moz-transform': rotateString,
|
|
'-o-transform': rotateString,
|
|
'-ms-transform': rotateString
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Read the valur from the input element
|
|
* If no value available, this.options.minimum is used
|
|
* @param snap boolean
|
|
* @return none
|
|
*/
|
|
readValueFromInput: function() {
|
|
var elementValue = parseInt(this.element.val(), 10);
|
|
if (isNaN(elementValue) === true) {
|
|
this.value = this.options.minimum;
|
|
} else {
|
|
this.value = Math.max(this.options.minimum, elementValue);
|
|
this.value = Math.min(this.options.maximum, this.value);
|
|
}
|
|
|
|
this.value -= this.options.minimum;
|
|
this.valueInPercent = this.value / (this.options.maximum - this.options.minimum);
|
|
},
|
|
|
|
/**
|
|
* Set the value to the input element
|
|
* and trigger the change event on the input element
|
|
* @param none
|
|
* @return none
|
|
*/
|
|
setValueToInput: function() {
|
|
if (this.value !== this.lastTriggeredValue) {
|
|
this.lastTriggeredValue = this.value;
|
|
this.element.val(this.value).trigger({type: 'change', plugin: this});
|
|
}
|
|
}
|
|
};
|
|
|
|
$.fn[pluginName] = function(options) {
|
|
return this.each(function() {
|
|
if (!$.data(this, 'plugin_' + pluginName)){
|
|
$.data(this, 'plugin_' + pluginName, new Plugin(this, options));
|
|
}
|
|
});
|
|
};
|
|
|
|
}(jQuery, window, document)); |