You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
389 lines
11 KiB
389 lines
11 KiB
define([ |
|
"dojo/_base/array", |
|
"dojo/_base/declare", |
|
"dojo/_base/event", |
|
"dojo/_base/lang", |
|
"dojo/_base/window", |
|
"dojo/dom-class", |
|
"dojo/dom-construct", |
|
"dojo/dom-attr", |
|
"dojo/touch", |
|
"dijit/_WidgetBase", |
|
"./iconUtils", |
|
"dojo/has", |
|
"dojo/has!dojo-bidi?dojox/mobile/bidi/ValuePickerSlot" |
|
], function(array, declare, event, lang, win, domClass, domConstruct, domAttr, touch, WidgetBase, iconUtils, has, BidiValuePickerSlot){ |
|
|
|
// module: |
|
// dojox/mobile/ValuePickerSlot |
|
|
|
var ValuePickerSlot = declare(has("dojo-bidi") ? "dojox.mobile.NonBidiValuePickerSlot" : "dojox.mobile.ValuePickerSlot", WidgetBase, { |
|
// summary: |
|
// A widget representing one slot of a ValuePicker widget. |
|
|
|
// items: Array |
|
// An array of array of key-label pairs |
|
// (e.g. [[0, "Jan"], [1,"Feb"], ...]). If key values for each label |
|
// are not necessary, labels can be used instead. |
|
items: [], |
|
|
|
// labels: String[] |
|
// An array of labels to be displayed on the value picker |
|
// (e.g. ["Jan","Feb",...]). This is a simplified version of the |
|
// items property. |
|
labels: [], |
|
|
|
// labelFrom: Number |
|
// The start value of display values of the value picker. This |
|
// parameter is especially useful when value picker has serial |
|
// values. |
|
labelFrom: 0, |
|
|
|
// labelTo: Number |
|
// The end value of display values of the value picker. |
|
labelTo: 0, |
|
|
|
// zeroPad: Number |
|
// Length of zero padding numbers. |
|
// Ex. zeroPad=2 -> "00", "01", ... |
|
// Ex. zeroPad=3 -> "000", "001", ... |
|
zeroPad: 0, |
|
|
|
// value: String |
|
// The initial value of the value picker. |
|
value: "", |
|
|
|
// step: Number |
|
// The steps between labelFrom and labelTo. |
|
step: 1, |
|
|
|
// readOnly: [const] Boolean |
|
// A flag used to indicate if the input field is readonly or not. |
|
// Note that changing the value of the property after the widget |
|
// creation has no effect. |
|
readOnly: false, |
|
|
|
// tabIndex: String |
|
// Tabindex setting for this widget so users can hit the tab key to |
|
// focus on it. |
|
tabIndex: "0", |
|
|
|
// key: Object |
|
// The key of the currently selected value in the items array. This is a read-only property. |
|
// Warning: Do not use this property directly, make sure to call the get() method. |
|
/*===== |
|
key: null, |
|
=====*/ |
|
|
|
// plusBtnLabel: String |
|
// (Accessibility) Text label for plus button |
|
plusBtnLabel: "", |
|
|
|
// plusBtnLabelRef: String |
|
// (Accessibility) Reference to a node id containing text label for plus button |
|
plusBtnLabelRef: "", |
|
|
|
// minusBtnLabel: String |
|
// (Accessibility) Text label for minus button |
|
minusBtnLabel: "", |
|
|
|
// minusBtnLabelRef: String |
|
// (Accessibility) Reference to a node id containing text label for minus button |
|
minusBtnLabelRef: "", |
|
|
|
/* internal properties */ |
|
baseClass: "mblValuePickerSlot", |
|
|
|
buildRendering: function(){ |
|
this.inherited(arguments); |
|
|
|
this.initLabels(); |
|
if(this.labels.length > 0){ |
|
this.items = []; |
|
for(var i = 0; i < this.labels.length; i++){ |
|
this.items.push([i, this.labels[i]]); |
|
} |
|
} |
|
|
|
this.plusBtnNode = domConstruct.create("div", { |
|
className: "mblValuePickerSlotPlusButton mblValuePickerSlotButton", |
|
title: "+" |
|
}, this.domNode); |
|
|
|
this.plusIconNode = domConstruct.create("div", { |
|
className: "mblValuePickerSlotIcon" |
|
}, this.plusBtnNode); |
|
iconUtils.createIcon("mblDomButtonGrayPlus", null, this.plusIconNode); |
|
|
|
this.inputAreaNode = domConstruct.create("div", { |
|
className: "mblValuePickerSlotInputArea" |
|
}, this.domNode); |
|
this.inputNode = domConstruct.create("input", { |
|
className: "mblValuePickerSlotInput", |
|
readonly: this.readOnly |
|
}, this.inputAreaNode); |
|
|
|
this.minusBtnNode = domConstruct.create("div", { |
|
className: "mblValuePickerSlotMinusButton mblValuePickerSlotButton", |
|
title: "-" |
|
}, this.domNode); |
|
this.minusIconNode = domConstruct.create("div", { |
|
className: "mblValuePickerSlotIcon" |
|
}, this.minusBtnNode); |
|
iconUtils.createIcon("mblDomButtonGrayMinus", null, this.minusIconNode); |
|
|
|
domAttr.set(this.plusBtnNode, "role", "button"); //a11y |
|
this._setPlusBtnLabelAttr(this.plusBtnLabel); |
|
this._setPlusBtnLabelRefAttr(this.plusBtnLabelRef); |
|
|
|
domAttr.set(this.inputNode, "role", "textbox"); |
|
var registry = require("dijit/registry"); |
|
var inputAreaNodeId = registry.getUniqueId("dojo_mobile__mblValuePickerSlotInput"); |
|
domAttr.set(this.inputNode, "id", inputAreaNodeId); |
|
domAttr.set(this.plusBtnNode, "aria-controls", inputAreaNodeId); |
|
|
|
domAttr.set(this.minusBtnNode, "role", "button"); |
|
domAttr.set(this.minusBtnNode, "aria-controls", inputAreaNodeId); |
|
this._setMinusBtnLabelAttr(this.minusBtnLabel); |
|
this._setMinusBtnLabelRefAttr(this.minusBtnLabelRef); |
|
|
|
if(this.value === "" && this.items.length > 0){ |
|
this.value = this.items[0][1]; |
|
} |
|
this._initialValue = this.value; |
|
}, |
|
|
|
startup: function(){ |
|
if(this._started){ return; } |
|
this._handlers = [ |
|
this.connect(this.plusBtnNode, touch.press, "_onTouchStart"), |
|
this.connect(this.minusBtnNode, touch.press, "_onTouchStart"), |
|
this.connect(this.plusBtnNode, "onkeydown", "_onClick"), // for desktop browsers |
|
this.connect(this.minusBtnNode, "onkeydown", "_onClick"), // for desktop browsers |
|
this.connect(this.inputNode, "onchange", lang.hitch(this, function(e){ |
|
this._onChange(e); |
|
})) |
|
]; |
|
this.inherited(arguments); |
|
this._set(this.plusBtnLabel); |
|
}, |
|
|
|
initLabels: function(){ |
|
// summary: |
|
// Initializes the labels of this slot according to the labelFrom and labelTo properties. |
|
// tags: |
|
// private |
|
if(this.labelFrom !== this.labelTo){ |
|
var a = this.labels = [], |
|
zeros = this.zeroPad && Array(this.zeroPad).join("0"); |
|
for(var i = this.labelFrom; i <= this.labelTo; i += this.step){ |
|
a.push(this.zeroPad ? (zeros + i).slice(-this.zeroPad) : i + ""); |
|
} |
|
} |
|
}, |
|
|
|
spin: function(/*Number*/steps){ |
|
// summary: |
|
// Spins the slot as specified by steps. |
|
|
|
// find the position of the current value |
|
var pos = -1, |
|
v = this.get("value"), |
|
len = this.items.length; |
|
for(var i = 0; i < len; i++){ |
|
if(this.items[i][1] === v){ |
|
pos = i; |
|
break; |
|
} |
|
} |
|
if(v == -1){ return; } |
|
pos += steps; |
|
if(pos < 0){ // shift to positive |
|
pos += (Math.abs(Math.ceil(pos / len)) + 1) * len; |
|
} |
|
var newItem = this.items[pos % len]; |
|
this.set("value", newItem[1]); |
|
}, |
|
|
|
setInitialValue: function(){ |
|
// summary: |
|
// Sets the initial value using this.value or the first item. |
|
this.set("value", this._initialValue); |
|
}, |
|
|
|
_onClick: function(e){ |
|
// summary: |
|
// Internal handler for click events. |
|
// tags: |
|
// private |
|
if(e && e.type === "keydown" && e.keyCode !== 13){ return; } |
|
if(this.onClick(e) === false){ return; } // user's click action |
|
var node = e.currentTarget; |
|
if(node === this.plusBtnNode || node === this.minusBtnNode){ |
|
this._btn = node; |
|
} |
|
this.spin(this._btn === this.plusBtnNode ? 1 : -1); |
|
}, |
|
|
|
onClick: function(/*Event*/ /*===== e =====*/){ |
|
// summary: |
|
// User defined function to handle clicks |
|
// tags: |
|
// callback |
|
}, |
|
|
|
_onChange: function(e){ |
|
// summary: |
|
// Internal handler for the input field's value change events |
|
// tags: |
|
// callback |
|
if(this.onChange(e) === false){ return; } // user's click action |
|
var v = this.get("value"), // text in the input field |
|
a = this.validate(v); |
|
this.set("value", a.length ? a[0][1] : this.value); |
|
}, |
|
|
|
onChange: function(/*Event*/ /*===== e =====*/){ |
|
// summary: |
|
// User defined function to handle value changes |
|
// tags: |
|
// callback |
|
}, |
|
|
|
validate: function(value){ |
|
return array.filter(this.items, function(a){ |
|
return (a[1] + "").toLowerCase() == (value + "").toLowerCase(); |
|
}); |
|
}, |
|
|
|
_onTouchStart: function(e){ |
|
this._conn = [ |
|
this.connect(win.body(), touch.move, "_onTouchMove"), |
|
this.connect(win.body(), touch.release, "_onTouchEnd") |
|
]; |
|
this.touchStartX = e.touches ? e.touches[0].pageX : e.clientX; |
|
this.touchStartY = e.touches ? e.touches[0].pageY : e.clientY; |
|
domClass.add(e.currentTarget, "mblValuePickerSlotButtonSelected"); |
|
this._btn = e.currentTarget; |
|
if(this._timer){ |
|
this._timer.remove(); // fail safe |
|
this._timer = null; |
|
} |
|
if(this._interval){ |
|
clearInterval(this._interval); // fail safe |
|
this._interval = null; |
|
} |
|
this._timer = this.defer(function(){ |
|
this._interval = setInterval(lang.hitch(this, function(){ |
|
this.spin(this._btn === this.plusBtnNode ? 1 : -1); |
|
}), 60); |
|
this._timer = null; |
|
}, 1000); |
|
event.stop(e); |
|
}, |
|
|
|
_onTouchMove: function(e){ |
|
var x = e.touches ? e.touches[0].pageX : e.clientX; |
|
var y = e.touches ? e.touches[0].pageY : e.clientY; |
|
if(Math.abs(x - this.touchStartX) >= 4 || |
|
Math.abs(y - this.touchStartY) >= 4){ // dojox/mobile/scrollable.threshold |
|
if(this._timer){ |
|
this._timer.remove(); // fail safe |
|
this._timer = null; |
|
} |
|
if(this._interval){ |
|
clearInterval(this._interval); // fail safe |
|
this._interval = null; |
|
} |
|
array.forEach(this._conn, this.disconnect, this); |
|
domClass.remove(this._btn, "mblValuePickerSlotButtonSelected"); |
|
} |
|
}, |
|
|
|
_onTouchEnd: function(e){ |
|
if(this._timer){ |
|
this._timer.remove(); |
|
this._timer = null; |
|
} |
|
array.forEach(this._conn, this.disconnect, this); |
|
domClass.remove(this._btn, "mblValuePickerSlotButtonSelected"); |
|
if(this._interval){ |
|
clearInterval(this._interval); |
|
this._interval = null; |
|
}else{ |
|
this._onClick(e); |
|
} |
|
}, |
|
|
|
_getKeyAttr: function(){ |
|
var val = this.get("value"); |
|
var item = array.filter(this.items, function(item){ |
|
return item[1] === val; |
|
})[0]; |
|
return item ? item[0] : null; |
|
}, |
|
|
|
_getValueAttr: function(){ |
|
// summary: |
|
// Gets the currently selected value. |
|
return this.inputNode.value; |
|
}, |
|
|
|
_setValueAttr: function(value){ |
|
// summary: |
|
// Sets a new value to this slot. |
|
this._spinToValue(value, true); |
|
}, |
|
|
|
_spinToValue: function(value, applyValue){ |
|
// summary: |
|
// Sets a new value to this slot. |
|
// tags: |
|
// private |
|
if(this.get("value") == value){ |
|
return; // no change; avoid notification |
|
} |
|
this.inputNode.value = value; |
|
// to avoid unnecessary notifications, applyValue is undefined when |
|
// _spinToValue is called by _DatePickerMixin. |
|
if(applyValue){ |
|
this._set("value", value); |
|
} |
|
var parent = this.getParent(); |
|
if(parent && parent.onValueChanged){ |
|
parent.onValueChanged(this); |
|
} |
|
}, |
|
|
|
_setTabIndexAttr: function(/*String*/ tabIndex){ |
|
this.plusBtnNode.setAttribute("tabIndex", tabIndex); |
|
this.minusBtnNode.setAttribute("tabIndex", tabIndex); |
|
}, |
|
|
|
_setAria: function(node, attr, value){ |
|
if(value){ |
|
domAttr.set(node, attr, value); |
|
}else{ |
|
domAttr.remove(node, attr); |
|
} |
|
}, |
|
|
|
_setPlusBtnLabelAttr: function(/*String*/ plusBtnLabel){ |
|
this._setAria(this.plusBtnNode, "aria-label", plusBtnLabel); |
|
}, |
|
|
|
_setPlusBtnLabelRefAttr: function(/*String*/ plusBtnLabelRef){ |
|
this._setAria(this.plusBtnNode, "aria-labelledby", plusBtnLabelRef); |
|
}, |
|
|
|
_setMinusBtnLabelAttr: function(/*String*/ minusBtnLabel){ |
|
this._setAria(this.minusBtnNode, "aria-label", minusBtnLabel); |
|
}, |
|
|
|
_setMinusBtnLabelRefAttr: function(/*String*/ minusBtnLabelRef){ |
|
this._setAria(this.minusBtnNode, "aria-labelledby", minusBtnLabelRef); |
|
} |
|
}); |
|
|
|
return has("dojo-bidi") ? declare("dojox.mobile.ValuePickerSlot", [ValuePickerSlot, BidiValuePickerSlot]) : ValuePickerSlot; |
|
});
|
|
|