webshims.register('form-datalist', function($, webshims, window, document, undefined, options){
"use strict";
var doc = document;
var lazyLoad = function(name){
if(!name || typeof name != 'string'){
name = 'DOM';
}
if(!lazyLoad[name+'Loaded']){
lazyLoad[name+'Loaded'] = true;
webshims.ready(name, function(){
webshims.loader.loadList(['form-datalist-lazy']);
});
}
};
var noDatalistSupport = {
submit: 1,
button: 1,
reset: 1,
hidden: 1,
range: 1,
date: 1,
month: 1
};
if(webshims.modules["form-number-date-ui"].loaded){
$.extend(noDatalistSupport, {
number: 1,
time: 1
});
}
/*
* implement propType "element" currently only used for list-attribute (will be moved to dom-extend, if needed)
*/
webshims.propTypes.element = function(descs){
webshims.createPropDefault(descs, 'attr');
if(descs.prop){return;}
descs.prop = {
get: function(){
var elem = $.attr(this, 'list');
if(elem){
elem = document.getElementById(elem);
if(elem && descs.propNodeName && !$.nodeName(elem, descs.propNodeName)){
elem = null;
}
}
return elem || null;
},
writeable: false
};
};
/*
* Implements datalist element and list attribute
*/
(function(){
var formsCFG = $.webshims.cfg.forms;
var listSupport = Modernizr.input.list;
if(listSupport && !formsCFG.customDatalist){return;}
var initializeDatalist = function(){
var inputListProto = {
//override autocomplete
autocomplete: {
attr: {
get: function(){
var elem = this;
var data = $.data(elem, 'datalistWidget');
if(data){
return data._autocomplete;
}
return ('autocomplete' in elem) ? elem.autocomplete : elem.getAttribute('autocomplete');
},
set: function(value){
var elem = this;
var data = $.data(elem, 'datalistWidget');
if(data){
data._autocomplete = value;
if(value == 'off'){
data.hideList();
}
} else {
if('autocomplete' in elem){
elem.autocomplete = value;
} else {
elem.setAttribute('autocomplete', value);
}
}
}
}
}
};
if(formsCFG.customDatalist && (!listSupport || !('selectedOption' in $('')[0]))){
//currently not supported x-browser (FF4 has not implemented and is not polyfilled )
inputListProto.selectedOption = {
prop: {
writeable: false,
get: function(){
var elem = this;
var list = $.prop(elem, 'list');
var ret = null;
var value, options;
if(!list){return ret;}
value = $.prop(elem, 'value');
if(!value){return ret;}
options = $.prop(list, 'options');
if(!options.length){return ret;}
$.each(options, function(i, option){
if(value == $.prop(option, 'value')){
ret = option;
return false;
}
});
return ret;
}
}
};
}
if(listSupport){
//options only return options, if option-elements are rooted: but this makes this part of HTML5 less backwards compatible
if(!($('').prop('options') || []).length ){
webshims.defineNodeNameProperty('datalist', 'options', {
prop: {
writeable: false,
get: function(){
var options = this.options || [];
if(!options.length){
var elem = this;
var select = $('select', elem);
if(select[0] && select[0].options && select[0].options.length){
options = select[0].options;
}
}
return options;
}
}
});
}
inputListProto.list = {
attr: {
get: function(){
var val = webshims.contentAttr(this, 'list');
if(val != null){
$.data(this, 'datalistListAttr', val);
if(!noDatalistSupport[$.prop(this, 'type')] && !noDatalistSupport[$.attr(this, 'type')]){
this.removeAttribute('list');
}
} else {
val = $.data(this, 'datalistListAttr');
}
return (val == null) ? undefined : val;
},
set: function(value){
var elem = this;
$.data(elem, 'datalistListAttr', value);
if (!noDatalistSupport[$.prop(this, 'type')] && !noDatalistSupport[$.attr(this, 'type')]) {
webshims.objectCreate(shadowListProto, undefined, {
input: elem,
id: value,
datalist: $.prop(elem, 'list')
});
} else {
elem.setAttribute('list', value);
}
$(elem).triggerHandler('listdatalistchange');
}
},
initAttr: true,
reflect: true,
propType: 'element',
propNodeName: 'datalist'
};
}
webshims.defineNodeNameProperties('input', inputListProto);
webshims.addReady(function(context, contextElem){
contextElem
.filter('datalist > select, datalist, datalist > option, datalist > select > option')
.closest('datalist')
.each(function(){
$(this).triggerHandler('updateDatalist');
})
;
});
};
/*
* ShadowList
*/
var shadowListProto = {
_create: function(opts){
if(noDatalistSupport[$.prop(opts.input, 'type')] || noDatalistSupport[$.attr(opts.input, 'type')]){return;}
var datalist = opts.datalist;
var data = $.data(opts.input, 'datalistWidget');
var that = this;
if(datalist && data && data.datalist !== datalist){
data.datalist = datalist;
data.id = opts.id;
$(data.datalist)
.off('updateDatalist.datalistWidget')
.on('updateDatalist.datalistWidget', $.proxy(data, '_resetListCached'))
;
data._resetListCached();
return;
} else if(!datalist){
if(data){
data.destroy();
}
return;
} else if(data && data.datalist === datalist){
return;
}
this.datalist = datalist;
this.id = opts.id;
this.hasViewableData = true;
this._autocomplete = $.attr(opts.input, 'autocomplete');
$.data(opts.input, 'datalistWidget', this);
lazyLoad('WINDOWLOAD');
if(webshims.isReady('form-datalist-lazy')){
this._lazyCreate(opts);
} else {
$(opts.input).one('focus', lazyLoad);
webshims.ready('form-datalist-lazy', function(){
if(!that._destroyed){
that._lazyCreate(opts);
}
});
}
},
destroy: function(e){
var input;
var autocomplete = $.attr(this.input, 'autocomplete');
$(this.input)
.off('.datalistWidget')
.removeData('datalistWidget')
;
this.shadowList.remove();
$(document).off('.datalist'+this.id);
$(window).off('.datalist'+this.id);
if(this.input.form && this.input.id){
$(this.input.form).off('submit.datalistWidget'+this.input.id);
}
this.input.removeAttribute('aria-haspopup');
if(autocomplete === undefined){
this.input.removeAttribute('autocomplete');
} else {
$(this.input).attr('autocomplete', autocomplete);
}
if(e && e.type == 'beforeunload'){
input = this.input;
setTimeout(function(){
$.attr(input, 'list', $.attr(input, 'list'));
}, 9);
}
this._destroyed = true;
}
};
webshims.loader.addModule('form-datalist-lazy', {
noAutoCallback: true,
options: $.extend(options, {shadowListProto: shadowListProto})
});
//init datalist update
initializeDatalist();
})();
});