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, name){ webshims.createPropDefault(descs, 'attr'); if(descs.prop){return;} descs.prop = { get: function(){ var elem = $.attr(this, name); 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 updateDatlistAndOptions = function(){ var id; if(!$.data(this, 'datalistWidgetData') && (id = $.prop(this, 'id'))){ $('input[list="'+ id +'"], input[data-wslist="'+ id +'"]').eq(0).attr('list', id); } else { $(this).triggerHandler('updateDatalist'); } }; 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(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') }); elem.setAttribute('data-wslist', value); } else { elem.setAttribute('list', value); } $(elem).triggerHandler('listdatalistchange'); } }, initAttr: true, reflect: true, propType: 'element', propNodeName: 'datalist' }; } else { webshims.defineNodeNameProperties('input', { list: { attr: { get: function(){ var val = webshims.contentAttr(this, 'list'); return (val == null) ? undefined : val; }, set: function(value){ var elem = this; webshims.contentAttr(elem, 'list', value); webshims.objectCreate(options.shadowListProto, undefined, {input: elem, id: value, datalist: $.prop(elem, 'list')}); $(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(updateDatlistAndOptions) ; }); }; /* * 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); $.data(datalist, 'datalistWidgetData', 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(); })(); });