/*
* jQuery.my 1.1.0
* Requires jQuery 1.11.0+, SugarJS 1.3.9-1.4.x
*
* $.my changes:
* — .radio and ui.#ctrl.listen pubsub functionality
* — new 'radio' event for pubsub
* — .inherit and .expose manifest properties
* — .die function, called on form disband
*
* Modal changes:
* — param .bound (default:false) if set to number defines how far must
* modal be kept from root bounds
* – nose is always aligned to callee
*
* More details at jquerymy.com
*
* @ermouth, thanks @carpogoryanin, @ftescht
* 2015-01-12
*/
;(function ($) {var _version = "jQuery.my 1.1.0";
// Some shortcuts and constants
var lang = "en",
wURL = window.URL || window.webkitURL,
ie8 = !document.addEventListener,
forms = _getref($,"my.f.repo")? $.my.f.repo():{_src:{}, _name:"Global manifest cache"},
restyles = _getref($,"my.f.restyles")? $.my.f.restyles():{},
$E = $.extend, T = $.type, 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",
rthis = /^this\./,
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));};
//=======================================
// 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)) {
console.log(obj);
return 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 i, file, root=root0||forms, obj=obj0, path, id, prev, res;
if (!(isO(root) && isO(obj) && isO(obj.ui) && isS(obj.id)))
return "Invalid arguments.";
if (!root.hasOwnProperty("_src")) root._src={};
id = obj.id;
//path = id.split(".");
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, true);}
catch (e) {
return "Invalid manifest, parse error.";
}
//blobify files
i = _files2urls (obj);
if (isS(i)) {
f.con(i);
return i;
}
//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);
if (ie8) res["_self"] = root._src[id];
else Object.defineProperty(res, "_self", {
get: function () { return root._src[id]; },
set: function () { throw "Can not change repo";},
enumerable : false,
configurable : true
});
return res;
}
function _files2urls (obj) {
var i, flist = [], file;
if (isO(obj.files) && Object.size(obj.files)) {
//blobify files
for (i in obj.files) {
file = obj.files[i];
if (isO(file) && file.data && !file.url) {
if (wURL) {
try {
f.base642blob(file.data,function(res){
file.blob = res;
file.url = wURL.createObjectURL(file.blob);
},(file.content_type||file.mime));
flist.push(i);
} catch(e) {
return "Invalid base64 data in files/"+i+".";
}
} else {
//ie8-9 fallback
file.url = 'data:'+(file.content_type||file.mime)+';base64,'+file.data;
flist.push(i);
}
}
}
}
return flist;
}
//########################################################
// Storage of rules defined by cascading selectors
// very similar to css. Leafs are processors
// 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,ctr = 0,
sP = "ui-sortable", sPlc= "."+sP+"-placeholder",
od = $o.data("formlist")||{},
gen = od.generator||{},
itemSel = gen.selector||">.my-form",
tmpl = gen.template||"
",
tmplIsVar = /\{/.test(tmpl),
hasher = gen.hash|| f.sdbmCode,
ider = gen.id|| f.sdbmCode,
extHasher = gen.ext,
delay = gen.delay||50,
sortable = $o.is("."+sP),
sorting = !!$o.children(sPlc).size(),
result=[], redraw = [],
$n, $drag, now = Date.now();
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 (sorting) return list;
// first we must estimate
// if putting new data over old
// changes anything
old= []; $c.each(function () {
var $x = $(this), xd = $x.data("my");
if (xd) old.push(xd.data);
});
//fast compare
eq=false;
if (old.length===list.length) for (eq=true, i=0;i idx and hashes
for (i=0;i1.4*delay)
|| now-gen.stamp > 100
|| !gen.stashed
|| (gen.stashed.length !== $c.size()-(sorting?1:0))
) {
if (sorting) $drag = $o.find('>.'+sP+'-helper');
var ri=0;
$c.each(function (idx, elt) {
var $x = $(elt), xd, xf, chash, dirty = false;
if (!sorting || !$x.hasClass(sP+'-helper')) {
if (sorting && $x.hasClass(sP + '-placeholder')) $x = $drag;
xd = $x.data("my");
xf = $x.data("formlist");
if (xd && xf) {
result.push(xd.data);
if (ri != xf.index) {
xf.index = ri;
dirty = !0;
}
if (extHasher) {
chash = hasher(xd.data, ri)+"";
if (chash !== xf.hash) {
xf.hash = chash;
dirty = !0;
}
}
ri += 1;
if (dirty) redraw.push($x);
}
}
});
for (i=0;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.data("uiSelectmenu")) $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.data("uiSelectmenu")) $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:'
Form init failed
',
ru:'
Ошибка инициализации формы
'
},
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":"sortupdate.my check.my",
".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) { // container getter
return _traverse($o, MY.containers)($o);
},
change:N,
recalcDepth:2, // depth of dependencies resolver tree
delay:0, // default delay of bind invocation
strict:false, // if true form assumed unjsonned
restyle:-1, // delay of ').appendTo($("body"));*/
$style = $(html(style[0], manClass)).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 = $('').appendTo($("body"));*/
$locstyle = $(html(style[1], formClass)).appendTo($("body"));
if (p.restyle>-1 && !restyles[cid]) {
restyles[cid] = (function restyle (){ _styler(true)}).debounce(p.restyle);
}
$root.data("my").restyle = _styler.fill(true).debounce(0);
}
else if (onlyLocals) {
$(html(style[1], formClass)).replaceAll($locstyle);
$locstyle = $('style#' + formClass);
}
$root.data("my").localStyle = $locstyle;
}
}
function html(styles, prefixCss) {
return ('');
}
}
//-----------------------------------------------------
// prepare files section
function _files () {
var i, pi = $.Deferred(), flist;
flist = _files2urls (manifest);
if (isS(flist)) {
_f("Error decoding base64 to local Blob/URL", flist);
pi.reject();
}
else {
if (wURL) for(i=0;i*").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)) {
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);
}
}
//-----------------------------------------------------
//build and init controls
function _controls (){
var formState={}, ctr=Object.size(ui);
$root.addClass(initCss);
// 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) {
var uiNode, v, $o;
for (var selector in ui) {
if (_fail) return;
uiNode = ui[selector];
$o = $root.find(selector);
if ($o.size()) {
if (uiNode.listen) $listeners[selector] = $o.eq(0);
try {
v = _bind(d, N, uiNode, $o);
if (v==N && formState[selector]!=N && v!==undefined)
_bind(d, formState[selector], uiNode, $o);
}
catch (e) {
f.con("Transient fail linking " +selector
+" of form $('.my-form-"+cid+"')",
e.message, e.stack
);
}
try {
if (v!=N) _field($o,v);
$o.eq(0).trigger("check.my");
} catch (e) {
_f("Error linking "+selector, e.message, e.stack);
}
}
}
$root.removeClass(initCss);
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(initCss);
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{
delete restyles[cid];
$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,
locFiles,
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;
// close modals if any
// if ($o.data("modals")) {}
// stop event listeners
$o.unbind(".my");
// exec done
if (isO(m) && m.manifest && isF(m.manifest.die)) {
try {
m.manifest.die.call(m.manifest, $o, m.manifest);
} catch(e){}
}
// 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{
delete restyles[cid];
$locstyle.remove();
}catch(e){}
}
// revoke data urls
if (window.URL && (locFiles = m.locFiles) && locFiles.length) {
for (var i=0;i< locFiles.length; i++) {
try { URL.revokeObjectURL(locFiles[i]); } catch(e) {}
}
}
// remove $.my from ui entries
ui = (m||{}).ui;
if (ui) {
ui.each(function (key){
var $we = $o.find(key), f, mw, i;
// close dependent modal
if (mw = $we.data("modal")) {
mw.cancel();
$we.removeData("modal");
}
//close child modals
if (mw = $we.data("modals")) {
for (i in mw) if (mw[i]) mw[i].cancel();
}
$we.unbind(".my");
try{
f = _traverse($we, MY.destroy);
if (isF(f)) f($we);
}catch(e){}
$we .removeData("formlist")
.removeData("myval")
.removeData("my");
});
}
if (fromDOM && $o.is(".my-form")){
$o.remove();
}
else if ($o.data("formlist") && $o.is(".my-form")) {
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>{x:1,y:{w:2,a:[1,2],z:3}}.
// Returns 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
',
*
* // Row with several controls and HTML, no label
* ["", "num#age",{style:"width:50px"}, "years ", "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 = '',
"inp" :iHead+'text" {ext}/>',
"sli" :iHead+'range" {ext}/>',
"dat" :iHead+'date" {ext}/>',
"btn" :iHead+'button" {ext}/>',
"but" :'',
"div" :'