text: /*
   * jQuery.my 0.9.9
   * Requires jQuery 1.11.0+, SugarJS 1.3.9-1.4.x
   *
   * 
   * More details at jquerymy.com
   * 
   * @ermouth, thanks @carpogoryanin, @ftescht
   * 2014-07-05
   */
  
  ;(function ($) {var _version = "jQuery.my 0.9.9";
  
  	// Some shortcuts and constants
  	var lang = "en",
  		forms = {_src:{}, _name:"Global manifest cache"},
  		$E = $.extend,  N = null, TMP,
  		n = function (o) {return o!==null && o!==undefined;},
  		d8 = "{yyyy}-{MM}-{dd}", h24="{HH}:{mm}",
  		Ob = "object", Da = "data", Ar = "array",
  		St = "string", Fu = "function", Ch = "change",
  
  		isA = Object.isArray,
  		isB = Object.isBoolean,
  		isS = Object.isString,
  		isO = Object.isObject,
  		isN = Object.isNumber,
  		isR = Object.isRegExp,
  		isF = Object.isFunction,
  		isP = function (a) {/*is promise*/return !!(null!=a&&(isO(a)||a.jquery)&&isF(a.then)&&isF(a.fail)&&isF(a.state));},
  		isRej = function (p) {/*is promise rejected*/ return isP(p)?(p.state()==="rejected"):null;};
  
  
  	//=======================================
  	// Manifest repo getter/setter and helpers
  
  	var _cache = function _localCache (A1, A2) {
  		// ( no args ) – returns all forms obj container
  		// ({manifest},  {container}) – caches form in container, id must be defined, return form or null
  		// ({manifest}) – caches form in local container, id must be defined
  		// ("form.Id", "exist") – true/false
  		// ("form.Id", {container}) – get from container
  		// ("form.Id") – get from local cache
  		var ref, obj;
  		if (isS(A1)) {
  			ref=A1;
  			obj = _getref(isO(A2)?A2:forms, ref);
  			if ("exist"===A2) return isO(obj);
  			return !obj?null:Object.clone(obj,true);
  		} else if (isO(A1)){
  			obj = _putmanifest (A1, A2);
  			if (!isO(obj)) return f.con(obj), null;
  			return obj;
  		} else if (undefined===A1) {
  			return forms._src;
  		} else if (null===A1) {
  			return Object.reject(forms,/^_/);
  		}else return null;
  	};
  
  	// - - - - - - - - - - - - - - - - - - - -
  
  	function _getref(obj,ref) {
  		//gets branch of obj by string ref like "data.list.items.1"
  		return (ref||"").split(".").reduce(function (a,b){
  			if (null!=a && null!=a[b]) return a[b];
  			else return undefined;
  		}, obj);
  	}
  
  	// - - - - - - - - - - - - - - - - - - - -
  
  	function _manifest (manifest, ref) {
  		// Dereferences pointer to form component,
  		// manifest is caller manifest obj,
  		// internal function
  		var t, ext;
  		if (isO(ref)) return ref;
  		else if (isS(ref)) {
  
  			//try to find it on manifest
  			t = _getref(manifest, ref);
  
  			//then in local repo as original
  			if (null==t) t = Object.clone(forms._src[ref],true);
  
  			//then in local repo as part of component
  			if (null==t) {
  				t = _getref(forms, ref);
  				if (isO(t) && isO(t._self)) t=Object.clone(t._self,true);
  				else if (isO(t)) t = Object.clone(t,true);
  			}
  
  			//then in ext repo as part of component
  			if (null==t && _getref(manifest,"params.cache")) {
  				ext = _getref(manifest,"params.cache");
  				if (isF(ext)) t = ext(ref);
  				else if (isO(ext)) t = _cache(ref, ext);
  
  				if (isO(t)){
  					if (isO(t._self)) t=Object.clone(t._self,true);
  					Object.merge(t, {params:{cache:ext}}, true);
  				}
  			}
  
  			if (null!=t && isO(t)) {
  				ext = ext||_getref(manifest,"params.cache");
  				if (ext) Object.merge(t, {params:{cache:ext}}, true);
  				return t;
  			}
  			else throw "Component "+ref+" not found";
  
  		} else if (isF(ref)) {
  			return ref.apply(manifest, Array.prototype.slice.call(arguments, 2));
  		} else return null;
  	}
  
  	// - - - - - - - - - - - - - - - - - - - -
  
  	function _putmanifest (obj0, root0) {
  		// Mounts obj to root in a branch, defined in
  		// obj.id property. If id =="x.y.z", root will be
  		// deep extended with {x:{y:{z:obj}}}.
  		// obj also is unjsonned and extended with _self ref,
  		// which point to original version of obj.
  
  		//Returns direct link to entire branch obj or string error.
  
  		var root=root0||forms, obj=obj0, path, id, prev, res;
  
  		if (!(isO(root) && isO(obj) && isO(obj.ui) && isO(obj.data) && isS(obj.id)))
  			return "Invalid arguments.";
  
  		if (!root.hasOwnProperty("_src")) root._src={};
  
  		id = obj.id;
  
  
  		path = id.split(".").compact(true);
  
  		try {obj=Object.clone(obj0, true);}
  		catch (e) {return "Can't mount circular-referencing obj.";}
  
  		//unwind string defs of functions
  		try {if (!obj.params || (obj.params && !obj.params.strict)) _unjson(obj);}
  		catch (e) {return "Invalid manifest, parse error.";}
  
  		//mark manifest as unjsonned
  		Object.merge(obj,{params:{strict:true}}, true);
  		// save it
  		root._src[id] = obj;
  
  
  		if (prev=f.mask(root, id)) {
  			if (prev.params && prev.params.protect) return "Can't mount on protected";
  			if (prev._self) delete prev._self;
  		} //else {
  			//no prev node, mount
  			Object.merge(root,f.unmask(obj, id),true);
  		//}
  		res = _getref(root,id);
  
  		Object.defineProperty(res, "_self", {
  			get: function () { return root._src[id]; },
  			set: function () { throw "Can not change repo";},
  			enumerable : false,
  			configurable : true
  		});
  
  		return res;
  	}
  
  
  	//########################################################
  	// Storage of rules defined by cascading selectors
  	// very similar to css. Leafs are processor
  	// or processing rules for this type of node
  
  	var MY = {
  
  
  	//getter and setter functions for different types of nodes
  
  	vals : {
  
  	/**/	".my-form": function ($o, v) {
  			//object is jQuery.my instance
  				if ($o && $o.my ) {var d = $o.my(Da); return Object.equal(d,v)?d:$o.my(Da, v, true);}
  				else return v||N;
  			},
  
  	/**/	".hasDatepicker":function ($o,v) {
  			//object has jQ UI datepicker
  				if(n(v)) $o.datepicker("setDate", ((v=="")?v:Date.create(v)));
  				var date = $o.datepicker("getDate");
  				return (date?date.format(d8):"");
  			},
  
  	/**/	".my-tags": function ($o,v) {
  				//object is jQ tags control
  				if (n(v)) {
  					if (isS(v) || isN(v)) $o.tags(Da,[v+""]);
  					else if (isA(v)) $o.tags(Da,v);
  				}
  				return $o.tags(Da);
  			},
  
  	/**/	".ui-draggable": function ($o,v) {
  			//object is jQ UI draggable
  				if (n(v) && isO(v)) {
  					var c = {};
  					if (!isNaN(v.left)) c.left = Number(v.left).ceil(2)+"px";
  					if (!isNaN(v.top)) c.top = Number(v.top).ceil(2)+"px";
  					if (c.left || c.top) $o.css(c);
  				}
  				var p = $o.position();
  				return {
  					left:((v&&!isNaN(v.left))?(v.left*1).ceil(2):p.left.ceil(2)),
  					top:((v&&!isNaN(v.top))?(v.top*1).ceil(2):p.top.ceil(2))
  				};
  			},
  
  	/**/	".my-form-list": function ($o,list) {
  			//object is list of forms
  				var i,old,mod,eq,
  					sP = "ui-sortable", sPlaceholder= ">."+sP+"-placeholder",
  					od = $o.data("formlist")||{},
  					gen = od.generator||{},
  					itemSel = gen.selector||">*",
  					tmpl = gen.template||"<div></div>",
  					delay = gen.delay||50,
  					sortable = $o.is("."+sP),
  					result=[],
  					$n;
  				var $c = sortable?$o.find($o.sortable("option","items")):$o.find(itemSel);
  
  				if (n(list) && isA(list)) {
  					//return list passed if dragging taking place
  					if (sortable && $o.find(sPlaceholder).size()!=0) {
  						return list;
  					}
  
  					//first we must define if putting new data over old will
  					// change anything
  					old= []; $c.each(function () {
  						var $x = $(this);
  						if ($x.data("my")) old.push($x.my("data"));
  					});
  
  					//fast compare
  					eq=false;
  					if (old.length==list.length) for (eq=true, i=0;i<old.length;i++) if (old[i]!==list[i]) eq=false;
  
  					if (!eq){
  						// more comprehemsive compare, for example
  						// applying [{a:1},{b:4}] over [{a:1,b:2},{a:3,b:4}]
  						// must not force any recalc
  						mod = $.extend(true, [], old, list);
  						if (!Object.equal(old,mod) || mod.length!=list.length) {
  							//we have new data, hash it
  							var hash=[], xhash={}, present={};
  							for (i=0;i<list.length;i++) {
  								hash[i]=f.sdbmCode(list[i]);
  								xhash[hash[i]]=i;
  							}
  
  							//clean childs with no match to new data
  							$c.filter("*:not(.ui-sortable-placeholder)").each(function () {
  								var $x = $(this),
  									md = $x.data("formlist")||{},
  									hash = md.sdbmCode;
  								if (hash && xhash.hasOwnProperty(hash)) present[hash] = $(this);
  								else $x.remove();
  							});
  
  							//iterate list
  							for (i=0;i<list.length;i++) {
  								if (present[hash[i]]) {
  									$n = present[hash[i]].detach().appendTo($o);
  									result.push($n.my("data"));
  								} else {
  									$n = $(/\{/.test(tmpl)?tmpl.assign(list[i]):tmpl).appendTo($o);
  									$n.data("formlist",{index:i});
  									$n.on("change.my", _itemChange.debounce(delay/1.3));
  									$n.data("formlist").list = list[i];
  
  									$n.my(
  										_manifest (gen.parent, gen.manifest, list[i], i, list, $o),
  										list[i]
  									);
  									result.push($n.my("data"));
  								}
  							}
  							(function () {$o.trigger(Ch);}).debounce();
  							return result;
  						}
  					}
  					return old;
  				} else if ($c.size()) {
  					$c.filter("*:not(.ui-sortable-placeholder)").each(function (idx){
  						var $x = $(this);
  						if ($x.is(".my-form") && $x.data("my")) {
  							result.push($x.my("data"));
  							if (idx != $x.data("formlist").index) {
  								$x.data("formlist").index = idx;
  								$x.my("redraw");
  							}
  						}
  					});
  					return result;
  				}
  				return list||[];
  
  				function _itemChange () {
  					var $this = $(this);
  					if ($this.data("my")) $this.data("formlist").sdbmCode = f.sdbmCode($this.my("data"));
  					$o.trigger("check.my");
  				}
  			},
  
  	/**/	".ui-sortable": function ($o, list) {
  				//jQ UI sortable
  				var a = [],
  					sP = "ui-sortable",
  					sPlaceholder= ">."+sP+"-placeholder",
  					$c = $o.find($o.sortable("option","items")),
  					$m;
  				if (n(list) && isA(list)) {
  					var w = {}, z={}, v = list.unique();
  					//return list passed if some field has focus of dragging taking place
  					if (
  						$o.find("input:focus:eq(0),textarea:focus:eq(0)").size() ||
  						$o.find(sPlaceholder).size()
  					) return v;
  					$c.each(function () {w[f.sdbmCode(f.extval($(this)))] = $(this);});
  					for (var i=v.length-1; i>=0; i--) {
  						var j = f.sdbmCode(v[i]);
  						if (w[j]) {
  							w[j].prependTo($o).show();z[j]=true;
  							if (a.indexOf(v[i])==-1) a.push(v[i]);
  						}
  					}
  					a=a.reverse();
  					for (i in w) if (!z[i]) w[i].hide();
  				} else {
  					var $p = $o.find(sPlaceholder), $q = $o.eq(0);
  					if ($p.size()!=0) {
  
  						//if placeholder state changed saving new data
  						if ($q.my()[sP] != $p.position().left+""+$p.position().top) {
  							$c = $c.filter(":visible:not(:disabled, .ui-state-disabled, .ui-sortable-helper)");
  							$m = $o.find($o.sortable("option","items")).filter(".ui-sortable-helper");
  							$c.each(function () {
  								var $x = $(this);
  								if ($x.is(".ui-sortable-placeholder")) {a.push(f.extval($m));}
  								else a.push(f.extval($x));
  							});
  							//caching placeholder state and data retrieved
  							$q.my()[sP] = $p.position().left+""+$p.position().top;
  							$q.my()[sP+"1"] = a;
  						} else a = $q.my()[sP+"1"];
  						if (a==N) $c.each(function () {a.push(f.extval($(this)));});
  					} else {
  						$c = $o.find($o.sortable("option","items")).filter(":visible:not(:disabled, .ui-state-disabled)");
  						$c.each(function () {a.push(f.extval($(this)));});
  					}
  				}
  				return a;
  			},
  
  	/**/	"input[type=date]":function ($o,v) {
  				//object is date input
  				if(n(v)) {
  					if (v!="") d = Date.create(v).format(d8); else d = "";
  					if (isS(d) && !/Invalid/.test(d)) $o.val(d);
  					return d;
  				}
  				var d = $o.val();
  				return (d!=""?Date.create(d).format(d8):"");
  			},
  
  	/**/	"input[type=time]":function ($o,v) {
  				//object is time input
  				if(n(v)) {
  					if (v!="") d = Date.create(v).format(h24); else d = "";
  					if (isS(d) && !/Invalid/.test(d)) $o.val(d);
  					return d;
  				}
  				var d = $o.val();
  				return (d!=""?Date.create(d).format(h24):"");
  			},
  
  
  	/**/	"input":{
  				"[type='text'],[type='number'],[type='search'],[type='hidden'],[type='password'],[type='button'],[type='range'],:not([type])":{
  				//nearly all main input types and button
  
  					".ui-slider-input": function ($o,v) {
  					//input with jQ UI slider() applied
  						if (n(v)) $o.val(v).slider("refresh");
  					},
  
  					".tagstrip input.value": function ($o,v) {
  					//input of tagstrip() applied
  						if (n(v)) $o.val(v).trigger("update");
  					},
  
  					"div.select2-container+input": function ($o, v) {
  					//select2
  						if (n(v) && JSON.stringify(v)!==JSON.stringify($o.select2("val"))) $o.select2("val", (isA(v)?v:[v]));
  						return $o.select2("val");
  					},
  
  					"": function ($o,v) {if(n(v)) $o.val(v+"");}
  				},
  
  				":radio":function ($o,v) {
  				//radio buttons
  					var pos = -1;
  					if (n(v)) {
  						$o.each(function (ind) {
  							var val = $(this).val();
  							if ((v+"")===(val+"")) pos=ind;
  						});
  						var jqcheck = $o.eq(0).checkboxradio;
  						if (jqcheck) $o.each(function (ind){
  								var $x = $(this);
  								if (pos!=ind && $x.is(":checked"))
  									$x.prop("checked",false).checkboxradio("refresh");
  							});
  						if (pos>-1) {
  							var $x = $o.eq(pos);
  							if (!$x.is(":checked")) {
  								$x.prop("checked",true);
  								if (jqcheck) $x.checkboxradio("refresh");
  							}
  						} else if (!jqcheck) $o.each(function () { $(this).prop("checked",false); });
  					}
  					if (pos==-1) for (var ind=0; ind<$o.size(); ind++) {
  						if ($o.eq(ind).is(":checked")) pos=ind;
  					}
  					return pos!=-1?$o.eq(pos).val():"";
  				},
  
  				":checkbox": function ($o, v0) {
  				//checkbox
  					var pos = -1, v = v0, a = [];
  					if (n(v)) {
  						if (!isA(v)) v = [v0];
  						var jqcheck = !!$o.eq(0).checkboxradio;
  						$o.each(function (ind) {
  							var $x = $(this), val = $x.val(), on = $x.is(":checked");
  							if (v.indexOf(val)!=-1) {
  								a.push(val);
  								if (!on) $x.prop("checked", true);
  							} else if (on) $x.prop("checked", false);
  							if (jqcheck) $x.checkboxradio("refresh");
  						});
  					} else {
  						$o.each(function () {
  							var $x = $(this);
  							if ($x.is(":checked")) a.push($x.val());
  						});
  					}
  					return a;
  				}
  			},
  
  	/**/	"select": {
  				".ui-slider-switch": function ($o,v) {
  				//on-off in jQ Mobile
  					if (n(v)) {
  						$o.val(v+"");
  						$o.slider("refresh");
  					}
  				},
  				"div.select2-container+select":{
  					"[multiple]": function ($o, v) {
  						if (n(v)) $o.select2("val", (isA(v)?v:[v]));
  						return $o.select2("val");
  					},
  					"":function ($o, v) {
  						if (n(v)) $o.select2("val", v+"");
  						return $o.select2("val");
  					}
  				},
  				"[multiple]": function ($o,v) {
  					if (n(v)) {
  						$o.val(v,[]);
  						if ($o.selectmenu) $o.selectmenu("refresh",true);
  						//the only way to check if we have jQ UI selectmenu() attached
  					}
  				},
  				"": function ($o,v) {
  					if (n(v)) {
  						$o.val(v+"");
  						if ($o.selectmenu) {
  						//now ditinguish between jQ selectmenu plugin and jQ Mobile
  							if ($o.selectmenu("option").theme!==undefined) $o.selectmenu("refresh",true);
  							else $o.find("option").each(function (i){
  								var $x = $(this);
  								if (f.extval($x) == v) $o.selectmenu("value",i);
  							});
  			}}}},
  
  	/**/	"textarea": {
  				".my-cleditor":function ($o,v) {
  					if(n(v)) $o.val(v).cleditor()[0].updateFrame();
  					return $o.val();
  				},
  				"div.redactor_box textarea,.redactor": function ($o,v) {
                      var r9 = $o.hasClass('my-redactor-9');
                      if(n(v)) {
                          if(r9) $o.redactor('set', v);
                          else $o.setCode(v, false);
                          return v;
                      }
                      return r9 ? $o.redactor('get') : $o.getCode();
  				},
  				".my-codemirror":function ($o,v){
  					if (n(v)) {
  						$o[0].nextSibling.CodeMirror.setValue(v);
  						return v;
  					}
  					return $o[0].nextSibling.CodeMirror.getValue();
  				},
  				"":function ($o,v) {if(n(v)) $o.val(v+"");}
  			},
  
  	/**/	"fieldset,form,section,aside,.my-container": function ($o, v) {
  			//object is class-manageable container,
  			//value is an array of css rules
  				var clist = Array.prototype.slice.call($o[0],0).sort(),
  					list = v;
  				if (n(v)) {
  					if (isS(v)) list = v.split(/[,\s]+/).compact(true);
  					if (isA(list)) {
  						list.sort();
  						if (list.join(" ")!==clist.join(" ")) {
  							$o.atrr("css", list.join(" "));
  							clist = list;
  						}
  					}
  				}
  				return clist;
  			},
  
  	/**/	"div,span,a,p,li,td,th,h1,h2,h3,h4,h5,h6":{
  				".ui-slider":function ($o, v){
  					if(n(v)) $o.slider("option",$o.slider("option","values")?"values":"value", f.clone(v));
  					return f.clone($o.slider("option","values")||$o.slider("option","value")||0);
  				},
  				".ui-buttonset": function ($o,v) {
  				//jQ UI buttonset ()
  					if (!n(v)) {
  						var jor = $o.find(":radio:checked");
  						if (jor.size() && jor.button) return jor.val()||jor.button("option", "label") ;
  					} else if (v!="") {
  						var jon = N;
  						$o.find(":radio").each(function () {
  							jon=( ($(this).val()||$(this).button("option", "label"))==v?$(this):jon );
  						});
  						if (jon) {
  							jon.attr("checked",true);
  							$o.buttonset("refresh");
  							return v;
  						}
  					}
  					$o.find(":radio:checked").attr("checked",false);
  					$o.buttonset("refresh");
  					return "";
  				},
  				".ace_editor":function ($o,v) {
  					if(n(v)) ace.edit($o[0]).setValue(v);
  					return ace.edit($o[0]).getValue(v);
  				},
  				"": function ($o,v) {
  					if(n(v)) $o.html(v);
  					return $o.html();
  				}
  			},
  	/**/	"pre,code":function ($o,v) {
  				if(n(v)) $o.html(v);
  				return $o.html();
  			},
  	/**/	"img":function ($o,v) {
  				if (n(v)) $o.attr("src",v);
  				return $o.attr("src")||"";
  			},
  	/**/	"":function ($o,v) {
  				if (n(v)) $o.html(v);
  				return $o.html()||$o.text()||String($o.val())||"";
  			}
  		},
  
  
  	//messages
  	//########################################################
  
  		msg:{
  			"":{en:"Invalid input", ru:(TMP="Неверное значение")},
  
  			formError:{en:"Form error",ru:"Ошибка формы"},
  			initFailed:{
  				en:'<p class="my-error">Form init failed</p>',
  				ru:'<p class="my-error">Ошибка инициализации формы</p>'
  			},
  
  			badInput:{en:"Invalid input", ru:TMP},
  			patternMismatch:{en:"Pattern mismatch", ru:"Не соответствует шаблону"},
  			rangeOverflow:{en:"Over maximum", ru:"Больше максимума"},
  			rangeUnderflow:{en:"Under minimum", ru:"Меньше минимума"},
  			stepMismatch:{en:"Step mismatch", ru:"Не кратно шагу"},
  			tooLong:{en:"Too long", ru:"Слишком длинно"},
  			typeMismatch:{en:"Invalid type", ru:"Неверный тип"},
  			valueMissing:{en:"Required", ru:"Обязательное поле"}
  		},
  
  
  	//different controls' events to watch for
  	//########################################################
  
  		events: {
  			".hasDatepicker":"change.my check.my",
  			".my-form,.my-tags":"change.my check.my",
  			".ui-slider":"slide.my check.my",
  			"div.redactor_box textarea":"redactor.my check.my",
  			".my-codemirror":"codemirror.my check.my",
  			".ace_editor":"ace.my check.my",
  			".my-form-list,.ui-sortable":"sortchange.my sortupdate.my check.my",
  			".ui-draggable":"drag.my dragstop.my check.my",
  			"a, .pseudolink, input[type=button], button": "click.my ",
  			"img, :radio, :checkbox": "click.my check.my ",
  			"div.select2-container+input,div.select2-container+select":"change.my check.my input.my",
  			".ui-buttonset,input, select, textarea":"blur.my change.my check.my"+(navigator.appName.to(5)==="Micro"?" keyup.my":" input.my"),
  			"":"check.my"
  		},
  
  	//functions retrieving container for different controls
  	//########################################################
  
  		containers: {
  			"*[data-role='fieldcontain'] *":{ //jQuery Mobile
  				"input,textarea,select,button,:radio": function ($o) {
  					return $o.parents('[data-role="fieldcontain"]').eq(0);
  				}
  			},
  			".tagstrip *.value": function ($o){ //$.tagstrip()
  				return $o.parents('.tagstrip').eq(0);
  			},
  			"div.redactor_box textarea":function ($o){
  				return $o.parents('div.redactor_box').eq(0).parent();
  			},
  			".my-tags,.hasDatepicker,.ui-widget,input,textarea,select,button" :{
  				".my-cleditor": function ($o) {
  					return $o.parents('div.cleditorMain').eq(0).parent();
  				},
  				"": function ($o) {
  					var p = $o[0].parentNode, t = p.nodeName;
  					if (/^(div|span|a|p|form|fieldset|li|ul|td|th|h\d)$/i.test(t)) return $(p);
  					else return $o.parents('div,span,a,p,form,fieldset,li,ul,td,th,h1,h2,h3,h4,h5,h6').eq(0);
  				}
  			},
  			"": function ($o) {return $o;}
  
  		},
  
  	//disablers and enablers
  	//########################################################
  
  		offon: { //if x==true disables control else enables
  			".ace_editor": function (x,$o) {ace.edit($o[0]).setReadOnly(x);},
  			".ui-selectable": function (x,$o) {f.jquix($o,"selectable",x);},
  			".ui-slider": function (x,$o) {f.jquix($o,"slider",x);},
  			".ui-draggable": function (x,$o) {f.jquix($o,"draggable",x);},
  			".ui-buttonset": function (x,$o) {f.jquix($o,"buttonset",x);},
  			".hasDatepicker": function (x,$o) {f.jquix($o,"datepicker",x);},
  			".my-form":function (x,$o){$o.my("disabled", !!x);},
  			"div.select2-container+input,div.select2-container+select": function (x,$o) {f.jquix($o,"select2",x);},
  			".my-cleditor": function (x,$o) { $o.cleditor()[0].disable(!!x);},
  			"": function (x, $o) {$o.attr("disabled", !!x);}
  		},
  
  	//destructors
  	//########################################################
  		destroy:{
  			".hasDatepicker":function ($o){$o.datepicker("destroy");},
  			".ui-slider":function ($o){$o.slider("destroy");},
  			".ui-sortable":{
  				".my-form-list":function ($o){
  					$o.find(">.my-form").each(function () {
  						$(this).my("remove");
  					});
                      $o.removeClass("my-form-list");
  					$o.sortable("destroy");
  				},
  				"":function ($o){$o.sortable("destroy");}
  			},
  			".my-form-list":function ($o){
  				$o.find(">.my-form").each(function () {
  					$(this).my("remove");
  				});
  
  			},
  			".ui-draggable":function ($o){$o.draggable("destroy");},
  			".my-redactor-8":function ($o){
                  $o.destroyEditor();
                  $o.removeClass("my-redactor-8");
              },
  			"div.select2-container+input,div.select2-container+select":function ($o){$o.select2('destroy');},
  			".my-form": function ($o) {$o.my("remove");},
              "textarea": {
                  ".my-codemirror": function ($o) {
                      $o[0].nextSibling.CodeMirror.toTextArea();
                      $o.removeClass("my-codemirror")
                  }
              }
  		}
  	};
  
  	//default values for .params section of manifest
  	//########################################################
  
  	MY.params = {
  		container:function ($o) {return _traverse($o, MY.containers)($o);},	// container getter
  		change:N,
  		recalcDepth:2,												// depth of dependencies resolver tree
  		delay:0,													// default delay of bind invocation
  		strict:false,												// if true form assumed unjsonned
  		locale:(TMP=(navigator.language||navigator.userLanguage||"en").substr(0,2)),
  		messages:Object.map(MY.msg, function (k,v){return v[TMP]||v.en;}),
  		errorTip:".my-error-tip",									// $ selector of err tip container
  		errorCss:"my-error",										// class applied on container on err
  		pendingCss:"my-form-pending",
  		animate:0,													//err tips animation duration
  		effect: function ($e, onoff, duration) { 					//err tips animation effect
  			if (onoff) return $e.fadeIn(duration); $e.fadeOut(duration);
  		},
  		remember:0, 												// undo steps to remember
  		silent:false,
  		history:{}, 												//form undo history
  		historyDelay:100 											//delay in ms between subsequent calls of history()
  	};
  
  	var f = _getHelpersLib();
  
  	//########## SYSTEM FUNCTIONS ##########
  
  	function _field ($o, v) {
  	//gets or sets the value of $o control
  	//selects appropriate function for setting-retrieving
  	//and attaches it to $o.data("myval");
  		var fn = $o.data("myval"), r, fval;
  		if (!fn) {
  		//finding appropriate function and caching it
  			fval = _traverse ($o, MY.vals);
  			if (isF(fval)) {
  				r = fval($o, N);
  				if (r===undefined) {
  				//if function returns undefined we use .val() by default
  					$o.data("myval", (function ($o, v) {
  						if (N != v) fval($o, v);
  						return $o.val();
  					}).fill($o, undefined));
  				} else $o.data("myval", fval.fill($o, undefined));
  			}
  			fn = $o.data("myval");
  		}
  		if (isF(fn)) {
  			r = fn();
  			if ((r!==v && (false==v || false==r)) || r != v || isO(v)) r = fn(v);
  			return r;
  		} else return N;
  	}
  
  
  	//=======================================
  
  	function _traverse ($o, rules) {
  	//traverses tree of rules to find
  	//first sprig with selector matching $o.
  	//returns match or null
  		var fval = N, flevel=0, fselector="";
  		go ($o,rules,1);
  		return fval;
  
  		// - - - - - - - - - - - - - - - - - - - - - - -
  
  		function go ($o, os, level) {
  			for (var i in os) if (i!="" && $o.is(i)) {
  				fselector = fselector+ (fselector?" ### ":"") + i;
  				var oi=os[i],otype = $.type(oi);
  				if ( !(/^(nul|un|ob)/).test(otype) && level>flevel) {
  					fval=oi; flevel = level; return;
  				} else if (otype==Ob) go ($o, oi, level+1); //recursion down
  			}
  			if (N != os[""] && typeof os[""]!=Ob && level>flevel)  {
  				fval=os[""];
  				flevel = level;
  			}
  		}
  	}
  
  
  	//=======================================
  
  	function _bind (data, val, uiNode, $formNode) {
  	//sets or retrieves data using bind function
  		var bind = uiNode.bind, i, path=[], ptr, preptr, bt = $.type(bind);
  		if (bt == Fu) {
  			return bind.call(_form($formNode).manifest, data, val, $formNode);
  		}
  		if (bt === St || bt === Ar) {
  			if (bt === St && !/\./.test(bind)) {
  				//index is flat
  				if (val != N) data[bind] = val;
  				else if (data[bind]===undefined) data[bind] = N;
  				return data[bind];
  			}
  			//index is composite, we need to traverse tree
  			//and create some branches if needed
  			if (bt === St) path = bind.split(".").each(function (a,i){this[i]=String(a).compact();});
  			if (bt === Ar) path = bind.slice(0).each(function (a,i){this[i]=String(a).compact();});
  			ptr = data;
  			for (i=0;i<path.length;i++) {
  				if (i==path.length-1) {
  					//we'r in the hole
  					if (val!=N) ptr[path[i]] = val;
  					else if (ptr[path[i]]===undefined) ptr[path[i]] = N;
  					return ptr[path[i]];
  				}
  				if (i==0) {
  					ptr = data[path[0]]; preptr= data;
  				} else {
  					preptr = preptr[path[i-1]];
  					ptr = ptr[path[i]];
  				}
  				if (ptr===undefined) ptr = preptr[path[i]] = {};
  			}
  		}
  		return N;
  	}
  
  
  	//=======================================
  
  	function _validate (data,val, uiNode, $formNode) {
  		//checks if val fails to meet uiNode.check condition
  		var pat = uiNode.check, i, v;
  		if (pat != N) {
  			var msg = _form($formNode).params.messages,
  				err = uiNode.error,
  				err0 = err||msg.patternMismatch||msg[""]||"Error";
  
  			if (
  					$formNode.size() &&
  					Object.prototype.hasOwnProperty.call($formNode[0], "validity") &&
  					!$formNode[0].validity.valid
  			) {
  				var syserr=$formNode[0].validationMessage+"";
  				if (syserr!=="") return syserr.substr(0,1).toUpperCase()+syserr.substr(1);
  				else {
  					v = $formNode[0].validity;
  					for (i in v) {
  						if (syserr==="" && i!="valid" && isB(v[i]) && v[i] && msg[i]) syserr=msg[i];
  					}
  					return syserr||err;
  				}
  			}
  
  			switch($.type(pat).to(1)){
  				case "f":	/*return pat.call(_form($formNode).manifest, data, val, $formNode);*/{
                      ret = pat.call(_form($formNode).manifest, data, val, $formNode);
                      return (ret === null || ret === undefined) ? "" : ret;
                  }
  				case "r":	return ( (pat.test(String(val))) ? "":err0 );
  				case "a":	return ( (pat.indexOf(val)>-1)?"":err0);
  				case "s":	return (val==pat?"":err0);
  				case "o":	return pat[val]?"":err0;
  				case "b":	{
  					if ($formNode.is(".my-form-list,.ui-sortable")) {
  						var sel = $formNode.data("listSrc")||$formNode.data("my").listSrc||">*", ret={};
  						$formNode
  							.find(sel)
  							.filter("*:not(.ui-sortable-placeholder)")
  							.each(function (idx){
  								var $e = $(this);
  								if ($e.data("my") && !$e.my("valid")) ret[idx]=$e.my("errors");
  							});
  						return ret;
  					} else if ($formNode.hasClass("my-form")){
  						return !pat?"":$formNode.my("valid")?"":$formNode.my("errors");
  					}
  					return "";
  				}
  			}
  			return msg.formError||"Error";
  		}
  		return "";
  	}
  
  
  	//=======================================
  
  	function _form ($formNode) {
  	//get control's root.my()
  		var $my = $formNode.my();
  		if (!$my) return null;
  		return $my.root?$my.root.my():$my;
  	}
  
  
  	//=======================================
  
  	function _css (onOff, $we, css0) {
  	//applies-discards conditional formatting or enables-disables field
  		var css = css0.compact(), r = css.replace(/:disabled/g,'');
  
  		$we.each(function () {
  			var $d = $(this),d = $d.my(), $o = (d?d.container:$d);
  			if (onOff) {
  				if (!$o.is(r)) $o.addClass(r);
  			} else $o.removeClass(r);
  
  			if (r!=css && d!==undefined && !!onOff != !!d.disabled) {
  				//we have :disabled
  				$d.my().disabled = !!onOff;
  				if (!d._disable) $d.my()._disable = _traverse($we, MY.offon).fill(undefined, $we);
  				d._disable(!!onOff);
  			}
  		});
  		return $we;
  	}
  
  
  	//=======================================
  
  	function _update ($o, value, depth) {
  	//validates and updates field and all dependent fields,
  	//applies conditional formatting
  		var $box, d, oui, p, val, css, oc,
  			$this = $o,
  			xdata = $this.my(),
  			err="Unknown error";
  
  		if (xdata) {
  			var selector = xdata.selector,
  				$root = xdata.root,
  				$we = $root.find(selector),
  				ui = $root.my().ui,
  				isForm = $o.hasClass("my-form"),
  				isList = $o.hasClass("my-form-list");
  			if (isForm){
  				$box = $o; d = xdata.ddata; oui = xdata.dui; p =  xdata.dparams;
  			}
  			else {
  				$box = xdata.container; d = xdata.data; oui = xdata.ui; p =  xdata.params;
  			}
  			//exec bind if any
  			if (oui.bind != N) {
  				if (n(value)) val = value;
  				else val = _field($we,_bind(d,N,oui,$we));
  
  				//validating and storing if correct
  				//applying or removing error styles
  				try {
  					err = _validate(d,val,oui, $we);
  				}
  				catch (e) {
  					f.con ("Error "+ e.name+" validating "+selector, $root );
  				}
  				var ec = p.errorCss;
  				var jqec = "ui-state-error";
  
  				try {
  					if (value!=N) val = _field($we,_bind(d,value,oui,$we));
  				}
  				catch (e) {
  					err=p.messages.formError||"Error";
  				}
  
  				if (err=="") {
  					if (!isForm) xdata.errors[selector]= "";
  					else xdata.derrors[selector]= "";
  					$box.removeClass(ec);
  					if ($box.attr("title")) $box.attr("title","");
  					if (!isForm && !isList) p.effect($box.find(p.errorTip), false ,(p.animate/2));
  					$this.removeClass(jqec); $this.find(".ui-widget").removeClass(jqec);
  				} else {
  					if (isForm) {
  						xdata.derrors[selector]= err;
  					} else if (isList) {
  						xdata.errors[selector]= err;
  					} else {
  						$box.addClass(ec);
  						xdata.errors[selector]= err;
  						var $tip=$box.find(p.errorTip).eq(0);
  						if ($tip.size()){
  							p.effect($tip.addClass(ec).html(err), true, p.animate);
  						} else $box.attr("title",(err || "").stripTags());
  					}
  
  					if ($this.is(".hasDatepicker")) {
  						if ($this.is("input")) $this.addClass(jqec);
  						else $this.find(".ui-widget").addClass(jqec);
  					}
  					if ($this.is(".ui-slider")) $this.addClass(jqec);
  				}
  			}
  			//applying conditional formatting if any
  			var cssval = (value==N?val:value);
  			if (oui.css) {
  				for (css in oui.css) {
  					oc = oui.css[css];
  					if (isR(oc)) _css (oc.test(cssval), $we, css);
  					else if (isF(oc)) _css (oc.call($root.my().manifest, d,cssval,$we), $we, css);
  				}
  			}
  
  			//recalculating dependent fields
  			var i, list = oui.recalc, dest = [], once = {}, item;
  
  			if (depth && oui.recalc &&  $root.my()) {
  				ui = $root.my().ui;
  				for (i=0; i<list.length; i++) {
  					if (list[i] && isS(list[i]) && (item = list[i].compact()) && ui[item]) {
  						if (ui[item].recalc) {
  							if (dest.indexOf(item) === -1) dest.push(item);
  						} else once[item]=true;
  					}
  				}
  				for (i=0; i<dest.length; i++) once = $E(true, once, _update($root.find(dest[i]), N, depth-1));
  
  				if (value!==N) {
  					// here is a trick -- we may call _update ($o, undefined, 1)
  					// and force update if we want only retrieve without recalc
  					for (i in once) if (once[i]===true && i!=selector) {
  						if (ui[i].delay && !ui[i].recalc) ui[i]._update($root.find(i), N, depth-1);
  						else _update($root.find(i), N, depth-1);
  					}
  					return {};
  				}
  			}
  			return once||{};
  		}
  	}
  
  
  	//=======================================
  
  	function _history (x, params, remove, silent) {
  	// push or retrieves current state to history,
  
  		var p = params, h, i, k, l, n, step, time, old, newh;
  		if (
  			!isO (p) ||
  			isNaN (l=p.remember) ||
  			!isO (h=p.history)
  		) return N;
  
  		if (isO(x) && l) {
  			step = Object.clone(x, true);
  			time = Date.now();
  			k = Object.keys(h).sort();
  			if (k.length && (time-k.last() < p.historyDelay || Object.equal(h[k.last()], step))) return N;
  			p.history[time] = step;
  			k.push(time);
  			if (k.length >= l*2) {
  				newh = {};
  				for (i=l; i<l*2; i++) newh[k[i]] = h[k[i]];
  				params.history = newh;
  			}
  			if (!silent) p.form.trigger(Ch);
  			return p.history[k.last()];
  		}
  		else if (!isNaN(x) || x===N) {
  			n = parseInt(x) || 0;
  			if (n<0) return N;
  			k = Object.keys(h).sort();
  			if (n>=k.length) n = k.length-1;
  			old = Object.clone(p.history[k[k.length-n-1]], true);
  			if (remove) {
  				newh = {};
  				for (i=0; i<k.length-n-1; i++) newh[k[i]] = h[k[i]];
  				params.history = newh;
  			}
  			if (!silent) p.form.trigger(Ch);
  			return old;
  		}
  		else if (!silent) p.form.trigger(Ch);
  		return N;
  	}
  
  
  	//=======================================
  
  	function _build ($o, $root, uiNode, selector) {
  	//initializes control
  		var rd = $root.my(),
  			p = (rd || {}).params,
  			ui=uiNode,
  			pi = null,
  			tracker,
  			v, ctr=0,
  			subform;
  
  		if (!rd) {
  			f.con ("Failed to find $root building "+selector+" selector.");
  			return null;
  		}
  
  		if ($o.size()) {
  			//first exec init
  			// init if we have one
  			if (ui.init!=N) tracker = _prepare(rd.manifest, ui.init, $o, rd);
  
  			if (isP(tracker))  {
  				//we ve got  async init
  				ctr+=1;
  				pi = $.Deferred();
  				tracker.then(_subform, function (msg, obj){
  					_fail("Init of "+selector+" failed: "+msg,obj);
  				});
  			} else _subform();
  		} else f.con ("Not found "+selector+" selector.",$root);
  
  		return pi;
  
  
  		// - - - - - - - - - - - - - - - - - - - - - - -
  
  		function _subform (){
  			var child=null;
  			// if we have manifest, retrieve it
  			if (isF(ui.manifest) || (isO(ui.manifest) && isO(ui.manifest.ui))) subform = ui.manifest;
  			else if (isS(ui.manifest)) {
  				// static bind if manifest is string ref,
  				// not dynamic to speed up long list renders
  				subform=_manifest (rd.manifest, ui.manifest);
  			}
  
  			// ...and apply
  			if (subform && isS(ui.bind)) {
  				//decrypt bind link and check if we have one in .data
  				var linked = _getref(rd.data,ui.bind);
  				if (pi===null) pi = $.Deferred();
  				if (isA(linked) || ui.list){
  
  					// we have list
  					$o.addClass("my-form-list");
  
  					//generate system fields
  					var ltmpl="", lsel =">*";
  					if (/^<.+>$/.test(ui.list)) ltmpl=ui.list;
  					else lsel = ui.list||lsel;
  					if (!ltmpl) {
  						var $t0 = $o.find(lsel);
  						ltmpl='<div></div>';
  						if ($t0.size()) {
  							ltmpl = $(ltmpl).append($t0.eq(0).clone(true)).html();
  							$t0.eq(0).remove();
  						}
  					}
  
  					//mount data
  					if (!$o.data("formlist")) $o.data("formlist",{});
  					$o.data("formlist").generator={
  						manifest:subform,
  						delay:(ui.delay||p.delay||10)/1.3,
  						template:ltmpl,
  						selector:lsel,
  						parent:rd.manifest,
  						bind:ui.bind
  					};
  
  					//mount insert
  					$o.on("insert.my", function (evt, obj){
  						evt.stopPropagation();
  						var p = {what:undefined, where:0};
  						if (null==obj) p.where=1e6;
  						else if (isO(obj)) Object.merge(p,obj);
  						else if (isS(obj) || isN(obj)) p.where = obj;
  						$(evt.target).my("insert",p.where, p.what);
  					});
  
  					//mount remove
  					$o.on("remove.my", function (evt){
  						evt.stopPropagation();
  						$(evt.target).my("remove");
  					});
  
  				} else {
  					try {
  						child = $o.my(
  							_manifest (rd.manifest, subform),
  							isO(linked)?linked:undefined
  						);
  					}
  					catch (e) {_fail("$.my subform init of " +selector+" failed: "+e.message, e.stack);}
  				}
  			}
  			if (isP(child)) {
  				//we've got promised subform init
  				child.then(_countdown, function (msg, obj){
  					_fail("Init of subform "+selector+" failed with error: "+msg,obj);
  				});
  			} else _countdown();
  		}
  
  
  		// - - - - - - - - - - - - - - - - - - - - - - -
  
  		function _fail (msg, obj){
  			f.con(msg, obj);
  			if (pi) pi.reject(msg, obj);
  		}
  
  
  		// - - - - - - - - - - - - - - - - - - - - - - -
  
  		function _countdown () {
  			//start applying monitors to controls
  			//right before this moment all controls are irresponsive
  			$o.each(function () {
  				var $this = $(this), events, cm;
  
  				//codemirror fix
  				cm = ($this[0].nextSibling && $this[0].nextSibling.CodeMirror)?$this[0].nextSibling.CodeMirror:null;
  				if (cm) $this.addClass("my-codemirror");
  
  				//get events
  				events = ui.events||_traverse($this, MY.events);
  
  				if (!$this.is(".my-form")) {
  					$this.data("my",{
  						events:events,
  						selector:selector,
  						initial:v,
  						previous:v,
  						root:$root,
  						container:p.container($this),
  						id:rd.id,
  						ui:ui,
  						data:rd.data,
  						params:p,
  						errors:rd.errors
  					});
  					uiNode._update = ui.delay?_update.debounce(ui.delay):N;
  				} else {
  					$E($this.data("my"),{
  						dui:ui,
  						root:$root,
  						selector:selector,
  						dparams:p,
  						devents:events,
  						ddata:rd.data,
  						container:p.container($this),
  						derrors:rd.errors
  					});
  				}
  
  				//special cleditor fix
  				//thanks ima007@stackoverflow.com for concept
  				if ($this.cleditor && $this.parent().is(".cleditorMain")) {
  					var cledit = $this.cleditor()[0];
  					if (cledit && cledit.$frame && cledit.$frame[0]) {
  						//mark as cleditor
  						$this.addClass("my-cleditor");
  						$E($this.data("my"), {container:p.container($this)});
  						var cChange = function (v) {$this.val(v).trigger(Ch);};
  						var cleditFrame, r = Number.random(1e5,1e6-1);
  						//aux event handlers for cleditor instance
  						$(cledit.$frame[0]).attr("id","cleditCool"+r);
  						if (!document.frames) cleditFrame = $("#cleditCool"+r)[0].contentWindow.document;
  						else cleditFrame = document.frames["cleditCool"+r].document;
  						var $ibody = $(cleditFrame).find("body");
  						$(cleditFrame).bind('keyup.my', function () {cChange($(this).find("body").html());});
  						$this.parent()
  							.find("div.cleditorToolbar")
  							.bind("click.my mouseup.my",function () {cChange($ibody.html());});
  						$("body").on("click", "div.cleditorPopup", function () {cChange($ibody.html());});
  					}
  				}
  
  				//redactor fix
  				else if ($this.is("div.redactor_box textarea")) {
                      //$this.getEditor().bind("input keyup blur",(function ($o){$o.trigger("redactor");}).fill($this));
                      var editor, version = 'my-redactor-9';
                      try {
                          editor = $this.getEditor();
                          version = 'my-redactor-8';
                      } catch (e) {
                          editor = $this.redactor('getEditor');
                      }
                      if(editor) {
                          $this.addClass(version);
                          editor.bind("input.my keyup.my blur.my",(function ($o){$o.trigger("redactor");}).fill($this));
                      }
                  }
  
  				//ace fix
  				else if ($this.is(".ace_editor"))
  					ace.edit($o[0]).on(Ch,(function ($o){$o.trigger("ace");}).fill($this));
  
  				// codemirror fix
  				else if (cm) {
  					cm.on(Ch, (function ($o){$o.trigger("codemirror");}).fill($this));
  				}
  
  				//create debounced change handler
  				$this.my()._changed = (_changed).debounce(uiNode.delay || p.delay);
  				$this.my()._recalc = (_recalc).debounce(uiNode.delay || p.delay);
  
  				//bind events to the control
  				$this.bind(events, function (evt) {
  					if (evt.type==Ch) evt.stopPropagation();
  					$this.my()._changed($this, $root, uiNode, p);
  				});
  
  				//bind events to the control
  				$this.on("recalc.my, redraw.my", function (evt) {
  					evt.stopPropagation();
  					$this.my()._recalc($this,$root,uiNode,p);
  				});
  			});
  
  			// we've done
  			if (pi) pi.resolve();
  		} // end _countdown
  	}
  
  
  	//=======================================
  
  	function _changed ($o,$root,uiNode,p) {
  	// called when control is changed
  		var d = $o.my();
  		if (d && !d.disabled) {
  			_history(d.ddata||d.data, d.dparams||d.params);
  			var $we = $root.find(d.selector),
  				val0 = _field($we,N);
  			_update($o, val0, uiNode.recalcDepth||p.recalcDepth);
  			if (d.root.parent().is(".ui-sortable")) d.root.parent().trigger("check");
  			if (p.change) p.change.call($o);
  		}
  	}
  
  //=======================================
  
  	function _recalc ($o,$root,uiNode,p) {
  	// called when control is changed
  		var d = $o.my();
  		if (d && !d.disabled) {
  			var $we = $root.find(d.selector);
  			if (($we).is(".my-form")) $we.my("redraw");
  			else _update($o,
  				($we.is(".my-form-list")?_getref($we.my().data,$we.data("formlist").generator.bind):N),
  				uiNode.recalcDepth||p.recalcDepth);
  		}
  	}
  
  
  	//=======================================
  
  	function _prepare (that, init0, $o, d) {
  	// prepares init, applies data if its string template,
      // dereferences it if it is pointer,
  	// and calls function or formgen
          var init;
  		if (isS(init0)) {
              init = _getref(that, init0);
              if (undefined === init) {
                  $o.html(init0.assign(d.data));
                  return null;
              }
          } else init = init0;
  		if (isF(init)) return init.apply(that, Array.prototype.slice.call(arguments,2));
  		else if (isA(init)) {
  			try {$o.formgen(init);}
  			catch(e){}
  		}
  		return null;
  	}
  
  
  	//=======================================
  
  	function _unjson (node){
  	//recursively unwinds string def of funcs and regexps, modifies  source obj!
  		for (var i in node) if (node.hasOwnProperty(i)) {
  			var n = node[i], t = $.type(n);
  			if (/^(ob|ar)/.test(t)) _unjson(n);
  			else if (t==St && /(^function\s\(|^new\sRegExp)/.test(n)) {
  				try{
  					node[i]=eval('0||('+n+')');
  				}catch(e){console.log(e.message, e.stack,n);}
  			}
  			// Fucking IE, it took 8 hours to find this 0|| trick –
  			// it allows IE8 eval functions work as expected
  		}
  	}
  
  
  	//=======================================
  
  	function _normalize (ui, manifest0) {
  	//unwinds ui recalcs, short defs and watch hooks, modifies source obj!
  	//move shorthand binds to bind attr
  		var manifest = isO(manifest0)?manifest0:null;
  		Object.each(ui, function (i,v){
  			var t = typeof v;
  			if (/^str|^fu/.test(t)) ui[i] = {bind:v};
  		});
  		Object.each(ui, function (i,v){
  			//correct ui definitions
  			//with simplified syntax -- unfolding
  			//unfold recalcs and watches
  			var list=[], watch=[], row , re=/\s?[,;]\s?/, rr, j;
  			if (v.recalc) {
  				if (isS(v.recalc)) list = v.recalc.split(re);
  				else if (isA(v.recalc)) list = v.recalc;
  				list = list.compact(true).unique();
  			}
  			if (list.length) ui[i].recalc=list;
  
  			if (null==v.bind) v.bind=function () {};
  
  			if (v.watch) {
  				if (isS(v.watch)) watch = v.watch.split(re);
  				else if (isA(v.watch)) watch = v.watch.slice(0);
  				watch = watch.compact(true).unique();
  				for (j=0; j<watch.length; j++) if (row = ui[watch[j]]) {
  					rr= row.recalc;
  					if (!rr) row.recalc=[i];
  					else if (isS(rr)) row.recalc+=","+i;
  					else if (isA(rr) && rr.indexOf(i)==-1) row.recalc.push(i);
  				}
  			}
  
  			if (null!==manifest) ["css","check","manifest","list"].each(function (elt){
  				if (isS(v[elt])) {
  					var ref = _getref (manifest, v[elt]);
  					if (ref!=null) v[elt]=ref;
  				}
  			});
  		});
  		return ui;
  	}
  
  
  	//=======================================
  
  	function _style ($o, manifest) {
  	// converts .style section of manifest
  	// into two <style> sections for form.
  	// returns custom style section, global is mounted
  	// to dom if not presented
  		var  aglob=[], aloc=[], man=manifest;
  		if (!isO(man) || !isO(man.style)) return "";
  
  		crawl(manifest.style, "", aglob, aloc);
  		return [aglob, aloc];
  
  
  		function crawl (branch, key, aglob, aloc){
  			var i, j, b, a;
  			if (isS(branch)) aglob.push(key+(/\{/.test(branch)?branch:'{'+branch+'}'));
  			else if (isA(branch) && branch.length) for (i=0;i<branch.length; i++) crawl(branch[i], key, aglob, aloc);
  			else if (isO(branch)) {
  				for (i in branch) {
  					a = unfold(key, i);
  					for (j=0;j<a.length;j++) crawl(branch[i],a[j], aglob, aloc);
  				}
  			}
  			else if (isF(branch)) {
  				try {
  					b = branch.call(manifest, $o, manifest);
  					crawl (b, key, aloc, aloc);
  				} catch (e) {}
  			}
  		}
  
  		function unfold (key, selector) {
  			var pre="", ext=selector+"", a;
  			if (" "===ext.to(1) || /^[a-z]/i.test(ext)) pre=" ";
  			a = ext.split(/\s*,\s*/).compact(true);
  			if (!a.length) a.push("");
  			return a.map(function (e) {return key+pre+e;});
  		}
  	}
  
  
  
  //######### PUBLIC METHODS ##############
  
  	var methods = {
  
  		//######### INIT ##############
  
  		"init" : function ( A0,A1,A2 ) {
  			var data0, defaults,
  				myid, cid, mid, manifest, html,
  				d, ui, p, data, i,
  				style, manClass, formClass,
  				$style, $locstyle,
  				pi = $.Deferred(),
  				_fail=false,
  				tracker,
  				ehandler=function () {},
  				mode="std",
  				backup="";
  
  			if (isS(A0)) {
  				data0=_cache(A0);
  				if (data0) {
  					if (isO(A2) && isO(A1)) {
  						data0=$E(data0,A1);
  						defaults=A2;
  					}
  					else defaults = A1;
  					mode="repo";
  				} else {
  					pi.reject("No manifest with id "+A0+" found in repo.");
  					return pi.promise();
  				}
  			} else {
  				data0=A0;
  				defaults = A1;
  			}
  
  			if (!data0) return this;
  
  			if (isO(defaults) && mode!="repo") data = $E(true,{},data0);
  			else data = data0;
  
  			var $root = this.eq(0), rd=$root.my();
  
  			if (isO(rd) && rd.id && rd.ui) {
  				f.con ("jQuery.my is already bind.",$root);
  				$root.my("ui",data.ui);
  				$root.my(Da,data.data);
  				return pi.resolve($root.my(Da)).promise();
  			}
  
  			//here we must unwind stringified fn and regexps defs
  			//if we are not in strict mode
  			if (!data.params || (data.params && !data.params.strict)) _unjson(data);
  			manifest = $E(true,{},data);
  
  			//mount params
  			p = $E(true, {}, MY.params, data.params||{});
  
  			ui = _normalize($E(true,{}, data.ui||{}), manifest);
  			cid = Number.random(268435456,4294967295).toString(16);
  			myid =  data.id || ("my"+cid);
              mid = f.sdbmCode(myid);
  			d={};
  
  			p.form=$root;
  
  			if (data.params && data.params.depth) p.recalcDepth=data.params.depth;
  
  			//bind this to 1st level manifest functions
  			for (i in manifest) if (isF(manifest[i])) manifest[i].bind(manifest);
  
  			//mount data
  			if (isO(defaults)) {
  				d = f.patch(defaults,data.data||{});
  				data.data=d;
  			} else d = data.data || {};
  
  			//mount error handler
  			if (data.error) {
  				if (isS(data.error)) {
  					ehandler = function () {
  						return data.error.assign(d);
  					};
  				} else if (isF(data.error)) {
  					ehandler = function (err, stack) {
  						html=null;
  						try {html = data.error(err,stack);}
  						catch (e) {	html = p.messages.initFailed;}
  						return html;
  					};
  				}
  			}
  
  
  			//extend root with promise methods
  			$.extend($root, pi.promise());
  
  			//mount params to form DOM node
  			$root.data("my", {
  				id: myid,
  				cid: cid,
                  mid: mid,
  				data:d,
  				params:p,
  				errors:Object.extended(),
  				ui:Object.extended(ui),
  				disabled:false,
  				manifest:manifest,
  				promise:pi.promise()
  			});
  			manifest.id = myid;
  
  			// mount classes and styles if any
  			$root.addClass("my-form");
  
  			manClass = "my-manifest-"+mid;
  			formClass = "my-form-"+cid;
  
  			$root.addClass(formClass+" "+manClass);
  
  			//fail init finalizer
  			pi.fail(function () {
  				$root.removeClass(formClass+" "+manClass);
  			});
  
  
  			if (manifest.style) {
  				style=_style ($root, manifest, manClass, formClass);
  				if (style && style[0].length) {
  					$style = $('style#'+manClass);
  					if ( !$style.size() ) {
  						$style = $('<style id="'+manClass+'" data-count="0">' +
  							"."+manClass+style[0].join(' \n.'+manClass)+'\n' +
  						'</style>').appendTo($("body"));
  					}
  
  					$style.data("count", $style.data ("count")*1+1);
  					$root.data("my").style = $style;
  				}
  
  				if (style && style[1].length) {
  					$locstyle = $('style#'+formClass);
  					if ( !$locstyle.size() ) {
  						$locstyle = $('<style id="'+formClass+'" data-count="0">' +
  							"."+formClass+style[1].join(' \n.'+formClass)+'\n' +
  						'</style>').appendTo($("body"));
  					}
  
  					$root.data("my").localStyle = $locstyle;
  
  				}
  			}
  
  
  			//initialize
  			if (data.init!=N) {
  				backup = $root.find(">*").clone();
  				try {
  					tracker = _prepare(manifest, data.init, $root, data);
  				} catch (e) {
  					_f(isS(e)?e:e.message, e.stack);
  					return $root;
  				}
  			}
  
  			// init returned promise?
  			if (isP(tracker)) {
  				$root.addClass(p.pendingCss);
  				tracker.then(function () {_controls();}, function (err,obj){_f(err, obj);});
  			} else _controls();
  
  			if (!_fail) {
  				if (!$root.my()) return _f("Internal error initializing controls",""), $root;
  
  				//save initial data for $.my("reset")
  				$root.data("my").initial = $E(true,{},d);
  
  				//init $.mobile if any
  				if ($.mobile) $.mobile.changePage($.mobile.activePage);
  			}
  
  			return $root;
  
  			//-----------------------------------------------------
  
  			//build and init controls
  			function _controls (){
  				var formState={}, ctr=Object.size(ui);
  
  				$root.addClass(p.pendingCss);
  
  				// build controls (init and premount)
  				Object.each(ui, function (selector) {
  					if (_fail) return;
  					var $o = $root.find(selector),
  						built = _build($o, $root, ui[selector], selector);
  
  					if (isP(built)) {
  						//we've got promise
  						built.then(
  							_countdown.fill(selector)
  						).fail(function (msg, obj){
  							_f("Error building "+selector+", "+msg, obj);
  						});
  					}
  					else if (!_fail) _countdown(selector);
  				});
  
  				function _countdown(selector){
  					if (!_fail) {
  						formState[selector]=_field($root.find(selector),N);
  						ctr-=1; if (ctr<.5) _values(formState);
  					}
  				}
  
  			}
  
  			//-----------------------------------------------------
  
  			//apply values to controls
  			function _values (formState) {
  				for (var selector in ui) {
  					if (_fail) return;
  					var  $o = $root.find(selector);
  					if ($o.size()) {
  						try {
  							var uiNode = ui[selector];
  							var v = _bind(d, N, uiNode, $o);
  							if (v!=N) _field($o,v);
  							else {
  								try {if (formState[selector]!=N && v!==undefined)
  									_bind(d, formState[selector], uiNode, $o);
  								} catch(e){}
  							}
  							$o.eq(0).trigger("check.my");
  						} catch (e) {
  							_f("Error linking "+selector, e.stack);
  						}
  					}
  				}
  				$root.removeClass(p.pendingCss);
  				backup=null;
  				pi.resolve(d);
  			}
  
  			//-----------------------------------------------------
  
  			// Fail handler
  			function _f (msg, obj) {
  				var html;
  				_fail=true;
  				f.con("Form "+myid+" failed to initialize: "+msg, obj);
  				$root.removeClass(p.pendingCss);
  				html = ehandler(msg, obj);
  				if (isS(html) || (isO(html) && html.jquery)) $root.html(html);
  				else if (html===true) $root.html(backup);
  				if (!p.silent) {
  					if(!$root.my().ddata) {
  						$root.removeData("my");
  						$root.removeClass("my-form");
  						if ($style) {
  							if ($style.data("count")=="1") {
  								try{$style.remove();}catch(e){}
  							}
  							else $style.data("count", $style.data("count")-1);
  						}
  						if ($locstyle) {
  							try{$locstyle.remove();}catch(e){}
  						}
  					}
  					pi.reject("Form "+myid+" failed to initialize: "+msg, obj);
  				} else pi.resolve(d);
  			}
  		}, //end init
  
  
  
  		//###### REDRAW ######
  
  		"redraw": function ( noRecalc, silent) {
  			var $x = this, d = $x.my();
  			if (d && d.ui) {
  				d.ui.each(function (key) {
  					var $n = $x.find(key);
  					_update($n, noRecalc?N:undefined , d.params.recalcDepth);
  					if (!noRecalc) {
  						if ($n.is(".my-form")) $n.my("redraw");
  						if ($n.is(".my-form-list")) $n.trigger("redraw");
  						else $n.trigger("check.my");
  					}
  				});
  				if (!silent && noRecalc) $x.trigger(Ch);
  			}
  			return $x;
  		},
  
  		//###### SET OR RETRIEVE DATA ######
  
  		"data": function (data, noRecalc) {
  			var $x = this;
  			if (isO(data)) {
  				$x.my().data = f.overlap($x.my().data, data);
  				this.my("redraw", noRecalc);
  			}
  			return $x.my().data;
  		},
  
  		//###### RETURNS ERRORS ######
  
  		"errors": function () {
  			var e0 = $(this).my().errors, e = {};
  			for (var i in e0) {
  				if (e0[i] && isS(e0[i])) e[i]=e0[i];
  				if (isO(e0[i]) && Object.keys(e0[i]).length) e[i]=e0[i];
  			}
  			return e;
  		},
  
  		//###### RETURNS true IF FORM VALID ######
  
  		"valid": function () {
  			var e = $(this).my().errors, ctr=0;
  			for (var i in e) {
  				if (e[i] && isS(e[i])) ctr++;
  				else if (isO(e[i]) && Object.keys(e[i]).length) ctr++;
  			}
  			return !ctr;
  		},
  
  		//###### RESET INITIAL VALUES ######
  
  		"reset": function () {
  			try {
  				f.kickoff(this.my().data, this.my().initial);
  				this.my("redraw");
  			} catch (e) {return false;}
  			return true;
  		},
  
  		//###### GET id OR SEARCH BY id ######
  
  		"id": function (id, obj) {
  			if (isS(id)) return _cache(id, obj);
  			else {
  				var d = this.my();
  				return (d && d.id)?d.id:N;
  			}
  		},
  
  		//###### UNMOUNT jQuery.my INSTANCE FROM THE DOM ######
  
  		"remove": function (fromDOM){
  			var $o = this,
  				$style,
  				$locstyle, m,
  				d, ui, cid, mid;
  
              if (!this.my()) return N;
  
  			//child elt requests form removal
  			if (this.my().root && !this.my().ddata) $o = this.my().root;
  
              m =  $o.my();
  			d =  m.data;
              cid = m.cid;
              mid = m.mid;
  
  
  			// stop event listeners
  			$o.unbind(".my");
  
  			// remove stylesheets
  			if ($style=m.style) {
  				if ($style.data("count")=="1") {
  					try{$style.remove();}catch(e){}
  				}
  				else $style.data("count", $style.data("count")-1);
  			}
  			if ($locstyle=m.localStyle) {
  				try{$locstyle.remove();}catch(e){}
  			}
  
  			// remove $.my from ui entries
  			ui = (m||{}).ui;
  			if (ui) {
  				ui.each(function (key){
  					var $we = $o.find(key), f;
  					$we.unbind(".my");
  					try{
  						f = _traverse($we, MY.destroy);
  						if (isF(f)) f($we);
  					}catch(e){}
  					$we .removeData("formlist")
  						.removeData("myval")
  						.removeData("my");
  				});
  			}
  
  			if (($o.data("formlist") && $o.is(".my-form"))|| fromDOM) {
  				var $p = $o.parents(".my-form-list").eq(0);
  				$o.remove();
  				$p.trigger("check");
  			}
  			else {
  				$o.removeData("formlist")
  				.removeData("myval")
  				.removeData("my")
  				.removeClass("my-form");
  			}
  
              $o.removeClass("my-form-"+cid+" my-manifest-"+mid);
  
  			return d;//returns data collected by removed instance
  		},
  
  		//###### UNDO ######
  
  		"undo": function (steps){
  			var $this = this,
  				d = $this.my(),
  				h = d.params.history,
  				k = Object.keys(h).sort(),
  				diff = 1*(parseInt(steps)||0),
  				state;
  			if (!k.length || diff<0) return N;
  			if (!d.params.errors || !d.params.errors.values().compact(true).length) {
  				if ( Object.equal(h[k.last()], d.data)) diff+=1;
  			} else if (!Object.equal(d.data, d.lastCorrect)) diff+=1;
  
  			state  = _history(diff, d.params, true);
  
  			if (state) {
  				f.kickoff($this.my().data, state);
  				$this.my("redraw");
  			}
  			return $this.my().data;
  		},
  
  		//###### UI RUNTIME GETTER-SETTER ######
  
  		"ui": function (u) {
  			var $x = this, d = $x.my(), a=[], i;
  			if (!d) return N;
  			var ui = $E(true, {}, d.ui);
  			if (!isO(u)) return d.ui;
  			for (i in u) if (true || !ui[i]) a.push(i); //controls to (re)init
  			d.ui = _normalize(f.overlap(d.ui,u));
  			for (i=0;i<a.length;i++) _build($x.find(a[i]), $x, d.ui[a[i]], a[i]);
  			for (i in u) $x.find(i).eq(0).trigger("check");
  			return d.ui;
  		},
  
  		//###### ENABLE-DISABLE FORM ######
  
  		"disabled": function (bool) {
  			var $d, i, dn,onOff,
  				$x = this,
  				d = $x.my();
  			if (!d) return undefined;
  			if (bool==N) return d.disabled;
  			if (!!bool) {
  				//disable all controls
  				for (i in d.ui) {
  					$d = $x.find(i).eq(0);
  					dn = $d.my();
  					if (dn) dn.predisabled = dn.disabled;
  					_css(true, $d, ":disabled");
  				}
  				$x.addClass("my-disabled");
  			} else {
  				for (i in d.ui) {
  					$d = $x.find(i).eq(0);
  					dn = $d.my();
  					onOff = false;
  					if (dn && dn.predisabled) onOff=true;
  					_css(onOff, $d, ":disabled");
  				}
  				$x.removeClass("my-disabled");
  				$x.my("redraw");
  			}
  		},
  
  		//###### FIND FORM NODE ######
  
  		"find": function _findUiNode(sel) {
  			var $x = this, d = $x.my();
  			if (d && d.root) $x=d.root;
  			return $x.find(sel);
  		},
  
  		//###### SYSTEM ######
  
  		"manifest": function (format) {return format=="json"?f.tojson(this.my().manifest):this.my().manifest;},
  		"version": function () {return _version;},
  		"history": function (a,c) {return _history(a, this.my().params, c);},
  		"val": function (v) {return _field(this, v);},
  		"container": function ($o) {return _traverse($o, MY.containers)($o);},
  		"promise": function (fn) {if (isF(fn)) this.my().promise.then(fn); return this.my().promise;},
  
  		//###### LIST RELATED ######
  		"index": function () {
  			var o = (this.my().root && !this.my().ddata)?this.my().root:this;
  			return (o.data("formlist")||{}).index;
  		},
  		"insert": function (where, what) {
  			var src = this.is(".my-form-list")?this:(this.my().root||this),
  				o = src.is(".my-form-list")?src:src.parent(".my-form-list"),
  				pos = (src.data("formlist")||{}).index,
  				list,
  				obj = what,
  				idx;
  			if (null==pos) pos=0;
  			list = _getref(o.my().data, o.data("formlist").generator.bind);
  			if (!isO(obj)) {
  				if (!isO(o.data("formlist").generator.manifest)) {
  					if (o===src) throw "No data to insert, cannot guess when manifest is function.";
  					obj = Object.clone(src.my().manifest.data,true);
  				}
  				else obj = Object.clone(o.data("formlist").generator.manifest.data,true)||{};
  			}
  
  
  			if ("before"===where) idx=pos;
  			else if("after"===where) idx=pos+1;
  			else if (!isNaN(where)) {
  				idx=(1*where).clamp(0, list.length);
  			} else throw "Invalid position for insert";
  			list.add(obj, idx);
  			o.trigger("redraw");
  		}
  	};
  
  	if (!$.my) $.my={};
  
  	$.extend($.my,{
  		f:f,
  		tojson:f.tojson,
  		fromjson:f.fromjson,
  		rules:MY,
  		cache:function (A1, A2) {
  			if (isF(A1)) return _cache = A1;
  			else return _cache(A1, A2);
  		},
  		version: function () {return _version;}
  	});
  
      // Chain runner
  
      $.my.chain = (function(){
          var delay = 1, timeout = 1000,
              chain = [],
              state = false,
              put = function (def, d, t) {
                  chain.push([def, d || delay, t || timeout]);
                  next();
              };
          put.delay = function (d) {
              if (!isNaN(d)) delay = (d-0).clamp(0,1e6);
              return delay;
          };
          put.timeout = function (d) {
              if (!isNaN(d)) timeout = (d-0).clamp(1,1e6);
              return timeout;
          };
          put.start = function () { state = true; next(); };
          put.stop = function () { state = false; };
  
          return put;
  
          function next (){
              if (chain.length && state) {
                  var f = chain.shift(),
                      res,
                      go = function () {  next.delay(f[1]);  }.once();
                  try {  res = f[0](); }
                  catch (e) {  go(); }
                  if (isP(res)) {
                      res.then(go, go);
                      go.delay(f[1]);
                  } else go();
              }
          }
      })();
  
  
  	// Mount everything over jQuery
  
  	$.fn.my = function (method) {
  		var form;
  		if (method===undefined) return this.data("my");
  		if (isS(method) && method.substr(0,1)=="{" ) {
  			try{form = JSON.parse(method);}catch(e){}
  			if (form) return methods.init.apply(this,[form].add(Array.prototype.slice.call(arguments, 1)));
  		}
  		if (isS(method) && methods[method]) return methods[method].apply( this, Array.prototype.slice.call(arguments, 1));
  		else if (isS(method) && _cache(method,"exist")) return methods.init.apply(this, arguments);
  		else if (typeof method === Ob || !method ) return methods.init.apply(this, arguments);
  		else $.error('Method ' + method + ' does not exist on jQuery.my');
  	};
  
  	return;
  
  
  
  	//### Helpers builder
  
  	function _getHelpersLib() {
  	return {
  		"con": function () {try {console.log (arguments);} catch (e) {}},
  		"clone": function (o) {return o.clone?o.clone():o;},
  		"extval": function ($x) {
  			var d = $x.my(); if (d&&d.data) return d.data;
  			return $x.data("value")||$x.val()||$x.text()||$x.html()||"";
  		},
  		"jquix": function ($o, plugin, offon) {return $o[plugin](offon?"disable":"enable");},
  		"overlap": function (o1, o2) {
  			if (!arguments.length) return {};
  			if (arguments.length === 1) return arguments[0];
  			for (var i=1; i<arguments.length;i++) Object.merge(arguments[0], arguments[i], false, _cmp);
  			return arguments[0];
  
  			function _cmp (key, a, b) {
  				if (b===undefined || b===null) return a;
  				if (!isO(b)) return b;
  				else return Object.merge(a,b,false);
  			}
  		},
  		"patch": function patcher (a,b) {
  			//applies b over a in deep, if a already has non-object node it stays untouched
  			//if no, b properties are cloned
  			// function merger ({y:{w:2,a:[1,2]}}, {x:1, y:{w:5,z:3,a:[3,4,5]}}) >>{x:1,y:{w:2,a:[1,2],z:3}}.
  			//return mutated a
  			for (var i in b) {
  				if (b.hasOwnProperty(i)) {
  					if (isO(b[i])) {
  						if (!a.hasOwnProperty(i))  a[i]=Object.clone(b[i],true);
  						else patcher (a[i],b[i]);
  					} else if (!a.hasOwnProperty(i)) {
  						if (isA(b[i])) a[i]=b[i].clone(true);
  						else a[i]=b[i];
  					}
  				}
  			}
  			return a;
  		},
  		"kickoff": function (a,b) {
  			//replaces a content with b content;
  			var def = b && typeof b == "object" ? Object.clone(b, true) : {}, i;
  			for (i in a) if (a.hasOwnProperty(i)) {
  				delete a[i];
  				if (def[i] !== undefined) a[i] = def[i];
  			}
  		},
  		"sdbmCode":function (s0){
  			//very fast hash used in Berkeley DB
  		    for (var s = JSON.stringify(s0), hash=0,i=0;i<s.length;i++) hash=s.charCodeAt(i)+(hash<<6)+(hash<<16)-hash;
  		    return (1e11+hash).toString(36);
  		},
  		"tojson":(function () {
  			function f(n){return n<10?'0'+n:n;}
  			Date.prototype.toJSON=function () {
  				var t=this;return t.getUTCFullYear()+'-'+f(t.getUTCMonth()+1)+'-'+f(t.getUTCDate())+
  				'T'+f(t.getUTCHours())+':'+f(t.getUTCMinutes())+':'+f(t.getUTCSeconds())+'Z';
  			};
  			RegExp.prototype.toJSON = function () {return "new RegExp("+this.toString()+")";};
  			var tabs= '\t'.repeat(10), fj = JSON.stringify;
  
  			// - - - - - - - - - - - - - - - - - - - - - - -
  			function s2 (w, ctab0, tab){
  				var tl=0,a,i,k,v,ctab=ctab0||0,xt = tabs;
  				if (tab && isS(tab)) {tl=String(tab).length;xt = String(tab).repeat(10);}
  				switch((typeof w).substr(0,3)){
  					case 'str': return fj(w);case'num':return isFinite(w)?''+String(w)+'':'null';
  					case 'boo': case'nul':return String(w);
  					case 'fun': return fj(w.toString().replace(/^(function)([^\(]*)(\(.*)/,"$1 $3").replace(/(})([^}]*$)/,'$1'));
  					case 'obj': if(!w) return'null';
  					if (typeof w.toJSON===Fu) return s2(w.toJSON(),ctab+(tab?1:0),tab);
  					a=[];
  					if (isA(w)){
  						for(i=0; i<w.length; i+=1){a.push(s2(w[i],ctab+(tab?1:0),tab)||'null');}
  						return'['+a.join(','+(tab?"\n"+xt.to(ctab*tl+tl):""))+']';
  					}
  					for (k in w) if (isS(k)) {
  						v=s2(w[k],ctab+(tab?1:0),tab);
  						if(v) a.push((tab?"\n"+xt.to(ctab*tl+tl):"")+s2(k,ctab+(tab?1:0),tab)+': '+v);
  					}
  					return '{'+a.join(',')+(tab?"\n"+xt.to(ctab*tl):"")+'}';
  				}
  			}
  
  			return s2.fill(undefined,0,undefined);
  
  		})(),
  		"fromjson": function (s) {var obj = JSON.parse(s); _unjson(obj);return obj;},
  		"mask":function (src, mask0) {
  			//returns src obj masked with mask
  			if (!isO(src)) return null;
  			var res, mask=mask0;
  			if (isS(mask)) {
  				return _getref(src, mask);
  			} else if (isA(mask)) {
  				res = [];
  				for (var i=0;i<mask.length;i++) {
  					res[i]=isS(mask[i])?_getref(src, mask[i])||null:null;
  				}
  				return res;
  			} else if (isO(mask))
  				return _merge(src, mask);
  			//- - - -
  			function _merge(src, mask) {
  				if (!isO(mask)) return {};
  				var dest = {};
  				for (var i in mask) {
  					if (!isO(mask[i]) && src.hasOwnProperty(i)) {
  						dest[i]=Object.clone(src[i],true);
  					}
  					else if (src.hasOwnProperty(i)) {
  						if (isO(src[i])) dest[i]=_merge(src[i],mask[i]);
  						else dest[i] = Object.clone(src[i],true);
  					}
  				}
  				return dest;
  			}
  		},
  		"unmask": function (src, mask) {
  			// unfolds masked into obj
  			var res={};
  			if (isO(src) && isO(mask)) return f.mask(src,mask);
  			else if (isA(src) && isA(mask)) {
  				for (var i=0;i<mask.length;i++) {
  					if (src[i]!=null) _blow(res, src[i], mask[i]);
  				}
  				return res;
  			} else if (isS(mask)) return _blow({}, src, mask);
  			else return null;
  
  			//- - -
  			function _blow(data, src, ref) {
  				var ptr, path, preptr, val=Object.clone(src,true), i=0;
  				if (!/\./.test(ref)) {
  					//ref is flat
  					if (null!=src) data[ref] = val;
  				} else {
  					path = ref.split(".").each(function (a,i){this[i]=String(a).compact();});
  					ptr = data;
  					for (;i<path.length;i++) {
  						if (i===path.length-1) ptr[path[i]] = val; //we'r in the hole
  						if (i===0) ptr = data[path[0]], preptr= data;
  						else preptr = preptr[path[i-1]], ptr = ptr[path[i]];
  						if (undefined===ptr) ptr = preptr[path[i]] = {};
  					}
  				}
  				return data;
  			}
  		},
  		"getref":_getref,
  		"unjson":function (obj) {
  			_unjson(obj);
  			return obj;
  		}
  	};
  	} //### end helpers
  
  
  })(jQuery); // end $.my
  
  
  
  //#############################################################################################
  
  /* jQuery.formgen 0.3
   * Generates forms markup for $.my from lean-syntax DSL.
   * Returns html string.
   *
   * $(somediv).formgen("[
   * 		// Redefines params for subsequent rows, can be partial
   * 		{ row:"400px", label:"100px", rowCss:"rowClass", labelCss:"", labelTag:""},
   *
   * 		// First row
   * 		["Text4label", "inp#Name.person-name",{placeholder:"Your name"}],
   *
   * 		// Some free HTML
   * 		'<div class="shim"><div>',
   *
   * 		// Row with several controls and HTML, no label
   * 		["", "num#age",{style:"width:50px"}, "<i>years</i> ", "num#year", {style:"width:100px"}, " born"],
   *
   * 		// Select with opts, understands many formats
   * 		["Choose one", "sel#mychoice",
   * 			{vals:[
   * 				"Fish",
   * 				"Meat",
   * 				{id:"Poultry", text:"Chicken"},
   * 				{"Ice Tea":"Tea1"}
   * 		]}]
   *
   *		//and so on. Shortcuts for controls are below in the code.
   * ]")
   *
   * */
  
  (function ($){
  	//Some shortcuts and constants
  	var $E = $.extend, n = function (o) {return o!==null && o!==undefined;},  N = null,
  		Ob="object", Da="data", Ar = "array", St = "string", Fu="function", Ch = "change",
  		isA = Object.isArray, isB = Object.isBoolean, isS = Object.isString, isO = Object.isObject,
  		isN = Object.isNumber, isR = Object.isRegExp, isF = Object.isFunction;
  	var iHead = '<input type="',
  		iTail = ' {ext} ';
  	var f = {
  		tmpl:{
  			"num"	:iHead+'number" {ext}/>',
  			"inp"	:iHead+'text" {ext}/>',
  			"sli"	:iHead+'range" {ext}/>',
  			"dat"	:iHead+'date" {ext}/>',
  			"btn"	:iHead+'button" {ext}/>',
  			"div"	:'<div {ext}>{txt}</div>',
  			"spn"	:'<span {ext}>{txt}</span>',
  			"sel"	:'<select {ext}>{txt}</select>',
  			"mul"	:'<select {ext} multiple="multiple">{txt}</select>',
  			"txt"	:'<textarea {ext}>{txt}</textarea>',
  			"err"	:' <span class="my-error-tip {class}" style="{style}">{txt}</span>',
  			"msg"	:'<div class="my-error-tip {class}" style="{style}">{txt}</div>',
  			"val"	:function (p) {
  				if (!isA(p.vals)) return "";
  				var p0=$E({style:"",css:""},p);
  				p0.txt=p.vals.reduce(function (a,b){
  					return a+'<span class="my-shortcut" '
  							+'onclick="$(this).parents(\'.my-row\').find(\'input,textarea\').val($(this).text()).trigger(\'blur\')">'
  							+b+'</span> ';
  				}," ");
  				return ('<span class="my-shortcuts {css}" style="{style}">{txt}</span>').assign(p0);
  			},
  			""	:'<{_tag} {ext}>{txt}</{_tag}>'
  		},
  		txt:{
  			sel:function (p) {
  				if (!p.vals) return "";
  				var obj = decrypt(p.vals);
  				return Object.keys(obj).reduce(function (a,b){
  					return a+'<option value="'+b+'">'+obj[b]+'</option>';
  				},'');
  			}
  		},
  		params:{
  			styles:{num:"width:30%;", dat:"width:30%;", inp:"width:100%",
  					txt:"width:100%;max-width:100%;min-height:1px;word-break:break-word;",
  					err:"display:none",msg:"display:none"},
  			alias: {number:"num",date:"dat",slider:"sli",textarea:"txt",input:"inp",
  					span:"spn",select:"select",vals:"val"},
  			row:"",
  			rowTag:"div",
  			rowCss:"my-row",
  			label:"",
  			labelTag:"span",
  			labelCss:"my-label"
  		},
  
  		defaults:{id:"","class":"",style:"",placeholder:"",value:"",rows:1},
  		attnames:{css:"class",plc:"placeholder",val:"value",txt:"",vals:"",tip:"title"}
  	};
  
  
  	function chain(a,b,sys) {
  		if (isS(b)) return a+b;
  		if (isO(b)) {
  			sys = $E(true,sys, b);
  			return a;
  		} else if (isA(b) && b.length>1 && isS(b[1])) {
  
  			var lbl = b[0],html="",key,type,a0,b0,i=1,j,p,tmpl,ext;
  
  			//iterate through row's inside items
  			while (i<b.length) {
  				if (isS(b[i])) {
  					b0 = b[i].replace(/\s/g,"");
  					a0 = b0.split(/[\.#]/i);
  					type=sys.alias[a0[0]]||a0[0];
  					key = b0.substr(a0[0].length);
  					if (/^[a-z0-9]+(#[a-z0-9\-_]+)?(\.[a-z0-9\-_]+)*$/i.test(b0)) {
  						tmpl = f.tmpl[type] || f.tmpl[""];
  						p={style:"","class":"",txt:""};ext="";
  
  						//mount params over p
  						var isExt = isO(b[i+1]);
  						if (isExt) {
  							i+=1;
  							for (j in b[i]) if (f.attnames[j]!=="") p[f.attnames[j]||j]=b[i][j];
  						}
  						//apply default styles-classes
  						if (!p.style && !p["class"] && sys.styles[type]) p.style=sys.styles[type];
  						if (!p.id && key.to(1)=="#") p.id=key.from(1).split(".")[0];
  						if (!p["class"] && /\./.test(key)) p["class"]=(key.to(1)=="#"?key.substr(p.id.length+1):key).split(".").compact(true).join(" ");
  
  						//combine attributes and others
  						for (j in p) ext+=j+'="'+p[j]+'" ';
  						if (isExt)	for (j in b[i]) if (f.attnames[j]==="") p[j]=b[i][j];
  						p.ext=ext;
  
  						//try to gen text if no
  						if (!p.txt && f.txt[type]) p.txt=f.txt[type](p);
  
  						//attach _tag
  						p._tag=type;
  
  						//execute template
  						html+=typeof tmpl == Fu?tmpl(p)||"":typeof tmpl == St?tmpl.assign(p):"";
  
  					} else html+=b[i];
  				}
  				i+=1;
  			}
  			//somth is generated, make row
  			if (html) {
  				html =
  					'<'+sys.rowTag+' class="'+sys.rowCss+'" '
  					+(sys.row?'style="width:'+sys.row+'; ':"")
  					+(sys.label && lbl?'padding-left:'+sys.label+'; ':"")
  					+'">'
  					+(lbl?(
  						'<'+sys.labelTag+' class="'+sys.labelCss+'" '
  						+(sys.label?'style="display:inline-block;width:'+sys.label+';margin-left:-'+sys.label+'" ':"")
  						+'>'+lbl+'</'+sys.labelTag+'>'
  					):"")
  					+html+'</'+sys.rowTag+'>';
  			}
  			return a+html;
  		}
  		return a;
  	}
  
  	function decrypt (elt0) {
  	//translates different forms like [val, val val]
  	//{id:"",text:""} {key:"",value:""} and so on
  	// into object {key1:val1, key2:val2, ...}
  		var elt = elt0;
  		if (isS(elt)) {
  			elt = elt.split(/[\s,]/).compact(true);
  		}
  		if (isA(elt)) {
  			var obj={};
  			for (var i=0;i<elt.length;i++) {
  				var e = elt[i];
  				if (isO(e)) {
  					var keys=Object.keys(e);
  					if (keys.length==1) obj[keys[0]]=e[keys[0]]+"";
  					else obj[e.id||e.key||e.name||""]=(e.text||e.value||e.title||"");
  
  				} else obj[e]=e+"";
  			}
  			elt=obj;
  		}
  		if (isO(elt)) return elt;
  		else return {};
  	}
  
  	function formgen (form, params){
  		//find params in form if any
  		var sys={};
  		if (isA(form)) {
  			$E(true,sys,f.params, params||{});
  			return form.length?form.reduce(chain.fill(undefined,undefined,sys),''):"";
  		} else if (isO(form)) {
  			$.extend(f, form)
  		}else return "";
  	}
  
  	//return formgen;
  	var methods={
  		init: function (form, params) {
  			return $(this).html(formgen(form, params));
  		}
  	};
  
  
  	if (!$.my) $.my={};
  	$.my.formgen = formgen;
  	$.fn.formgen = function (method) {
  		if (isS(method) && methods[method]) return methods[method].apply( this, Array.prototype.slice.call(arguments, 1));
  		else if (typeof method === 'object' || !method ) return methods.init.apply(this,arguments);
  		else $.error('Method '+ method+' does not exist on jQuery.formgen');
  	};
  
  })(jQuery);
  
  
  
  
  //#############################################################################################
  
  /* jQuery.my.modal 0.5
   * Requires Sugar 1.4.1+, jQuery 1.11+, $.my 0.9.7+
   *
   * Modal dialog constructor/manager. Singleton, allows only one instance of popup.
   * Returns promise, which is resolved on dialog normal close or rejected if modal fails to init.
   * After content is succesfully initialized promise progress receives notification "Ready".
   *
   * $.my.modal (Obj, done, width) >> null or
   * 									promise [resolve(formData or true), reject (errMessage, errStack)]
   *
   * Obj is one of following types:
   * 		1. jQuery image – will raise modal with the image and text from title or data-text attributes
   * 		2. HTML string – will raise modal with html content
   * 		3. Object of type
   * 			{
   * 				manifest: formManifest Object,
   * 				data: initialData Object or none,
   * 				width: formWidth Number or none,
   * 				done: callback Function (formErrors, data) or none,
   * 				esc: false, enables/disables close on escape keypress
   * 			}
   * 			will raise modal with $.my form inside. Form must call $.my.modal(false) or emit
   * 			"commit" event on itself to close with sendind data. Calling $.my.modal(true) or
   * 			emitting "cancel" event on form will close modal sending null as data with no error.
   *
   * 			Callback in obj overlaps done provided as second arg, same for width.
   *
   * 			Callback is called prior promise and unlike promise receives 2 arguments,
   * 			not one, even when form succeded. If callback returns true, dialog remains
   *			opened and promise – pending.
   *
   * 		4. null, undefined or false – close dialog and calls done(formErrors, data),
   * 		   if done return false or null promise is resoved with data,
   * 		   else modal stays open
   * 		5. true (strict boolean) – close dialog and calls done (null, null),
   * 		   then promise is rejected with "Cancelled" string
   *
   * 		If modal is already open invoking $.my.modal return promise that is immidiately
   * 		rejected with error "Locked", done is called with (null, null).
   *
   * $.my.modal.visible() >> Boolean
   * 		Indicates if modal is opened.
   *
   * $.my.modal.parent (selector or null) >> jQuery object
   * 		Sets or gets parent DOM node, where all $.my.modal stuff is prepended.
   * 		To work good must be called prior to 1st $.my.modal call.
  */
  
  (function (jQuery){
  
  	var $bg, $m, $f, root={}, parent = "body", isOpen = false, promise,
  		isA = Object.isArray, isB = Object.isBoolean, isS = Object.isString, isO = Object.isObject,
  		isN = Object.isNumber, isR = Object.isRegExp, isF = Object.isFunction;
  
  	//Close modal on escape
  
  	$(document).keydown(function (e) {
  	    if (e.keyCode == 27
  	    	&& $(parent).find(">.my-modal").is(":visible")
  	    	&& (
  	    		!$f.is(".my-form")
  	    		||
  	    		$f.data("escapable")
  	    		||
  	    		Object.equal($f.data("my").initial, $f.my("data"))
  	    	)
  	    ) root.modal(true);
  	});
  
  	root.modal = function modal (obj, done0, css0) {
  		var done = done0||(function () {return false;}),
  			proxy,
  			stop = false,
  			show = false,
  			data = true,
  			text="",
  			$img,
  			width=css0||820,
  			height=300;
  
  		// check if opened
  		if (isOpen) {
  
  			// No obj – close
  			if (isB(obj) || obj===null || obj===undefined) {
  				if (isF($m.data("done"))) done = $m.data("done");
  				else done = function () {return false;};
  
  				stop=false; data=true;
  
  				if (!obj) {
  					if (!$f.is(".my-form")) stop=done(null,true);
  					else {
  						stop=done($f.my("valid")?null:$f.my("errors"),$f.my("data"));
  						if (!stop) data=$f.my("remove");
  					}
  				} else if (obj===true && promise.state()!="rejected"){
  					if ($f.is(".my-form")) $f.my("remove");
  					done(null,null);
  					promise.reject("Cancelled");
  				}
  				if (!stop) {
  					$f.html("");
  					$m.css({display:"none"});
  					(function () {if (!isOpen) $bg.css({display:"none"});}).delay(5);
  					isOpen = false;
  					$m.data("done",null);
  					(function () {promise.resolve(data);}).delay(0);
  				}
  				return;
  			}
  
  			// Trying to open new modal
  			// when prev is not closed
  			proxy=$.Deferred();
  			done(null,null);
  			proxy.reject("Locked");
  			return proxy.promise();
  		}
  
  		promise = $.Deferred();
  		isOpen = true;
  
  		// create wrappers if none defined
  		if (!$m || !$(parent).find(">.my-modal").size()) {
  			$m= $('<div class="my-modal" style="display:none;"></div>').prependTo($(parent));
  		}
  		//rebuild modal form obj
  		$m.html('<div class="my-modal-form"></div>');
  		$f = $m.find(".my-modal-form");
  		$('<div class="my-modal-close" title="Close">×</div>')
  			.prependTo($m).on("click",function () {root.modal(true);});
  
  		if (!$bg || !$(parent).find(">.my-modal-screen").size())  {
  			$bg= $('<div class="my-modal-screen" style="display:none;"></div>')
  					.prependTo($(parent)).on("click", function () {
  						if (!$f.is(".my-form")||$f.data("escapable")) root.modal(true);
  					});
  		}
  
  		// jQuery image
  		if (typeof obj == "object" && obj.jquery) {
  			if (obj.is("img")) {
  
  				text = obj.attr("alt") || obj.attr("title") || obj.data("text");
  
  				$f.html((function () {
  					var $i = obj,
  					w = $i[0].naturalWidth || $i[0].width,
  					h = $i[0].naturalHeight || $i[0].height,
  					w0=jQuery(window).width()-90,
  					h0=jQuery(window).height()-90,
  					html;
  					if (h0<h) w = (w*(h0/h))|0, h=h0;
  					if (w0<w) w=w0, h=(h*(w0/w))|0;
  					html = '<img src = "'+$i.attr("src")+'" class="db" style="max-width:'+w+'px;max-height:'+h+'px">';
  					width=w<300?300:w;
  					return html;
  				})() + (text?'<h4 class="mt10">'+text+'</h4>':""));
  
  				$f.find("img").on("click",function () {root.modal();});
  				$m.css({top:"3000px",display:"block"});
  				height=$m.height();
  				show=1;
  			}
  		}
  
  		// $.my form
  		else if (isO(obj) && obj.manifest) {
  			data = obj.data ||{};
  			done = isF(obj.done)?obj.done:done;
  			width=1*($.my.f.getref(
  					isS(obj.manifest)?$.my.cache(obj.manifest):obj.manifest,
  					"params.width"
  				)
  				||obj.width||width);
  			$m.css({top:"3000px",display:"block"});
  			$f.data("my",null);
  			show=1;
  			$f.my(obj.manifest, data).then(function () {
  				_adjust.delay(20);
  				promise.notify("Ready");
  			}).fail(function (err){
  				promise.reject(err);
  				show=0;
  				root.modal(true);
  			});
  			$m.data("done", done);
  			$f.data("escapable", !!obj.esc);
  			$f.bind("commit.my", function () {
  				root.modal();
  			}.debounce(50))
  			.bind("cancel.my", function () {
  				root.modal(true);
  			}.debounce(50))
  			.bind("layout.my", function () {
  				_adjust();
  			});
  		}
  
  		//plain html
  		else if (isS(obj)) {
  			$f.html(obj);
  			$m.css({top:"3000px", display:"block", width:"auto"});
  			width=1*($m.width()||width);
  			show=1;
  		}
  
  		else if (obj===true) return isOpen = false, promise.reject("Cancelled").promise();
  		else return isOpen = false, promise.reject("Invalid data").promise();
  
  		//show all objs
  		if (show) {
  			_adjust(width);
  			//If we have images, count them and reheight on load
  			$img = $f.find("img").filter(function () {return $(this).attr("src")!="";});
  			if ($img.size()) {
  				var _imgdone = (function () {
  					_adjust(css0);
  					promise.notify("Ready");
  				}).after($img.size());
  				$img.each(function () {$(this).bind("load", _imgdone);});
  			} else {
  				promise.notify("Ready");
  			}
  		}
  
  		return promise.promise();
  	};
  
  	root.modal.loading = function (onoff) {
  		$(parent).find(">.my-modal").toggleClass("my-modal-loading",!!onoff);
  	};
  
  	root.modal.parent = function (s) {
  	// sets parent DOM node selector for $.my.modal
  		if (!s || !$(s).size()) return $(parent);
  		parent = s;
  	};
  
  	root.modal.visible = function () {return isOpen;};
  
  	if (!jQuery.my) jQuery.my={};
  	jQuery.my.modal = root.modal;
  
  
  
  	function _adjust (width0){
  		//adjust modal position,
  		// centers it when content is ready
  
  		var W = window,
  			h=W.innerHeight || jQuery(W).height(),
  			w=W.innerWidth || jQuery(W).width(),
  			height = $m.height(),
  			width = width0||$f.width();
  		$m.removeClass("loading")
  			.css({
  				left:((w-width-60)/2)+"px",
  				width:(width*1+60)+"px",
  				display:"block"
  			});
  		$bg.css({width:(w*2)+"px", height:(h*2)+"px",display:"block"});
  
  		if (height>h) {
  			$m.height(h);
  			$f.css({"overflow-y":"overlay"});
  		} else {
  			$f.css({"overflow-y":"none"});
  		}
  
  		$m.animate(
  			{top:(height>=h?0:(h-height)/3.5)+"px"},
  			parseFloat($m.css("top"))<h?300:0
  		);
  	}
  
  })(jQuery);
  