UNPKG

mk-databridge

Version:

A jQuery plugin to read/set data to forms. Supports nested objects, boolean and array checkboxes, on fly data formatting, non-input elements.

214 lines (176 loc) 5.35 kB
var _MODE_SET = 'set'; var _MODE_GET = 'get'; var databridge = { /** текущий корневой элемент * @type jQuery */ $root:null, /** текущий набор подэлементов с данными (с атрибутами name) * @type jQuery */ $inputs:null, options:null, /** текущий набор с данными * @type object */ data:null, errorsList:[], // list of mapping errors after getting values init: function(el,options){ this.clear(); this.options = $.extend({}, _defaults, options); this.root = el; this.$root = $(el); this.inputs = _collectElements(this.root,this.options); return this; }, clear: function(){ this.$root = null; this.$inputs = null; this.options = null; this.data = null; this._checkboxes = {}; return this; }, set: function(data){ this.data = data; var self = this; // unset all radios, forEach does not work in IE for querySelector [].forEach.call( this.root.querySelectorAll('[type=radio]'), function(el){el.checked = false;}); // apply elements this.errorsList = []; for(var name in this.inputs){ if(!this.inputs.hasOwnProperty(name)){continue;} var elData = this.inputs[name]; // find data value suitable for element var val = _var( name, data ); // map data value if(elData.mapper){ val = elData.mapper.call(elData.el, val,_MODE_SET,this); }else{ // converts undefined and null values only if no mapper given if(elData.type === 'checkbox'){ if(undefined === val){val = false;} if(null === val){val = false;} }else{ if(undefined === val){val = this.options.undefined;} if(null === val){val = this.options.null;} } } // apply data to DOM, calling in context of databridge _setVal.call(self,elData,val); } }, get: function(){ this.data = {}; var self = this; // get inputs this.errorsList = []; for(var name in this.inputs){ if(!this.inputs.hasOwnProperty(name)){continue;} var elData = this.inputs[name]; var val = _getElementVal.call(self,elData); // map data value if(elData.mapper){ try{ mappedval = elData.mapper.call(elData.el, val,_MODE_GET,this); val = mappedval; }catch(e){ this.errorsList.push({ varname:name,value:val,input:elData.el,message:e.message }); } } _appendData( self.data, name, val ); } if(this.errorsList.length)console.warn('Errors during data mapping occured!',this.errorsList); return this.data; }, getErrors: function(){ return (this.errorsList.length)?this.errorsList:null; } }; // every item of the list is a object {el,mapper,type} _collectElements = function(container, options){ var list = container.querySelectorAll('[name], [data-name]'); // group all elements by var names. It is necessery for checkboxes // 'cos several checkboxes with the same name can be scattered around the html var elements = {}; // forEach does not work in IE for querySelector, // so use [].forEach.call in context of DOMList [].forEach.call(list,function(el){ var name = _inputName(el); if(!elements[name]) elements[name] = {}; var elementData = elements[name]; elementData.type = _type(el); elementData.isInput = ('INPUT' === el.tagName); elementData.mapper = _getMapper(el,options); if(el.type === 'checkbox' || el.type === 'radio'){ if(!elementData.el)elementData.el = []; elementData.el.push(el); }else{ elementData.el = el; } }); return elements; }; _type = function(el){ var type = el.tagName.toLowerCase(); // input element if('input' === type){ var itype = el.getAttribute('type'); if(itype) type = itype; } return type; } _collectCheckboxes = function(container){ var list = container.querySelectorAll('[name][type=checkbox]'); var checkboxes = {}; list.forEach(function(el){ var name = _inputName(el); if(!checkboxes[name])checkboxes[name] = []; checkboxes[name].push(el); }); return checkboxes; }; /** * find variable in container by variable name with dot notation * @param {string} name * @param {object} container * @returns {undefined} */ var _var = function(name,container){ var steps = name.split('.'); var val = container || window; for(var i=0; i<steps.length; i++){ if(undefined === val)break; val = val[ steps[i] ]; } return val; }; /** * значение атрибута NAME, а если его нет, то DATA-NAME * @param {DOMElement} el * @returns {string} */ var _inputName = function(el){ var name = el.getAttribute('name') || el.getAttribute('data-name'); // unify to point notation name = name.replace( /\[/g,'.' ); name = name.replace( /\]/g,'' ); return name; }; // _flattenObject - is a bad approach, because not all data from // data-object need to be applied to DOM, so no reason to iterate whole object $.fn.databridge = function(data,options){ databridge.init(this.get(0),options) // command if('string' === typeof data){ switch(data){ case 'errors': return databridge.getErrors(); }; return this; // set data }else if(data){ databridge.set(data); return this; // get data }else{ return databridge.get(); } };