silk-gui
Version:
GUI for developers and Node OS
180 lines (163 loc) • 4.06 kB
JavaScript
var _ = require('../../util')
var Watcher = require('../../watcher')
var dirParser = require('../../parsers/directive')
module.exports = {
bind: function () {
var self = this
var el = this.el
// check options param
var optionsParam = this._checkParam('options')
if (optionsParam) {
initOptions.call(this, optionsParam)
}
this.number = this._checkParam('number') != null
this.multiple = el.hasAttribute('multiple')
this.listener = function () {
var value = self.multiple
? getMultiValue(el)
: el.value
value = self.number
? _.isArray(value)
? value.map(_.toNumber)
: _.toNumber(value)
: value
self.set(value, true)
}
_.on(el, 'change', this.listener)
checkInitialValue.call(this)
},
update: function (value) {
/* jshint eqeqeq: false */
var el = this.el
el.selectedIndex = -1
var multi = this.multiple && _.isArray(value)
var options = el.options
var i = options.length
var option
while (i--) {
option = options[i]
option.selected = multi
? indexOf(value, option.value) > -1
: value == option.value
}
},
unbind: function () {
_.off(this.el, 'change', this.listener)
if (this.optionWatcher) {
this.optionWatcher.teardown()
}
}
}
/**
* Initialize the option list from the param.
*
* @param {String} expression
*/
function initOptions (expression) {
var self = this
var descriptor = dirParser.parse(expression)[0]
function optionUpdateWatcher (value) {
if (_.isArray(value)) {
self.el.innerHTML = ''
buildOptions(self.el, value)
if (self._watcher) {
self.update(self._watcher.value)
}
} else {
_.warn('Invalid options value for v-model: ' + value)
}
}
this.optionWatcher = new Watcher(
this.vm,
descriptor.expression,
optionUpdateWatcher,
{
deep: true,
filters: _.resolveFilters(this.vm, descriptor.filters)
}
)
// update with initial value
optionUpdateWatcher(this.optionWatcher.value)
}
/**
* Build up option elements. IE9 doesn't create options
* when setting innerHTML on <select> elements, so we have
* to use DOM API here.
*
* @param {Element} parent - a <select> or an <optgroup>
* @param {Array} options
*/
function buildOptions (parent, options) {
var op, el
for (var i = 0, l = options.length; i < l; i++) {
op = options[i]
if (!op.options) {
el = document.createElement('option')
if (typeof op === 'string') {
el.text = el.value = op
} else {
el.text = op.text
el.value = op.value
}
} else {
el = document.createElement('optgroup')
el.label = op.label
buildOptions(el, op.options)
}
parent.appendChild(el)
}
}
/**
* Check the initial value for selected options.
*/
function checkInitialValue () {
var initValue
var options = this.el.options
for (var i = 0, l = options.length; i < l; i++) {
if (options[i].hasAttribute('selected')) {
if (this.multiple) {
(initValue || (initValue = []))
.push(options[i].value)
} else {
initValue = options[i].value
}
}
}
if (typeof initValue !== 'undefined') {
this._initValue = this.number
? _.toNumber(initValue)
: initValue
}
}
/**
* Helper to extract a value array for select[multiple]
*
* @param {SelectElement} el
* @return {Array}
*/
function getMultiValue (el) {
return Array.prototype.filter
.call(el.options, filterSelected)
.map(getOptionValue)
}
function filterSelected (op) {
return op.selected
}
function getOptionValue (op) {
return op.value || op.text
}
/**
* Native Array.indexOf uses strict equal, but in this
* case we need to match string/numbers with soft equal.
*
* @param {Array} arr
* @param {*} val
*/
function indexOf (arr, val) {
/* jshint eqeqeq: false */
var i = arr.length
while (i--) {
if (arr[i] == val) return i
}
return -1
}