/*global require, exports, document, Error*/
var AbstractControl = require("./abstract-control").AbstractControl,
KeyComposer = require("../../composer/key-composer").KeyComposer;
var CLASS_PREFIX = "montage-NumberField";
/**
* @class AbstractNumberField
* @extends AbstractControl
*/
var AbstractNumberField = exports.AbstractNumberField = AbstractControl.specialize(
/** @lends AbstractNumberField# */
{
// Life Cycle
constructor: {
value: function AbstractNumberField() {
if (this.constructor === AbstractNumberField) {
throw new Error("AbstractNumberField cannot be instantiated.");
}
this._propertyNamesUsed = {};
this.defineBinding( "classList.has('montage--disabled')", { "<-": "!enabled" });
}
},
enterDocument: {
value: function (firstTime) {
if (firstTime) {
// read initial values from the input type=range
var used = this._propertyNamesUsed;
if (!used.min) {
this.min = this.element.getAttribute('min') || this._min;
}
if (!used.max) {
this.max = this.element.getAttribute('max') || this._max;
}
if (!used.step) {
this.step = this.element.getAttribute('step') || this._step;
}
if (!used.value) {
this.value = this.element.getAttribute('value') || this._value;
}
delete this._propertyNamesUsed;
this._numberFieldTextFieldComponent.addEventListener("action", this, false);
this._numberFieldMinusComponent.addEventListener("action", this, false);
this._numberFieldPlusComponent.addEventListener("action", this, false);
this._numberFieldTextFieldComponent.delegate = this;
// needs to be fixed for pointer handling
this.element.addEventListener("mousedown", this, false);
this.element.tabIndex = "-1";
this._upKeyComposer = KeyComposer.createKey(this, "up", "increase");
this._downKeyComposer = KeyComposer.createKey(this, "down", "decrease");
this._rightKeyComposer = KeyComposer.createKey(this, "right", "increase");
this._leftKeyComposer = KeyComposer.createKey(this, "left", "decrease");
}
}
},
textFieldShouldBeginEditing: {
value: function () {
return this.enabled;
}
},
textFieldDidChange: {
value: function () {
}
},
textFieldDidEndEditing: {
value: function () {
this.value = this._numberFieldTextFieldComponent.value;
}
},
textFieldShouldAcceptValue: {
value: function (textField, value) {
if (this._activeValueChange === true) {
return true;
}
}
},
prepareForActivationEvents: {
value: function () {
this._upKeyComposer.addEventListener("keyPress", this, false);
this._downKeyComposer.addEventListener("keyPress", this, false);
this._leftKeyComposer.addEventListener("keyPress", this, false);
this._rightKeyComposer.addEventListener("keyPress", this, false);
}
},
draw: {
value: function () {
this.element.setAttribute("aria-valuemax", this.max);
this.element.setAttribute("aria-valuemin", this.min);
this.element.setAttribute("aria-valuenow", this.value);
}
},
// Event Handlers
acceptsActiveTarget: {
value: true
},
handleMousedown: {
value: function () {
this.element.focus();
}
},
/**
* Handle increment-button action
* @private
*/
handlePlusAction: {
value: function () {
this._activeValueChange = true;
var step = this.step * this._stepDecimal;
var stepBase = (typeof this.min === "number") ? this.min * this._stepDecimal : 0;
var value = (this.value * this._stepDecimal) - stepBase;
if (value % step) {
if (value < 0) {
value -= value % step;
} else {
value += step - (value % step);
}
} else {
value += step;
}
this.value = (value + stepBase) / this._stepDecimal;
this._activeValueChange = false;
}
},
/**
* Handle decrement-button action
* @private
*/
handleMinusAction: {
value: function () {
this._activeValueChange = true;
var step = this.step * this._stepDecimal;
var stepBase = (typeof this.min === "number") ? this.min * this._stepDecimal : 0;
var value = (this.value * this._stepDecimal) - stepBase;
if (value % step) {
if (value > 0) {
value -= value % step;
} else {
value -= step + (value % step);
}
} else {
value -= step;
}
this.value = (value + stepBase) / this._stepDecimal;
this._activeValueChange = false;
}
},
handleKeyPress: {
value: function (event) {
if (!this.enabled) {
return;
}
if(event.identifier === "increase") {
this.handlePlusAction();
} else if (event.identifier === "decrease") {
this.handleMinusAction();
}
}
},
handleAction: {
value: function (event) {
if (event.target === this._numberFieldTextFieldComponent ||
event.target === this._numberFieldMinusComponent ||
event.target === this._numberFieldPlusComponent) {
event.stopPropagation();
this.dispatchActionEvent();
}
}
},
// Properties
_value: {
value: 0
},
_required: {
value: false
},
_min: {
value: "any"
},
_max: {
value: "any"
},
_step: {
value: 1
},
/**
* The maximum value allowed in the InputNumber. Can be any number or the string "any".
* @type {number|string}
* @default "any"
*/
min: {
get: function () {
return this._min;
},
set: function (value) {
if (value !== "any" && isNaN(value = parseFloat(value))) {
return false;
}
if (this._min !== value) {
if (this._propertyNamesUsed) {
this._propertyNamesUsed.min = true;
}
this._min = value;
this.needsDraw = true;
if (typeof value === "number" && this.value < value) {
this.value = value;
}
}
}
},
/**
* The maximum value allowed in the InputNumber. Can be any number or the string "any".
* @type {number|string}
* @default "any"
*/
max: {
get: function () {
return this._max;
},
set: function (value) {
if (value !== "any" && isNaN(value = parseFloat(value))) {
return false;
}
if (this._max !== value) {
if (this._propertyNamesUsed) {
this._propertyNamesUsed.max = true;
}
this._max = value;
this.needsDraw = true;
if (typeof value === "number" && this.value > value) {
this.value = value;
}
}
}
},
_stepDecimal: {
value: 1
},
/**
* The amount the value changes when using the plus/minus buttons. Can be any positive number.
* @type {number}
* @default 1
*/
step: {
get: function () {
return this._step;
},
set: function (value) {
if (isNaN(value = parseFloat(value)) || value <= 0) {
return false;
}
if (this._step !== value) {
if (this._propertyNamesUsed) {
this._propertyNamesUsed.step = true;
}
this._step = value;
var decimalPart = String(value).match(/\.(\d+)$/);
if (decimalPart) {
this._stepDecimal = Math.pow(10, decimalPart[1].length);
} else {
this._stepDecimal = 1;
}
}
}
},
/**
* The value of the InputNumber
* @type {number}
* @default 0
*/
value: {
get: function () {
return this._value;
},
set: function (value) {
if (value === null || value === undefined || typeof value.valueOf === "undefined") {
this._value = "";
} else if (! isNaN(value = parseFloat(value))) {
if (typeof this.min === 'number' && value < this.min) {
value = this.min;
}
if (typeof this.max === 'number' && value > this.max) {
value = this.max;
}
if (this._value !== value) {
if (this._propertyNamesUsed) {
this._propertyNamesUsed.value = true;
}
this._value = value;
this.needsDraw = true;
}
}
if(this._numberFieldTextFieldComponent && this._value !== this._numberFieldTextFieldComponent.value) {
this._numberFieldTextFieldComponent.value = this._value;
}
}
},
/**
* Wether or not the number field acccepts user input
* @type {number}
* @default 0
*/
enabled: {
value: true
},
// Machinery
_numberFieldTextFieldComponent: {
value: null
},
_numberFieldMinusComponent: {
value: null
},
_numberFieldPlusComponent: {
value: null
},
_activeValueChange: {
value: false
}
});