6080 lines
213 KiB
JavaScript
6080 lines
213 KiB
JavaScript
/*
|
|
The following errors were found when attempting to minify this file:
|
|
- Line 54: Parse error. missing : after property id
|
|
- Line 54: Parse error. illegal character
|
|
- Line 54: Parse error. missing ; before statement
|
|
- Line 54: Parse error. illegal character
|
|
- Line 54: Parse error. syntax error
|
|
- Line 55: Parse error. syntax error
|
|
- Line 56: Parse error. syntax error
|
|
- Line 57: Parse error. syntax error
|
|
- Line 59: Parse error. syntax error
|
|
- Line 60: Parse error. illegal character
|
|
- Line 60: Parse error. missing ) after argument list
|
|
- Line 60: Parse error. illegal character
|
|
- Line 60: Parse error. syntax error
|
|
- Line 61: Parse error. syntax error
|
|
- Line 62: Parse error. syntax error
|
|
- Line 63: Parse error. syntax error
|
|
- Line 64: Parse error. syntax error
|
|
- Line 66: Parse error. syntax error
|
|
- Line 68: Parse error. missing ; before statement
|
|
- Line 76: Parse error. syntax error
|
|
- Line 78: Parse error. syntax error
|
|
- Line 83: Parse error. invalid return
|
|
- Line 84: Parse error. syntax error
|
|
- Line 86: Parse error. missing ; before statement
|
|
- Line 90: Parse error. syntax error
|
|
- Line 91: Parse error. syntax error
|
|
- Line 92: Parse error. syntax error
|
|
- Line 94: Parse error. syntax error
|
|
- Line 96: Parse error. missing ; before statement
|
|
- Line 102: Parse error. illegal character
|
|
- Line 102: Parse error. missing ; before statement
|
|
- Line 102: Parse error. illegal character
|
|
- Line 103: Parse error. syntax error
|
|
- Line 104: Parse error. syntax error
|
|
- Line 107: Parse error. syntax error
|
|
- Line 110: Parse error. illegal character
|
|
- Line 110: Parse error. missing ; before statement
|
|
- Line 110: Parse error. illegal character
|
|
- Line 114: Parse error. invalid return
|
|
- Line 115: Parse error. syntax error
|
|
- Line 117: Parse error. missing ; before statement
|
|
- Line 118: Parse error. syntax error
|
|
- Line 119: Parse error. syntax error
|
|
- Line 120: Parse error. syntax error
|
|
- Line 125: Parse error. invalid return
|
|
- Line 130: Parse error. missing ; after for-loop initializer
|
|
- Line 130: Parse error. missing ; before statement
|
|
- Line 137: Parse error. syntax error
|
|
- Line 144: Parse error. syntax error
|
|
- Line 146: Parse error. syntax error
|
|
- Line 148: Parse error. syntax error
|
|
- Line 149: Parse error. syntax error
|
|
- Line 150: Parse error. syntax error
|
|
- Line 440: Parse error. missing ; before statement
|
|
- Line 4762: Parse error. IE8 (and below) will parse trailing commas in array and object literals incorrectly. If you are targeting newer versions of JS, set the appropriate language_in option.
|
|
- Line 5035: Parse error. IE8 (and below) will parse trailing commas in array and object literals incorrectly. If you are targeting newer versions of JS, set the appropriate language_in option.
|
|
- Line 5145: Parse error. syntax error
|
|
- Line 5150: Parse error. illegal character
|
|
- Line 5150: Parse error. missing } in compound statement
|
|
- Line 5150: Parse error. missing ; before statement
|
|
- Line 5150: Parse error. illegal character
|
|
- Line 5151: Parse error. syntax error
|
|
- Line 5153: Parse error. missing ) in parenthetical
|
|
- Line 5154: Parse error. syntax error
|
|
- Line 5159: Parse error. illegal character
|
|
- Line 5159: Parse error. missing } in compound statement
|
|
- Line 5159: Parse error. missing ; before statement
|
|
- Line 5159: Parse error. illegal character
|
|
- Line 5161: Parse error. syntax error
|
|
- Line 5163: Parse error. invalid return
|
|
- Line 5163: Parse error. syntax error
|
|
- Line 5168: Parse error. invalid return
|
|
- Line 5168: Parse error. illegal character
|
|
- Line 5168: Parse error. missing } in compound statement
|
|
- Line 5168: Parse error. missing ; before statement
|
|
- Line 5168: Parse error. illegal character
|
|
- Line 5169: Parse error. syntax error
|
|
- Line 5170: Parse error. syntax error
|
|
- Line 5172: Parse error. invalid return
|
|
- Line 5174: Parse error. syntax error
|
|
- Line 5175: Parse error. syntax error
|
|
- Line 5176: Parse error. syntax error
|
|
*/
|
|
/*!
|
|
* pickadate.js v3.6.4, 2019/05/25
|
|
* By Amsul, http://amsul.ca
|
|
* Hosted on http://amsul.github.io/pickadate.js
|
|
* Licensed under MIT
|
|
*/
|
|
(function(n){typeof define=="function"&&define.amd?define("picker",["jquery"],n):typeof exports=="object"?module.exports=n(require("jquery")):typeof window=="object"?window.Picker=n(jQuery):this.Picker=n(jQuery)})(function(n){function t(e,s,h,a){function g(){return t._.node("div",t._.node("div",t._.node("div",t._.node("div",v.component.nodes(y.open),w.box),w.wrap),w.frame),w.holder,'tabindex="-1"')}function rt(){b.data(s,v).addClass(w.input).val(b.data("value")?v.get("select",p.format):e.value).on("focus."+y.id+" click."+y.id,function(n){n.preventDefault();v.open()}).on("mousedown",function(){y.handlingOpen=!0;var t=function(){setTimeout(function(){n(document).off("mouseup",t);y.handlingOpen=!1},0)};n(document).on("mouseup",t)});if(!p.editable)b.on("keydown."+y.id,it);i(e,{haspopup:!0,readonly:!1,owns:e.id+"_root"})}function ut(){i(v.$root[0],"hidden",!0)}function nt(){v.$holder.on({keydown:it,"focus.toOpen":tt,blur:function(){b.removeClass(w.target)},focusin:function(n){v.$root.removeClass(w.focused);n.stopPropagation()},"mousedown click":function(t){var i=r(t,e);i!=v.$holder[0]&&(t.stopPropagation(),t.type!="mousedown"||n(i).is("input, select, textarea, button, option")||(t.preventDefault(),v.$holder.eq(0).focus()))}}).on("click","[data-pick], [data-nav], [data-clear], [data-close]",function(){var r=n(this),i=r.data(),u=r.hasClass(w.navDisabled)||r.hasClass(w.disabled),t=o();t=t&&(t.type||t.href?t:null);(u||t&&!n.contains(v.$root[0],t))&&v.$holder.eq(0).focus();!u&&i.nav?v.set("highlight",v.component.item.highlight,{nav:i.nav}):!u&&"pick"in i?(v.set("select",i.pick),p.closeOnSelect&&v.close(!0)):i.clear?(v.clear(),p.closeOnClear&&v.close(!0)):i.close&&v.close(!0)})}function ft(){var t;p.hiddenName===!0?(t=e.name,e.name=""):(t=[typeof p.hiddenPrefix=="string"?p.hiddenPrefix:"",typeof p.hiddenSuffix=="string"?p.hiddenSuffix:"_submit"],t=t[0]+e.name+t[1]);v._hidden=n('<input type=hidden name="'+t+'"'+(b.data("value")||e.value?' value="'+v.get("select",p.formatSubmit)+'"':"")+">")[0];b.on("change."+y.id,function(){v._hidden.value=e.value?v.get("select",p.formatSubmit):""})}function et(){if(k&&c)v.$holder.find("."+w.frame).one("transitionend",function(){v.$holder.eq(0).focus()});else setTimeout(function(){v.$holder.eq(0).focus()},0)}function tt(n){n.stopPropagation();b.addClass(w.target);v.$root.addClass(w.focused);v.open()}function it(n){var t=n.keyCode,i=/^(8|46)$/.test(t);if(t==27)return v.close(!0),!1;(t==32||i||!y.open&&v.component.key[t])&&(n.preventDefault(),n.stopPropagation(),i?v.clear().close():v.open())}if(!e)return t;var k=!1,y={id:e.id||"P"+Math.abs(~~(Math.random()*new Date)),handlingOpen:!1},p=h?n.extend(!0,{},h.defaults,a):a||{},w=n.extend({},t.klasses(),p.klass),b=n(e),d=function(){return this.start()},v=d.prototype={constructor:d,$node:b,start:function(){if(y&&y.start)return v;y.methods={};y.start=!0;y.open=!1;y.type=e.type;e.autofocus=e==o();e.readOnly=!p.editable;p.id=e.id=e.id||y.id;e.type!="text"&&(e.type="text");v.component=new h(v,p);v.$root=n('<div class="'+w.picker+'" id="'+e.id+'_root" />');ut();v.$holder=n(g()).appendTo(v.$root);nt();p.formatSubmit&&ft();rt();p.containerHidden?n(p.containerHidden).append(v._hidden):b.after(v._hidden);p.container?n(p.container).append(v.$root):b.after(v.$root);v.on({start:v.component.onStart,render:v.component.onRender,stop:v.component.onStop,open:v.component.onOpen,close:v.component.onClose,set:v.component.onSet}).on({start:p.onStart,render:p.onRender,stop:p.onStop,open:p.onOpen,close:p.onClose,set:p.onSet});return k=l(v.$holder[0]),e.autofocus&&v.open(),v.trigger("start").trigger("render")},render:function(t){return t?(v.$holder=n(g()),nt(),v.$root.html(v.$holder)):v.$root.find("."+w.box).html(v.component.nodes(y.open)),v.trigger("render")},stop:function(){return y.start?(v.close(),v._hidden&&v._hidden.parentNode.removeChild(v._hidden),v.$root.remove(),b.removeClass(w.input).removeData(s),setTimeout(function(){b.off("."+y.id)},0),e.type=y.type,e.readOnly=!1,v.trigger("stop"),y.methods={},y.start=!1,v):v},open:function(o){if(y.open)return v;if(b.addClass(w.active),setTimeout(function(){v.$root.addClass(w.opened);i(v.$root[0],"hidden",!1)},0),o!==!1){y.open=!0;k&&n("body").css("overflow","hidden").css("padding-right","+="+f());et();u.on("click."+y.id+" focusin."+y.id,function(n){if(!y.handlingOpen){var t=r(n,e);n.isSimulated||t==e||t==document||n.which==3||v.close(t===v.$holder[0])}}).on("keydown."+y.id,function(i){var u=i.keyCode,f=v.component.key[u],o=r(i,e);u==27?v.close(!0):o==v.$holder[0]&&(f||u==13)?(i.preventDefault(),f?t._.trigger(v.component.key.go,v,[t._.trigger(f)]):v.$root.find("."+w.highlighted).hasClass(w.disabled)||(v.set("select",v.component.item.highlight),p.closeOnSelect&&v.close(!0))):n.contains(v.$root[0],o)&&u==13&&(i.preventDefault(),o.click())})}return v.trigger("open")},close:function(t){return(t&&(p.editable?e.focus():(v.$holder.off("focus.toOpen").focus(),setTimeout(function(){v.$holder.on("focus.toOpen",tt)},0))),b.removeClass(w.active),setTimeout(function(){v.$root.removeClass(w.opened+" "+w.focused);i(v.$root[0],"hidden",!0)},0),!y.open)?v:(y.open=!1,k&&n("body").css("overflow","").css("padding-right","-="+f()),u.off("."+y.id),v.trigger("close"))},clear:function(n){return v.set("clear",null,n)},set:function(t,i,r){var u,f,o=n.isPlainObject(t),e=o?t:{};if(r=o&&n.isPlainObject(i)?i:r||{},t){o||(e[t]=i);for(u in e)f=e[u],u in v.component.item&&(f===undefined&&(f=null),v.component.set(u,f,r)),(u=="select"||u=="clear")&&p.updateInput&&b.val(u=="clear"?"":v.get(u,p.format)).trigger("change");v.render()}return r.muted?v:v.trigger("set",e)},get:function(n,i){if(n=n||"value",y[n]!=null)return y[n];if(n=="valueSubmit"){if(v._hidden)return v._hidden.value;n="value"}if(n=="value")return e.value;if(n in v.component.item){if(typeof i=="string"){var r=v.component.get(n);return r?t._.trigger(v.component.formats.toString,v.component,[i,r]):""}return v.component.get(n)}},on:function(t,i,r){var u,e,o=n.isPlainObject(t),f=o?t:{};if(t){o||(f[t]=i);for(u in f)e=f[u],r&&(u="_"+u),y.methods[u]=y.methods[u]||[],y.methods[u].push(e)}return v},off:function(){var n,t,i=arguments;for(n=0,namesCount=i.length;n<namesCount;n+=1)t=i[n],t in y.methods&&delete y.methods[t];return v},trigger:function(n,i){var r=function(n){var r=y.methods[n];r&&r.map(function(n){t._.trigger(n,v,[i])})};return r("_"+n),r(n),v}};return new d}function l(n){var t,i="position";return n.currentStyle?t=n.currentStyle[i]:window.getComputedStyle&&(t=getComputedStyle(n)[i]),t=="fixed"}function f(){var t,i,r,u;return h.height()<=s.height()?0:(t=n('<div style="visibility:hidden;width:100px" />').appendTo("body"),i=t[0].offsetWidth,t.css("overflow","scroll"),r=n('<div style="width:100%" />').appendTo(t),u=r[0].offsetWidth,t.remove(),i-u)}function r(n,t){var i=[];return(n.path&&(i=n.path),n.originalEvent&&n.originalEvent.path&&(i=n.originalEvent.path),i&&i.length>0)?t&&i.indexOf(t)>=0?t:i[0]:n.target}function i(t,i,r){if(n.isPlainObject(i))for(var u in i)e(t,u,i[u]);else e(t,i,r)}function e(n,t,i){n.setAttribute((t=="role"?"":"aria-")+t,i)}function a(t,i){var r,u,f;n.isPlainObject(t)||(t={attribute:i});i="";for(r in t)u=(r=="role"?"":"aria-")+r,f=t[r],i+=f==null?"":u+'="'+t[r]+'"';return i}function o(){try{return document.activeElement}catch(n){}}var s=n(window),u=n(document),h=n(document.documentElement),c=document.documentElement.style.transition!=null;return t.klasses=function(n){return n=n||"picker",{picker:n,opened:n+"--opened",focused:n+"--focused",input:n+"__input",active:n+"__input--active",target:n+"__input--target",holder:n+"__holder",frame:n+"__frame",wrap:n+"__wrap",box:n+"__box"}},t._={group:function(n){for(var i,u="",r=t._.trigger(n.min,n);r<=t._.trigger(n.max,n,[r]);r+=n.i)i=t._.trigger(n.item,n,[r]),u+=t._.node(n.node,i[0],i[1],i[2]);return u},node:function(t,i,r,u){return i?(i=n.isArray(i)?i.join(""):i,r=r?' class="'+r+'"':"",u=u?" "+u:"","<"+t+r+u+">"+i+"<\/"+t+">"):""},lead:function(n){return(n<10?"0":"")+n},trigger:function(n,t,i){return typeof n=="function"?n.apply(t,i||[]):n},digits:function(n){return/\d/.test(n[1])?2:1},isDate:function(n){return{}.toString.call(n).indexOf("Date")>-1&&this.isInteger(n.getDate())},isInteger:function(n){return{}.toString.call(n).indexOf("Number")>-1&&n%1==0},ariaAttr:a},t.extend=function(i,r){n.fn[i]=function(u,f){var e=this.data(i);return u=="picker"?e:e&&typeof u=="string"?t._.trigger(e[u],e,[f]):this.each(function(){var f=n(this);f.data(i)||new t(this,i,r,u)})};n.fn[i].defaults=r.defaults},t});
|
|
/*!
|
|
* Date picker for pickadate.js v3.6.4
|
|
* http://amsul.github.io/pickadate.js/date.htm
|
|
*/
|
|
(function(n){typeof define=="function"&&define.amd?define(["./picker","jquery"],n):typeof exports=="object"?module.exports=n(require("./picker.js"),require("jquery")):n(Picker,jQuery)})(function(n,t){function r(n,t){var i=this,r=n.$node[0],o=r.value,u=n.$node.data("value"),f=u||o,s=u?t.formatSubmit:t.format,e=function(){return r.currentStyle?r.currentStyle.direction=="rtl":getComputedStyle(n.$root[0]).direction=="rtl"};i.settings=t;i.$node=n.$node;i.queue={min:"measure create",max:"measure create",now:"now create",select:"parse create validate",highlight:"parse navigate create validate",view:"parse create validate viewset",disable:"deactivate",enable:"activate"};i.item={};i.item.clear=null;i.item.disable=(t.disable||[]).slice(0);i.item.enable=-function(n){return n[0]===!0?n.shift():-1}(i.item.disable);i.set("min",t.min).set("max",t.max).set("now");f?i.set("select",f,{format:s,defaultValue:!0}):i.set("select",null).set("highlight",i.item.now);i.key={40:7,38:-7,39:function(){return e()?-1:1},37:function(){return e()?1:-1},go:function(n){var t=i.item.highlight,r=new Date(t.year,t.month,t.date+n);i.set("highlight",r,{interval:n});this.render()}};n.on("render",function(){n.$root.find("."+t.klass.selectMonth).on("change",function(){var i=this.value;i&&(n.set("highlight",[n.get("view").year,i,n.get("highlight").date]),n.$root.find("."+t.klass.selectMonth).trigger("focus"))});n.$root.find("."+t.klass.selectYear).on("change",function(){var i=this.value;i&&(n.set("highlight",[i,n.get("view").month,n.get("highlight").date]),n.$root.find("."+t.klass.selectYear).trigger("focus"))})},1).on("open",function(){var r="";i.disabled(i.get("now"))&&(r=":not(."+t.klass.buttonToday+")");n.$root.find("button"+r+", select").attr("disabled",!1)},1).on("close",function(){n.$root.find("button, select").attr("disabled",!0)},1)}var u=7,f=6,i=n._;r.prototype.set=function(n,t,i){var r=this,u=r.item;return t===null?(n=="clear"&&(n="select"),u[n]=t,r):(u[n=="enable"?"disable":n=="flip"?"enable":n]=r.queue[n].split(" ").map(function(u){return t=r[u](n,t,i)}).pop(),n=="select"?r.set("highlight",u.select,i):n=="highlight"?r.set("view",u.highlight,i):n.match(/^(flip|min|max|disable|enable)$/)&&(u.select&&r.disabled(u.select)&&r.set("select",u.select,i),u.highlight&&r.disabled(u.highlight)&&r.set("highlight",u.highlight,i)),r)};r.prototype.get=function(n){return this.item[n]};r.prototype.create=function(n,r,u){var f,e=this;return r=r===undefined?n:r,r==-Infinity||r==Infinity?f=r:t.isPlainObject(r)&&i.isInteger(r.pick)?r=r.obj:t.isArray(r)?(r=new Date(r[0],r[1],r[2]),r=i.isDate(r)?r:e.create().obj):r=i.isInteger(r)||i.isDate(r)?e.normalize(new Date(r),u):e.now(n,r,u),{year:f||r.getFullYear(),month:f||r.getMonth(),date:f||r.getDate(),day:f||r.getDay(),obj:f||r,pick:f||r.getTime()}};r.prototype.createRange=function(n,r){var f=this,u=function(n){return n===!0||t.isArray(n)||i.isDate(n)?f.create(n):n};return i.isInteger(n)||(n=u(n)),i.isInteger(r)||(r=u(r)),i.isInteger(n)&&t.isPlainObject(r)?n=[r.year,r.month,r.date+n]:i.isInteger(r)&&t.isPlainObject(n)&&(r=[n.year,n.month,n.date+r]),{from:u(n),to:u(r)}};r.prototype.withinRange=function(n,t){return n=this.createRange(n.from,n.to),t.pick>=n.from.pick&&t.pick<=n.to.pick};r.prototype.overlapRanges=function(n,t){var i=this;return n=i.createRange(n.from,n.to),t=i.createRange(t.from,t.to),i.withinRange(n,t.from)||i.withinRange(n,t.to)||i.withinRange(t,n.from)||i.withinRange(t,n.to)};r.prototype.now=function(n,t,i){return t=new Date,i&&i.rel&&t.setDate(t.getDate()+i.rel),this.normalize(t,i)};r.prototype.navigate=function(n,i,r){var s,f,u,e,c=t.isArray(i),h=t.isPlainObject(i),o=this.item.view;if(c||h){for(h?(f=i.year,u=i.month,e=i.date):(f=+i[0],u=+i[1],e=+i[2]),r&&r.nav&&o&&o.month!==u&&(f=o.year,u=o.month),s=new Date(f,u+(r&&r.nav?r.nav:0),1),f=s.getFullYear(),u=s.getMonth();new Date(f,u,e).getMonth()!==u;)e-=1;i=[f,u,e]}return i};r.prototype.normalize=function(n){return n.setHours(0,0,0,0),n};r.prototype.measure=function(n,t){var r=this;return i.isInteger(t)?t=r.now(n,t,{rel:t}):t?typeof t=="string"&&(t=r.parse(n,t)):t=n=="min"?-Infinity:Infinity,t};r.prototype.viewset=function(n,t){return this.create([t.year,t.month,1])};r.prototype.validate=function(n,r,u){var f=this,c=r,e=u&&u.interval?u.interval:1,h=f.item.enable===-1,l,a,o=f.item.min,s=f.item.max,v,y,p=h&&f.item.disable.filter(function(n){if(t.isArray(n)){var u=f.create(n).pick;u<r.pick?l=!0:u>r.pick&&(a=!0)}return i.isInteger(n)}).length;if((!u||!u.nav&&!u.defaultValue)&&(!h&&f.disabled(r)||h&&f.disabled(r)&&(p||l||a)||!h&&(r.pick<=o.pick||r.pick>=s.pick)))for(h&&!p&&(!a&&e>0||!l&&e<0)&&(e*=-1);f.disabled(r);){if(Math.abs(e)>1&&(r.month<c.month||r.month>c.month)&&(r=c,e=e>0?1:-1),r.pick<=o.pick?(v=!0,e=1,r=f.create([o.year,o.month,o.date+(r.pick===o.pick?0:-1)])):r.pick>=s.pick&&(y=!0,e=-1,r=f.create([s.year,s.month,s.date+(r.pick===s.pick?0:1)])),v&&y)break;r=f.create([r.year,r.month,r.date+e])}return r};r.prototype.disabled=function(n){var r=this,u=r.item.disable.filter(function(u){return i.isInteger(u)?n.day===(r.settings.firstDay?u:u-1)%7:t.isArray(u)||i.isDate(u)?n.pick===r.create(u).pick:t.isPlainObject(u)?r.withinRange(u,n):void 0});return u=u.length&&!u.filter(function(n){return t.isArray(n)&&n[3]=="inverted"||t.isPlainObject(n)&&n.inverted}).length,r.item.enable===-1?!u:u||n.pick<r.item.min.pick||n.pick>r.item.max.pick};r.prototype.parse=function(n,t,r){var f=this,u={};return!t||typeof t!="string"?t:(r&&r.format||(r=r||{},r.format=f.settings.format),f.formats.toArray(r.format).map(function(n){var r=f.formats[n],e=r?i.trigger(r,f,[t,u]):n.replace(/^!/,"").length;r&&(u[n]=t.substr(0,e));t=t.substr(e)}),[u.yyyy||u.yy,+(u.mm||u.m)-1,u.dd||u.d])};r.prototype.formats=function(){function n(n,t,i){var r=n.match(/[^\x00-\x7F]+|[a-zA-Z0-9_\u0080-\u00FF]+/)[0];return i.mm||i.m||(i.m=t.indexOf(r)+1),r.length}function t(n){return n.match(/[a-zA-Z0-9_\u0080-\u00FF]+/)[0].length}return{d:function(n,t){return n?i.digits(n):t.date},dd:function(n,t){return n?2:i.lead(t.date)},ddd:function(n,i){return n?t(n):this.settings.weekdaysShort[i.day]},dddd:function(n,i){return n?t(n):this.settings.weekdaysFull[i.day]},m:function(n,t){return n?i.digits(n):t.month+1},mm:function(n,t){return n?2:i.lead(t.month+1)},mmm:function(t,i){var r=this.settings.monthsShort;return t?n(t,r,i):r[i.month]},mmmm:function(t,i){var r=this.settings.monthsFull;return t?n(t,r,i):r[i.month]},yy:function(n,t){return n?2:(""+t.year).slice(2)},yyyy:function(n,t){return n?4:t.year},toArray:function(n){return n.split(/(d{1,4}|m{1,4}|y{4}|yy|!.)/g)},toString:function(n,t){var r=this;return r.formats.toArray(n).map(function(n){return i.trigger(r.formats[n],r,[0,t])||n.replace(/^!/,"")}).join("")}}}();r.prototype.isDateExact=function(n,r){var u=this;return i.isInteger(n)&&i.isInteger(r)||typeof n=="boolean"&&typeof r=="boolean"?n===r:(i.isDate(n)||t.isArray(n))&&(i.isDate(r)||t.isArray(r))?u.create(n).pick===u.create(r).pick:t.isPlainObject(n)&&t.isPlainObject(r)?u.isDateExact(n.from,r.from)&&u.isDateExact(n.to,r.to):!1};r.prototype.isDateOverlap=function(n,r){var u=this,f=u.settings.firstDay?1:0;return i.isInteger(n)&&(i.isDate(r)||t.isArray(r))?(n=n%7+f,n===u.create(r).day+1):i.isInteger(r)&&(i.isDate(n)||t.isArray(n))?(r=r%7+f,r===u.create(n).day+1):t.isPlainObject(n)&&t.isPlainObject(r)?u.overlapRanges(n,r):!1};r.prototype.flipEnable=function(n){var t=this.item;t.enable=n||(t.enable==-1?1:-1)};r.prototype.deactivate=function(n,r){var f=this,u=f.item.disable.slice(0);return r=="flip"?f.flipEnable():r===!1?(f.flipEnable(1),u=[]):r===!0?(f.flipEnable(-1),u=[]):r.map(function(n){for(var e,r=0;r<u.length;r+=1)if(f.isDateExact(n,u[r])){e=!0;break}e||(i.isInteger(n)||i.isDate(n)||t.isArray(n)||t.isPlainObject(n)&&n.from&&n.to)&&u.push(n)}),u};r.prototype.activate=function(n,r){var f=this,u=f.item.disable,e=u.length;return r=="flip"?f.flipEnable():r===!0?(f.flipEnable(1),u=[]):r===!1?(f.flipEnable(-1),u=[]):r.map(function(n){for(var o,s,h,r=0;r<e;r+=1)if(s=u[r],f.isDateExact(s,n)){o=u[r]=null;h=!0;break}else if(f.isDateOverlap(s,n)){t.isPlainObject(n)?(n.inverted=!0,o=n):t.isArray(n)?(o=n,o[3]||o.push("inverted")):i.isDate(n)&&(o=[n.getFullYear(),n.getMonth(),n.getDate(),"inverted"]);break}if(o)for(r=0;r<e;r+=1)if(f.isDateExact(u[r],n)){u[r]=null;break}if(h)for(r=0;r<e;r+=1)if(f.isDateOverlap(u[r],n)){u[r]=null;break}o&&u.push(o)}),u.filter(function(n){return n!=null})};r.prototype.nodes=function(n){var r=this,t=r.settings,o=r.item,c=o.now,l=o.select,a=o.highlight,e=o.view,w=o.disable,s=o.min,h=o.max,b=function(n,r){return t.firstDay&&(n.push(n.shift()),r.push(r.shift())),i.node("thead",i.node("tr",i.group({min:0,max:u-1,i:1,node:"th",item:function(i){return[n[i],t.klass.weekdays,'scope=col title="'+r[i]+'"']}})))}((t.showWeekdaysFull?t.weekdaysFull:t.weekdaysShort).slice(0),t.weekdaysFull.slice(0)),v=function(n){return i.node("div"," ",t.klass["nav"+(n?"Next":"Prev")]+(n&&e.year>=h.year&&e.month>=h.month||!n&&e.year<=s.year&&e.month<=s.month?" "+t.klass.navDisabled:""),"data-nav="+(n||-1)+' tabindex="0" '+i.ariaAttr({role:"button",controls:r.$node[0].id+"_table"})+' title="'+(n?t.labelMonthNext:t.labelMonthPrev)+'"')},y=function(){var u=t.showMonthsShort?t.monthsShort:t.monthsFull;return t.selectMonths?i.node("select",i.group({min:0,max:11,i:1,node:"option",item:function(n){return[u[n],0,"value="+n+(e.month==n?" selected":"")+(e.year==s.year&&n<s.month||e.year==h.year&&n>h.month?" disabled":"")]}}),t.klass.selectMonth,(n?"":"disabled")+" "+i.ariaAttr({controls:r.$node[0].id+"_table"})+' title="'+t.labelMonthSelect+'"'):i.node("div",u[e.month],t.klass.month)},p=function(){var o=e.year,l=t.selectYears===!0?5:~~(t.selectYears/2),v,y;if(l){var c=s.year,a=h.year,u=o-l,f=o+l;return c>u&&(f+=c-u,u=c),a<f&&(v=u-c,y=f-a,u-=v>y?y:v,f=a),i.node("select",i.group({min:u,max:f,i:1,node:"option",item:function(n){return[n,0,"value="+n+(o==n?" selected":"")]}}),t.klass.selectYear,(n?"":"disabled")+" "+i.ariaAttr({controls:r.$node[0].id+"_table"})+' title="'+t.labelYearSelect+'"')}return i.node("div",o,t.klass.year)};return i.node("div",(t.selectYears?p()+y():y()+p())+v()+v(1),t.klass.header)+i.node("table",b+i.node("tbody",i.group({min:0,max:f-1,i:1,node:"tr",item:function(n){var f=t.firstDay&&r.create([e.year,e.month,1]).day===0?-7:0;return[i.group({min:u*n-e.day+f+1,max:function(){return this.min+u-1},i:1,node:"td",item:function(n){n=r.create([e.year,e.month,n+(t.firstDay?1:0)]);var u=l&&l.pick==n.pick,f=a&&a.pick==n.pick,o=w&&r.disabled(n)||n.pick<s.pick||n.pick>h.pick,v=i.trigger(r.formats.toString,r,[t.format,n]),y=t.id+"_"+n.pick;return[i.node("div",n.date,function(i){return i.push(e.month==n.month?t.klass.infocus:t.klass.outfocus),c.pick==n.pick&&i.push(t.klass.now),u&&i.push(t.klass.selected),f&&i.push(t.klass.highlighted),o&&i.push(t.klass.disabled),i.join(" ")}([t.klass.day]),"data-pick="+n.pick+" id="+y+' tabindex="0" '+i.ariaAttr({role:"gridcell",label:v,selected:u&&r.$node.val()===v?!0:null,activedescendant:f?n.pick:null,disabled:o?!0:null})),""]}})]}})),t.klass.table,'id="'+r.$node[0].id+'_table" '+i.ariaAttr({role:"grid",controls:r.$node[0].id,readonly:!0}))+i.node("div",i.node("button",t.today,t.klass.buttonToday,"type=button data-pick="+c.pick+(n&&!r.disabled(c)?"":" disabled")+" "+i.ariaAttr({controls:r.$node[0].id}))+i.node("button",t.clear,t.klass.buttonClear,"type=button data-clear=1"+(n?"":" disabled")+" "+i.ariaAttr({controls:r.$node[0].id}))+i.node("button",t.close,t.klass.buttonClose,"type=button data-close=true "+(n?"":" disabled")+" "+i.ariaAttr({controls:r.$node[0].id})),t.klass.footer)};r.defaults=function(n){return{labelMonthNext:"Next month",labelMonthPrev:"Previous month",labelMonthSelect:"Select a month",labelYearSelect:"Select a year",monthsFull:["January","February","March","April","May","June","July","August","September","October","November","December"],monthsShort:["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],weekdaysFull:["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],weekdaysShort:["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],today:"Today",clear:"Clear",close:"Close",closeOnSelect:!0,closeOnClear:!0,updateInput:!0,format:"d mmmm, yyyy",klass:{table:n+"table",header:n+"header",navPrev:n+"nav--prev",navNext:n+"nav--next",navDisabled:n+"nav--disabled",month:n+"month",year:n+"year",selectMonth:n+"select--month",selectYear:n+"select--year",weekdays:n+"weekday",day:n+"day",disabled:n+"day--disabled",selected:n+"day--selected",highlighted:n+"day--highlighted",now:n+"day--today",infocus:n+"day--infocus",outfocus:n+"day--outfocus",footer:n+"footer",buttonClear:n+"button--clear",buttonToday:n+"button--today",buttonClose:n+"button--close"}}}(n.klasses().picker+"__");n.extend("pickadate",r)});
|
|
/*!
|
|
* Time picker for pickadate.js v3.6.4
|
|
* http://amsul.github.io/pickadate.js/time.htm
|
|
*/
|
|
(function(n){typeof define=="function"&&define.amd?define(["./picker","jquery"],n):typeof exports=="object"?module.exports=n(require("./picker.js"),require("jquery")):n(Picker,jQuery)})(function(n,t){function r(n,t){var i=this,f=n.$node[0].value,r=n.$node.data("value"),u=r||f,e=r?t.formatSubmit:t.format;i.settings=t;i.$node=n.$node;i.queue={interval:"i",min:"measure create",max:"measure create",now:"now create",select:"parse create validate",highlight:"parse create validate",view:"parse create validate",disable:"deactivate",enable:"activate"};i.item={};i.item.clear=null;i.item.interval=t.interval||30;i.item.disable=(t.disable||[]).slice(0);i.item.enable=-function(n){return n[0]===!0?n.shift():-1}(i.item.disable);i.set("min",t.min).set("max",t.max).set("now");u?i.set("select",u,{format:e}):i.set("select",null).set("highlight",i.item.now);i.key={40:1,38:-1,39:1,37:-1,go:function(n){i.set("highlight",i.item.highlight.pick+n*i.item.interval,{interval:n*i.item.interval});this.render()}};n.on("render",function(){var i=n.$root.children(),r=i.find("."+t.klass.viewset),u=function(n){return["webkit","moz","ms","o",""].map(function(t){return(t?"-"+t+"-":"")+n})},f=function(n,t){u("transform").map(function(i){n.css(i,t)});u("transition").map(function(i){n.css(i,t)})};r.length&&(f(i,"none"),i[0].scrollTop=~~r.position().top-r[0].clientHeight*2,f(i,""))},1).on("open",function(){n.$root.find("button").attr("disabled",!1)},1).on("close",function(){n.$root.find("button").attr("disabled",!0)},1)}var e=24,u=60,o=12,f=e*u,i=n._;r.prototype.set=function(n,t,i){var r=this,u=r.item;return t===null?(n=="clear"&&(n="select"),u[n]=t,r):(u[n=="enable"?"disable":n=="flip"?"enable":n]=r.queue[n].split(" ").map(function(u){return t=r[u](n,t,i)}).pop(),n=="select"?r.set("highlight",u.select,i):n=="highlight"?r.set("view",u.highlight,i):n=="interval"?r.set("min",u.min,i).set("max",u.max,i):n.match(/^(flip|min|max|disable|enable)$/)&&(u.select&&r.disabled(u.select)&&r.set("select",t,i),u.highlight&&r.disabled(u.highlight)&&r.set("highlight",t,i),n=="min"&&r.set("max",u.max,i)),r)};r.prototype.get=function(n){return this.item[n]};r.prototype.create=function(n,r,o){var s=this;return r=r===undefined?n:r,i.isDate(r)&&(r=[r.getHours(),r.getMinutes()]),t.isPlainObject(r)&&i.isInteger(r.pick)?r=r.pick:t.isArray(r)?r=+r[0]*u+ +r[1]:i.isInteger(r)||(r=s.now(n,r,o)),n=="max"&&r<s.item.min.pick&&(r+=f),n!="min"&&n!="max"&&(r-s.item.min.pick)%s.item.interval!=0&&(r+=s.item.interval),r=s.normalize(n,r,o),{hour:~~(e+r/u)%e,mins:(u+r%u)%u,time:(f+r)%f,pick:r%f}};r.prototype.createRange=function(n,r){var f=this,u=function(n){return n===!0||t.isArray(n)||i.isDate(n)?f.create(n):n};return i.isInteger(n)||(n=u(n)),i.isInteger(r)||(r=u(r)),i.isInteger(n)&&t.isPlainObject(r)?n=[r.hour,r.mins+n*f.settings.interval]:i.isInteger(r)&&t.isPlainObject(n)&&(r=[n.hour,n.mins+r*f.settings.interval]),{from:u(n),to:u(r)}};r.prototype.withinRange=function(n,t){return n=this.createRange(n.from,n.to),t.pick>=n.from.pick&&t.pick<=n.to.pick};r.prototype.overlapRanges=function(n,t){var i=this;return n=i.createRange(n.from,n.to),t=i.createRange(t.from,t.to),i.withinRange(n,t.from)||i.withinRange(n,t.to)||i.withinRange(t,n.from)||i.withinRange(t,n.to)};r.prototype.now=function(n,t){var f=this.item.interval,o=new Date,r=o.getHours()*u+o.getMinutes(),s=i.isInteger(t),e;return r-=r%f,e=t<0&&f*t+r<=-f,r+=n=="min"&&e?0:f,s&&(r+=f*(e&&n!="max"?t+1:t)),r};r.prototype.normalize=function(n,t){var i=this.item.interval,r=this.item.min&&this.item.min.pick||0;return t-(n=="min"?0:(t-r)%i)};r.prototype.measure=function(n,r,f){var o=this;return r||(r=n=="min"?[0,0]:[e-1,u-1]),typeof r=="string"?r=o.parse(n,r):r===!0||i.isInteger(r)?r=o.now(n,r,f):t.isPlainObject(r)&&i.isInteger(r.pick)&&(r=o.normalize(n,r.pick,f)),r};r.prototype.validate=function(n,t,i){var r=this,u=i&&i.interval?i.interval:r.item.interval;return r.disabled(t)&&(t=r.shift(t,u)),t=r.scope(t),r.disabled(t)&&(t=r.shift(t,u*-1)),t};r.prototype.disabled=function(n){var r=this,u=r.item.disable.filter(function(u){return i.isInteger(u)?n.hour==u:t.isArray(u)||i.isDate(u)?n.pick==r.create(u).pick:t.isPlainObject(u)?r.withinRange(u,n):void 0});return u=u.length&&!u.filter(function(n){return t.isArray(n)&&n[2]=="inverted"||t.isPlainObject(n)&&n.inverted}).length,r.item.enable===-1?!u:u||n.pick<r.item.min.pick||n.pick>r.item.max.pick};r.prototype.shift=function(n,t){var i=this,r=i.item.min.pick,u=i.item.max.pick;for(t=t||i.item.interval;i.disabled(n);)if(n=i.create(n.pick+=t),n.pick<=r||n.pick>=u)break;return n};r.prototype.scope=function(n){var t=this.item.min.pick,i=this.item.max.pick;return this.create(n.pick>i?i:n.pick<t?t:n)};r.prototype.parse=function(n,t,r){var s,c,l,f,o,h=this,e={};if(!t||typeof t!="string")return t;r&&r.format||(r=r||{},r.format=h.settings.format);h.formats.toArray(r.format).map(function(n){var r,u=h.formats[n],f=u?i.trigger(u,h,[t,e]):n.replace(/^!/,"").length;u&&(r=t.substr(0,f),e[n]=r.match(/^\d+$/)?+r:r);t=t.substr(f)});for(f in e)o=e[f],i.isInteger(o)?f.match(/^(h|hh)$/i)?(s=o,(f=="h"||f=="hh")&&(s%=12)):f=="i"&&(c=o):f.match(/^a$/i)&&o.match(/^p/i)&&("h"in e||"hh"in e)&&(l=!0);return(l?s+12:s)*u+c};r.prototype.formats={h:function(n,t){return n?i.digits(n):t.hour%o||o},hh:function(n,t){return n?2:i.lead(t.hour%o||o)},H:function(n,t){return n?i.digits(n):""+t.hour%24},HH:function(n,t){return n?i.digits(n):i.lead(t.hour%24)},i:function(n,t){return n?2:i.lead(t.mins)},a:function(n,t){return n?4:f/2>t.time%f?"a.m.":"p.m."},A:function(n,t){return n?2:f/2>t.time%f?"AM":"PM"},toArray:function(n){return n.split(/(h{1,2}|H{1,2}|i|a|A|!.)/g)},toString:function(n,t){var r=this;return r.formats.toArray(n).map(function(n){return i.trigger(r.formats[n],r,[0,t])||n.replace(/^!/,"")}).join("")}};r.prototype.isTimeExact=function(n,r){var u=this;return i.isInteger(n)&&i.isInteger(r)||typeof n=="boolean"&&typeof r=="boolean"?n===r:(i.isDate(n)||t.isArray(n))&&(i.isDate(r)||t.isArray(r))?u.create(n).pick===u.create(r).pick:t.isPlainObject(n)&&t.isPlainObject(r)?u.isTimeExact(n.from,r.from)&&u.isTimeExact(n.to,r.to):!1};r.prototype.isTimeOverlap=function(n,r){var u=this;return i.isInteger(n)&&(i.isDate(r)||t.isArray(r))?n===u.create(r).hour:i.isInteger(r)&&(i.isDate(n)||t.isArray(n))?r===u.create(n).hour:t.isPlainObject(n)&&t.isPlainObject(r)?u.overlapRanges(n,r):!1};r.prototype.flipEnable=function(n){var t=this.item;t.enable=n||(t.enable==-1?1:-1)};r.prototype.deactivate=function(n,r){var f=this,u=f.item.disable.slice(0);return r=="flip"?f.flipEnable():r===!1?(f.flipEnable(1),u=[]):r===!0?(f.flipEnable(-1),u=[]):r.map(function(n){for(var e,r=0;r<u.length;r+=1)if(f.isTimeExact(n,u[r])){e=!0;break}e||(i.isInteger(n)||i.isDate(n)||t.isArray(n)||t.isPlainObject(n)&&n.from&&n.to)&&u.push(n)}),u};r.prototype.activate=function(n,r){var f=this,u=f.item.disable,e=u.length;return r=="flip"?f.flipEnable():r===!0?(f.flipEnable(1),u=[]):r===!1?(f.flipEnable(-1),u=[]):r.map(function(n){for(var o,s,h,r=0;r<e;r+=1)if(s=u[r],f.isTimeExact(s,n)){o=u[r]=null;h=!0;break}else if(f.isTimeOverlap(s,n)){t.isPlainObject(n)?(n.inverted=!0,o=n):t.isArray(n)?(o=n,o[2]||o.push("inverted")):i.isDate(n)&&(o=[n.getFullYear(),n.getMonth(),n.getDate(),"inverted"]);break}if(o)for(r=0;r<e;r+=1)if(f.isTimeExact(u[r],n)){u[r]=null;break}if(h)for(r=0;r<e;r+=1)if(f.isTimeOverlap(u[r],n)){u[r]=null;break}o&&u.push(o)}),u.filter(function(n){return n!=null})};r.prototype.i=function(n,t){return i.isInteger(t)&&t>0?t:this.item.interval};r.prototype.nodes=function(n){var t=this,r=t.settings,u=t.item.select,f=t.item.highlight,e=t.item.view,o=t.item.disable;return i.node("ul",i.group({min:t.item.min.pick,max:t.item.max.pick,i:t.item.interval,node:"li",item:function(n){n=t.create(n);var s=n.pick,h=u&&u.pick==s,c=f&&f.pick==s,l=o&&t.disabled(n),a=i.trigger(t.formats.toString,t,[r.format,n]);return[i.trigger(t.formats.toString,t,[i.trigger(r.formatLabel,t,[n])||r.format,n]),function(n){return h&&n.push(r.klass.selected),c&&n.push(r.klass.highlighted),e&&e.pick==s&&n.push(r.klass.viewset),l&&n.push(r.klass.disabled),n.join(" ")}([r.klass.listItem]),"data-pick="+n.pick+" "+i.ariaAttr({role:"option",label:a,selected:h&&t.$node.val()===a?!0:null,activedescendant:c?!0:null,disabled:l?!0:null})]}})+i.node("li",i.node("button",r.clear,r.klass.buttonClear,"type=button data-clear=1"+(n?"":" disabled")+" "+i.ariaAttr({controls:t.$node[0].id})),"",i.ariaAttr({role:"presentation"})),r.klass.list,i.ariaAttr({role:"listbox",controls:t.$node[0].id}))};r.defaults=function(n){return{clear:"Clear",format:"h:i A",interval:30,closeOnSelect:!0,closeOnClear:!0,updateInput:!0,klass:{picker:n+" "+n+"--time",holder:n+"__holder",list:n+"__list",listItem:n+"__list-item",disabled:n+"__list-item--disabled",selected:n+"__list-item--selected",highlighted:n+"__list-item--highlighted",viewset:n+"__list-item--viewset",now:n+"__list-item--now",buttonClear:n+"__button--clear"}}}(n.klasses().picker);n.extend("pickatime",r)});
|
|
// Gets Json object using a given url.
|
|
function GetJson(url) {
|
|
var result;
|
|
$.ajaxSetup({ async: false });
|
|
$.post(url, function (data) {
|
|
result = data;
|
|
});
|
|
$.ajaxSetup({ async: true });
|
|
return result;
|
|
}
|
|
|
|
function maxLength(field, maxChars) {
|
|
if (field.value.length >= maxChars) {
|
|
event.returnValue = false;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
function maxLengthPaste(field, maxChars) {
|
|
var copyText = field.value + window.clipboardData.getData("Text");
|
|
field.value = copyText.substr(0, maxChars);
|
|
if ((copyText.length) > maxChars) {
|
|
return false;
|
|
}
|
|
event.returnValue = true;
|
|
}
|
|
|
|
function appendFilesToInput(appendInput, mainFileInput) {
|
|
const valid = validateMultiFileUpload(appendInput, true);
|
|
if (!valid) {
|
|
alert("The file you are trying to upload is not an allowed file type or has an invalid file name. These are the accepted characters: A-z 0-9 ~ ! ( ) - + = [ ] { } , . _");
|
|
appendInput.value = null;
|
|
return;
|
|
}
|
|
const dt = new DataTransfer();
|
|
const originalFiles = Array.from(mainFileInput.files);
|
|
const existingSet = new Set(originalFiles.map(({ name, size, lastModified }) => `${name}-${size}-${lastModified}`));
|
|
originalFiles.forEach(file => {
|
|
dt.items.add(file);
|
|
})
|
|
const addedFiles = Array.from(appendInput.files);
|
|
addedFiles.forEach(file => {
|
|
if (!existingSet.has(`${file.name}-${file.size}-${file.lastModified}`)) {
|
|
dt.items.add(file);
|
|
}
|
|
})
|
|
mainFileInput.files = dt.files;
|
|
mainFileInput.dispatchEvent(new Event("change"));
|
|
}
|
|
|
|
function fileAppender(mainFileInput) {
|
|
const li = document.createElement("li");
|
|
const label = document.createElement("label");
|
|
label.innerHTML = '<span class="visuallyhidden">Add Files</span><svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24" aria-hidden="true"><path d="M440-440H200v-80h240v-240h80v240h240v80H520v240h-80v-240Z"/></svg>'
|
|
const appendInput = document.createElement("input");
|
|
appendInput.type = "file";
|
|
appendInput.multiple = true;
|
|
appendInput.className = "visuallyhidden";
|
|
appendInput.addEventListener("change", e => {
|
|
appendFilesToInput(appendInput, mainFileInput);
|
|
});
|
|
label.appendChild(appendInput);
|
|
const placeholder = document.createElement("span");
|
|
li.appendChild(placeholder);
|
|
li.appendChild(label);
|
|
return li;
|
|
}
|
|
|
|
function removeFileFromInput(input, idx) {
|
|
const dt = new DataTransfer();
|
|
const files = Array.from(input.files);
|
|
files.splice(idx, 1);
|
|
files.forEach(file => {
|
|
dt.items.add(file);
|
|
});
|
|
input.files = dt.files;
|
|
}
|
|
|
|
function listItemFile(input, file, idx) {
|
|
const li = document.createElement("li");
|
|
const fileNameSpan = document.createElement("span");
|
|
fileNameSpan.textContent = file.name;
|
|
li.appendChild(fileNameSpan);
|
|
const removeButton = document.createElement("button");
|
|
removeButton.title = `Remove ${file.name}`;
|
|
removeButton.addEventListener("click", e => {
|
|
e.preventDefault();
|
|
removeFileFromInput(input, idx);
|
|
input.dispatchEvent(new Event("change"));
|
|
});
|
|
removeButton.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24" fill="darkred" aria-hidden="true"><path d="m336-280 144-144 144 144 56-56-144-144 144-144-56-56-144 144-144-144-56 56 144 144-144 144 56 56ZM480-80q-83 0-156-31.5T197-197q-54-54-85.5-127T80-480q0-83 31.5-156T197-763q54-54 127-85.5T480-880q83 0 156 31.5T763-763q54 54 85.5 127T880-480q0 83-31.5 156T763-197q-54 54-127 85.5T480-80Zm0-80q134 0 227-93t93-227q0-134-93-227t-227-93q-134 0-227 93t-93 227q0 134 93 227t227 93Zm0-320Z"></path></svg>';
|
|
const removeButtonSpan = document.createElement("span");
|
|
removeButtonSpan.textContent = `Remove ${file.name}`;
|
|
removeButtonSpan.className = "visuallyhidden";
|
|
removeButton.appendChild(removeButtonSpan);
|
|
li.appendChild(removeButton);
|
|
return li;
|
|
}
|
|
|
|
function listFileInputFiles(fileInput) {
|
|
if (fileInput.nextElementSibling?.className === "file-list") {
|
|
fileInput.nextElementSibling.remove();
|
|
}
|
|
const files = fileInput.files || [];
|
|
const valid = files.length && validateMultiFileUpload(fileInput, true)
|
|
if (!valid) {
|
|
fileInput.value = null;
|
|
return;
|
|
}
|
|
if (files.length > 1) {
|
|
const ul = document.createElement("ul");
|
|
ul.className = "file-list";
|
|
for (let i = 0; i < files.length; i++) {
|
|
const li = listItemFile(fileInput, files[i], i);
|
|
ul.appendChild(li);
|
|
}
|
|
const fileAppenderLi = fileAppender(fileInput);
|
|
ul.appendChild(fileAppenderLi);
|
|
fileInput.after(ul);
|
|
}
|
|
}
|
|
|
|
function initFileInputs() {
|
|
if (!window.FeatureToggles.isActive("CMS.FormCenter.MultipleFileUploadEnhancement")) {
|
|
return;
|
|
}
|
|
document.querySelectorAll(".FileUpload input[type='file']").forEach(fileInput => {
|
|
listFileInputFiles(fileInput);
|
|
fileInput.addEventListener("change", e => {
|
|
listFileInputFiles(fileInput)
|
|
})
|
|
});
|
|
}
|
|
;
|
|
/// <reference path="../../../../Scripts/jquery-1.3.2-vsdoc.js">
|
|
/// <reference path="../../../../Scripts/json2.js">
|
|
/// <reference path="../../../../Scripts/HtmlExtensionSupport.js">
|
|
/// <reference path="../../../../Scripts/UrlLibrary.js">
|
|
/// <reference path="../../../../Common/GlobalJSFunctionsDetail.js" />
|
|
/// <reference path="Conditions.js" />
|
|
/// <reference path="Validation.js" />
|
|
/// <reference path="/Assets/Scripts/DynamicForm/PrintSubmission.js" />
|
|
/// <reference path="../../../../Assets/Scripts/DateTimePicker/cp.datetimepicker.js" />
|
|
|
|
//////// ENUMERATIONS ///////////
|
|
var FormCenterHomeJS = (function ($) {
|
|
var self = this;
|
|
|
|
// This value is set on the ItemDetailFE.ascx page, using a value from the
|
|
// View Model.
|
|
self.submissionURL = '';
|
|
|
|
return self;
|
|
}(jQuery));
|
|
|
|
var FormCenterHomeScriptResources = GetJson("/FormCenter/Localization");
|
|
|
|
// Field formats.
|
|
var FieldValidation = {
|
|
Any: 0,
|
|
Letters: 1,
|
|
Numeric: 2,
|
|
PhoneNumber: 3,
|
|
PostalCode: 4,
|
|
EmailAddress: 5,
|
|
RegEx: 6,
|
|
Currency: 7,
|
|
Date: 8,
|
|
Time: 9,
|
|
DateTime: 10,
|
|
DateSpan: 11,
|
|
TimeSpan: 12,
|
|
DateTimeSpan: 13
|
|
};
|
|
|
|
// Supported field types.
|
|
var FieldTypes = {
|
|
ShortAnswer: 1,
|
|
LongAnswer: 2,
|
|
Checkboxes: 3,
|
|
RadioButtons: 4,
|
|
Dropdown: 5,
|
|
FileUpload: 6,
|
|
ReplyEmail: 10,
|
|
Password: 11,
|
|
DateTime: 18,
|
|
EPayment: 23
|
|
};
|
|
|
|
//////// GLOBALS ////////////////
|
|
|
|
// Stores instantiated form validator/condition instances for the current form.
|
|
var formValidators = null;
|
|
var formConditions = null;
|
|
|
|
// Use with form submissions.
|
|
var redirectNewWindow = null;
|
|
|
|
//////// SUPPORT METHODS ////////f
|
|
|
|
// Shows extra information for a form detail.
|
|
function moreInfo(id) {
|
|
changeInfo(id, 'addClass', 'removeClass');
|
|
}
|
|
|
|
// Hides extra information for a form detail.
|
|
function lessInfo(id) {
|
|
changeInfo(id, 'removeClass', 'addClass');
|
|
}
|
|
|
|
// Internal. Hides or shows extra information.
|
|
function changeInfo(id, lessFn, moreFn) {
|
|
$([document.getElementById('spnLess' + id), document.getElementById('lnkMore' + id)])[lessFn]('hidden');
|
|
$(document.getElementById('spnMore' + id))[moreFn]('hidden');
|
|
}
|
|
|
|
// Used to expand and collapse category sections.
|
|
function expandCollaspseCategory(catID) {
|
|
var $elem = $('#section' + catID);
|
|
var $elemArrow = $('#span' + catID);
|
|
var supportSlide = !isie7; // If we can find a better way to detect the incorrect behavior IE7 mode is exhibiting, go for it...
|
|
|
|
if ($elem.is(':visible')) {
|
|
$elemArrow.html('►');
|
|
|
|
if (supportSlide)
|
|
$elem.slideUp(150);
|
|
else
|
|
$elem.hide();
|
|
}
|
|
else {
|
|
$elemArrow.html('▼');
|
|
|
|
if (supportSlide)
|
|
$elem.slideDown(150);
|
|
else
|
|
$elem.show();
|
|
}
|
|
}
|
|
|
|
// Assigns bulk action checkbox group to jQuery expression.
|
|
function assignBulkCheckGroup($children, $parent) {
|
|
// Isolate children from parent in event of overlap ($children containing the $parent).
|
|
// Vastly simplifies the logic in the event handlers by doing this.
|
|
var $isolatedChildren = $children.filter(
|
|
function (index) {
|
|
return $parent.filter(this).length == 0;
|
|
}
|
|
);
|
|
|
|
// Cache current number of checked children.
|
|
$isolatedChildren.lengthChecked = $isolatedChildren.filter(':checked').length;
|
|
|
|
// Hook childen change.
|
|
$isolatedChildren.change(function (event) {
|
|
$isolatedChildren.lengthChecked += (this.checked ? 1 : -1);
|
|
|
|
var allChildrenChecked = ($isolatedChildren.lengthChecked == $isolatedChildren.length);
|
|
|
|
$parent.each(function () {
|
|
this.checked = allChildrenChecked;
|
|
});
|
|
});
|
|
|
|
// Hook parent change.
|
|
$parent.change(function (event) {
|
|
var parentChecked = this.checked;
|
|
|
|
$isolatedChildren.lengthChecked = this.checked ? $isolatedChildren.length : 0;
|
|
|
|
$isolatedChildren.each(function () {
|
|
this.checked = parentChecked;
|
|
});
|
|
});
|
|
}
|
|
|
|
function initCommon() {
|
|
// Hack: Internet Explorer 7 mode bizarre display bugs.
|
|
$('.sidebar .search.noStyles').css('zoom', '1');
|
|
|
|
// Hook search category bulk checks.
|
|
var $bulkCheck = $('#categoryFilter_0');
|
|
var $bulkGroup = $('.categoryList input[type="checkbox"]');
|
|
|
|
assignBulkCheckGroup($bulkGroup, $bulkCheck);
|
|
|
|
// Get input jQuery refs.
|
|
var $inputFCTerm = $('#inputFCTerm');
|
|
var $inputFCSearch = $('#inputFCSearch');
|
|
|
|
// Hook search button and textbox to capture enter key.
|
|
$inputFCTerm.keydown(function (event) {
|
|
// If user presses enter, submit for search.
|
|
switch (event.which) {
|
|
case 10: // LF
|
|
case 13: // CR
|
|
event.preventDefault();
|
|
$inputFCSearch.click();
|
|
break;
|
|
}
|
|
});
|
|
|
|
$inputFCSearch.click(function (event) {
|
|
event.preventDefault();
|
|
|
|
// Build search querystring.
|
|
var qs = {};
|
|
var formID = extractFormIDFromURL(location.href);
|
|
var categoryID = extractCategoryIDFromURL(location.href);
|
|
|
|
var searchTerm = $.trim(isNull($inputFCTerm.val(), ''));
|
|
|
|
if (formID)
|
|
qs.formID = formID;
|
|
|
|
if (categoryID)
|
|
qs.categoryID = categoryID;
|
|
|
|
if (searchTerm != '')
|
|
qs.term = searchTerm;
|
|
|
|
if (!$bulkCheck[0].checked) {
|
|
var values = [];
|
|
|
|
$bulkGroup.filter(':checked').each(function () {
|
|
values.push(this.value);
|
|
});
|
|
|
|
if (values.length > 0)
|
|
qs.categoryFilter = values.join(',');
|
|
}
|
|
|
|
// Set search URL and go.
|
|
window.location = '/FormCenter/Search' + (new QueryStringBuilder(qs)).toString();
|
|
});
|
|
|
|
// Hook search category dropdown.
|
|
var $catList = $('#categoryList');
|
|
var $catListToggle = $('a.mega');
|
|
|
|
$catListToggle.click(function (e) {
|
|
e.preventDefault();
|
|
|
|
if ($catList.hasClass('open'))
|
|
$catList.slideUp(300);
|
|
else
|
|
$catList.slideDown(300);
|
|
|
|
$catListToggle.toggleClass('active');
|
|
$catList.toggleClass('open');
|
|
});
|
|
|
|
// Show print dialog if URL contains standard print query string.
|
|
if (containsPrint(location.href))
|
|
window.print();
|
|
}
|
|
|
|
// Initialization for search (Search.aspx view).
|
|
function initSearch() {
|
|
if (this.wasInit)
|
|
return;
|
|
|
|
this.wasInit = true;
|
|
|
|
attachCategoryVisibilityTogglers();
|
|
|
|
initCommon();
|
|
}
|
|
|
|
// Initialization for category/form list (Index.aspx view).
|
|
function initCategoryList() {
|
|
if (this.wasInit)
|
|
return;
|
|
|
|
this.wasInit = true;
|
|
|
|
attachCategoryVisibilityTogglers();
|
|
|
|
initCommon();
|
|
}
|
|
|
|
// Initialization for confirmation (Confirmation.aspx view).
|
|
function initConfirmation() {
|
|
if (this.wasInit)
|
|
return;
|
|
|
|
this.wasInit = true;
|
|
|
|
initCommon();
|
|
}
|
|
|
|
function initPostSubmissionSpam() {
|
|
initCommon();
|
|
}
|
|
|
|
|
|
// Causes current form to be submitted for printing.
|
|
function printForm(saveData) {
|
|
if (formValidate()) { //check for valid responses before creating print window
|
|
var frm = document.aspnetForm;
|
|
var $form = $(document.aspnetForm);
|
|
var printPrevType = (getPrintPreviewType() == 1 ? 'Print' : 'Preview');
|
|
var formID = extractFormIDFromURL(location.href);
|
|
|
|
$form.attr('target', 'PrintWindow');
|
|
// Change target/action to print data.
|
|
frm.action = '/FormCenter/Print?formID=' + formID + '&' + printPrevType + '=YES&Save=' + (saveData ? 'True' : 'False');
|
|
|
|
var savedProgressID = $('#hdnSavedProgressID').val();
|
|
|
|
if (savedProgressID != null) {
|
|
frm.action = frm.action + '&savedProgressID=' + savedProgressID;
|
|
}
|
|
|
|
submitForm($form, true, saveData, formID);
|
|
} else {
|
|
enableDisableSubmit(true);
|
|
}
|
|
}
|
|
|
|
function submitFormHandler(e) {
|
|
try {
|
|
let json = JSON.parse(this.response);
|
|
|
|
if (this.status !== 200 || json.Success !== true) {
|
|
$('.submissionConfirmationMessage').html("Something went wrong with the submission. Please try again.");
|
|
$('.submissionConfirmation').show();
|
|
enableDisableSubmit(true);
|
|
return;
|
|
}
|
|
|
|
const responseUrl = this.responseURL && new URL(this.responseURL);
|
|
const saveSubmission = responseUrl && responseUrl.searchParams.get('save') && responseUrl.searchParams.get('save').toLowerCase() === 'true';
|
|
const doPrint = responseUrl && responseUrl.searchParams.get('print') && responseUrl.searchParams.get('print').toLowerCase() === 'true';
|
|
const redirectAfterSubmissionSaved = json.Redirect && json.Redirect === true && saveSubmission === true;
|
|
|
|
if (redirectAfterSubmissionSaved)
|
|
{
|
|
if (json.IsHttps === false) {
|
|
json.Message = json.Message.replace("https://", "http://");
|
|
}
|
|
|
|
location.href = json.Message;
|
|
}
|
|
else {
|
|
if (saveSubmission === true) {
|
|
$('.submissionConfirmationMessage').html(json.Message);
|
|
typeof displayStep === "function" && displayStep(1);
|
|
$('.submitForm').hide();
|
|
$('.submissionConfirmation').show();
|
|
}
|
|
|
|
enableDisableSubmit(true);
|
|
}
|
|
|
|
if (doPrint === true) {
|
|
window.open("/FormCenter/Print?formId=" + json.FormId.toString() + '&save=' + (saveSubmission ? 'True' : 'False'));
|
|
}
|
|
|
|
if (json.Success === true) {
|
|
document.aspnetForm.reset();
|
|
|
|
if (typeof (grecaptcha) !== "undefined")
|
|
{
|
|
grecaptcha.reset();
|
|
}
|
|
}
|
|
|
|
!window.isRemoveSetHeights && typeof SetHeights === "function" && SetHeights();
|
|
} catch (ex) {
|
|
console.log(ex);
|
|
}
|
|
}
|
|
|
|
function submitForm($form, print, saveSubmission, formID) {
|
|
var submittedViaWidget = $form.parents(".widgetBody").length > 0;
|
|
|
|
serializeSpecialFields();
|
|
|
|
if (submittedViaWidget !== true) {
|
|
var printWindow;
|
|
|
|
if (print === true)
|
|
{
|
|
printWindow = window.open("", "PrintWindow");
|
|
|
|
// Deferred object at the window level, this becomes available on the opened window via window.opener
|
|
// Whichever view gets rendered will resolve it and pass the appropriate status code.
|
|
window.deferredStatus = $.Deferred();
|
|
}
|
|
|
|
if (printWindow) {
|
|
window.deferredStatus.done(function (status) {
|
|
if (status === CP_DynamicForm_PrintSubmission.StatusCodes.Success) {
|
|
if (saveSubmission && saveSubmission === true) {
|
|
window.location = '/FormCenter/Confirmation?formID=' + formID;
|
|
}
|
|
} else {
|
|
printWindow.close();
|
|
|
|
if (saveSubmission) {
|
|
window.location = '/FormCenter/ErrorSubmittingForm?formID=' + formID;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
$form.submit();
|
|
}
|
|
else {
|
|
var submissionUrl = FormCenterHomeJS.submissionURL + (saveSubmission ? '&save=True' : "");
|
|
submissionUrl = submissionUrl + (print ? '&print=True' : '');
|
|
|
|
var formData = new FormData($form[0]);
|
|
var xhr = new XMLHttpRequest();
|
|
xhr.onload = submitFormHandler;
|
|
xhr.onloadend = ajaxPostBackEnd;
|
|
xhr.onerror = function() { console.log("A network error occurred with the transaction."); };
|
|
xhr.open("POST", submissionUrl);
|
|
ajaxPostBackStart('Loading');
|
|
xhr.send(formData);
|
|
}
|
|
}
|
|
|
|
function serializeSpecialFields()
|
|
{
|
|
submitCheckboxHandler();
|
|
|
|
submitRadioHandler();
|
|
|
|
submitDateTimeHandler();
|
|
}
|
|
|
|
// Initialization for form item printing (Print.aspx view).
|
|
function initFormPrint() {
|
|
if (this.wasInit)
|
|
return;
|
|
|
|
this.wasInit = true;
|
|
|
|
initCommon();
|
|
|
|
initValidationAndConditions();
|
|
|
|
registerFormPrintEvents();
|
|
}
|
|
|
|
function initValidationAndConditions() {
|
|
// Register validation.
|
|
if (typeof getFormValidatorData !== 'undefined') {
|
|
formValidators = FieldValidator.createMulti(getFormValidatorData());
|
|
} else {
|
|
formValidators = [];
|
|
}
|
|
|
|
// Register conditions.
|
|
if (getFormConditionData) {
|
|
formConditions = FieldConditions.createMulti(getFormConditionData(), formValidators);
|
|
} else {
|
|
formConditions = FieldConditions.createMulti([], formValidators);
|
|
}
|
|
|
|
// Register condition runner and make initial condition check.
|
|
formConditionsRunner = new FieldConditionsRunner(formConditions, $.noop, true, false); // TODO: Finish up, put debugCondRunnerLogResults for $.noop.
|
|
formConditionsRunner.run();
|
|
}
|
|
|
|
// Initialization for form item (Item.aspx view).
|
|
function initForm() {
|
|
|
|
if (this.wasInit)
|
|
return;
|
|
|
|
this.wasInit = true;
|
|
|
|
initCommon();
|
|
initValidationAndConditions();
|
|
initDatePickers();
|
|
initTimePickers();
|
|
initFileInputs();
|
|
registerFormEvents(); // Hook up buttons (submit/cancel/etc).
|
|
}
|
|
|
|
function initDatePickers() {
|
|
if ($('.telerikDatePicker').length > 0) {
|
|
$('.telerikDatePicker').each(function () {
|
|
if (typeof (cp) !== 'undefined')
|
|
cp.datetimepicker && cp.datetimepicker.createDatePicker(this);
|
|
});
|
|
}
|
|
}
|
|
|
|
function initTimePickers() {
|
|
if ($('.formCenterTimePicker').length > 0) {
|
|
$('.formCenterTimePicker').focusout(function () {
|
|
var text = $(this).val().toLowerCase();
|
|
if (text != '' && (text.indexOf('am') < 0 && text.indexOf('pm') < 0)) {
|
|
text = $(this).val().trim() + ' AM';
|
|
$(this).val(text);
|
|
}
|
|
});
|
|
}
|
|
else {
|
|
$('.telerikTimePicker').each(function () {
|
|
if (typeof (cp) !== 'undefined')
|
|
cp.datetimepicker && cp.datetimepicker.createTimePicker(this,
|
|
{
|
|
format: 'h:i A'
|
|
});
|
|
});
|
|
}
|
|
}
|
|
|
|
// Registers event handlers for form.
|
|
function registerFormPrintEvents() {
|
|
$('#btnFormContinue').click(function (event) {
|
|
event.preventDefault();
|
|
|
|
var frm = document.aspnetForm;
|
|
var formID = extractFormIDFromURL(location.href);
|
|
var $form = $(document.aspnetForm);
|
|
|
|
// Change target/action to confirmation page.
|
|
frm.action = '/FormCenter/Confirmation?formID=' + formID;
|
|
|
|
$form.trigger("submit");
|
|
});
|
|
}
|
|
|
|
// Registers event handlers for form.
|
|
function registerFormEvents() {
|
|
// Remove troublemakers (prevent the jQuery submit handler from ever firing, and not needed for this screen anyways).
|
|
document.aspnetForm.removeAttribute('onsubmit');
|
|
document.aspnetForm.onsubmit = null;
|
|
|
|
var $form = $(document.aspnetForm);
|
|
|
|
// TODO: Plug this into other validation.
|
|
var wantCopyAddr, $wantCopyAddrLI;
|
|
var wantCopy = document.getElementById('wantCopy');
|
|
|
|
if (wantCopy) {
|
|
wantCopyAddr = document.getElementById('wantCopyAddress');
|
|
$wantCopyAddrLI = $(wantCopyAddr.parentNode.parentNode);
|
|
|
|
$(wantCopy).change(function (event) {
|
|
if (this.checked)
|
|
$wantCopyAddrLI.show();
|
|
else
|
|
$wantCopyAddrLI.hide();
|
|
});
|
|
}
|
|
|
|
/* #2378*/
|
|
$(document).keydown(function (event) {
|
|
if ((event.keyCode == 13) &&
|
|
(event.target.id != 'btnFormSubmit') &&
|
|
(event.target.tagName != 'TEXTAREA') &&
|
|
(event.target.getAttribute('data-enable-enter-keypress') != 'true')) {
|
|
event.preventDefault();
|
|
return false;
|
|
}
|
|
if (event.keyCode === 32 &&
|
|
event.target.tagName === 'A' &&
|
|
event.target.getAttribute('role') === 'button') {
|
|
$(event.target).trigger('click');
|
|
event.preventDefault();
|
|
return false;
|
|
}
|
|
});
|
|
|
|
$form.submit(function () {
|
|
// Run validation, stop data submission if necessary.
|
|
if (!formValidate()) {
|
|
enableDisableSubmit(true);
|
|
return false;
|
|
}
|
|
|
|
if (isNull(redirectNewWindow, '').trimEnd() != '')
|
|
window.open(redirectNewWindow);
|
|
|
|
// Field types requiring special serialization code (radio/check/file).
|
|
// The deserialization of dictionaries from forms in MVC 2 is somewhat sensitive.
|
|
serializeSpecialFields();
|
|
return true;
|
|
});
|
|
|
|
$('#btnFormSubmit').click(function (event) {
|
|
event.preventDefault();
|
|
if (formValidate()) {
|
|
processSubmit(handleSubmitClick);
|
|
}
|
|
});
|
|
|
|
$('#btnFormPrint').click(function (event) {
|
|
event.preventDefault();
|
|
printForm(false);
|
|
});
|
|
|
|
$('#btnFormSubmitPrint').click(function (event) {
|
|
event.preventDefault();
|
|
if (formValidate()) {
|
|
processSubmit(handleSubmitPrintClick);
|
|
}
|
|
});
|
|
|
|
$('#btnCalculateTotals').click(function (e) {
|
|
e.preventDefault();
|
|
|
|
submitCheckboxHandler();
|
|
submitRadioHandler();
|
|
|
|
if (formValidate()) {
|
|
var $form = $(document.aspnetForm);
|
|
|
|
$.ajax({
|
|
url: '/FormCenter/Home/CalculateTotal?formID=' + $('#hdnFormID').val(),
|
|
type: 'POST',
|
|
data: $(document.aspnetForm).serializeArray(),
|
|
success: function (response) {
|
|
openCpModal({
|
|
title: FormCenterHomeScriptResources.CalculateTotals,
|
|
className: 'modalCalculateTotal',
|
|
isFrontEnd: window.location.href.indexOf('/Admin/FormCenter') == -1,
|
|
htmlContent: response
|
|
});
|
|
},
|
|
error: function (xhr, textStatus, exception) {
|
|
alert('Error: ' + xhr.statusText + '\nStatus: ' + xhr.status);
|
|
}
|
|
});
|
|
}
|
|
});
|
|
|
|
$('#btnProceedToCheckOut').click(function (e) {
|
|
proceedToCheckOut(e);
|
|
});
|
|
|
|
if ($('.submissionConfirmationOk').length > 0)
|
|
{
|
|
$('.submissionConfirmationOk').unbind('click').click(function () {
|
|
$('.submitForm').show();
|
|
$('.submissionConfirmation').hide();
|
|
!window.isRemoveSetHeights && typeof SetHeights === "function" && SetHeights();
|
|
})
|
|
}
|
|
}
|
|
|
|
function onRecaptchaLoadCallback() {
|
|
window.gRecaptchaClientId = grecaptcha.render('inline-recaptcha', {
|
|
'sitekey': document.querySelector('#inline-recaptcha').dataset.sitekey,
|
|
'badge': 'inline',
|
|
'size': 'invisible'
|
|
});
|
|
}
|
|
|
|
function processSubmit(callback) {
|
|
const recaptcha = document.querySelector('.recaptcha');
|
|
if (!recaptcha) {
|
|
callback();
|
|
return;
|
|
}
|
|
|
|
if (!window.FeatureToggles.isActive("CMS.FormCenter.RecaptchaV3Enabled")) {
|
|
window.recaptchaCallback = callback;
|
|
grecaptcha.execute();
|
|
return;
|
|
}
|
|
|
|
grecaptcha.ready(function() {
|
|
grecaptcha.execute(window.gRecaptchaClientId, { action: 'submit' }).then(callback);
|
|
});
|
|
}
|
|
|
|
function enableDisableSubmit(enable) {
|
|
if (enable) {
|
|
if ($('#btnFormSubmitPrint').length > 0) {
|
|
$('#btnFormSubmitPrint').removeClass('inactive');
|
|
$('#btnFormSubmitPrint').click(function (event) {
|
|
handleSubmitPrintClick();
|
|
});
|
|
}
|
|
if ($('#btnFormSubmit').length > 0) {
|
|
$('#btnFormSubmit').removeClass('inactive');
|
|
$('#btnFormSubmit').click(function (event) {
|
|
event.preventDefault();
|
|
if (formValidate()) {
|
|
closeModalDialog('editItemBehavior');
|
|
processSubmit(handleSubmitClick);
|
|
}
|
|
});
|
|
}
|
|
} else {
|
|
if ($('#btnFormSubmitPrint').length > 0) {
|
|
$('#btnFormSubmitPrint').addClass("inactive");
|
|
$('#btnFormSubmitPrint').unbind();
|
|
}
|
|
if ($('#btnFormSubmit').length > 0) {
|
|
$('#btnFormSubmit').addClass("inactive");
|
|
$('#btnFormSubmit').unbind();
|
|
}
|
|
}
|
|
}
|
|
|
|
function handleSubmitClick() {
|
|
enableDisableSubmit(false);
|
|
|
|
var frm = document.aspnetForm;
|
|
|
|
frm.action = FormCenterHomeJS.submissionURL;
|
|
|
|
var $form = $(frm);
|
|
|
|
var formID = extractFormIDFromURL(location.href);
|
|
|
|
submitForm($form, false, true, formID);
|
|
}
|
|
|
|
function handleSubmitPrintClick() {
|
|
enableDisableSubmit(false);
|
|
printForm(true);
|
|
}
|
|
|
|
function proceedToCheckOut(e) {
|
|
e.preventDefault();
|
|
|
|
serializeSpecialFields();
|
|
|
|
if (formValidate()) {
|
|
var savedProgressID = $('#hdnSavedProgressID').val();
|
|
|
|
var submitUrl = '/FormCenter/Home/ProceedToCheckOut?formID=' + $('#hdnFormID').val();
|
|
|
|
if (savedProgressID != null) {
|
|
submitUrl = submitUrl + '&savedProgressID=' + savedProgressID;
|
|
}
|
|
|
|
var $form = $(document.aspnetForm);
|
|
$form.attr('action', submitUrl);
|
|
$form.submit();
|
|
}
|
|
}
|
|
|
|
function getModalDialogObjects() {
|
|
return {
|
|
iframe: document.getElementById('liveEditDialog'),
|
|
windowElement: $('.modalContainerCP')[0],
|
|
behavior: $find('editItemBehavior'),
|
|
titleElement: $('.modalTitle')[0]
|
|
};
|
|
}
|
|
|
|
function getBooleanValue(value) {
|
|
switch (value.toLowerCase()) {
|
|
case "true": case "yes": case "1": return true;
|
|
case "false": case "no": case "0": case null: return false;
|
|
default: return Boolean(string);
|
|
}
|
|
}
|
|
|
|
// Validates current form.
|
|
function formValidate() {
|
|
var assignFocus = true;
|
|
var ERROR_MSG = FormCenterHomeScriptResources.InvalidEmailAddressFormat2;
|
|
var wantCopy = document.getElementById('wantCopy');
|
|
|
|
if (wantCopy) {
|
|
wantCopyAddr = document.getElementById('wantCopyAddress');
|
|
$wantCopyAddrLI = $(wantCopyAddr.parentNode.parentNode);
|
|
|
|
if ($wantCopyAddrLI.is(":visible") && wantCopyAddr.value != "") {
|
|
if (!emailValidate(wantCopyAddr.value)) {
|
|
errorShow(ERROR_MSG);
|
|
return false;
|
|
}
|
|
}
|
|
if (wantCopy.checked == false)//if the checkbox is not checked then empty the emailaddress textbox
|
|
document.getElementById('wantCopyAddress').value = "";
|
|
}
|
|
else {
|
|
//if the checkbox is not checked then empty the emailaddress textbox
|
|
var wantCopyAddress = document.getElementById('wantCopyAddress');
|
|
if (wantCopyAddress)
|
|
document.getElementById('wantCopyAddress').value = "";
|
|
}
|
|
|
|
if (typeof formValidators.keys !== 'undefined') {
|
|
var liCurrent = $('li.current');
|
|
var step = "1";
|
|
if (liCurrent.length > 0) {
|
|
step = liCurrent.attr('id').replace('liStep', '');
|
|
}
|
|
|
|
var wizardStep = $('#wizard' + step);
|
|
|
|
if (wizardStep.length > 0) {
|
|
wizardStep = wizardStep[0];
|
|
}
|
|
|
|
var checkForInappropriateWords = new Array();
|
|
|
|
for (var i = 0, len = formValidators.keys.length; i < len; i++) {
|
|
var key = formValidators.keys[i];
|
|
|
|
if (wizardStep == null || wizardStep.length == 0 || (wizardStep != null && wizardStep.contains(formValidators[key].elemContainer))) {
|
|
if (!formValidators[key].validate(assignFocus, checkForInappropriateWords))
|
|
assignFocus = false;
|
|
}
|
|
}
|
|
|
|
if (validateInappropriateWords(checkForInappropriateWords)) {
|
|
assignFocus = false;
|
|
}
|
|
}
|
|
|
|
return assignFocus;
|
|
}
|
|
|
|
function validateInappropriateWords(checkForInappropriateWords) {
|
|
var data = {};
|
|
var j = 0;
|
|
var inappropriateWordsFound = false;
|
|
|
|
for (j = 0; j < checkForInappropriateWords.length; j++) {
|
|
data[checkForInappropriateWords[j].id.toString()] = checkForInappropriateWords[j].elemInput.value.trim();
|
|
}
|
|
|
|
var results = HasInappropriateWordsMultiple(data);
|
|
|
|
for (j = 0; j < checkForInappropriateWords.length; j++) {
|
|
if (results[checkForInappropriateWords[j].id.toString()] == true) {
|
|
checkForInappropriateWords[j].handleInappropriateWords(inappropriateWordsFound == false);
|
|
|
|
inappropriateWordsFound = true;
|
|
}
|
|
}
|
|
|
|
return inappropriateWordsFound;
|
|
}
|
|
|
|
// Displays error message if email validation failed
|
|
function errorShow(message) {
|
|
var elemContainer = $('.anonEmail');
|
|
elemContainer.addClass('error');
|
|
|
|
if (elemContainer.find('.explanation').length == 0) {
|
|
var errorElemMsg = document.createElement('p');
|
|
errorElemMsg.className = 'explanation';
|
|
errorElemMsg.innerHTML = message;
|
|
elemContainer.append(errorElemMsg);
|
|
}
|
|
}
|
|
|
|
// Determines if URL contains print querystring.
|
|
function containsPrint(url) {
|
|
return url.search(/(&|\?)print\=yes(&.*|)$/i) > -1;
|
|
}
|
|
|
|
|
|
// Extracts CategoryID identifier from URL specified.
|
|
function extractCategoryIDFromURL(url) {
|
|
|
|
url = url.toLowerCase();
|
|
var indexOfForm = url.lastIndexOf("/formcenter/");
|
|
if (indexOfForm > -1) {
|
|
//extract the categoryurl
|
|
var formURL = url.substr(indexOfForm + 12);
|
|
|
|
|
|
//check whether u have formid appended
|
|
|
|
if (formURL.indexOf("/") > -1)
|
|
formURL = formURL.substr(0, formURL.indexOf("/"));
|
|
//check wheter the url has categoryName
|
|
var indexOfHypen = formURL.lastIndexOf("-");
|
|
var categoryID = 0;
|
|
if (indexOfHypen > -1)
|
|
categoryID = formURL.substr(indexOfHypen + 1);
|
|
else
|
|
categoryID = formURL;
|
|
return categoryID;
|
|
}
|
|
else
|
|
return null;
|
|
}
|
|
|
|
|
|
// Extracts form identifier from URL specified.
|
|
function extractFormIDFromURL(url) {
|
|
return $('#hdnFormID').val();
|
|
|
|
//there is some query string appended if so remove those
|
|
if (url.indexOf("/?") > -1) {
|
|
url = url.substr(0, url.indexOf("/?"));
|
|
}
|
|
|
|
url = url.toLowerCase();
|
|
var indexOfForm = url.lastIndexOf("/formcenter/");
|
|
if (indexOfForm > -1) {
|
|
//extract the url
|
|
var formURL = url.substr(indexOfForm + 12);
|
|
|
|
if (formURL.indexOf("/") > -1) {
|
|
formURL = formURL.substr(formURL.indexOf("/") + 1);
|
|
//check wheter the url has categoryName
|
|
var indexOfHypen = formURL.lastIndexOf("-");
|
|
var formID = 0;
|
|
if (indexOfHypen > -1)
|
|
formID = formURL.substr(indexOfHypen + 1);
|
|
else if (formURL == "")
|
|
return null;
|
|
else
|
|
formID = formURL;
|
|
|
|
return formID;
|
|
}
|
|
else
|
|
return null;
|
|
}
|
|
else
|
|
return null;
|
|
}
|
|
|
|
// Attaches expand/collapse handlers to section headers.
|
|
function attachCategoryVisibilityTogglers() {
|
|
$('#FormCenterContent .contentMain').click(function (event) {
|
|
var target = event.target;
|
|
|
|
if (/^lnkLess[0-9]*$/.test(target.id)) {
|
|
// Form details should shrink.
|
|
lessInfo(parseInt(target.id.substr(7), 10));
|
|
event.preventDefault();
|
|
}
|
|
else if (/^lnkMore[0-9]*$/.test(target.id)) {
|
|
// Form details should expand.
|
|
moreInfo(parseInt(target.id.substr(7), 10));
|
|
event.preventDefault();
|
|
}
|
|
else {
|
|
// If the arrow glyph was clicked, treat it as if the header was clicked.
|
|
if (target.nodeName == 'SPAN' && target.className == 'arrow')
|
|
target = target.parentNode;
|
|
|
|
// If the header was clicked.
|
|
if (target.nodeName == 'H2') {
|
|
var parent = target.parentNode;
|
|
|
|
// If parent identifier matches expected pattern, get category ID and expand/collapse it.
|
|
if (parent && /^cat[0-9]*$/.test(parent.id)) {
|
|
var categoryID = parseInt(parent.id.substr(3), 10);
|
|
|
|
expandCollaspseCategory(categoryID);
|
|
|
|
// Prevent default code from executing.
|
|
event.preventDefault();
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
// Called by form submittal to handle radio buttons for serialization.
|
|
function submitRadioHandler() {
|
|
var names = [];
|
|
var values = {};
|
|
$checkBoxSelector = null;
|
|
$webCheckBox = $('div.formWrap ol.cpForm input[type=radio]');
|
|
$mobileCheckBox = $('.mobileGuts input[type=radio]');
|
|
|
|
if ($webCheckBox.length != 0 && $mobileCheckBox.length == 0)
|
|
$checkBoxSelector = $webCheckBox;
|
|
|
|
if ($webCheckBox.length == 0 && $mobileCheckBox.length != 0)
|
|
$checkBoxSelector = $mobileCheckBox;
|
|
if ($checkBoxSelector != null) {
|
|
// Get list of radio inputs and their values.
|
|
$checkBoxSelector.each(function () {
|
|
if (!names.contains(this.name)) {
|
|
names.push(this.name);
|
|
values[this.name] = '';
|
|
}
|
|
|
|
if (this.checked)
|
|
values[this.name] = this.value;
|
|
})
|
|
}
|
|
|
|
// Map collected data to hidden inputs submitted to server.
|
|
for (var i = 0, len = names.length; i < len; i++) {
|
|
var inputName = names[i];
|
|
var id = (inputName.match(/_/g) || []).length == 2 ? inputName.substring(inputName.indexOf('_') + 1) : inputName;
|
|
document.getElementById(id).value = values[inputName];
|
|
}
|
|
}
|
|
|
|
// Called by form submittal to handle checkboxes for serialization.
|
|
function submitCheckboxHandler() {
|
|
var names = [];
|
|
var values = {};
|
|
$checkBoxSelector = null;
|
|
$webCheckBox = $('div.formWrap ol.cpForm input[type=checkbox]');
|
|
$mobileCheckBox = $('.mobileGuts input[type=checkbox]');
|
|
|
|
if ($webCheckBox.length != 0 && $mobileCheckBox.length == 0)
|
|
$checkBoxSelector = $webCheckBox;
|
|
|
|
if ($webCheckBox.length == 0 && $mobileCheckBox.length != 0)
|
|
$checkBoxSelector = $mobileCheckBox;
|
|
|
|
// Get list of checkbox inputs and their values.
|
|
if ($checkBoxSelector != null) {
|
|
$checkBoxSelector.each(function () {
|
|
if (!names.contains(this.name)) {
|
|
names.push(this.name);
|
|
values[this.name] = [];
|
|
}
|
|
|
|
if (this.checked) {
|
|
var val = this.value.replace(/\,/g, '_comma_');
|
|
if (val.trim() == '')
|
|
val = '_empty_value_';
|
|
values[this.name].push(val);
|
|
|
|
}
|
|
});
|
|
}
|
|
|
|
// Map collected data to hidden inputs submitted to server.
|
|
for (var i = 0, len = names.length; i < len; i++)
|
|
document.getElementById(names[i]).value = values[names[i]].join(',');
|
|
}
|
|
|
|
function submitDateTimeHandler() {
|
|
var names = [];
|
|
var values = [];
|
|
|
|
var $datePickers, $timePickers;
|
|
if ($('.formCenterDatePicker').length > 0) {
|
|
$datePickers = $('.formCenterDatePicker');
|
|
$timePickers = $('.formCenterTimePicker');
|
|
}
|
|
else {
|
|
$datePickers = $('.telerikDatePicker');
|
|
$timePickers = $('.telerikTimePicker');
|
|
}
|
|
|
|
$datePickers.each(function () {
|
|
if (!names.contains(this.name)) {
|
|
names.push(this.name);
|
|
values[this.name] = [this.value];
|
|
}
|
|
else {
|
|
values[this.name].push(this.value);
|
|
}
|
|
});
|
|
|
|
$timePickers.each(function () {
|
|
if (!names.contains(this.name)) {
|
|
names.push(this.name);
|
|
values[this.name] = ['', '', this.value];
|
|
}
|
|
else {
|
|
if (values[this.name].length == 1) {
|
|
values[this.name].push('');
|
|
}
|
|
values[this.name].push(this.value);
|
|
}
|
|
});
|
|
|
|
for (var i = 0, len = names.length; i < len; i++)
|
|
document.getElementById(names[i]).value = values[names[i]].join();
|
|
}
|
|
|
|
$(function () {
|
|
$('#aspnetForm,form[name=aspnetForm]').attr('enctype', "multipart/form-data");
|
|
$('#aspnetForm,form[name=aspnetForm]').addClass('submitForm');
|
|
});
|
|
;
|
|
|
|
/// <reference path="../../../../Scripts/jquery-1.3.2-vsdoc.js" />
|
|
/// <reference path="../../../../Scripts/json2.js" />
|
|
/// <reference path="../../../../Scripts/HtmlExtensionSupport.js" />
|
|
/// <reference path="../../../../Common/GlobalJSFunctionsDetail.js" />
|
|
/// <reference path="../../../../Assets/Scripts/InappropriateWords.js" />
|
|
/// <reference path="Home.js" />
|
|
|
|
// Overridden on a per-form basis. Retrieves validator information for submission validation.
|
|
//function getFormValidatorData() {
|
|
// return [];
|
|
//}
|
|
|
|
var FormCenterHomeScriptResources = GetJson("/FormCenter/Localization");
|
|
var originalTextColorRadio;
|
|
var originalTextColorCheckBox;
|
|
|
|
$.getScript("/Assets/Scripts/InappropriateWords.js");
|
|
|
|
// Used to properly localize strings.
|
|
String.prototype.format = function () {
|
|
var args = arguments;
|
|
return this.replace(/{(\d+)}/g, function (match, number) {
|
|
return typeof args[number] != 'undefined'
|
|
? args[number]
|
|
: match
|
|
;
|
|
});
|
|
};
|
|
|
|
|
|
// Performs validation for a form field.
|
|
function FieldValidator(elemInput, id, fieldType, isInternal, isRequired, formatValidation, formatValidationCustom, minAnswer, maxAnswer, maxLength) {
|
|
var self = this;
|
|
this.elemInputIsArray = $.isArray(elemInput);
|
|
|
|
if (this.elemInputIsArray) {
|
|
for (var i = 0, len = elemInput.length; i < len; i++) {
|
|
if (typeof (elemInput[i]) == "string")
|
|
elemInput[i] = document.getElementById(elemInput[i]);
|
|
}
|
|
|
|
if (elemInput.length > 0 && elemInput[0]) {
|
|
this.elemContainer = elemInput[0].parentNode.parentNode.parentNode.parentNode;
|
|
this.elemInput = elemInput;
|
|
}
|
|
}
|
|
else {
|
|
if (typeof (elemInput) == "string")
|
|
elemInput = document.getElementById(elemInput);
|
|
|
|
if (elemInput) {
|
|
if (fieldType === FieldTypes.DatePicker)
|
|
this.elemContainer = elemInput.parentNode.parentNode.parentNode.parentNode;
|
|
else
|
|
this.elemContainer = elemInput.parentNode.parentNode;
|
|
this.elemInput = elemInput;
|
|
}
|
|
}
|
|
|
|
this.enabled = true;
|
|
this.$elemContainer = $(this.elemContainer);
|
|
this.fieldType = fieldType;
|
|
if (this.fieldType === FieldTypes.FileUpload) {
|
|
//When validating <input type="file" />, we need to update the elemInput reference since it is created a new one when failing inside validateFileUpload method - LC
|
|
var validateInputFileUpload = function () {
|
|
var $this = $(this);
|
|
if (!validateMultiFileUpload($this[0], true)) {
|
|
alert("The file you are trying to upload is not an allowed file type or has an invalid file name. These are the accepted characters: A-z 0-9 ~ ! ( ) - + = [ ] { } , . _");
|
|
self.elemInput.value = null;
|
|
self.elemInput.focus();
|
|
}
|
|
};
|
|
$(this.elemInput).attr("change", "").removeAttr("onChange").unbind("change").change(validateInputFileUpload);
|
|
}
|
|
this.id = id;
|
|
this.isInternal = isInternal;
|
|
this.isRequired = isRequired;
|
|
this.formatValidation = formatValidation;
|
|
this.formatValidationCustom = formatValidationCustom;
|
|
this.elemErrorNote = null;
|
|
this.minAnswer = minAnswer;
|
|
this.maxAnswer = maxAnswer;
|
|
this.maxLength = maxLength;
|
|
this.focusElementIndex = 0;
|
|
this.alreadySetFocus = false;
|
|
var isRequiredInfoLableDisplayed = false;
|
|
if (this.isRequired && !isRequiredInfoLableDisplayed) {
|
|
$('#liRequiredFieldInfo').show();
|
|
isRequiredInfoLableDisplayed = true;
|
|
}
|
|
}
|
|
|
|
// Static. Creates a single validation object from an options object.
|
|
FieldValidator.createSingle = function (options) {
|
|
return new FieldValidator(
|
|
options.elemInput,
|
|
options.id,
|
|
options.fieldType,
|
|
options.isInternal,
|
|
options.isRequired,
|
|
options.formatValidation,
|
|
options.formatValidationCustom,
|
|
options.minAnswer,
|
|
options.maxAnswer,
|
|
options.maxLength);
|
|
};
|
|
|
|
// Static. Creates array of validation objects from array of option objects.
|
|
FieldValidator.createMulti = function (optionsArray) {
|
|
var key = null;
|
|
|
|
var ret = {
|
|
keys: [],
|
|
add: function (validator) {
|
|
var key = validator.id;
|
|
this[key] = validator;
|
|
this.keys.push(key);
|
|
}
|
|
};
|
|
|
|
for (var i = 0, len = optionsArray.length; i < len; i++)
|
|
ret.add(FieldValidator.createSingle(optionsArray[i]));
|
|
|
|
return ret;
|
|
};
|
|
|
|
// Internal. Used by validation() to ensure the input does not exceed the max length.
|
|
FieldValidator.prototype.validateMaxLength = function (errorMsg) {
|
|
var ERROR_MSG = FormCenterHomeScriptResources.MaxLengthErrorMessage.format(this.maxLength);
|
|
if (this.maxLength == null)
|
|
return true;
|
|
var cleanValue = this.elemInput.value.trim();
|
|
if (cleanValue != null && cleanValue.length > this.maxLength) {
|
|
errorMsg.push(ERROR_MSG);
|
|
return false;
|
|
}
|
|
return true;
|
|
};
|
|
|
|
// Internal. Used by validate() to ensure user-enterable fields are phone numbers (of NANP format).
|
|
FieldValidator.prototype.validatePhone = function (errorMsg) {
|
|
var ERROR_MSG = this.getErrorMessage(FormCenterHomeScriptResources.InvalidPhoneNumberFormat);
|
|
var origLen = errorMsg.length;
|
|
|
|
switch (this.fieldType) {
|
|
case FieldTypes.ShortAnswer:
|
|
case FieldTypes.LongAnswer:
|
|
case FieldTypes.Password:
|
|
var cleanValue = this.elemInput.value.trim();
|
|
if (cleanValue != '' && !phoneValidate(cleanValue, intCountryCode))
|
|
errorMsg.push(ERROR_MSG);
|
|
break;
|
|
}
|
|
|
|
// Return false if validation check failed (number of errors changed).
|
|
return origLen == errorMsg.length;
|
|
}
|
|
|
|
// Internal. Used by validate() to ensure user-enterable fields are postal codes.
|
|
FieldValidator.prototype.validatePostal = function (errorMsg) {
|
|
var ERROR_MSG = this.getErrorMessage(FormCenterHomeScriptResources.InvalidPostalCode);
|
|
var origLen = errorMsg.length;
|
|
|
|
switch (this.fieldType) {
|
|
case FieldTypes.ShortAnswer:
|
|
case FieldTypes.LongAnswer:
|
|
case FieldTypes.Password:
|
|
var cleanValue = this.elemInput.value.trim();
|
|
|
|
if (cleanValue != '' && !isZipCode(cleanValue, intCountryCode))
|
|
errorMsg.push(ERROR_MSG);
|
|
|
|
break;
|
|
}
|
|
|
|
// Return false if validation check failed (number of errors changed).
|
|
return origLen == errorMsg.length;
|
|
}
|
|
|
|
// Internal. Used by validate() to ensure user-enterable fields are email addresses.
|
|
FieldValidator.prototype.validateEmail = function (errorMsg) {
|
|
var ERROR_MSG = this.getErrorMessage(FormCenterHomeScriptResources.InvalidEmailAddressFormat2);
|
|
var origLen = errorMsg.length;
|
|
|
|
switch (this.fieldType) {
|
|
case FieldTypes.ShortAnswer:
|
|
case FieldTypes.LongAnswer:
|
|
case FieldTypes.Password:
|
|
case FieldTypes.ReplyEmail:
|
|
var cleanValue = this.elemInput.value.trim();
|
|
|
|
if (cleanValue != '' && !emailValidate(cleanValue))
|
|
errorMsg.push(ERROR_MSG);
|
|
|
|
break;
|
|
}
|
|
|
|
// Return false if validation check failed (number of errors changed).
|
|
return origLen == errorMsg.length;
|
|
}
|
|
function getNumericThousandsSeparator() {
|
|
var dummy = new Number(11111111);
|
|
var thousandSep = dummy.toLocaleString().replace(/1/g, '');
|
|
return (thousandSep.length > 0 ? thousandSep.charAt(0) : ',');
|
|
}
|
|
// Internal. Used by validate() to ensure user-enterable fields match custom expression.
|
|
FieldValidator.prototype.validateCustom = function (expr, errorMsg) {
|
|
var ERROR_MSG = this.getErrorMessage(FormCenterHomeScriptResources.InvalidGenericField);
|
|
var origLen = errorMsg.length;
|
|
|
|
var customRegExp = new RegExp(expr);
|
|
|
|
switch (this.fieldType) {
|
|
case FieldTypes.ShortAnswer:
|
|
case FieldTypes.LongAnswer:
|
|
case FieldTypes.Password:
|
|
var cleanValue = this.elemInput.value.trim();
|
|
|
|
if (cleanValue != '' && (!customRegExp.test(cleanValue)))
|
|
errorMsg.push(ERROR_MSG);
|
|
|
|
break;
|
|
}
|
|
|
|
// Return false if validation check failed (number of errors changed).
|
|
return origLen == errorMsg.length;
|
|
}
|
|
|
|
// Internal. Used by validate() to ensure user-enterable fields are letters only.
|
|
FieldValidator.prototype.validateLetters = function (errorMsg) {
|
|
var ERROR_MSG = this.getErrorMessage(FormCenterHomeScriptResources.LettersOnlyField);
|
|
var origLen = errorMsg.length;
|
|
|
|
var letterRegExp = /^[a-zA-Z ]+$/;
|
|
|
|
switch (this.fieldType) {
|
|
case FieldTypes.ShortAnswer:
|
|
case FieldTypes.LongAnswer:
|
|
case FieldTypes.Password:
|
|
var cleanValue = this.elemInput.value.trim();
|
|
|
|
if (cleanValue != '' && (!letterRegExp.test(cleanValue)))
|
|
errorMsg.push(ERROR_MSG);
|
|
|
|
break;
|
|
}
|
|
|
|
// Return false if validation check failed (number of errors changed).
|
|
return origLen == errorMsg.length;
|
|
}
|
|
|
|
// Internal. Used by validate() to ensure user-enterable fields are numeric (numbers, decimal separators, and group separators allowed).
|
|
FieldValidator.prototype.validateNumeric = function (errorMsg) {
|
|
var ERROR_MSG = this.getErrorMessage(FormCenterHomeScriptResources.NumericOnlyField);
|
|
var origLen = errorMsg.length;
|
|
|
|
var numericRegExp = new RegExp("^(([0-9]+?[0-9%G]+?[0-9](|%D[0-9]+))|([0-9]*(|%D[0-9]+)))$"
|
|
.replace(/%G/g, RegExp.metaEscape(getNumericThousandsSeparator()))
|
|
.replace(/%D/g, RegExp.metaEscape(getNumericDecimalSeparator())));
|
|
|
|
switch (this.fieldType) {
|
|
case FieldTypes.ShortAnswer:
|
|
case FieldTypes.LongAnswer:
|
|
case FieldTypes.Password:
|
|
var cleanValue = this.elemInput.value.trim();
|
|
|
|
if (cleanValue != '' && (!numericRegExp.test(cleanValue)))
|
|
errorMsg.push(ERROR_MSG);
|
|
|
|
break;
|
|
}
|
|
|
|
// Return false if validation check failed (number of errors changed).
|
|
return origLen == errorMsg.length;
|
|
}
|
|
|
|
FieldValidator.prototype.validateCurrency = function (errorMsg) {
|
|
var ERROR_MSG = this.getErrorMessage(FormCenterHomeScriptResources.CurrencyOnlyField);
|
|
var origLen = errorMsg.length;
|
|
|
|
var value = this.elemInput.value.trim();
|
|
|
|
if (!this.isValidMoney(value)) {
|
|
errorMsg.push(ERROR_MSG);;
|
|
}
|
|
|
|
// Return false if validation check failed (nubmer of errors changed).
|
|
return origLen == errorMsg.length;
|
|
}
|
|
|
|
FieldValidator.prototype.isValidMoney = function (value) {
|
|
if (value && value != '')
|
|
return /^\$?\s*\d+(,\d{3})*(\.\d{1,2})?$/.test(value);
|
|
else
|
|
return true;
|
|
};
|
|
|
|
//#region Date Validation
|
|
|
|
FieldValidator.prototype.validateDate = function (errorMsg) {
|
|
var origLen = errorMsg.length;
|
|
|
|
var date = $(this.elemInput[0]).val();
|
|
var ERROR_MSG = this.getErrorMessage(FormCenterHomeScriptResources.InvalidDate);
|
|
|
|
if (!this.isValidDate(date, this.isRequired)) {
|
|
errorMsg.push(ERROR_MSG);
|
|
}
|
|
|
|
// Return false if validation check failed (nubmer of errors changed).
|
|
return origLen == errorMsg.length;
|
|
}
|
|
|
|
FieldValidator.prototype.validateTime = function (errorMsg) {
|
|
var origLen = errorMsg.length;
|
|
|
|
var time = $(this.elemInput[0]).val();
|
|
var ERROR_MSG = this.getErrorMessage(FormCenterHomeScriptResources.InvalidTime);
|
|
|
|
if (!this.isValidTime(time, this.isRequired)) {
|
|
errorMsg.push(ERROR_MSG);
|
|
}
|
|
|
|
// Return false if validation check failed (nubmer of errors changed).
|
|
return origLen == errorMsg.length;
|
|
}
|
|
|
|
FieldValidator.prototype.validateDateTime = function (errorMsg) {
|
|
var origLen = errorMsg.length;
|
|
|
|
var date = $(this.elemInput[0]).val();
|
|
|
|
var time = $(this.elemInput[1]).val();
|
|
|
|
var validDate = this.isValidDate(date, this.isRequired);
|
|
|
|
var validTime = this.isValidTime(time, this.isRequired);
|
|
var ERROR_MSG = this.getErrorMessage(FormCenterHomeScriptResources.InvalidDateTime);
|
|
|
|
if (!validDate && !validTime) {
|
|
if (!this.alreadySetFocus) {
|
|
this.focusElementIndex = 0;
|
|
this.alreadySetFocus = true;
|
|
}
|
|
errorMsg.push(ERROR_MSG);
|
|
}
|
|
else if (!validDate) {
|
|
if (!this.alreadySetFocus) {
|
|
this.focusElementIndex = 0;
|
|
this.alreadySetFocus = true;
|
|
}
|
|
errorMsg.push(ERROR_MSG);
|
|
}
|
|
else if (!validTime) {
|
|
if (!this.alreadySetFocus) {
|
|
this.focusElementIndex = 1;
|
|
this.alreadySetFocus = true;
|
|
}
|
|
errorMsg.push(ERROR_MSG);
|
|
}
|
|
|
|
// Return false if validation check failed (nubmer of errors changed).
|
|
return origLen == errorMsg.length;
|
|
}
|
|
|
|
FieldValidator.prototype.validateDateSpan = function (errorMsg) {
|
|
var origLen = errorMsg.length;
|
|
|
|
var date = $(this.elemInput[0]).val();
|
|
|
|
var dateSpan = $(this.elemInput[1]).val();
|
|
|
|
if (!this.isValidDate(date, this.isRequired)) {
|
|
if (!this.alreadySetFocus) {
|
|
this.focusElementIndex = 0;
|
|
this.alreadySetFocus = true;
|
|
}
|
|
errorMsg.push(FormCenterHomeScriptResources.InvalidStartDate);
|
|
}
|
|
if (!this.isValidDate(dateSpan, this.isRequired)) {
|
|
if (!this.alreadySetFocus) {
|
|
this.focusElementIndex = 1;
|
|
this.alreadySetFocus = true;
|
|
}
|
|
errorMsg.push(FormCenterHomeScriptResources.InvalidEndDate);
|
|
}
|
|
if (!this.isValidDateRange(date, dateSpan, this.isRequired, this.isRequired)) {
|
|
if (!this.alreadySetFocus) {
|
|
this.focusElementIndex = 0;
|
|
this.alreadySetFocus = true;
|
|
}
|
|
errorMsg.push(FormCenterHomeScriptResources.InvalidDateSpan);
|
|
}
|
|
|
|
// Return false if validation check failed (nubmer of errors changed).
|
|
return origLen == errorMsg.length;
|
|
}
|
|
|
|
FieldValidator.prototype.validateTimeSpan = function (errorMsg) {
|
|
var origLen = errorMsg.length;
|
|
|
|
var time = $(this.elemInput[0]).val();
|
|
|
|
var timeSpan = $(this.elemInput[1]).val();
|
|
|
|
if (!this.isValidTime(time, this.isRequired)) {
|
|
if (!this.alreadySetFocus) {
|
|
this.focusElementIndex = 0;
|
|
this.alreadySetFocus = true;
|
|
}
|
|
errorMsg.push(FormCenterHomeScriptResources.InvalidStartTime);
|
|
}
|
|
|
|
if (!this.isValidTime(timeSpan, this.isRequired)) {
|
|
if (!this.alreadySetFocus) {
|
|
this.focusElementIndex = 1;
|
|
this.alreadySetFocus = true;
|
|
}
|
|
errorMsg.push(FormCenterHomeScriptResources.InvalidEndTime);
|
|
}
|
|
|
|
if (!this.isValidTimeRange(time, timeSpan, this.isRequired, this.isRequired)) {
|
|
if (!this.alreadySetFocus) {
|
|
this.focusElementIndex = 0;
|
|
this.alreadySetFocus = true;
|
|
}
|
|
errorMsg.push(FormCenterHomeScriptResources.InvalidTimeSpan);
|
|
}
|
|
|
|
// Return false if validation check failed (nubmer of errors changed).
|
|
return origLen == errorMsg.length;
|
|
}
|
|
|
|
FieldValidator.prototype.validateDateTimeSpan = function (errorMsg) {
|
|
var origLen = errorMsg.length;
|
|
|
|
var date = $(this.elemInput[0]).val();
|
|
|
|
var dateSpan = $(this.elemInput[1]).val();
|
|
|
|
var time = $(this.elemInput[2]).val();
|
|
|
|
var timeSpan = $(this.elemInput[3]).val();
|
|
|
|
var validDate = this.isValidDate(date, this.isRequired);
|
|
|
|
var validTime = this.isValidTime(time, this.isRequired);
|
|
|
|
if (!validDate && !validTime) {
|
|
if (!this.alreadySetFocus) {
|
|
this.focusElementIndex = 0;
|
|
this.alreadySetFocus = true;
|
|
}
|
|
errorMsg.push(FormCenterHomeScriptResources.InvalidStartDateTime);
|
|
}
|
|
else if (!validDate) {
|
|
if (!this.alreadySetFocus) {
|
|
this.focusElementIndex = 0;
|
|
this.alreadySetFocus = true;
|
|
}
|
|
errorMsg.push(FormCenterHomeScriptResources.InvalidStartDate);
|
|
}
|
|
else if (!validTime) {
|
|
if (!this.alreadySetFocus) {
|
|
this.focusElementIndex = 2;
|
|
this.alreadySetFocus = true;
|
|
}
|
|
errorMsg.push(FormCenterHomeScriptResources.InvalidStartTime);
|
|
}
|
|
|
|
var validSpanDate = this.isValidDate(dateSpan, this.isRequired);
|
|
|
|
var validSpanTime = this.isValidTime(timeSpan, this.isRequired);
|
|
|
|
if (!validSpanDate && !validSpanTime) {
|
|
if (!this.alreadySetFocus) {
|
|
this.focusElementIndex = 1;
|
|
this.alreadySetFocus = true;
|
|
}
|
|
errorMsg.push(FormCenterHomeScriptResources.InvalidEndDateTime);
|
|
}
|
|
else if (!validSpanDate) {
|
|
if (!this.alreadySetFocus) {
|
|
this.focusElementIndex = 1;
|
|
this.alreadySetFocus = true;
|
|
}
|
|
errorMsg.push(FormCenterHomeScriptResources.InvalidEndDate);
|
|
}
|
|
else if (!validSpanTime) {
|
|
if (!this.alreadySetFocus) {
|
|
this.focusElementIndex = 3;
|
|
this.alreadySetFocus = true;
|
|
}
|
|
errorMsg.push(FormCenterHomeScriptResources.InvalidEndTime);
|
|
}
|
|
var isDayBeforeMonth = getDateFormat().toLocaleLowerCase() == "dd/mm/yyyy" ? true : false;
|
|
if (!this.isValidDateTimeRange(getDateFromInput(this.elemInput[0], isDayBeforeMonth), getDateFromInput(this.elemInput[1], isDayBeforeMonth), this.isRequired, this.isRequired, time, timeSpan, this.isRequired, this.isRequired)) {
|
|
if (!this.alreadySetFocus) {
|
|
this.focusElementIndex = 1;
|
|
this.alreadySetFocus = true;
|
|
}
|
|
errorMsg.push(FormCenterHomeScriptResources.InvalidDateTimeSpan);
|
|
}
|
|
|
|
// Return false if validation check failed (nubmer of errors changed).
|
|
return origLen == errorMsg.length;
|
|
}
|
|
|
|
FieldValidator.prototype.isValidDate = function (date, dateRequired) {
|
|
var objDate = new dateValidator();
|
|
|
|
return objDate.dateValidateNew(date, dateRequired, '');
|
|
}
|
|
|
|
FieldValidator.prototype.isValidTime = function (time, timeRequired) {
|
|
var objDate = new dateValidator();
|
|
|
|
var amIndex = time.toLowerCase().indexOf('am');
|
|
var pmIndex = time.toLowerCase().indexOf('pm');
|
|
|
|
if (amIndex != -1) {
|
|
time = time.substring(0, amIndex).replace(" ", "");
|
|
|
|
if (time.trim() === "") {
|
|
return false;
|
|
}
|
|
}
|
|
else if (pmIndex != -1) {
|
|
time = time.substring(0, pmIndex).replace(" ", "");
|
|
|
|
if (time.trim() === "") {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return objDate.timeValidate(time, timeRequired, '');
|
|
}
|
|
|
|
FieldValidator.prototype.isValidTimeRange = function (startTime, endTime, startTimeRequired, endTimeRequired) {
|
|
var objDate = new dateValidator();
|
|
|
|
objDate.ysnAllowTimeOnly = true;
|
|
objDate.dtiStartTime = this.stripAMPM(startTime);
|
|
objDate.ysnStartTimeRequired = startTimeRequired;
|
|
objDate.dtiEndTime = this.stripAMPM(endTime);
|
|
objDate.ysnEndTimeRequired = endTimeRequired;
|
|
objDate.ysnDatesAreEqual = true;
|
|
objDate.strStartAMPM = this.getAMPMString(startTime);
|
|
objDate.strEndAMPM = this.getAMPMString(endTime);
|
|
objDate.timesAlreadyValidated = true;
|
|
|
|
return objDate.timeOrderValidate();
|
|
|
|
}
|
|
|
|
FieldValidator.prototype.isValidDateRange = function (startDate, endDate, startDateRequired, endDateRequired) {
|
|
var objDate = new dateValidator();
|
|
objDate.setStartDate(checkDateFormatReturnUSString(startDate, false));
|
|
objDate.setEndDate(checkDateFormatReturnUSString(endDate, false));
|
|
objDate.setStartDateRequired(startDateRequired);
|
|
objDate.setEndDateRequired(endDateRequired);
|
|
objDate.datesAlreadyValidated = true;
|
|
|
|
return objDate.dateOrderValidateNew();
|
|
}
|
|
|
|
FieldValidator.prototype.isValidDateTimeRange = function (startDate, endDate, startDateRequired, endDateRequired, startTime, endTime, startTimeRequired, endTimeRequired) {
|
|
var objDate = new dateValidator();
|
|
|
|
|
|
objDate.setStartDate(checkDateFormatReturnUSString(startDate, false));
|
|
objDate.setEndDate(checkDateFormatReturnUSString(endDate, false));
|
|
objDate.setStartDateRequired(startDateRequired);
|
|
objDate.setEndDateRequired(endDateRequired);
|
|
objDate.ysnAllowEqualDates = true;
|
|
objDate.datesAlreadyValidated = true;
|
|
|
|
var validDateOrder = objDate.dateOrderValidateNew();
|
|
|
|
objDate.strStartAMPM = this.getAMPMString(startTime);
|
|
objDate.strEndAMPM = this.getAMPMString(endTime);
|
|
objDate.ysnAllowTimeOnly = true;
|
|
objDate.ysnAllowEqualTimes = !objDate.ysnDatesAreEqual;
|
|
objDate.dtiStartTime = this.stripAMPM(startTime);
|
|
objDate.ysnStartTimeRequired = startTimeRequired;
|
|
objDate.dtiEndTime = this.stripAMPM(endTime);
|
|
objDate.ysnEndTimeRequired = endTimeRequired;
|
|
objDate.timesAlreadyValidated = true;
|
|
|
|
var validTimeOrder = objDate.timeOrderValidate();
|
|
|
|
return validDateOrder && validTimeOrder;
|
|
}
|
|
|
|
FieldValidator.prototype.stripAMPM = function (time) {
|
|
var amIndex = time.toLowerCase().indexOf('am');
|
|
var pmIndex = time.toLowerCase().indexOf('pm');
|
|
|
|
if (amIndex != -1) {
|
|
time = time.substring(0, amIndex).replace(" ", "");
|
|
}
|
|
else if (pmIndex != -1) {
|
|
time = time.substring(0, pmIndex).replace(" ", "");
|
|
}
|
|
|
|
return time;
|
|
}
|
|
|
|
FieldValidator.prototype.getAMPMString = function (time) {
|
|
var amIndex = time.toLowerCase().indexOf('am');
|
|
var pmIndex = time.toLowerCase().indexOf('pm');
|
|
|
|
var AMPM = '';
|
|
|
|
if (amIndex != -1) {
|
|
AMPM = time.substring(amIndex).replace(" ", "");
|
|
}
|
|
else if (pmIndex != -1) {
|
|
AMPM = time.substring(pmIndex).replace(" ", "");
|
|
}
|
|
return AMPM;
|
|
}
|
|
|
|
//#endregion Date Validation
|
|
|
|
//checkbox validation done for min and max answer selcted
|
|
FieldValidator.prototype.checkBoxValidation = function (errorMsg) {
|
|
var origLen = errorMsg.length;
|
|
if (this.fieldType == FieldTypes.Checkboxes && ((this.minAnswer != "" && this.minAnswer != undefined) || (this.maxAnswer != "" && this.maxAnswer != undefined))) {
|
|
var i = 0, len = this.elemInput.length;
|
|
var checkedCount = 0;
|
|
for (; i < len; i++) {
|
|
if (this.elemInput[i].checked)
|
|
checkedCount++;
|
|
}
|
|
//validation for checkbox min max fields
|
|
if (this.minAnswer != "" && this.minAnswer != undefined) {
|
|
if (checkedCount < parseInt(this.minAnswer, 10)) // the message is not localized on purpose as this has text and dynamic value
|
|
errorMsg.push("This field must have at least " + parseInt(this.minAnswer, 10) + " options selected");
|
|
}
|
|
|
|
if (origLen == errorMsg.length && this.maxAnswer != "" && this.maxAnswer != undefined) {
|
|
if (checkedCount > parseInt(this.maxAnswer, 10)) // the message is not localized on purpose as this has text and dynamic value
|
|
errorMsg.push("This field can have at most " + parseInt(this.maxAnswer, 10) + " options selected");
|
|
}
|
|
}
|
|
|
|
// Return false if validation check failed (number of errors changed).
|
|
return origLen == errorMsg.length;
|
|
};
|
|
|
|
// Internal. Used by validate() to ensure required fields are set.
|
|
FieldValidator.prototype.validateRequired = function (errorMsg) {
|
|
var ERROR_MSG = this.getErrorMessage(FormCenterHomeScriptResources.RequiredField);//FormCenterHomeScriptResources.RequiredField;
|
|
var origLen = errorMsg.length;
|
|
switch (this.fieldType) {
|
|
case FieldTypes.ShortAnswer:
|
|
case FieldTypes.LongAnswer:
|
|
case FieldTypes.ReplyEmail:
|
|
case FieldTypes.Password:
|
|
case FieldTypes.FileUpload:
|
|
case FieldTypes.CodeSnippet:
|
|
case FieldTypes.InputLink:
|
|
case FieldTypes.DatePicker:
|
|
case FieldTypes.InputImage:
|
|
if (this.elemInput != 'undefined' && this.elemInput.value != undefined) {
|
|
if (this.elemInput.value.trim() == '')
|
|
errorMsg.push(ERROR_MSG);
|
|
}
|
|
break;
|
|
case FieldTypes.Checkboxes:
|
|
case FieldTypes.RadioButtons:
|
|
var i = 0, len = this.elemInput.length;
|
|
|
|
for (; i < len; i++) {
|
|
if (this.elemInput[i].checked)
|
|
break;
|
|
}
|
|
|
|
if (i == len)
|
|
errorMsg.push(ERROR_MSG);
|
|
|
|
break;
|
|
case FieldTypes.Dropdown:
|
|
case FieldTypes.FormLink:
|
|
if (this.elemInput.selectedIndex <= 0)
|
|
errorMsg.push(ERROR_MSG);
|
|
|
|
break;
|
|
case FieldTypes.DateTime:
|
|
var i = 0, len = this.elemInput.length;
|
|
|
|
for (; i < len; i++) {
|
|
if (this.elemInput[i].value.trim() == '') {
|
|
errorMsg.push(ERROR_MSG);
|
|
this.focusElementIndex = i;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
// Return false if validation check failed (number of errors changed).
|
|
return origLen == errorMsg.length;
|
|
};
|
|
|
|
// Performs validation for field and updates format if necessary.
|
|
FieldValidator.prototype.validate = function (assignFocusOnFailure, checkForInappropriateWords) {
|
|
var errorMsg = [];
|
|
|
|
// Element not present OR disabled, pass (skipped).
|
|
if (this.elemInput == null || !this.enabled)
|
|
return true;
|
|
|
|
if (!this.isRequired || (this.isRequired && this.validateRequired(errorMsg))) {
|
|
//checkbox validation done for min and max answer selected
|
|
if (this.checkBoxValidation(errorMsg)) {
|
|
switch (this.formatValidation) {
|
|
case FieldValidation.Letters:
|
|
this.validateLetters(errorMsg);
|
|
break;
|
|
case FieldValidation.Numeric:
|
|
this.validateNumeric(errorMsg);
|
|
break;
|
|
case FieldValidation.Currency:
|
|
this.validateCurrency(errorMsg);
|
|
break;
|
|
case FieldValidation.PhoneNumber:
|
|
this.validatePhone(errorMsg);
|
|
break;
|
|
case FieldValidation.PostalCode:
|
|
this.validatePostal(errorMsg);
|
|
break;
|
|
case FieldValidation.EmailAddress:
|
|
this.validateEmail(errorMsg);
|
|
break;
|
|
case FieldValidation.RegEx:
|
|
this.validateCustom(this.formatValidationCustom, errorMsg);
|
|
break;
|
|
case FieldValidation.Date:
|
|
this.validateDate(errorMsg);
|
|
break;
|
|
case FieldValidation.Time:
|
|
this.validateTime(errorMsg);
|
|
break;
|
|
case FieldValidation.DateTime:
|
|
this.validateDateTime(errorMsg);
|
|
break;
|
|
case FieldValidation.DateSpan:
|
|
this.validateDateSpan(errorMsg);
|
|
break;
|
|
case FieldValidation.TimeSpan:
|
|
this.validateTimeSpan(errorMsg);
|
|
break;
|
|
case FieldValidation.DateTimeSpan:
|
|
this.validateDateTimeSpan(errorMsg);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (this.fieldType === FieldTypes.DatePicker && this.elemInput.value.trim() != '') {
|
|
//validate date picker value (based on telerik mvc datePicker)
|
|
var datepicker = $(this.elemInput).data("tDatePicker");
|
|
if ($(this.elemInput).hasClass('t-state-error') || datepicker.parse(this.elemInput.value.trim()) == null)
|
|
errorMsg.push("Invalid date format");
|
|
}
|
|
if (this.fieldType === FieldTypes.ShortAnswer || this.fieldType === FieldTypes.LongAnswer) {
|
|
var validator = this;
|
|
|
|
if (checkForInappropriateWords != null && typeof (checkForInappropriateWords.push) == 'function') {
|
|
checkForInappropriateWords.push(validator);
|
|
} else {
|
|
if (HasInappropriateWords(this.elemInput.value.trim())) {
|
|
$(this.elemInput).addClass('t-state-error');
|
|
errorMsg.push(FormCenterHomeScriptResources.InappropriateWords);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (FeatureToggles.isActive("CMS.FormCenter.EnableServerSideValidationsForSubmissions")
|
|
&& this.fieldType === FieldTypes.FileUpload
|
|
&& this.elemInput.value.trim() != '') {
|
|
if (Math.round((this.elemInput.files[0].size / 1024)) > 20480)
|
|
errorMsg.push("Files cannot be larger than 20 MB.");
|
|
}
|
|
|
|
this.validateMaxLength(errorMsg);
|
|
|
|
if (errorMsg.length > 0 && $(this.elemInput).is(":visible")) {
|
|
this.errorShow(errorMsg);
|
|
|
|
if (assignFocusOnFailure) {
|
|
this.focusErrorElement();
|
|
}
|
|
|
|
return false;
|
|
} else {
|
|
this.errorClear();
|
|
return true;
|
|
}
|
|
|
|
};
|
|
|
|
// Performs validation for admin edit internal only fields and updates format if necessary.
|
|
FieldValidator.prototype.validateInternal = function (assignFocusOnFailure, checkForInappropriateWords) {
|
|
if (this.isInternal) {
|
|
var errorMsg = [];
|
|
|
|
// Element not present OR disabled, pass (skipped).
|
|
if (this.elemInput == null || !this.enabled)
|
|
return true;
|
|
|
|
if (!this.isRequired || (this.isRequired && this.validateRequired(errorMsg))) {
|
|
//checkbox validation done for min and max answer selected
|
|
if (this.checkBoxValidation(errorMsg)) {
|
|
switch (this.formatValidation) {
|
|
case FieldValidation.Letters:
|
|
this.validateLetters(errorMsg);
|
|
break;
|
|
case FieldValidation.Numeric:
|
|
this.validateNumeric(errorMsg);
|
|
break;
|
|
case FieldValidation.Currency:
|
|
this.validateCurrency(errorMsg);
|
|
break;
|
|
case FieldValidation.PhoneNumber:
|
|
this.validatePhone(errorMsg);
|
|
break;
|
|
case FieldValidation.PostalCode:
|
|
this.validatePostal(errorMsg);
|
|
break;
|
|
case FieldValidation.EmailAddress:
|
|
this.validateEmail(errorMsg);
|
|
break;
|
|
case FieldValidation.RegEx:
|
|
this.validateCustom(this.formatValidationCustom, errorMsg);
|
|
break;
|
|
case FieldValidation.Date:
|
|
this.validateDate(errorMsg);
|
|
break;
|
|
case FieldValidation.Time:
|
|
this.validateTime(errorMsg);
|
|
break;
|
|
case FieldValidation.DateTime:
|
|
this.validateDateTime(errorMsg);
|
|
break;
|
|
case FieldValidation.DateSpan:
|
|
this.validateDateSpan(errorMsg);
|
|
break;
|
|
case FieldValidation.TimeSpan:
|
|
this.validateTimeSpan(errorMsg);
|
|
break;
|
|
case FieldValidation.DateTimeSpan:
|
|
this.validateDateTimeSpan(errorMsg);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (this.fieldType === FieldTypes.DatePicker && this.elemInput.value.trim() != '') {
|
|
//validate date picker value (based on telerik mvc datePicker)
|
|
var datepicker = $(this.elemInput).data("tDatePicker");
|
|
if ($(this.elemInput).hasClass('t-state-error') || datepicker.parse(this.elemInput.value.trim()) == null)
|
|
errorMsg.push("Invalid date format");
|
|
}
|
|
if (this.fieldType === FieldTypes.ShortAnswer || this.fieldType === FieldTypes.LongAnswer) {
|
|
var validator = this;
|
|
|
|
if (checkForInappropriateWords != null && typeof (checkForInappropriateWords.push) == 'function') {
|
|
checkForInappropriateWords.push(validator);
|
|
} else {
|
|
if (HasInappropriateWords(this.elemInput.value.trim())) {
|
|
$(this.elemInput).addClass('t-state-error');
|
|
errorMsg.push(FormCenterHomeScriptResources.InappropriateWords);
|
|
}
|
|
}
|
|
}
|
|
|
|
this.validateMaxLength(errorMsg);
|
|
|
|
if (errorMsg.length > 0 && $(this.elemInput).is(":visible")) {
|
|
this.errorShow(errorMsg);
|
|
|
|
if (assignFocusOnFailure) {
|
|
this.focusErrorElement();
|
|
}
|
|
|
|
return false;
|
|
} else {
|
|
this.errorClear();
|
|
return true;
|
|
}
|
|
}
|
|
else {
|
|
return true;
|
|
}
|
|
};
|
|
|
|
FieldValidator.prototype.handleInappropriateWords = function (focus) {
|
|
var errorMsg = [];
|
|
$(this.elemInput).addClass('t-state-error');
|
|
errorMsg.push(FormCenterHomeScriptResources.InappropriateWords);
|
|
this.errorShow(errorMsg);
|
|
if (focus) {
|
|
this.focusErrorElement();
|
|
}
|
|
};
|
|
|
|
FieldValidator.prototype.focusErrorElement = function () {
|
|
var scrollIntoView = function (element) {
|
|
var offset = $(element).offset();
|
|
var elementTop = offset.top;
|
|
// Add a 50px padding to the top of the element, just for aesthetics
|
|
$(document).scrollTop(elementTop - 150);
|
|
};
|
|
|
|
if (this.elemInputIsArray) {
|
|
if ($(this.elemInput).is(":visible")) {
|
|
if (this.elemInput.length > this.focusElementIndex) {
|
|
this.elemInput[this.focusElementIndex].focus();
|
|
}
|
|
else {
|
|
this.elemInput[0].focus();
|
|
}
|
|
scrollIntoView(this.elemInput[0]);
|
|
}
|
|
this.alreadySetFocus = false;
|
|
this.focusElementIndex = 0;
|
|
}
|
|
else {
|
|
if ($(this.elemInput).is(":visible")) {
|
|
this.elemInput.focus();
|
|
scrollIntoView(this.elemInput);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Shows error message for field. Called by validate() if field value fails validation.
|
|
FieldValidator.prototype.errorShow = function (messages) {
|
|
if (this.elemErrorNote)
|
|
this.errorClear();
|
|
|
|
// Element not defined, cannot perform operation.
|
|
if (this.elemInput == null)
|
|
return;
|
|
|
|
this.$elemContainer.addClass('error');
|
|
this.elemErrorNote = [];
|
|
|
|
$('ol li.error div').addClass('selfClear');
|
|
|
|
if (this.elemInput != undefined) {
|
|
if (this.elemInput["0"] != undefined) {
|
|
//Check current element is radio
|
|
if (this.elemInput["0"].type == "radio" & !(this.$elemContainer.closest('li').hasClass('error'))) {
|
|
this.$elemContainer.closest('li').addClass('error');
|
|
originalTextColorRadio = this.$elemContainer.closest("fieldset")[0].childNodes[0].style.color;
|
|
this.$elemContainer.closest("fieldset")[0].childNodes[0].style.color = "#800000";
|
|
}
|
|
else //Check current element is checkbox
|
|
if (this.elemInput["0"].type == "checkbox" & !(this.$elemContainer.closest('li').hasClass('error'))) {
|
|
this.$elemContainer.closest('li').addClass('error');
|
|
originalTextColorCheckBox = this.$elemContainer.closest("fieldset")[0].childNodes[0].style.color;
|
|
this.$elemContainer.closest("fieldset")[0].childNodes[0].style.color = "#800000";
|
|
}
|
|
}
|
|
}
|
|
|
|
for (var i = 0, len = messages.length; i < len; i++) {
|
|
var errorElemMsg = document.createElement('p');
|
|
|
|
errorElemMsg.className = 'explanation';
|
|
|
|
var multiSelectable = this.elemInput[0] !== undefined && this.elemInput.tagName !== "SELECT";
|
|
if (multiSelectable) {
|
|
errorElemMsg.id = 'errorMsg' + this.elemInput[0].id;
|
|
}
|
|
else {
|
|
errorElemMsg.id = 'errorMsg' + this.elemInput.id;
|
|
}
|
|
|
|
errorElemMsg.innerHTML = messages[i];
|
|
|
|
if (this.elemInput != undefined) {
|
|
if (this.elemInput["0"] != undefined) {
|
|
if (this.elemInput["0"].type == "radio" || this.elemInput["0"].type == "checkbox") {
|
|
errorElemMsg.style.backgroundColor = "#940E0D"
|
|
errorElemMsg.style.color = "#FFFFFF";
|
|
errorElemMsg.style.fontWeight = "bold"
|
|
errorElemMsg.style.lineHeight = "1.2"
|
|
errorElemMsg.style.width = "94%"
|
|
errorElemMsg.style.margin = ".5em 0 0"
|
|
errorElemMsg.style.padding = ".5em"
|
|
}
|
|
}
|
|
}
|
|
|
|
this.elemContainer.appendChild(errorElemMsg);
|
|
|
|
this.elemErrorNote.push(errorElemMsg);
|
|
}
|
|
!window.isRemoveSetHeights && typeof SetHeights === "function" && SetHeights();
|
|
};
|
|
|
|
// Clears error message for field. Called by validate() if field value passes validation.
|
|
FieldValidator.prototype.errorClear = function () {
|
|
if (this.elemInput != undefined) {
|
|
if (this.elemInput["0"] != undefined) {
|
|
var i = 0, len = this.elemInput.length;
|
|
for (; i < len; i++) {
|
|
if (this.elemInput[i].checked)
|
|
break;
|
|
}
|
|
|
|
if (i != len) {
|
|
if (this.elemInput["0"].type == "radio") {
|
|
this.$elemContainer.closest('li').removeClass('error');
|
|
this.$elemContainer.closest("fieldset")[0].childNodes[0].style.color = originalTextColorRadio;
|
|
}
|
|
else if (this.elemInput["0"].type == "checkbox") {
|
|
this.$elemContainer.closest('li').removeClass('error');
|
|
this.$elemContainer.closest("fieldset")[0].childNodes[0].style.color = originalTextColorCheckBox;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (this.elemErrorNote == null)
|
|
return;
|
|
else if (this.elemInput == null)
|
|
return;
|
|
|
|
if (this.elemErrorNote.length > 0) {
|
|
var errorParent = this.elemErrorNote[0].parentNode;
|
|
|
|
for (var i = 0, len = this.elemErrorNote.length; i < len; i++)
|
|
errorParent.removeChild(this.elemErrorNote[i]);
|
|
|
|
this.elemErrorNote = null;
|
|
}
|
|
|
|
this.$elemContainer.removeClass('error');
|
|
!window.isRemoveSetHeights && typeof SetHeights === "function" && SetHeights();
|
|
};
|
|
|
|
FieldValidator.prototype.getErrorMessage = function (errorTemplateMsg) {
|
|
var $fieldElemContainer = $(this.elemContainer);
|
|
|
|
if (!$fieldElemContainer.hasClass('formFieldContainer'))
|
|
{
|
|
$fieldElemContainer = $fieldElemContainer.parents('.formFieldContainer');
|
|
}
|
|
|
|
var $fieldLabelElem = $fieldElemContainer.find('.labelForFieldElement');
|
|
var fieldLabel = '';
|
|
|
|
if ($fieldLabelElem.length > 0)
|
|
{
|
|
fieldLabel = $fieldLabelElem.text().replace('*', '');
|
|
}
|
|
else
|
|
{
|
|
$fieldLabelElem = $fieldElemContainer.find('legend');
|
|
if ($fieldLabelElem.length > 0)
|
|
{
|
|
fieldLabel = $fieldLabelElem.text().replace('*', '');
|
|
}
|
|
}
|
|
|
|
var errorMsg = errorTemplateMsg.format(fieldLabel);
|
|
|
|
return errorMsg;
|
|
};
|
|
/// <reference path="../../../../Scripts/jquery-1.3.2-vsdoc.js"/>
|
|
/// <reference path="../../../../Scripts/json2.js"/>
|
|
/// <reference path="../../../../Scripts/HtmlExtensionSupport.js"/>
|
|
/// <reference path="../../../../Common/GlobalJSFunctionsDetail.js" />
|
|
/// <reference path="Home.js" />
|
|
|
|
// Configurable in debug console. Controls behavior of FieldConditionsRunner.log().
|
|
var condRunnerHideLogDupes = true;
|
|
|
|
// Missing methods for IE
|
|
if (!Element.prototype.matches) {
|
|
Element.prototype.matches =
|
|
Element.prototype.msMatchesSelector ||
|
|
Element.prototype.webkitMatchesSelector;
|
|
}
|
|
|
|
if (!Element.prototype.closest) {
|
|
Element.prototype.closest = function (s) {
|
|
var el = this;
|
|
|
|
do {
|
|
if (Element.prototype.matches.call(el, s)) return el;
|
|
el = el.parentElement || el.parentNode;
|
|
} while (el !== null && el.nodeType === 1);
|
|
return null;
|
|
};
|
|
}
|
|
|
|
////////// Helper Functions ///////////////
|
|
|
|
$("*").focus(function () {
|
|
var inputs = $("input[type=text][cp5ph=true], textarea[cp5ph=true]");
|
|
if (inputs != null) {
|
|
for (var i = 0; i < inputs.length; i++) {
|
|
var elementName = inputs[i].getAttribute('id');
|
|
if (elementName != null) {
|
|
var top = $('#' + elementName).offset().top;
|
|
var left = $('#' + elementName).offset().left;
|
|
if (top != -2 && left != -2)
|
|
$('#ph_' + elementName).offset({ left: (left + 5), top: (top + 5) });
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
// Helper that handles replacement of special characters.
|
|
// * value - variable to handle characters of.
|
|
function HandleCharacters(value) {
|
|
return value.replace(/,/g, '_d');
|
|
}
|
|
|
|
if (Array.prototype.any == null && !$.isFunction(Array.prototype.any)) {
|
|
// Returns true if callback condition returns true for any items in array.
|
|
// * callback - Callback function with single argument for current index. The 'this' reference points to the array.
|
|
Array.prototype.any = function (callback) {
|
|
if (this == null)
|
|
return false;
|
|
|
|
for (var i = 0, len = this.length; i < len; i++) {
|
|
if (callback.call(this, i))
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (Array.prototype.all == null && !$.isFunction(Array.prototype.all)) {
|
|
// Returns true if callback condition returns true for all items in array.
|
|
// * callback - Callback function with single argument for current index. The 'this' reference points to the array.
|
|
Array.prototype.all = function (callback) {
|
|
if (this == null)
|
|
return false;
|
|
|
|
for (var i = 0, len = this.length; i < len; i++) {
|
|
if (!callback.call(this, i))
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
};
|
|
}
|
|
|
|
if (Error.argNull == null || !$.isFunction(Error.argNull)) {
|
|
// Static helper that creates error when null argument that should not be null is encountered.
|
|
// * argName - Name of problematic null argument.
|
|
Error.argNull = function (argName) {
|
|
return new ReferenceError(argName + " cannot be null");
|
|
}
|
|
}
|
|
|
|
// Overridden on a per-form basis. Retrieves validator information for submission validation.
|
|
// * validators - Validator dictionary.
|
|
function getFormConditionData() {
|
|
return [];
|
|
}
|
|
|
|
// Prints useful debugging information for FieldConditionsRunner.run() results. Use as done callback.
|
|
// Note: When minifiying the JavaScript, this function can/should be removed.
|
|
// * logs - Log generated by run().
|
|
function debugCondRunnerLogResults(logs) {
|
|
if (logs == null)
|
|
throw new Error.argNull("logs");
|
|
|
|
console.log("## run() started at " + (new Date()) + " ##");
|
|
|
|
for (var i = 0, iLen = logs.length; i < iLen; i++) {
|
|
var log = logs[i];
|
|
var resultEx = log.isMet ? " (met)" : " (not met)";
|
|
|
|
console.log(
|
|
"field: " + log.id +
|
|
", condIndex: " + log.condIndex +
|
|
", action: " + ConditionActions.getName(log.actType) +
|
|
", result: " + ConditionRunnerActResult.getName(log.result) + resultEx);
|
|
|
|
if (log.needRestart)
|
|
console.log("-- restart --");
|
|
}
|
|
|
|
console.log("## run() ended ##");
|
|
console.log("\t");
|
|
}
|
|
|
|
// Helper that repositions line siblings by re-assigning left/middle/right classes.
|
|
// * liSiblingList - Array of LI elements for a form line.
|
|
function repositionLineSiblings(liSiblingList) {
|
|
if (liSiblingList == null)
|
|
throw new Error.argNull("liSiblingList");
|
|
|
|
var sibling, $sibling;
|
|
var len = liSiblingList.length;
|
|
|
|
if (len > 0) {
|
|
var operableNodes = [];
|
|
|
|
for (var i = 0; i < len; i++) {
|
|
sibling = liSiblingList[i];
|
|
$sibling = $(sibling);
|
|
|
|
if (!$sibling.hasClass('hidden'))
|
|
operableNodes.push($sibling);
|
|
}
|
|
|
|
len = operableNodes.length;
|
|
|
|
if (len > 0) {
|
|
operableNodes[0].removeClass('middle right').addClass('left');
|
|
|
|
var j = 1;
|
|
|
|
for (; j < len - 1; j++)
|
|
operableNodes[j].removeClass('left right').addClass('middle');
|
|
|
|
if (j < len)
|
|
operableNodes[j].removeClass('left middle').addClass('right');
|
|
}
|
|
}
|
|
}
|
|
|
|
// Helper that gets sibling field LI elements for a line field LI element.
|
|
// * liElem - The LI element for a form field to get line siblings LIs for.
|
|
function getLineSiblings(liElem) {
|
|
|
|
if (liElem == null && (window.location.href).toString().toLowerCase().indexOf("print") != -1)
|
|
return "";
|
|
|
|
if (liElem == null)
|
|
throw new Error.argNull("liElem");
|
|
|
|
var nodeList = [];
|
|
var startNode = liElem;
|
|
|
|
// Get start of line.
|
|
while (startNode && !$(startNode).hasClass('lineStart'))
|
|
startNode = previousElementSibling(startNode);
|
|
|
|
// Get end of line.
|
|
if (startNode) {
|
|
var endNode = startNode;
|
|
var $endNode = null;
|
|
|
|
while (endNode != null) {
|
|
$endNode = $(endNode);
|
|
|
|
if ($endNode.hasClass('lineEnd'))
|
|
break;
|
|
|
|
endNode = nextElementSibling(endNode);
|
|
}
|
|
}
|
|
|
|
if (startNode && endNode) {
|
|
var nodeEnum = startNode;
|
|
|
|
while (true) {
|
|
nodeList.push(nodeEnum);
|
|
|
|
if (nodeEnum == endNode)
|
|
break;
|
|
|
|
nodeEnum = nextElementSibling(nodeEnum);
|
|
}
|
|
}
|
|
|
|
return nodeList;
|
|
}
|
|
|
|
////////// Enumerations ///////////////////
|
|
|
|
// Condition actions.
|
|
var ConditionActions = {
|
|
None: 0,
|
|
Show: 1,
|
|
Hide: 2,
|
|
Require: 3,
|
|
RedirectTo: 4,
|
|
getName: function (value) {
|
|
for (var k in ConditionActions) {
|
|
if (ConditionActions[k] == value)
|
|
return k;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
};
|
|
|
|
// Condition comparison types.
|
|
var ConditionComparison = {
|
|
None: 0,
|
|
GreaterThan: 1,
|
|
LessThan: 2,
|
|
EqualTo: 3,
|
|
NotEqualTo: 4,
|
|
Contains: 5,
|
|
Available: 6,
|
|
Unavailable: 7,
|
|
Between: 8,
|
|
getName: function (value) {
|
|
for (var k in ConditionComparison) {
|
|
if (ConditionComparison[k] == value)
|
|
return k;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
};
|
|
|
|
// Condition runner action results.
|
|
var ConditionRunnerActResult = {
|
|
None: 0,
|
|
SuccessImplicit: 1,
|
|
SuccessExplicit: 2,
|
|
SkipDuplicate: 3,
|
|
SkipContradiction: 4,
|
|
getName: function (value) {
|
|
for (var k in ConditionRunnerActResult) {
|
|
if (ConditionRunnerActResult[k] == value)
|
|
return k;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
};
|
|
|
|
////////// FieldCondition Class ///////////
|
|
|
|
// Internal. Creates instance of FieldCondition class.
|
|
// * parent - Parent FieldConditions instance.
|
|
// * condInfo - Object containing properties {compType, compValue, actElemInput, actID, actType, actFieldType, actURL, actURLNewWindow}.
|
|
function FieldCondition(parent, condInfo) {
|
|
if (parent == null)
|
|
throw new Error.argNull("parent");
|
|
else if (condInfo == null)
|
|
throw new Error.argNull("condInfo");
|
|
|
|
$.extend(this, condInfo);
|
|
|
|
// Set instance parent.
|
|
this.parent = parent;
|
|
|
|
// Process actElemInput argument.
|
|
var actElemInput = this.actElemInput;
|
|
this.actElemInputIsArray = $.isArray(actElemInput);
|
|
|
|
if (this.actElemInputIsArray) {
|
|
for (var i = 0, len = actElemInput.length; i < len; i++) {
|
|
if (typeof (actElemInput[i]) == "string")
|
|
actElemInput[i] = document.getElementById(actElemInput[i]);
|
|
}
|
|
|
|
if (actElemInput.length > 0 && actElemInput[0]) {
|
|
this.actElemContainer = actElemInput[0].closest('.formFieldContainer');
|
|
this.actElemInput = actElemInput;
|
|
}
|
|
}
|
|
else {
|
|
if (typeof (actElemInput) == "string")
|
|
actElemInput = document.getElementById(actElemInput);
|
|
|
|
if (actElemInput) {
|
|
this.actElemContainer = actElemInput.closest('.formFieldContainer');
|
|
this.actElemInput = actElemInput;
|
|
}
|
|
}
|
|
|
|
// Get shared container.
|
|
this.$actElemContainer = $(this.actElemContainer);
|
|
}
|
|
|
|
// Static. Creates multiple instances from list of condition information.
|
|
FieldCondition.createMulti = function (parent, condList) {
|
|
if (parent == null)
|
|
throw new Error.argNull("parent");
|
|
else if (condList == null)
|
|
throw new Error.argNull("condList");
|
|
|
|
var ret = [];
|
|
|
|
for (var i = 0, len = condList.length; i < len; i++)
|
|
ret.push(new FieldCondition(parent, condList[i]));
|
|
|
|
return ret;
|
|
}
|
|
|
|
// Checks if condition is met and performs any necessary action.
|
|
// * updateUI - Determines if UI is updated after check. Treated as true if null/unspecified.
|
|
FieldCondition.prototype.check = function (updateUI) {
|
|
// TODO: Handle checkboxes/radios/dropdowns when Akila gets that done on back-end.
|
|
var thisValue = [];
|
|
var compValue = this.compValue;
|
|
var isMet = false;
|
|
|
|
switch (this.actFieldType) {
|
|
case FieldTypes.Checkboxes:
|
|
for (var i = 0, len = this.actElemInput.length; i < len; i++) {
|
|
if (this.actElemInput[i].checked)
|
|
thisValue.push(this.actElemInput[i].value.replace(/\,/g, '_comma_'));
|
|
}
|
|
break;
|
|
case FieldTypes.RadioButtons:
|
|
for (var i = 0, len = this.actElemInput.length; i < len; i++) {
|
|
if (this.actElemInput[i].checked) {
|
|
thisValue = [this.actElemInput[i].value];
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case FieldTypes.DateTime:
|
|
var tempArray = new Array();
|
|
for (var i = 0, len = this.actElemInput.length; i < len; i++) {
|
|
tempArray.push([this.actElemInput[i].value])
|
|
}
|
|
thisValue = [tempArray.join(',')];
|
|
break;
|
|
default:
|
|
thisValue = [this.actElemInput.value];
|
|
}
|
|
|
|
var extractMoneyValue = function (v) {
|
|
var stringBeginsWith = function (val, beginString) {
|
|
return val.substring(0, beginString.length) === beginString
|
|
}
|
|
if (v && v != '') {
|
|
if (stringBeginsWith(v, '$')) {
|
|
return v.substring(1).replace(',', '');
|
|
}
|
|
}
|
|
return v.replace(',', '');
|
|
};
|
|
|
|
|
|
var isNum = function (val) {
|
|
var extracted = extractMoneyValue(val);
|
|
return parseFloat(extracted) === +extracted;
|
|
};
|
|
|
|
var checkAgainst = function (compValue) {
|
|
switch (this.compType) {
|
|
case ConditionComparison.GreaterThan:
|
|
return thisValue.any(function (index) {
|
|
if (isNum(this[index]) && isNum(HandleCharacters(compValue))) {
|
|
return parseFloat(extractMoneyValue(this[index])) > parseFloat(extractMoneyValue(HandleCharacters(compValue)));
|
|
}
|
|
return false;
|
|
});
|
|
case ConditionComparison.LessThan:
|
|
return thisValue.any(function (index) {
|
|
if (isNum(this[index]) && isNum(HandleCharacters(compValue))) {
|
|
return parseFloat(extractMoneyValue(this[index])) < parseFloat(extractMoneyValue(HandleCharacters(compValue)));
|
|
}
|
|
return false;
|
|
});
|
|
case ConditionComparison.EqualTo:
|
|
if (this.actFieldType == FieldTypes.DateTime) {
|
|
if (this.actIsDateTimeSpan) {
|
|
return this.checkDateEqualSpan(thisValue[0], compValue);
|
|
}
|
|
else {
|
|
return this.checkDateEqualTo(thisValue[0], compValue);
|
|
}
|
|
}
|
|
else {
|
|
return thisValue.any(function (index) {
|
|
return (isNull(HandleCharacters(this[index]), '') == isNull(HandleCharacters(compValue), ''));
|
|
});
|
|
}
|
|
case ConditionComparison.NotEqualTo:
|
|
return thisValue.any(function (index) { return (isNull(this[index], '').toUpperCase() != isNull(HandleCharacters(compValue), '').toUpperCase()); });
|
|
break;
|
|
case ConditionComparison.Contains:
|
|
var containsExp = new RegExp(RegExp.metaEscape(HandleCharacters(compValue)), 'i');
|
|
|
|
return thisValue.any(function (index) {
|
|
return containsExp.test(this[index]);
|
|
});
|
|
case ConditionComparison.Unavailable:
|
|
return this.$actElemContainer.hasClass('unavailable');
|
|
case ConditionComparison.Available:
|
|
return !this.$actElemContainer.hasClass('unavailable');
|
|
case ConditionComparison.Between:
|
|
return this.checkDateBetween(thisValue[0], compValue);
|
|
return;
|
|
case ConditionComparison.None:
|
|
default:
|
|
return true;
|
|
}
|
|
};
|
|
|
|
switch (this.actFieldType) {
|
|
case FieldTypes.Checkboxes:
|
|
var compArr = compValue.split(',');
|
|
|
|
isMet = true;
|
|
|
|
for (var i = 0, len = compArr.length; i < len; i++) {
|
|
|
|
if (!checkAgainst.call(this, compArr[i])) {
|
|
isMet = false;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
isMet = checkAgainst.call(this, compValue);
|
|
}
|
|
|
|
if (updateUI)
|
|
this.doAction(isMet);
|
|
|
|
return isMet;
|
|
};
|
|
|
|
// Performs changes to applying field based on condition action and comparison result.
|
|
FieldCondition.prototype.doAction = function (isMet) {
|
|
var validator = (this.parent.validators ? this.parent.validators[this.parent.id] : null);
|
|
|
|
switch (this.actType) {
|
|
case ConditionActions.Show:
|
|
this.doActionShow(isMet, validator);
|
|
break;
|
|
case ConditionActions.Hide:
|
|
this.doActionHide(isMet, validator);
|
|
break;
|
|
case ConditionActions.Require:
|
|
this.doActionRequire(isMet, validator);
|
|
break;
|
|
case ConditionActions.RedirectTo:
|
|
this.doActionRedirect(isMet, validator);
|
|
break;
|
|
}
|
|
};
|
|
|
|
FieldCondition.prototype.doActionShow = function (isMet, validator) {
|
|
if (isMet) {
|
|
this.parent.$elemContainer.removeClass('hidden');
|
|
this.parent.$elemContainer.removeClass('unavailable');
|
|
|
|
// Update classes assigned to fields (e.g. since what was "left" may now be hidden and a new "left" is needed).
|
|
repositionLineSiblings(getLineSiblings(this.parent.elemContainer));
|
|
|
|
if (this.parent.$elemContainer.find('label > span') != null && this.parent.$elemContainer.find('label > span').length > 0)
|
|
$('#liRequiredFieldInfo').show();
|
|
|
|
if (validator)
|
|
validator.enabled = true;
|
|
}
|
|
else {
|
|
this.parent.$elemContainer.addClass('hidden');
|
|
this.parent.$elemContainer.addClass('unavailable');
|
|
|
|
// Update classes assigned to fields (e.g. since what was "left" may now be hidden and a new "left" is needed).
|
|
repositionLineSiblings(getLineSiblings(this.parent.elemContainer));
|
|
|
|
if (validator)
|
|
validator.enabled = false;
|
|
}
|
|
};
|
|
|
|
FieldCondition.prototype.doActionHide = function (isMet, validator) {
|
|
if (isMet) {
|
|
this.parent.$elemContainer.addClass('hidden');
|
|
this.parent.$elemContainer.addClass('unavailable');
|
|
|
|
// Update classes assigned to fields (e.g. since what was "left" may now be hidden and a new "left" is needed).
|
|
repositionLineSiblings(getLineSiblings(this.parent.elemContainer));
|
|
|
|
if (validator)
|
|
validator.enabled = false;
|
|
}
|
|
else {
|
|
this.parent.$elemContainer.removeClass('hidden');
|
|
this.parent.$elemContainer.removeClass('unavailable');
|
|
|
|
// Update classes assigned to fields (e.g. since what was "left" may now be hidden and a new "left" is needed).
|
|
repositionLineSiblings(getLineSiblings(this.parent.elemContainer));
|
|
|
|
if (validator)
|
|
validator.enabled = true;
|
|
}
|
|
};
|
|
|
|
FieldCondition.prototype.doActionRequire = function (isMet, validator) {
|
|
if (isMet) {
|
|
this.parent.$elemReq.removeClass('hidden');
|
|
|
|
if (validator == null) {
|
|
validator = new FieldValidator(this.parent.elemInput, this.parent.id, this.parent.fieldType, this.parent.isInternal, true, FieldValidation.Any);
|
|
this.parent.validators.add(validator);
|
|
}
|
|
|
|
this.parent.$elemContainer.find('label.labelForFieldElement > span').show();
|
|
$('#liRequiredFieldInfo').show();
|
|
if (validator)
|
|
validator.isRequired = true;
|
|
}
|
|
else {
|
|
this.parent.$elemReq.addClass('hidden');
|
|
this.parent.$elemContainer.find('label.labelForFieldElement > span').hide();
|
|
|
|
if (validator)
|
|
validator.isRequired = false;
|
|
}
|
|
};
|
|
|
|
FieldCondition.prototype.doActionRedirect = function (isMet, validator) {
|
|
// TODO: Some point alter this to pass in references for submitRedirect/redirectNewWindow. Not ideal at moment to ref. global vars.
|
|
var submitRedirect = document.getElementById('submitRedirect');
|
|
|
|
redirectNewWindow = null;
|
|
submitRedirect.value = '';
|
|
|
|
if (isMet) {
|
|
if (this.actURLNewWindow) {
|
|
redirectNewWindow = this.actURL;
|
|
submitRedirect.value = '';
|
|
}
|
|
else
|
|
submitRedirect.value = this.actURL;
|
|
}
|
|
};
|
|
|
|
// Attachs event handler to field to monitor changes.
|
|
// * condRunner - FieldConditionsRunner instance to attach.
|
|
// Note: Calling more than once will have no effect.
|
|
FieldCondition.prototype.attachRunner = function (condRunner) {
|
|
if (condRunner == null)
|
|
throw new Error.argNull("condRunner");
|
|
|
|
// Do not try to bind this if it is an internal field condition and we are not running internal field
|
|
// conditions.
|
|
if (this.parent.isInternal && !condRunner.runInternal)
|
|
return;
|
|
|
|
if ($.data(this.actElemInput, "runner_chghook") == null) {
|
|
var changeHook = function (event) {
|
|
condRunner.run(true);
|
|
!window.isRemoveSetHeights && typeof SetHeights === "function" && SetHeights();
|
|
};
|
|
$.data(this.actElemInput, "runner_chghook", changeHook);
|
|
$(this.actElemInput).change(changeHook);
|
|
}
|
|
};
|
|
|
|
// Removes event handler that monitors changes.
|
|
// Note: Calling more than once will have no effect.
|
|
FieldCondition.prototype.detachRunner = function () {
|
|
var changeHook = $.data(this.actElemInput, "runner_chghook");
|
|
|
|
if (changeHook) {
|
|
$(this.actElemInput).unbind('change', changeHook);
|
|
$.removeData(this.actElemInput, "runner_chghook");
|
|
}
|
|
};
|
|
|
|
FieldCondition.prototype.checkDateCommon = function (thisValue, compValue) {
|
|
var defaultDate = '1/1/70';
|
|
var defualtDateObj = new Date(defaultDate);
|
|
|
|
var compArr = compValue.split(',');
|
|
var valueArr = thisValue.split(',');
|
|
|
|
var hasTimeOfDayStart = false;
|
|
var hasTimeOfDayEnd = false;
|
|
|
|
var compareStartDate;
|
|
var compareEndDate;
|
|
|
|
if (compArr[0] == '') {
|
|
compareStartDate = new Date(defaultDate + ' ' + compArr[2]);
|
|
}
|
|
else {
|
|
compareStartDate = new Date(compArr[0] + ' ' + compArr[2]);
|
|
}
|
|
|
|
if (compArr[1] == '') {
|
|
compareEndDate = new Date(defaultDate + ' ' + compArr[3]);
|
|
}
|
|
else {
|
|
compareEndDate = new Date(compArr[1] + ' ' + compArr[3]);
|
|
}
|
|
|
|
var validStartDate = new Date(defaultDate);
|
|
var validEndDate = new Date(defaultDate);
|
|
var timeOfDayStart = '';
|
|
var timeOfDayEnd = '';
|
|
|
|
if (valueArr.length == 4) {
|
|
if (valueArr[0] == '') {
|
|
validStartDate = new Date(defaultDate + ' ' + valueArr[2]);
|
|
}
|
|
else {
|
|
validStartDate = new Date(valueArr[0] + ' ' + valueArr[2]);
|
|
}
|
|
|
|
timeOfDayStart = this.getAMPMString(valueArr[2]);
|
|
|
|
if (timeOfDayStart != '')
|
|
hasTimeOfDayStart = true;
|
|
|
|
if (valueArr[1] == '') {
|
|
validEndDate = new Date(defaultDate + ' ' + valueArr[3]);
|
|
}
|
|
else {
|
|
validEndDate = new Date(valueArr[1] + ' ' + valueArr[3]);
|
|
}
|
|
|
|
timeOfDayEnd = this.getAMPMString(valueArr[3]);
|
|
|
|
if (timeOfDayEnd != '')
|
|
hasTimeOfDayEnd = true;
|
|
}
|
|
else if (valueArr.length == 2) {
|
|
timeOfDayStart = this.getAMPMString(valueArr[0]);
|
|
|
|
if (timeOfDayStart == '') {
|
|
if (valueArr[0] != '') {
|
|
validStartDate = new Date(valueArr[0]);
|
|
hasTimeOfDayStart = true;
|
|
}
|
|
}
|
|
else {
|
|
validStartDate = new Date(defaultDate + ' ' + valueArr[0]);
|
|
hasTimeOfDayStart = true;
|
|
}
|
|
|
|
timeOfDayEnd = this.getAMPMString(valueArr[1]);
|
|
|
|
if (timeOfDayEnd == '') {
|
|
if (valueArr[1] != '') {
|
|
validEndDate = new Date(valueArr[1]);
|
|
hasTimeOfDayEnd = true;
|
|
}
|
|
}
|
|
else {
|
|
validEndDate = new Date(defaultDate + ' ' + valueArr[1]);
|
|
hasTimeOfDayEnd = true;
|
|
}
|
|
}
|
|
|
|
return {
|
|
validStartDate: validStartDate,
|
|
compareStartDate: compareStartDate,
|
|
compareEndDate: compareEndDate,
|
|
hasTimeOfDayStart: hasTimeOfDayStart,
|
|
validEndDate: validEndDate,
|
|
hasTimeOfDayEnd: hasTimeOfDayEnd
|
|
};
|
|
};
|
|
|
|
FieldCondition.prototype.checkDateBetween = function (thisValue, compValue) {
|
|
var checkDateCommonData = this.checkDateCommon(thisValue, compValue);
|
|
|
|
return (checkDateCommonData.validStartDate.getTime() >= checkDateCommonData.compareStartDate.getTime() && checkDateCommonData.validStartDate.getTime() <= checkDateCommonData.compareEndDate.getTime() && checkDateCommonData.hasTimeOfDayStart)
|
|
&& (checkDateCommonData.validEndDate.getTime() <= checkDateCommonData.compareEndDate.getTime() && checkDateCommonData.validEndDate.getTime() >= checkDateCommonData.compareStartDate.getTime() && checkDateCommonData.hasTimeOfDayEnd);
|
|
};
|
|
|
|
FieldCondition.prototype.checkDateEqualSpan = function (thisValue, compValue) {
|
|
var checkDateCommonData = this.checkDateCommon(thisValue, compValue);
|
|
|
|
return (checkDateCommonData.validStartDate.getTime() == checkDateCommonData.compareStartDate.getTime() && checkDateCommonData.hasTimeOfDayStart)
|
|
&& (checkDateCommonData.validEndDate.getTime() == checkDateCommonData.compareEndDate.getTime() && checkDateCommonData.hasTimeOfDayEnd);
|
|
};
|
|
|
|
FieldCondition.prototype.checkDateEqualTo = function (thisValue, compValue) {
|
|
var defaultDate = '1/1/70';
|
|
var defualtDateObj = new Date(defaultDate);
|
|
|
|
var compArr = compValue.split(',');
|
|
var valueArr = thisValue.split(',');
|
|
|
|
var compareDate;
|
|
|
|
var hasTimeOfDay = false;
|
|
|
|
if (compArr[0] == '') {
|
|
compareDate = new Date(defaultDate + ' ' + compArr[2]);
|
|
}
|
|
else {
|
|
compareDate = new Date(compArr[0] + ' ' + compArr[2]);
|
|
}
|
|
|
|
var validDate = new Date(defaultDate);
|
|
var timeOfDay = '';
|
|
|
|
if (valueArr.length == 1) {
|
|
timeOfDay = this.getAMPMString(valueArr[0]);
|
|
|
|
if (timeOfDay == '') {
|
|
if (valueArr[0] != '') {
|
|
validDate = new Date(valueArr[0]);
|
|
hasTimeOfDay = true;
|
|
}
|
|
}
|
|
else {
|
|
hasTimeOfDay = true;
|
|
validDate = new Date(valueArr[0]);
|
|
}
|
|
}
|
|
else if (valueArr.length == 2) {
|
|
timeOfDay = this.getAMPMString(valueArr[1]);
|
|
|
|
if (timeOfDay != '')
|
|
hasTimeOfDay = true;
|
|
|
|
if (valueArr[0] != '') {
|
|
validDate = new Date((valueArr[0] + ' ' + valueArr[1] + ' ').replace(/\-/g, '/'));
|
|
}
|
|
else {
|
|
validDate = new Date((defaultDate + ' ' + valueArr[0] + ' ').replace(/\-/g, '/'));
|
|
}
|
|
}
|
|
|
|
return validDate.getTime() == compareDate.getTime() && hasTimeOfDay;
|
|
};
|
|
|
|
FieldCondition.prototype.getAMPMString = function (time) {
|
|
var amIndex = time.indexOf('AM');
|
|
var pmIndex = time.indexOf('PM');
|
|
|
|
var timeOfDay = '';
|
|
|
|
// First see if the time is military time... if so we need to convert it to AM/PM so the rest
|
|
// of this function will return the proper value.
|
|
|
|
if (amIndex === -1 && pmIndex === -1) {
|
|
time = militaryTimeToStandard(time);
|
|
amIndex = time.indexOf('AM');
|
|
pmIndex = time.indexOf('PM');
|
|
}
|
|
|
|
if (amIndex != -1) {
|
|
timeOfDay = time.substring(amIndex).replace(" ", "");
|
|
}
|
|
else if (pmIndex != -1) {
|
|
timeOfDay = time.substring(pmIndex).replace(" ", "");
|
|
}
|
|
|
|
return timeOfDay;
|
|
};
|
|
|
|
function militaryTimeToStandard(militaryTime) {
|
|
var colonIndex = militaryTime.indexOf(':');
|
|
var hours = '';
|
|
var min = '';
|
|
|
|
if (colonIndex === -1) {
|
|
hours = militaryTime.substring(0, 2);
|
|
min = militaryTime.substring(2, 4);
|
|
} else {
|
|
var tokens = militaryTime.split(':');
|
|
hours = tokens[0];
|
|
min = tokens[1];
|
|
}
|
|
|
|
if (hours < 12) {
|
|
ampm = 'AM';
|
|
} else {
|
|
hours = hours - 12;
|
|
ampm = 'PM';
|
|
}
|
|
|
|
return parseInt(hours) + ':' + ('00' + min).slice('-2') + ' ' + ampm; ß
|
|
}
|
|
|
|
|
|
////////// FieldConditions Class //////////
|
|
|
|
// Creates instance of FieldConditions class.
|
|
// * elemInput - Reference to DOM element of field with condition. May be a DOM element or string containing ID. May also be array of DOM elements or IDs. Multiple elements must share same parent.
|
|
// * id - The ID of the field with the condition.
|
|
// * fieldType - The field type of the field with the condition.
|
|
// * isInternal - Whether or not this condition is for internal-only fields.
|
|
// * condList - List (array) of zero or more condition clauses for the field.
|
|
// * validators - Validator dictionary.
|
|
function FieldConditions(options, validators) {
|
|
if (options.elemInput == null)
|
|
throw new Error.argNull("options.elemInput");
|
|
else if (options.id == null)
|
|
throw new Error.argNull("id");
|
|
else if (options.fieldType == null)
|
|
throw new Error.argNull("options.fieldType");
|
|
else if (options.condList == null)
|
|
throw new Error.argNull("options.condList");
|
|
else if (validators == null)
|
|
throw new Error.argNull("validators");
|
|
|
|
this.elemInputIsArray = $.isArray(options.elemInput);
|
|
|
|
// Process elemInput argument.
|
|
if (this.elemInputIsArray) {
|
|
for (var i = 0, len = options.elemInput.length; i < len; i++) {
|
|
if (typeof (options.elemInput[i]) == "string")
|
|
options.elemInput[i] = document.getElementById(options.elemInput[i]);
|
|
}
|
|
|
|
if (options.elemInput.length > 0 && options.elemInput[0]) {
|
|
this.elemContainer = options.elemInput[0].closest('.formFieldContainer');
|
|
this.elemInput = options.elemInput;
|
|
}
|
|
}
|
|
else {
|
|
if (typeof (options.elemInput) == "string")
|
|
options.elemInput = document.getElementById(options.elemInput);
|
|
|
|
if (options.elemInput) {
|
|
this.elemContainer = options.elemInput.closest('.formFieldContainer');
|
|
this.elemInput = options.elemInput;
|
|
}
|
|
}
|
|
|
|
this.validators = validators;
|
|
this.elemReq = document.getElementById('r_' + options.id);
|
|
this.$elemContainer = $(this.elemContainer);
|
|
this.$elemReq = $(this.elemReq);
|
|
this.fieldType = options.fieldType;
|
|
this.id = options.id;
|
|
this.isInternal = options.isInternal;
|
|
this.conditions = FieldCondition.createMulti(this, options.condList);
|
|
}
|
|
|
|
// Static. Creates a single conditions object from an options object.
|
|
FieldConditions.createSingle = function (options, validators) {
|
|
if (options == null)
|
|
throw new Error.argNull("options");
|
|
else if (validators == null)
|
|
throw new Error.argNull("validators");
|
|
|
|
return new FieldConditions(
|
|
options,
|
|
validators);
|
|
};
|
|
|
|
// Static. Creates dictionary of conditions objects from array of option objects.
|
|
FieldConditions.createMulti = function (optionsArray, validators) {
|
|
if (optionsArray == null)
|
|
throw new Error.argNull("optionsArray");
|
|
else if (validators == null)
|
|
throw new Error.argNull("validators");
|
|
|
|
var ret = { keys: [] };
|
|
var key = null;
|
|
|
|
for (var i = 0, len = optionsArray.length; i < len; i++) {
|
|
if (optionsArray[i].id != null) {
|
|
key = optionsArray[i].id;
|
|
ret[key] = FieldConditions.createSingle(optionsArray[i], validators);
|
|
ret.keys.push(key);
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
};
|
|
|
|
// Attachs event handler to field to monitor changes.
|
|
// * condRunner - FieldConditionsRunner instance to attach.
|
|
// Note: Calling more than once will have no effect.
|
|
FieldConditions.prototype.attachRunner = function (condRunner) {
|
|
if (condRunner == null)
|
|
throw new Error.argNull("condRunner");
|
|
|
|
for (var i = 0, len = this.conditions.length; i < len; i++)
|
|
this.conditions[i].attachRunner(condRunner);
|
|
};
|
|
|
|
// Removes event handler that monitors changes.
|
|
// Note: Calling more than once will have no effect.
|
|
FieldConditions.prototype.detachRunner = function () {
|
|
for (var i = 0, len = this.conditions.length; i < len; i++)
|
|
this.conditions[i].detachRunner();
|
|
};
|
|
|
|
////////// FieldConditionsRunner Class ////
|
|
|
|
// Creates new instance of conditions runners for the field conditions objects provided.
|
|
// * allFieldConditions - Array of FieldConditions objects to execute checks for.
|
|
// * doneCallback - Method executed when run() completes. Recieves log information as argument.
|
|
// * autoAttach - Determines if allFieldConditions are automatically attached to this instance by the constructor. If null/unspecified, treated as true.
|
|
// * runInternal - Set to true to execute internal-only fields.
|
|
function FieldConditionsRunner(allFieldConditions, doneCallback, autoAttach, runInternal) {
|
|
if (allFieldConditions == null)
|
|
throw new Error.argNull("allFieldConditions");
|
|
else if (doneCallback == null)
|
|
throw new Error.argNull("doneCallback");
|
|
|
|
this.fieldConditions = allFieldConditions;
|
|
this.doneCallback = doneCallback;
|
|
this.runInternal = runInternal;
|
|
|
|
if (autoAttach == null || autoAttach)
|
|
this.attachConditions();
|
|
}
|
|
|
|
// Static. Internal. Creates static information object for state tracking during execution.
|
|
FieldConditionsRunner.createFieldStateInfo = function () {
|
|
var ret = {};
|
|
|
|
ret[ConditionActions.None] = ConditionRunnerActResult.None;
|
|
ret[ConditionActions.Show] = ConditionRunnerActResult.None;
|
|
ret[ConditionActions.Hide] = ConditionRunnerActResult.None;
|
|
ret[ConditionActions.Require] = ConditionRunnerActResult.None;
|
|
ret[ConditionActions.RedirectTo] = ConditionRunnerActResult.None;
|
|
|
|
return ret;
|
|
};
|
|
|
|
// Attaches to FieldConditions/FieldCondition objects that runner will use.
|
|
FieldConditionsRunner.prototype.attachConditions = function () {
|
|
for (var i = 0, iLen = this.fieldConditions.keys.length; i < iLen; i++)
|
|
this.fieldConditions[this.fieldConditions.keys[i]].attachRunner(this);
|
|
};
|
|
|
|
// Detaches to FieldConditions/FieldCondition objects that runner may have used.
|
|
FieldConditionsRunner.prototype.detachConditions = function () {
|
|
for (var i = 0, iLen = this.fieldConditions.keys.length; i < iLen; i++)
|
|
this.fieldConditions[this.fieldConditions.keys[i]].detachRunner(this);
|
|
};
|
|
|
|
// Internal. Cleans up runner for use again.
|
|
FieldConditionsRunner.prototype.cleanup = function () {
|
|
if (this.fieldConditionResults != null) {
|
|
delete this.fieldConditionResults;
|
|
delete this.fieldConditionLog;
|
|
}
|
|
};
|
|
|
|
// Internal. Called by run() to log result for each condition.
|
|
FieldConditionsRunner.prototype.log = function (id, condIndex, actType, result, isMet, needRestart) {
|
|
if (condRunnerHideLogDupes && result == ConditionRunnerActResult.SkipDuplicate)
|
|
return;
|
|
|
|
this.fieldConditionLog.push({
|
|
id: id,
|
|
condIndex: condIndex,
|
|
actType: actType,
|
|
result: result,
|
|
isMet: isMet,
|
|
needRestart: needRestart
|
|
});
|
|
};
|
|
|
|
// Internal. Sets whether or not the condition engine is executing.
|
|
FieldConditionsRunner.prototype.setRunning = function (value) {
|
|
if (arguments.length == 0)
|
|
this.isRunning = true;
|
|
else
|
|
this.isRunning = value;
|
|
};
|
|
|
|
// Gets whether or not the condition engine is executing.
|
|
FieldConditionsRunner.prototype.getRunning = function () {
|
|
return this.isRunning;
|
|
};
|
|
|
|
// Internal. Tells condition execution task to reset cycle (done whenever something changes).
|
|
FieldConditionsRunner.prototype.setRestart = function (value) {
|
|
if (arguments == null || arguments.length == 0)
|
|
this.needRestart = true;
|
|
else
|
|
this.needRestart = value;
|
|
};
|
|
|
|
// Internal. Gets whether or not the runner needs to restart.
|
|
FieldConditionsRunner.prototype.getRestart = function () {
|
|
return this.needRestart;
|
|
};
|
|
|
|
// Launches execution of conditions.
|
|
// * fromEvent - Should be set to true when triggerd from DOM events.
|
|
// NOTE: fromEvent may be a race condition but will be left for now while debugging (to simplify logs).
|
|
FieldConditionsRunner.prototype.run = function (fromEvent) {
|
|
if (this.getRunning() && !fromEvent)
|
|
this.setRestart();
|
|
else {
|
|
this.setRunning();
|
|
|
|
this.cleanup();
|
|
|
|
this.runTask();
|
|
|
|
this.doneCallback(this.fieldConditionLog);
|
|
|
|
this.setRunning(false);
|
|
}
|
|
};
|
|
|
|
// Internal. Executes conditions. Should not be called while already running, so run() has sync mechanism for this.
|
|
// * runInternal - If true, conditions for internal conditions will be ran.
|
|
FieldConditionsRunner.prototype.runTask = function (runInternal) {
|
|
var needRestart = true;
|
|
|
|
// Prepare objects for execution.
|
|
this.fieldConditionLog = [];
|
|
this.fieldConditionResults = [];
|
|
this.setRedirectUrl = false;
|
|
|
|
// Populate state information.
|
|
var condCondList = this.fieldConditions;
|
|
var condListInfo = null;
|
|
var condList = null;
|
|
|
|
for (var i = 0, iLen = condCondList.keys.length; i < iLen; i++)
|
|
this.fieldConditionResults[condCondList[condCondList.keys[i]].id] = FieldConditionsRunner.createFieldStateInfo();
|
|
|
|
// Number of restart actions (tracked to eventually kill infinite loops that should not happen).
|
|
var numRestarts = 0;
|
|
|
|
// Start/restart actions.
|
|
while (needRestart && (++numRestarts < 65535)) {
|
|
needRestart = false;
|
|
this.setRestart(needRestart);
|
|
|
|
for (var i = 0, iLen = condCondList.keys.length; i < iLen && !needRestart; i++) {
|
|
condListInfo = condCondList[condCondList.keys[i]];
|
|
|
|
// If we are on the front-end, we don't want to have anything to do with back-end (internal only)
|
|
// conditions, they will cause javascript errors.
|
|
if (!this.runInternal && condListInfo.isInternal == true)
|
|
continue;
|
|
|
|
if (condListInfo.isInternal == false && document.URL.indexOf("Admin") > -1 && document.URL.indexOf("Submissions") > -1)
|
|
continue;
|
|
|
|
condList = condListInfo.conditions;
|
|
|
|
for (var j = 0, jLen = condList.length; j < jLen && !needRestart; j++) {
|
|
var cond = condList[j];
|
|
var results = this.fieldConditionResults[condListInfo.id];
|
|
var logState = ConditionRunnerActResult.None;
|
|
|
|
// Determine whether or not condition is met.
|
|
var isMet = cond.check(false);
|
|
|
|
// This is because the effective action for a failed explicit Show is an implicit Hide (and vice versa).
|
|
var effectiveActType = cond.actType;
|
|
|
|
// Determine if action is contradictory or duplicate and skip UI changes if so.
|
|
switch (effectiveActType) {
|
|
case ConditionActions.Show:
|
|
// Show can collide with hide.
|
|
var resultsHide = results[ConditionActions.Hide];
|
|
|
|
if (resultsHide == ConditionRunnerActResult.SuccessExplicit || (resultsHide == ConditionRunnerActResult.SuccessImplicit && !isMet))
|
|
logState = ConditionRunnerActResult.SkipContradiction;
|
|
|
|
break;
|
|
case ConditionActions.Hide:
|
|
// Hide can collide with show.
|
|
var resultsShow = results[ConditionActions.Show];
|
|
|
|
if (resultsShow == ConditionRunnerActResult.SuccessExplicit || (resultsShow == ConditionRunnerActResult.SuccessImplicit && !isMet))
|
|
logState = ConditionRunnerActResult.SkipContradiction;
|
|
|
|
break;
|
|
case ConditionActions.RedirectTo:
|
|
// Redirects work differently since they span all fields. Extra contradiction detection needed.
|
|
if (this.setRedirectUrl)
|
|
logState = ConditionRunnerActResult.SkipContradiction;
|
|
else if (isMet)
|
|
this.setRedirectUrl = true;
|
|
break;
|
|
}
|
|
|
|
if (isMet && results[effectiveActType] == ConditionRunnerActResult.SuccessExplicit)
|
|
logState = ConditionRunnerActResult.SkipDuplicate;
|
|
else if (!isMet && results[effectiveActType] == ConditionRunnerActResult.SuccessExplicit)
|
|
logState = ConditionRunnerActResult.SkipContradiction;
|
|
else if (!isMet && results[effectiveActType] == ConditionRunnerActResult.SuccessImplicit)
|
|
logState = ConditionRunnerActResult.SkipDuplicate;
|
|
|
|
if (logState == ConditionRunnerActResult.None) {
|
|
// Determine log result.
|
|
logState = isMet ? ConditionRunnerActResult.SuccessExplicit : ConditionRunnerActResult.SuccessImplicit;
|
|
|
|
// Update running state info.
|
|
results[effectiveActType] = logState;
|
|
|
|
// Update form UI.
|
|
cond.doAction(isMet);
|
|
|
|
// Restart task.
|
|
this.setRestart();
|
|
needRestart = true;
|
|
}
|
|
|
|
// Log result.
|
|
this.log(condListInfo.id, j, cond.actType, logState, isMet, needRestart);
|
|
|
|
// Some other action could cause this to get set.
|
|
if (this.getRestart())
|
|
needRestart = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (numRestarts >= 65535)
|
|
console[console.warn ? "warn" : "log"]("Infinite Loop (heuristic-detected) in FieldConditionsRunner.runTask()");
|
|
};
|
|
;
|
|
// This file contains javascript functions for the "External Integration" field type.
|
|
// REQUIREMENTS:
|
|
// FieldSetDesigner.js
|
|
|
|
// These values must match the values provided in CivicPlus.Entities.Modules.FormCenter.Enums.ExternalIntegrationCommandType
|
|
var ExternalIntegrationCommandType = {
|
|
XMLCommand: 1,
|
|
EchoCommand: 2
|
|
};
|
|
|
|
// Closure containing the client-side functionality for the External Integration field type.
|
|
// * externalIntegrationClass - The name of the CSS class that is used to identify external integration buttons.
|
|
function ExternalIntegration(externalIntegrationClassname, fieldSetDesigner) {
|
|
this.cssClassname = externalIntegrationClassname;
|
|
this.fieldSetDesigner = fieldSetDesigner;
|
|
|
|
this.hookEvents = function () {
|
|
var my = this;
|
|
if (my.cssClassname == null)
|
|
throw 'No external integration button CSS class name has been specified.'
|
|
|
|
$('.' + this.cssClassname).click(function (event) {
|
|
var fieldID = null;
|
|
fieldID = event.target.id;
|
|
my.sendRequest(fieldID);
|
|
});
|
|
};
|
|
|
|
this.sendRequest = function (fieldID) {
|
|
var params = this.getFormData();
|
|
var formID = $('#Item_FormID').val();
|
|
var intFieldID = fieldID.replace('e_', '');
|
|
|
|
var my = this;
|
|
var sendData = {
|
|
id: formID,
|
|
fieldID: intFieldID,
|
|
parameters: JSON.stringify(params)
|
|
};
|
|
|
|
$.ajax({
|
|
type: 'post',
|
|
url: '/FormCenter/ExecuteExternalIntegrationCommand',
|
|
data: sendData,
|
|
success: function (data) { my.updateFormData(data, my); },
|
|
error: function (xhr, status, error) { alert('An error occurred while sending the request.'); },
|
|
beforeSend: function () {
|
|
ajaxPostBackStart('Loading');
|
|
},
|
|
complete: function () {
|
|
ajaxPostBackEnd();
|
|
}
|
|
});
|
|
};
|
|
|
|
// This function takes the values from the JSON result and sets the appropriate
|
|
// form field values.
|
|
this.updateFormData = function (responseFromController, externalIntegrationObject) {
|
|
|
|
if (responseFromController.error) {
|
|
alert('An error occurred: ' + responseFromController.errorMessage);
|
|
return;
|
|
}
|
|
|
|
for (var i = 0, len = responseFromController.length; i < len; i++) {
|
|
var param = responseFromController[i];
|
|
switch (responseFromController[i].Type) {
|
|
case FieldTypes.ShortAnswer:
|
|
externalIntegrationObject.updateShortAnswerField(param);
|
|
break;
|
|
case FieldTypes.LongAnswer:
|
|
externalIntegrationObject.updateLongAnswerField(param);
|
|
break;
|
|
case FieldTypes.Checkboxes:
|
|
externalIntegrationObject.updateCheckboxField(param);
|
|
break;
|
|
case FieldTypes.Dropdown:
|
|
externalIntegrationObject.updateDropdownField(param);
|
|
break;
|
|
case FieldTypes.Password:
|
|
externalIntegrationObject.updatePasswordField(param);
|
|
break;
|
|
case FieldTypes.RadioButtons:
|
|
externalIntegrationObject.updateRadioField(param);
|
|
break;
|
|
case FieldTypes.DateTime:
|
|
externalIntegrationObject.updateDateField(param);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
this.getFormData = function () {
|
|
var params = [];
|
|
|
|
this.appendShortAnswerFields(params);
|
|
this.appendCheckboxFields(params);
|
|
this.appendRadioFields(params);
|
|
this.appendDropdowns(params);
|
|
this.appendTextAreaFields(params);
|
|
this.appendPasswordFields(params);
|
|
|
|
return params;
|
|
};
|
|
|
|
this.createFieldValue = function (fieldID, type, value, externalName) {
|
|
return { ID: fieldID, Type: type, Value: value, ExternalName: externalName};
|
|
};
|
|
|
|
this.appendShortAnswerFields = function (params) {
|
|
var my = this;
|
|
$('input[id^="e_"][type="text"]').each(function (i) {
|
|
var element = $(this);
|
|
params.push(my.createFieldValue(element.attr('id'), FieldTypes.ShortAnswer, element.val(), my.externalSubmissionName(element)));
|
|
});
|
|
};
|
|
|
|
this.externalSubmissionName = function($element) {
|
|
return $element.parents('*[data-external-submission-name]').attr('data-external-submission-name');
|
|
};
|
|
|
|
this.updateShortAnswerField = function (param) {
|
|
var id = param.ID;
|
|
$('#' + param.ID).val(param.Value);
|
|
}
|
|
|
|
this.appendCheckboxFields = function (params) {
|
|
var my = this;
|
|
$('input[id^="e_"][type="checkbox"]').each(function (index) {
|
|
var item = $(this);
|
|
var value = item.attr('value');
|
|
var checked = false;
|
|
if (item.is(':checked')) {
|
|
checked = true;
|
|
}
|
|
var fieldValue = my.createFieldValue(item.attr('id'), FieldTypes.Checkboxes, value, my.externalSubmissionName(item));
|
|
fieldValue.Checked = checked;
|
|
params.push(fieldValue);
|
|
});
|
|
};
|
|
|
|
this.updateCheckboxField = function (param) {
|
|
var id = param.ID;
|
|
if (param.Checked == true)
|
|
$('#' + param.ID).attr('checked', true);
|
|
else
|
|
$('#' + param.ID).attr('checked', false);
|
|
};
|
|
|
|
this.appendRadioFields = function (params) {
|
|
var my = this;
|
|
$('input[id^="e_"][type="radio"]').each(function (index) {
|
|
var item = $(this);
|
|
var value = item.attr('value');
|
|
var checked = false;
|
|
if (item.is(':checked')) {
|
|
checked = true;
|
|
}
|
|
var fieldValue = my.createFieldValue(item.attr('id'), FieldTypes.RadioButtons, value, my.externalSubmissionName(item));
|
|
fieldValue.Checked = checked;
|
|
|
|
params.push(fieldValue);
|
|
});
|
|
};
|
|
|
|
this.updateRadioField = function (param) {
|
|
var id = param.ID;
|
|
var value = param.Value;
|
|
var button = $('input[id^="' + id + '"][value="' + value + '"]');
|
|
button.attr('checked', 'checked');
|
|
};
|
|
|
|
this.appendDropdowns = function (params) {
|
|
var my = this;
|
|
$('select[id^="e_"]').each(function (index) {
|
|
var item = $(this);
|
|
params.push(my.createFieldValue(item.attr('id'), FieldTypes.Dropdown, item.val(), my.externalSubmissionName(item)));
|
|
});
|
|
};
|
|
|
|
this.updateDropdownField = function (param) {
|
|
$('#' + param.ID).val(param.Value);
|
|
}
|
|
|
|
this.appendTextAreaFields = function (params) {
|
|
var my = this;
|
|
$('textarea[id^="e_"]').each(function (index) {
|
|
var element = $(this);
|
|
|
|
if (element.closest('li.LongAnswer').length > 0) {
|
|
params.push(my.createFieldValue(element.attr('id'), FieldTypes.LongAnswer, element.text(), my.externalSubmissionName(element)));
|
|
}
|
|
});
|
|
};
|
|
|
|
this.updateLongAnswerField = function (param) {
|
|
$('#' + param.ID).text(param.Value);
|
|
}
|
|
|
|
this.appendPasswordFields = function (params) {
|
|
var my = this;
|
|
$('input[id^="e_"][type="password"').each(function (i) {
|
|
var element = $(this);
|
|
params.push(my.createFieldValue(element.attr('id'), FieldTypes.Password, element.val(), my.externalSubmissionName(element)));
|
|
});
|
|
}
|
|
|
|
this.updatePasswordField = function (param) {
|
|
// We don't want to update password information with values returned from the remote service.
|
|
return;
|
|
}
|
|
|
|
this.updateDateField = function (param) {
|
|
var my = this;
|
|
var isSpan = (param.Value.indexOf(',') >= 0);
|
|
|
|
var dateID = param.ID + '_date';
|
|
var timeID = param.ID + '_time';
|
|
var endDateID = param.ID + '_dateSpan';
|
|
var endTimeID = param.ID + '_timeSpan';
|
|
|
|
var updateDate = function (selector, val) {
|
|
var datePicker = $('#' + selector);
|
|
if (datePicker.length == 0)
|
|
return;
|
|
|
|
datePicker.data('tDatePicker').value(val);
|
|
};
|
|
|
|
var updateTime = function (selector, val) {
|
|
var timePicker = $('#' + selector);
|
|
if (timePicker.length == 0)
|
|
return;
|
|
timePicker.data('tTimePicker').value(val);
|
|
};
|
|
|
|
if (isSpan) {
|
|
var dateArray = param.Value.split(',');
|
|
var startDate = new Date(dateArray[0]);
|
|
var endDate = new Date(dateArray[1]);
|
|
|
|
updateDate(dateID, startDate);
|
|
updateTime(timeID, startDate);
|
|
|
|
updateDate(endDateID, endDate);
|
|
updateTime(endTimeID, endDate);
|
|
} else {
|
|
var startDate = new Date(param.Value);
|
|
updateDate(dateID, startDate);
|
|
updateTime(timeID, startDate);
|
|
}
|
|
}
|
|
};
|
|
|
|
// Calls subroutines used to initialize the ExternalIntegration javascript object.
|
|
ExternalIntegration.prototype.initialize = function () {
|
|
this.hookEvents();
|
|
};
|
|
|
|
var CP_DynamicForm_ExternalSubmissionFE = function($) {
|
|
|
|
var self = this;
|
|
|
|
|
|
self.getExternalSubmissionData = function() {
|
|
var $fieldContainers = $('.formFieldContainer');
|
|
var data = {};
|
|
$fieldContainers.each(function (index) {
|
|
var $container = $(this);
|
|
var externalSubmissionName = self.getExternalSubmissionName($container);
|
|
var singleFieldData = self.getSingleFieldData($container);
|
|
if (singleFieldData != null) {
|
|
data[externalSubmissionName] = self.getSingleFieldData($container);
|
|
}
|
|
});
|
|
return data;
|
|
};
|
|
|
|
self.getExternalSubmissionName = function($container) {
|
|
return $container.attr('data-external-submission-name');
|
|
};
|
|
|
|
self.getSingleFieldData = function($container) {
|
|
var self = this;
|
|
var result = null;
|
|
if ($container.hasClass('ShortAnswer')) {
|
|
result = self.submissionDataShortAnswer($container);
|
|
} else if ($container.hasClass('LongAnswer')) {
|
|
result = self.submissionDataLongAnswer($container);
|
|
} else if ($container.hasClass('Checkboxes')) {
|
|
result = self.submissionDataCheckboxes($container);
|
|
} else if ($container.hasClass('Dropdown')) {
|
|
result = self.submissionDataDropdown($container);
|
|
} else if ($container.hasClass('RadioButtons')) {
|
|
result = self.submissionDataRadioButtons($container);
|
|
} else if ($container.hasClass('ReplyEmail')) {
|
|
result = self.submissionDataReplyEmail($container);
|
|
} else if ($container.hasClass('DateTime')) {
|
|
result = self.submissionDataDateTime($container);
|
|
} else if ($container.hasClass('DateTimeSpan')) {
|
|
result = self.submissionDataDateTimeSpan($container);
|
|
} else if ($container.hasClass('Password')) {
|
|
result = self.submissionDataPassword($container);
|
|
}
|
|
|
|
return result;
|
|
};
|
|
|
|
//
|
|
// There needs to be one function for each type of field that will be passed to
|
|
// an external submission service.
|
|
//
|
|
|
|
self.submissionDataShortAnswer = function($container) {
|
|
return $('input[id^=e_]', $container).val();
|
|
};
|
|
|
|
self.submissionDataLongAnswer = function($container) {
|
|
return $('textarea', $container).html();
|
|
};
|
|
|
|
self.submissionDataPassword = function($container) {
|
|
return $('input[id^=e_]', $container).val();
|
|
};
|
|
|
|
self.submissionDataCheckboxes = function($container) {
|
|
var result = [];
|
|
var $checkedItems = $('input[type=checkbox]:checked', $container);
|
|
$checkedItems.each(function (index) {
|
|
result.push($(this).attr('Value'));
|
|
});
|
|
return result.join();
|
|
};
|
|
|
|
self.submissionDataDropdown = function($container) {
|
|
return $('select option:selected').val();
|
|
};
|
|
|
|
self.submissionDataRadioButtons = function($container) {
|
|
return $('input:radio:checked', $container).val();
|
|
};
|
|
|
|
self.submissionDataReplyEmail = function($container) {
|
|
return $('input[id^=e_]', $container).val();
|
|
};
|
|
|
|
self.isDateTimeValueEmpty = function(val) {
|
|
// The telerik controls sometimes have just "0" if the user does not
|
|
// enter a value, so this function checks for both an empty value and for "0"
|
|
if (val == null || val == '' || val == '0')
|
|
return true;
|
|
else
|
|
return false;
|
|
};
|
|
|
|
self.submissionDataDateTime = function($container) {
|
|
var $dateDiv = $('div.date', $container).not('.time')[0];
|
|
var $timeDiv = $('div.time', $container)[0];
|
|
|
|
var date = $('input', $dateDiv).val();
|
|
var time = $('input', $timeDiv).val();
|
|
|
|
return formatDateTime(date, time);
|
|
};
|
|
|
|
self.formatDateTime = function(date, time) {
|
|
var dateEmpty = isDateTimeValueEmpty(date);
|
|
var timeEmpty = isDateTimeValueEmpty(time);
|
|
|
|
if (dateEmpty && timeEmpty)
|
|
return '';
|
|
else if (!dateEmpty && timeEmpty)
|
|
return date;
|
|
else if (dateEmpty && !timeEmpty)
|
|
return time;
|
|
else
|
|
return date + ' ' + time;
|
|
}
|
|
|
|
self.submissionDataDateTimeSpan = function($container) {
|
|
var $startDateDiv = $('div.date', $container).not('.time')[0];
|
|
var $startTimeDiv = $('div.date.time', $container)[0];
|
|
var startDate = $('input', $startDateDiv).val();
|
|
var startTime = $('input', $startTimeDiv).val();
|
|
|
|
var $endDateDiv = $('div.date', $container).not('.time')[1];
|
|
var $endTimeDiv = $('div.date.time', $container)[1];
|
|
var endDate = $('input', $endDateDiv).val();
|
|
var endTime = $('input', $endTimeDiv).val();
|
|
|
|
var startDateTime = formatDateTime(startDate, startTime);
|
|
var endDateTime = formatDateTime(endDate, endTime);
|
|
|
|
if (startDateTime == '' && endDateTime == '')
|
|
return '';
|
|
|
|
return startDateTime + ',' + endDateTime;
|
|
};
|
|
|
|
|
|
return self;
|
|
}(jQuery);
|
|
|
|
|
|
var CP_DynamicForm_PrintSubmission = (function () {
|
|
self.StatusCodes = {
|
|
Loading: 1,
|
|
Success: 2,
|
|
Spam: 3,
|
|
Timeout: 4
|
|
};
|
|
|
|
return self;
|
|
})();
|
|
/*
|
|
* ----------------------------- JSTORAGE -------------------------------------
|
|
* Simple local storage wrapper to save data on the browser side, supporting
|
|
* all major browsers - IE6+, Firefox2+, Safari4+, Chrome4+ and Opera 10.5+
|
|
*
|
|
* Copyright (c) 2010 - 2012 Andris Reinman, andris.reinman@gmail.com
|
|
* Project homepage: www.jstorage.info
|
|
*
|
|
* Licensed under MIT-style license:
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
* SOFTWARE.
|
|
*/
|
|
|
|
(function () {
|
|
var
|
|
/* jStorage version */
|
|
JSTORAGE_VERSION = "0.4.3",
|
|
|
|
/* detect a dollar object or create one if not found */
|
|
$ = window.jQuery || window.$ || (window.$ = {}),
|
|
|
|
/* check for a JSON handling support */
|
|
JSON = {
|
|
parse:
|
|
window.JSON && (window.JSON.parse || window.JSON.decode) ||
|
|
String.prototype.evalJSON && function (str) { return String(str).evalJSON(); } ||
|
|
$.parseJSON ||
|
|
$.evalJSON,
|
|
stringify:
|
|
Object.toJSON ||
|
|
window.JSON && (window.JSON.stringify || window.JSON.encode) ||
|
|
$.toJSON
|
|
};
|
|
|
|
// Break if no JSON support was found
|
|
if (!JSON.parse || !JSON.stringify) {
|
|
throw new Error("No JSON support found, include //cdnjs.cloudflare.com/ajax/libs/json2/20110223/json2.js to page");
|
|
}
|
|
|
|
var
|
|
/* This is the object, that holds the cached values */
|
|
_storage = { __jstorage_meta: { CRC32: {} } },
|
|
|
|
/* Actual browser storage (localStorage or globalStorage['domain']) */
|
|
_storage_service = { jStorage: "{}" },
|
|
|
|
/* DOM element for older IE versions, holds userData behavior */
|
|
_storage_elm = null,
|
|
|
|
/* How much space does the storage take */
|
|
_storage_size = 0,
|
|
|
|
/* which backend is currently used */
|
|
_backend = false,
|
|
|
|
/* onchange observers */
|
|
_observers = {},
|
|
|
|
/* timeout to wait after onchange event */
|
|
_observer_timeout = false,
|
|
|
|
/* last update time */
|
|
_observer_update = 0,
|
|
|
|
/* pubsub observers */
|
|
_pubsub_observers = {},
|
|
|
|
/* skip published items older than current timestamp */
|
|
_pubsub_last = +new Date(),
|
|
|
|
/* Next check for TTL */
|
|
_ttl_timeout,
|
|
|
|
/**
|
|
* XML encoding and decoding as XML nodes can't be JSON'ized
|
|
* XML nodes are encoded and decoded if the node is the value to be saved
|
|
* but not if it's as a property of another object
|
|
* Eg. -
|
|
* $.jStorage.set("key", xmlNode); // IS OK
|
|
* $.jStorage.set("key", {xml: xmlNode}); // NOT OK
|
|
*/
|
|
_XMLService = {
|
|
|
|
/**
|
|
* Validates a XML node to be XML
|
|
* based on jQuery.isXML function
|
|
*/
|
|
isXML: function (elm) {
|
|
var documentElement = (elm ? elm.ownerDocument || elm : 0).documentElement;
|
|
return documentElement ? documentElement.nodeName !== "HTML" : false;
|
|
},
|
|
|
|
/**
|
|
* Encodes a XML node to string
|
|
* based on http://www.mercurytide.co.uk/news/article/issues-when-working-ajax/
|
|
*/
|
|
encode: function (xmlNode) {
|
|
if (!this.isXML(xmlNode)) {
|
|
return false;
|
|
}
|
|
try { // Mozilla, Webkit, Opera
|
|
return new XMLSerializer().serializeToString(xmlNode);
|
|
} catch (E1) {
|
|
try { // IE
|
|
return xmlNode.xml;
|
|
} catch (E2) { }
|
|
}
|
|
return false;
|
|
},
|
|
|
|
/**
|
|
* Decodes a XML node from string
|
|
* loosely based on http://outwestmedia.com/jquery-plugins/xmldom/
|
|
*/
|
|
decode: function (xmlString) {
|
|
var dom_parser = ("DOMParser" in window && (new DOMParser()).parseFromString) ||
|
|
(window.ActiveXObject && function (_xmlString) {
|
|
var xml_doc = new ActiveXObject('Microsoft.XMLDOM');
|
|
xml_doc.async = 'false';
|
|
xml_doc.loadXML(_xmlString);
|
|
return xml_doc;
|
|
}),
|
|
resultXML;
|
|
if (!dom_parser) {
|
|
return false;
|
|
}
|
|
resultXML = dom_parser.call("DOMParser" in window && (new DOMParser()) || window, xmlString, 'text/xml');
|
|
return this.isXML(resultXML) ? resultXML : false;
|
|
}
|
|
};
|
|
|
|
|
|
////////////////////////// PRIVATE METHODS ////////////////////////
|
|
|
|
/**
|
|
* Initialization function. Detects if the browser supports DOM Storage
|
|
* or userData behavior and behaves accordingly.
|
|
*/
|
|
function _init() {
|
|
/* Check if browser supports localStorage */
|
|
var localStorageReallyWorks = false;
|
|
if ("localStorage" in window) {
|
|
try {
|
|
window.localStorage.setItem('_tmptest', 'tmpval');
|
|
localStorageReallyWorks = true;
|
|
window.localStorage.removeItem('_tmptest');
|
|
} catch (BogusQuotaExceededErrorOnIos5) {
|
|
// Thanks be to iOS5 Private Browsing mode which throws
|
|
// QUOTA_EXCEEDED_ERRROR DOM Exception 22.
|
|
}
|
|
}
|
|
|
|
if (localStorageReallyWorks) {
|
|
try {
|
|
if (window.localStorage) {
|
|
_storage_service = window.localStorage;
|
|
_backend = "localStorage";
|
|
_observer_update = _storage_service.jStorage_update;
|
|
}
|
|
} catch (E3) {/* Firefox fails when touching localStorage and cookies are disabled */ }
|
|
}
|
|
/* Check if browser supports globalStorage */
|
|
else if ("globalStorage" in window) {
|
|
try {
|
|
if (window.globalStorage) {
|
|
_storage_service = window.globalStorage[window.location.hostname];
|
|
_backend = "globalStorage";
|
|
_observer_update = _storage_service.jStorage_update;
|
|
}
|
|
} catch (E4) {/* Firefox fails when touching localStorage and cookies are disabled */ }
|
|
}
|
|
/* Check if browser supports userData behavior */
|
|
else {
|
|
_storage_elm = document.createElement('link');
|
|
if (_storage_elm.addBehavior) {
|
|
|
|
/* Use a DOM element to act as userData storage */
|
|
_storage_elm.style.behavior = 'url(#default#userData)';
|
|
|
|
/* userData element needs to be inserted into the DOM! */
|
|
document.getElementsByTagName('head')[0].appendChild(_storage_elm);
|
|
|
|
try {
|
|
_storage_elm.load("jStorage");
|
|
} catch (E) {
|
|
// try to reset cache
|
|
_storage_elm.setAttribute("jStorage", "{}");
|
|
_storage_elm.save("jStorage");
|
|
_storage_elm.load("jStorage");
|
|
}
|
|
|
|
var data = "{}";
|
|
try {
|
|
data = _storage_elm.getAttribute("jStorage");
|
|
} catch (E5) { }
|
|
|
|
try {
|
|
_observer_update = _storage_elm.getAttribute("jStorage_update");
|
|
} catch (E6) { }
|
|
|
|
_storage_service.jStorage = data;
|
|
_backend = "userDataBehavior";
|
|
} else {
|
|
_storage_elm = null;
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Load data from storage
|
|
_load_storage();
|
|
|
|
// remove dead keys
|
|
_handleTTL();
|
|
|
|
// start listening for changes
|
|
_setupObserver();
|
|
|
|
// initialize publish-subscribe service
|
|
_handlePubSub();
|
|
|
|
// handle cached navigation
|
|
if ("addEventListener" in window) {
|
|
window.addEventListener("pageshow", function (event) {
|
|
if (event.persisted) {
|
|
_storageObserver();
|
|
}
|
|
}, false);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Reload data from storage when needed
|
|
*/
|
|
function _reloadData() {
|
|
var data = "{}";
|
|
|
|
if (_backend == "userDataBehavior") {
|
|
_storage_elm.load("jStorage");
|
|
|
|
try {
|
|
data = _storage_elm.getAttribute("jStorage");
|
|
} catch (E5) { }
|
|
|
|
try {
|
|
_observer_update = _storage_elm.getAttribute("jStorage_update");
|
|
} catch (E6) { }
|
|
|
|
_storage_service.jStorage = data;
|
|
}
|
|
|
|
_load_storage();
|
|
|
|
// remove dead keys
|
|
_handleTTL();
|
|
|
|
_handlePubSub();
|
|
}
|
|
|
|
/**
|
|
* Sets up a storage change observer
|
|
*/
|
|
function _setupObserver() {
|
|
if (_backend == "localStorage" || _backend == "globalStorage") {
|
|
if ("addEventListener" in window) {
|
|
window.addEventListener("storage", _storageObserver, false);
|
|
} else {
|
|
document.attachEvent("onstorage", _storageObserver);
|
|
}
|
|
} else if (_backend == "userDataBehavior") {
|
|
setInterval(_storageObserver, 1000);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Fired on any kind of data change, needs to check if anything has
|
|
* really been changed
|
|
*/
|
|
function _storageObserver() {
|
|
var updateTime;
|
|
// cumulate change notifications with timeout
|
|
clearTimeout(_observer_timeout);
|
|
_observer_timeout = setTimeout(function () {
|
|
|
|
if (_backend == "localStorage" || _backend == "globalStorage") {
|
|
updateTime = _storage_service.jStorage_update;
|
|
} else if (_backend == "userDataBehavior") {
|
|
_storage_elm.load("jStorage");
|
|
try {
|
|
updateTime = _storage_elm.getAttribute("jStorage_update");
|
|
} catch (E5) { }
|
|
}
|
|
|
|
if (updateTime && updateTime != _observer_update) {
|
|
_observer_update = updateTime;
|
|
_checkUpdatedKeys();
|
|
}
|
|
|
|
}, 25);
|
|
}
|
|
|
|
/**
|
|
* Reloads the data and checks if any keys are changed
|
|
*/
|
|
function _checkUpdatedKeys() {
|
|
var oldCrc32List = JSON.parse(JSON.stringify(_storage.__jstorage_meta.CRC32)),
|
|
newCrc32List;
|
|
|
|
_reloadData();
|
|
newCrc32List = JSON.parse(JSON.stringify(_storage.__jstorage_meta.CRC32));
|
|
|
|
var key,
|
|
updated = [],
|
|
removed = [];
|
|
|
|
for (key in oldCrc32List) {
|
|
if (oldCrc32List.hasOwnProperty(key)) {
|
|
if (!newCrc32List[key]) {
|
|
removed.push(key);
|
|
continue;
|
|
}
|
|
if (oldCrc32List[key] != newCrc32List[key] && String(oldCrc32List[key]).substr(0, 2) == "2.") {
|
|
updated.push(key);
|
|
}
|
|
}
|
|
}
|
|
|
|
for (key in newCrc32List) {
|
|
if (newCrc32List.hasOwnProperty(key)) {
|
|
if (!oldCrc32List[key]) {
|
|
updated.push(key);
|
|
}
|
|
}
|
|
}
|
|
|
|
_fireObservers(updated, "updated");
|
|
_fireObservers(removed, "deleted");
|
|
}
|
|
|
|
/**
|
|
* Fires observers for updated keys
|
|
*
|
|
* @param {Array|String} keys Array of key names or a key
|
|
* @param {String} action What happened with the value (updated, deleted, flushed)
|
|
*/
|
|
function _fireObservers(keys, action) {
|
|
keys = [].concat(keys || []);
|
|
if (action == "flushed") {
|
|
keys = [];
|
|
for (var key in _observers) {
|
|
if (_observers.hasOwnProperty(key)) {
|
|
keys.push(key);
|
|
}
|
|
}
|
|
action = "deleted";
|
|
}
|
|
for (var i = 0, len = keys.length; i < len; i++) {
|
|
if (_observers[keys[i]]) {
|
|
for (var j = 0, jlen = _observers[keys[i]].length; j < jlen; j++) {
|
|
_observers[keys[i]][j](keys[i], action);
|
|
}
|
|
}
|
|
if (_observers["*"]) {
|
|
for (var j = 0, jlen = _observers["*"].length; j < jlen; j++) {
|
|
_observers["*"][j](keys[i], action);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Publishes key change to listeners
|
|
*/
|
|
function _publishChange() {
|
|
var updateTime = (+new Date()).toString();
|
|
|
|
if (_backend == "localStorage" || _backend == "globalStorage") {
|
|
_storage_service.jStorage_update = updateTime;
|
|
} else if (_backend == "userDataBehavior") {
|
|
_storage_elm.setAttribute("jStorage_update", updateTime);
|
|
_storage_elm.save("jStorage");
|
|
}
|
|
|
|
_storageObserver();
|
|
}
|
|
|
|
/**
|
|
* Loads the data from the storage based on the supported mechanism
|
|
*/
|
|
function _load_storage() {
|
|
/* if jStorage string is retrieved, then decode it */
|
|
if (_storage_service.jStorage) {
|
|
try {
|
|
_storage = JSON.parse(String(_storage_service.jStorage));
|
|
} catch (E6) { _storage_service.jStorage = "{}"; }
|
|
} else {
|
|
_storage_service.jStorage = "{}";
|
|
}
|
|
_storage_size = _storage_service.jStorage ? String(_storage_service.jStorage).length : 0;
|
|
|
|
if (!_storage.__jstorage_meta) {
|
|
_storage.__jstorage_meta = {};
|
|
}
|
|
if (!_storage.__jstorage_meta.CRC32) {
|
|
_storage.__jstorage_meta.CRC32 = {};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* This functions provides the "save" mechanism to store the jStorage object
|
|
*/
|
|
function _save() {
|
|
_dropOldEvents(); // remove expired events
|
|
try {
|
|
_storage_service.jStorage = JSON.stringify(_storage);
|
|
// If userData is used as the storage engine, additional
|
|
if (_storage_elm) {
|
|
_storage_elm.setAttribute("jStorage", _storage_service.jStorage);
|
|
_storage_elm.save("jStorage");
|
|
}
|
|
_storage_size = _storage_service.jStorage ? String(_storage_service.jStorage).length : 0;
|
|
} catch (E7) {/* probably cache is full, nothing is saved this way*/ }
|
|
}
|
|
|
|
/**
|
|
* Function checks if a key is set and is string or numberic
|
|
*
|
|
* @param {String} key Key name
|
|
*/
|
|
function _checkKey(key) {
|
|
if (!key || (typeof key != "string" && typeof key != "number")) {
|
|
throw new TypeError('Key name must be string or numeric');
|
|
}
|
|
if (key == "__jstorage_meta") {
|
|
throw new TypeError('Reserved key name');
|
|
}
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Removes expired keys
|
|
*/
|
|
function _handleTTL() {
|
|
var curtime, i, TTL, CRC32, nextExpire = Infinity, changed = false, deleted = [];
|
|
|
|
clearTimeout(_ttl_timeout);
|
|
|
|
if (!_storage.__jstorage_meta || typeof _storage.__jstorage_meta.TTL != "object") {
|
|
// nothing to do here
|
|
return;
|
|
}
|
|
|
|
curtime = +new Date();
|
|
TTL = _storage.__jstorage_meta.TTL;
|
|
|
|
CRC32 = _storage.__jstorage_meta.CRC32;
|
|
for (i in TTL) {
|
|
if (TTL.hasOwnProperty(i)) {
|
|
if (TTL[i] <= curtime) {
|
|
delete TTL[i];
|
|
delete CRC32[i];
|
|
delete _storage[i];
|
|
changed = true;
|
|
deleted.push(i);
|
|
} else if (TTL[i] < nextExpire) {
|
|
nextExpire = TTL[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
// set next check
|
|
if (nextExpire != Infinity) {
|
|
_ttl_timeout = setTimeout(_handleTTL, nextExpire - curtime);
|
|
}
|
|
|
|
// save changes
|
|
if (changed) {
|
|
_save();
|
|
_publishChange();
|
|
_fireObservers(deleted, "deleted");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Checks if there's any events on hold to be fired to listeners
|
|
*/
|
|
function _handlePubSub() {
|
|
var i, len;
|
|
if (!_storage.__jstorage_meta.PubSub) {
|
|
return;
|
|
}
|
|
var pubelm,
|
|
_pubsubCurrent = _pubsub_last;
|
|
|
|
for (i = len = _storage.__jstorage_meta.PubSub.length - 1; i >= 0; i--) {
|
|
pubelm = _storage.__jstorage_meta.PubSub[i];
|
|
if (pubelm[0] > _pubsub_last) {
|
|
_pubsubCurrent = pubelm[0];
|
|
_fireSubscribers(pubelm[1], pubelm[2]);
|
|
}
|
|
}
|
|
|
|
_pubsub_last = _pubsubCurrent;
|
|
}
|
|
|
|
/**
|
|
* Fires all subscriber listeners for a pubsub channel
|
|
*
|
|
* @param {String} channel Channel name
|
|
* @param {Mixed} payload Payload data to deliver
|
|
*/
|
|
function _fireSubscribers(channel, payload) {
|
|
if (_pubsub_observers[channel]) {
|
|
for (var i = 0, len = _pubsub_observers[channel].length; i < len; i++) {
|
|
// send immutable data that can't be modified by listeners
|
|
_pubsub_observers[channel][i](channel, JSON.parse(JSON.stringify(payload)));
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Remove old events from the publish stream (at least 2sec old)
|
|
*/
|
|
function _dropOldEvents() {
|
|
if (!_storage.__jstorage_meta.PubSub) {
|
|
return;
|
|
}
|
|
|
|
var retire = +new Date() - 2000;
|
|
|
|
for (var i = 0, len = _storage.__jstorage_meta.PubSub.length; i < len; i++) {
|
|
if (_storage.__jstorage_meta.PubSub[i][0] <= retire) {
|
|
// deleteCount is needed for IE6
|
|
_storage.__jstorage_meta.PubSub.splice(i, _storage.__jstorage_meta.PubSub.length - i);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!_storage.__jstorage_meta.PubSub.length) {
|
|
delete _storage.__jstorage_meta.PubSub;
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* Publish payload to a channel
|
|
*
|
|
* @param {String} channel Channel name
|
|
* @param {Mixed} payload Payload to send to the subscribers
|
|
*/
|
|
function _publish(channel, payload) {
|
|
if (!_storage.__jstorage_meta) {
|
|
_storage.__jstorage_meta = {};
|
|
}
|
|
if (!_storage.__jstorage_meta.PubSub) {
|
|
_storage.__jstorage_meta.PubSub = [];
|
|
}
|
|
|
|
_storage.__jstorage_meta.PubSub.unshift([+new Date, channel, payload]);
|
|
|
|
_save();
|
|
_publishChange();
|
|
}
|
|
|
|
|
|
/**
|
|
* JS Implementation of MurmurHash2
|
|
*
|
|
* SOURCE: https://github.com/garycourt/murmurhash-js (MIT licensed)
|
|
*
|
|
* @author <a href="mailto:gary.court@gmail.com">Gary Court</a>
|
|
* @see http://github.com/garycourt/murmurhash-js
|
|
* @author <a href="mailto:aappleby@gmail.com">Austin Appleby</a>
|
|
* @see http://sites.google.com/site/murmurhash/
|
|
*
|
|
* @param {string} str ASCII only
|
|
* @param {number} seed Positive integer only
|
|
* @return {number} 32-bit positive integer hash
|
|
*/
|
|
|
|
function murmurhash2_32_gc(str, seed) {
|
|
var
|
|
l = str.length,
|
|
h = seed ^ l,
|
|
i = 0,
|
|
k;
|
|
|
|
while (l >= 4) {
|
|
k =
|
|
((str.charCodeAt(i) & 0xff)) |
|
|
((str.charCodeAt(++i) & 0xff) << 8) |
|
|
((str.charCodeAt(++i) & 0xff) << 16) |
|
|
((str.charCodeAt(++i) & 0xff) << 24);
|
|
|
|
k = (((k & 0xffff) * 0x5bd1e995) + ((((k >>> 16) * 0x5bd1e995) & 0xffff) << 16));
|
|
k ^= k >>> 24;
|
|
k = (((k & 0xffff) * 0x5bd1e995) + ((((k >>> 16) * 0x5bd1e995) & 0xffff) << 16));
|
|
|
|
h = (((h & 0xffff) * 0x5bd1e995) + ((((h >>> 16) * 0x5bd1e995) & 0xffff) << 16)) ^ k;
|
|
|
|
l -= 4;
|
|
++i;
|
|
}
|
|
|
|
switch (l) {
|
|
case 3: h ^= (str.charCodeAt(i + 2) & 0xff) << 16;
|
|
case 2: h ^= (str.charCodeAt(i + 1) & 0xff) << 8;
|
|
case 1: h ^= (str.charCodeAt(i) & 0xff);
|
|
h = (((h & 0xffff) * 0x5bd1e995) + ((((h >>> 16) * 0x5bd1e995) & 0xffff) << 16));
|
|
}
|
|
|
|
h ^= h >>> 13;
|
|
h = (((h & 0xffff) * 0x5bd1e995) + ((((h >>> 16) * 0x5bd1e995) & 0xffff) << 16));
|
|
h ^= h >>> 15;
|
|
|
|
return h >>> 0;
|
|
}
|
|
|
|
////////////////////////// PUBLIC INTERFACE /////////////////////////
|
|
|
|
$.jStorage = {
|
|
/* Version number */
|
|
version: JSTORAGE_VERSION,
|
|
|
|
/**
|
|
* Sets a key's value.
|
|
*
|
|
* @param {String} key Key to set. If this value is not set or not
|
|
* a string an exception is raised.
|
|
* @param {Mixed} value Value to set. This can be any value that is JSON
|
|
* compatible (Numbers, Strings, Objects etc.).
|
|
* @param {Object} [options] - possible options to use
|
|
* @param {Number} [options.TTL] - optional TTL value
|
|
* @return {Mixed} the used value
|
|
*/
|
|
set: function (key, value, options) {
|
|
_checkKey(key);
|
|
|
|
options = options || {};
|
|
|
|
// undefined values are deleted automatically
|
|
if (typeof value == "undefined") {
|
|
this.deleteKey(key);
|
|
return value;
|
|
}
|
|
|
|
if (_XMLService.isXML(value)) {
|
|
value = { _is_xml: true, xml: _XMLService.encode(value) };
|
|
} else if (typeof value == "function") {
|
|
return undefined; // functions can't be saved!
|
|
} else if (value && typeof value == "object") {
|
|
// clone the object before saving to _storage tree
|
|
value = JSON.parse(JSON.stringify(value));
|
|
}
|
|
|
|
_storage[key] = value;
|
|
|
|
_storage.__jstorage_meta.CRC32[key] = "2." + murmurhash2_32_gc(JSON.stringify(value), 0x9747b28c);
|
|
|
|
this.setTTL(key, options.TTL || 0); // also handles saving and _publishChange
|
|
|
|
_fireObservers(key, "updated");
|
|
return value;
|
|
},
|
|
|
|
/**
|
|
* Looks up a key in cache
|
|
*
|
|
* @param {String} key - Key to look up.
|
|
* @param {mixed} def - Default value to return, if key didn't exist.
|
|
* @return {Mixed} the key value, default value or null
|
|
*/
|
|
get: function (key, def) {
|
|
_checkKey(key);
|
|
if (key in _storage) {
|
|
if (_storage[key] && typeof _storage[key] == "object" && _storage[key]._is_xml) {
|
|
return _XMLService.decode(_storage[key].xml);
|
|
} else {
|
|
return _storage[key];
|
|
}
|
|
}
|
|
return typeof (def) == 'undefined' ? null : def;
|
|
},
|
|
|
|
/**
|
|
* Deletes a key from cache.
|
|
*
|
|
* @param {String} key - Key to delete.
|
|
* @return {Boolean} true if key existed or false if it didn't
|
|
*/
|
|
deleteKey: function (key) {
|
|
_checkKey(key);
|
|
if (key in _storage) {
|
|
delete _storage[key];
|
|
// remove from TTL list
|
|
if (typeof _storage.__jstorage_meta.TTL == "object" &&
|
|
key in _storage.__jstorage_meta.TTL) {
|
|
delete _storage.__jstorage_meta.TTL[key];
|
|
}
|
|
|
|
delete _storage.__jstorage_meta.CRC32[key];
|
|
|
|
_save();
|
|
_publishChange();
|
|
_fireObservers(key, "deleted");
|
|
return true;
|
|
}
|
|
return false;
|
|
},
|
|
|
|
/**
|
|
* Sets a TTL for a key, or remove it if ttl value is 0 or below
|
|
*
|
|
* @param {String} key - key to set the TTL for
|
|
* @param {Number} ttl - TTL timeout in milliseconds
|
|
* @return {Boolean} true if key existed or false if it didn't
|
|
*/
|
|
setTTL: function (key, ttl) {
|
|
var curtime = +new Date();
|
|
_checkKey(key);
|
|
ttl = Number(ttl) || 0;
|
|
if (key in _storage) {
|
|
|
|
if (!_storage.__jstorage_meta.TTL) {
|
|
_storage.__jstorage_meta.TTL = {};
|
|
}
|
|
|
|
// Set TTL value for the key
|
|
if (ttl > 0) {
|
|
_storage.__jstorage_meta.TTL[key] = curtime + ttl;
|
|
} else {
|
|
delete _storage.__jstorage_meta.TTL[key];
|
|
}
|
|
|
|
_save();
|
|
|
|
_handleTTL();
|
|
|
|
_publishChange();
|
|
return true;
|
|
}
|
|
return false;
|
|
},
|
|
|
|
/**
|
|
* Gets remaining TTL (in milliseconds) for a key or 0 when no TTL has been set
|
|
*
|
|
* @param {String} key Key to check
|
|
* @return {Number} Remaining TTL in milliseconds
|
|
*/
|
|
getTTL: function (key) {
|
|
var curtime = +new Date(), ttl;
|
|
_checkKey(key);
|
|
if (key in _storage && _storage.__jstorage_meta.TTL && _storage.__jstorage_meta.TTL[key]) {
|
|
ttl = _storage.__jstorage_meta.TTL[key] - curtime;
|
|
return ttl || 0;
|
|
}
|
|
return 0;
|
|
},
|
|
|
|
/**
|
|
* Deletes everything in cache.
|
|
*
|
|
* @return {Boolean} Always true
|
|
*/
|
|
flush: function () {
|
|
_storage = { __jstorage_meta: { CRC32: {} } };
|
|
_save();
|
|
_publishChange();
|
|
_fireObservers(null, "flushed");
|
|
return true;
|
|
},
|
|
|
|
/**
|
|
* Returns a read-only copy of _storage
|
|
*
|
|
* @return {Object} Read-only copy of _storage
|
|
*/
|
|
storageObj: function () {
|
|
function F() { }
|
|
F.prototype = _storage;
|
|
return new F();
|
|
},
|
|
|
|
/**
|
|
* Returns an index of all used keys as an array
|
|
* ['key1', 'key2',..'keyN']
|
|
*
|
|
* @return {Array} Used keys
|
|
*/
|
|
index: function () {
|
|
var index = [], i;
|
|
for (i in _storage) {
|
|
if (_storage.hasOwnProperty(i) && i != "__jstorage_meta") {
|
|
index.push(i);
|
|
}
|
|
}
|
|
return index;
|
|
},
|
|
|
|
/**
|
|
* How much space in bytes does the storage take?
|
|
*
|
|
* @return {Number} Storage size in chars (not the same as in bytes,
|
|
* since some chars may take several bytes)
|
|
*/
|
|
storageSize: function () {
|
|
return _storage_size;
|
|
},
|
|
|
|
/**
|
|
* Which backend is currently in use?
|
|
*
|
|
* @return {String} Backend name
|
|
*/
|
|
currentBackend: function () {
|
|
return _backend;
|
|
},
|
|
|
|
/**
|
|
* Test if storage is available
|
|
*
|
|
* @return {Boolean} True if storage can be used
|
|
*/
|
|
storageAvailable: function () {
|
|
return !!_backend;
|
|
},
|
|
|
|
/**
|
|
* Register change listeners
|
|
*
|
|
* @param {String} key Key name
|
|
* @param {Function} callback Function to run when the key changes
|
|
*/
|
|
listenKeyChange: function (key, callback) {
|
|
_checkKey(key);
|
|
if (!_observers[key]) {
|
|
_observers[key] = [];
|
|
}
|
|
_observers[key].push(callback);
|
|
},
|
|
|
|
/**
|
|
* Remove change listeners
|
|
*
|
|
* @param {String} key Key name to unregister listeners against
|
|
* @param {Function} [callback] If set, unregister the callback, if not - unregister all
|
|
*/
|
|
stopListening: function (key, callback) {
|
|
_checkKey(key);
|
|
|
|
if (!_observers[key]) {
|
|
return;
|
|
}
|
|
|
|
if (!callback) {
|
|
delete _observers[key];
|
|
return;
|
|
}
|
|
|
|
for (var i = _observers[key].length - 1; i >= 0; i--) {
|
|
if (_observers[key][i] == callback) {
|
|
_observers[key].splice(i, 1);
|
|
}
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Subscribe to a Publish/Subscribe event stream
|
|
*
|
|
* @param {String} channel Channel name
|
|
* @param {Function} callback Function to run when the something is published to the channel
|
|
*/
|
|
subscribe: function (channel, callback) {
|
|
channel = (channel || "").toString();
|
|
if (!channel) {
|
|
throw new TypeError('Channel not defined');
|
|
}
|
|
if (!_pubsub_observers[channel]) {
|
|
_pubsub_observers[channel] = [];
|
|
}
|
|
_pubsub_observers[channel].push(callback);
|
|
},
|
|
|
|
/**
|
|
* Publish data to an event stream
|
|
*
|
|
* @param {String} channel Channel name
|
|
* @param {Mixed} payload Payload to deliver
|
|
*/
|
|
publish: function (channel, payload) {
|
|
channel = (channel || "").toString();
|
|
if (!channel) {
|
|
throw new TypeError('Channel not defined');
|
|
}
|
|
|
|
_publish(channel, payload);
|
|
},
|
|
|
|
/**
|
|
* Reloads the data from browser storage
|
|
*/
|
|
reInit: function () {
|
|
_reloadData();
|
|
}
|
|
};
|
|
|
|
// Initialize jStorage
|
|
_init();
|
|
|
|
})();
|
|
window.pageHandleResponsive = true;
|
|
|
|
$.when(window.Pages.rwdReady).done(function () {
|
|
|
|
toggleClassMedia("maxWidth600px", ".cpForm:media(this-max-width: 600px)");
|
|
toggleClassMedia("maxWidth515px", "#FormCenterContent:media(this-max-width: 515px)");
|
|
toggleClassMedia("maxWidth485px", "#FormCenterContent:media(this-max-width: 485px)");
|
|
toggleClassMedia("maxWidth440px", ".cpForm:media(this-max-width: 440px)");
|
|
toggleClassMedia("maxWidth395px", "#FormCenterContent:media(this-max-width: 395px)");
|
|
toggleClassMedia("maxWidth380px", ".cpForm:media(this-max-width: 380px)");
|
|
});
|
|
/**
|
|
cp.datepicker.js provides an JavaScript Date picker control.
|
|
*/
|
|
var cp = cp || {};
|
|
$(document).ready(function() {
|
|
cp.datetimepicker = (function($) {
|
|
// These are the publicly-accessible methods
|
|
if (!$) { return; }
|
|
return {
|
|
createTimePicker: createTimePicker,
|
|
createDatePicker: createDatePicker,
|
|
};
|
|
|
|
|
|
// getOptionsFromHtmlAttributes parses through any attribute in the user-specific HTML date or time picker and
|
|
// overwrites the values in "options" with values corresponding to "data-cp-datetimepicker-{optionName}". The
|
|
// "optionName" portion of the attribute should correspond to a property of the pickadate or pickatime
|
|
// options element.
|
|
function getOptionsFromHtmlAttributes($inputElement, originalOptions) {
|
|
$($inputElement[0].attributes).each(function() {
|
|
if (this.name.match('^data-cp-datetimepicker-')) {
|
|
var optionName = this.name.replace('data-cp-datetimepicker-', '');
|
|
var originalValue = originalOptions[optionName];
|
|
if (!originalValue) {
|
|
// We only want to set the option value if it was not already set in the JavaScript options.
|
|
// That is, the JavaScript options should always win over the HTML5 options.
|
|
|
|
var optionVal = this.value;
|
|
if (parseInt(optionVal)) {
|
|
optionVal = parseInt(optionVal);
|
|
}
|
|
|
|
originalOptions[optionName] = optionVal;
|
|
}
|
|
}
|
|
});
|
|
return originalOptions;
|
|
}
|
|
|
|
// createTimePicker creates a new time picker object out of the DOM object specified by timeTextboxSelector.
|
|
// The timeTextboxSelector object must be an HTML INPUT element.
|
|
function createTimePicker(timeTextboxSelector, options) {
|
|
|
|
// The actualy Input Time component will be a hidden field. The DOM object in the source HTML
|
|
// will be the editable text field for the user.
|
|
|
|
options = options || {};
|
|
|
|
var $inputTextBox = $(timeTextboxSelector);
|
|
|
|
if (!($inputTextBox) || $inputTextBox.length === 0) {
|
|
// We got passed a bad selector
|
|
console.log('ERR: cp.datetimepicker :: Could not find element: ' + timeTextboxSelector);
|
|
return;
|
|
}
|
|
|
|
// Set any options that were specified directly from within the HTML tag.
|
|
options = getOptionsFromHtmlAttributes($inputTextBox, options);
|
|
|
|
//Wrap fieldSetSelector inside div if wrapInsideDiv is true
|
|
var fieldSetSelector = '<fieldset class="fieldset__datetimepicker__wrapper">';
|
|
|
|
if (options.wrapInsideDiv) {
|
|
fieldSetSelector = '<div class="t-widget t-timepicker">' + fieldSetSelector + '</div>';
|
|
}
|
|
|
|
$fieldset = $(fieldSetSelector);
|
|
$inputTextBox.wrap($fieldset);
|
|
|
|
var $hiddenTimeInput = $('<input type="text" class="cp_input_datetime" tabindex="-1"/>');
|
|
$hiddenTimeInput.attr('data-cp-time-textbox-selector', timeTextboxSelector);
|
|
|
|
$hiddenTimeInput.attr('aria-hidden', true);
|
|
|
|
$hiddenTimeInput.insertAfter($inputTextBox);
|
|
|
|
// We need to force "editable" in the options, otherwise things start to break.
|
|
options.editable = true;
|
|
$hiddenTimeInput.pickatime(options);
|
|
|
|
var pickerObject = $hiddenTimeInput.pickatime('picker'); // reference to the pickatime object
|
|
|
|
$inputTextBox.on({
|
|
change: function() {
|
|
var inputTime = this.value;
|
|
pickerObject.set('customSelect', insertColonIfOverFourChars(inputTime), options);
|
|
},
|
|
focus: function() {
|
|
pickerObject.open(false);
|
|
|
|
// We have to set the tab-index of the "clear" button, so tabbing through fields works properly.
|
|
$('.picker__button--clear').attr('tabindex', -1);
|
|
},
|
|
blur: function() {
|
|
pickerObject.close(true);
|
|
}
|
|
});
|
|
|
|
pickerObject.on('set', function(e) {
|
|
var inputVal = $inputTextBox.val();
|
|
|
|
var selectedVal = autoTimeFormat(inputVal);
|
|
|
|
if (typeof e.clear === "object" || (typeof e.select !== "undefined" && !isNaN(e.select)) || (e.customSelect && !selectedVal.match(/^([1-9]|1[012])(:[0-5]\d) [APap][mM]$/))) {
|
|
selectedVal = this.get('value') === '' ? '12:00 AM' : this.get('value');
|
|
}
|
|
|
|
$inputTextBox.val(selectedVal);
|
|
$inputTextBox.attr('value', selectedVal);
|
|
|
|
});
|
|
|
|
// Priming initialization if there was a value specified for the input
|
|
if ($inputTextBox.val() !== '') {
|
|
pickerObject.set('select', $inputTextBox.val(), options);
|
|
}
|
|
|
|
$(pickerObject.$root).find('.picker__list').attr('aria-label', 'Time List');
|
|
|
|
return pickerObject;
|
|
}
|
|
|
|
// createDatePicker creates a new date picker object using the element specified by "dateTextboxSelector".
|
|
// The dateTextboxSelector object must be an HTML INPUT element.
|
|
function createDatePicker(dateTextboxSelector, options) {
|
|
// The actualy Input Date component will be a hidden field. The DOM object in the source HTML
|
|
// will be the editable text field for the user.
|
|
options = options || {};
|
|
|
|
var $inputTextBox = $(dateTextboxSelector);
|
|
|
|
if (!($inputTextBox) || $inputTextBox.length === 0) {
|
|
// We got passed a bad selector
|
|
console.log('ERR: cp.datetimepicker :: Could not find element: ' + dateTextboxSelector);
|
|
return;
|
|
}
|
|
|
|
// Set any options that were specified directly from within the HTML tag.
|
|
options = getOptionsFromHtmlAttributes($inputTextBox, options);
|
|
var dateAsString = $inputTextBox.val();
|
|
if (options.format == 'dd/mm/yyyy' && dateAsString.length > 0) {
|
|
var splitDate = autoCompleteDate(dateAsString, options.format).split('/');
|
|
$inputTextBox.val(splitDate[1] + '/' + splitDate[0] + '/' + splitDate[2]);
|
|
}
|
|
|
|
if (options.useNativeControls) {
|
|
$inputTextBox.prop('type', 'date');
|
|
return;
|
|
}
|
|
|
|
var $hiddenDateInput = $('<input type="text" class="cp_input_datetime" tabindex="-1"/>');
|
|
$hiddenDateInput.attr('data-cp-date-textbox-selector', dateTextboxSelector);
|
|
|
|
$hiddenDateInput.attr('aria-hidden', true);
|
|
var fieldsetSelector = '<fieldset class="fieldset__datetimepicker__wrapper"></fieldset>';
|
|
if (options.wrapInsideDiv)
|
|
fieldsetSelector = '<div class="t-widget t-datepicker">' + fieldsetSelector + '</div>';
|
|
|
|
$fieldset = $(fieldsetSelector);
|
|
$fieldset.attr("data-cp-datetimepicker-selector", dateTextboxSelector);
|
|
$inputTextBox.wrap($fieldset);
|
|
$hiddenDateInput.insertAfter($inputTextBox);
|
|
|
|
// We have to force the editable option, otherwise things start to break.
|
|
options.editable = true;
|
|
|
|
$hiddenDateInput.pickadate(options);
|
|
|
|
var pickerObject = $hiddenDateInput.pickadate('picker'); // reference to the pickadate object
|
|
$inputTextBox.attr("role", "combobox");
|
|
$inputTextBox.attr("aria-haspopup", "dialog");
|
|
$inputTextBox.attr("aria-expanded", "false");
|
|
$inputTextBox.attr("aria-controls", pickerObject.$root.attr("id"));
|
|
|
|
// Add handler to make sure datepicker is visible when shown
|
|
pickerObject && pickerObject.on('open', function() {
|
|
$inputTextBox.attr("aria-expanded", "true");
|
|
// Add a slight delay to ensure that the flyout has been added to the dom and the flyoutHeight is nonzero
|
|
setTimeout(function() {
|
|
// Get the position of the datepicker flyout relative to the top of the document
|
|
var flyoutTop = pickerObject.$holder[0].getBoundingClientRect().top;
|
|
|
|
// Get the height of the datepicker flyout
|
|
var flyoutHeight = pickerObject.$holder[0].offsetHeight;
|
|
|
|
// Get the height of the viewport
|
|
var viewportHeight = window.innerHeight;
|
|
|
|
// Calculate the amount of scrolling needed to make the entire datepicker flyout visible
|
|
var scrollAmount = flyoutTop + flyoutHeight - viewportHeight;
|
|
|
|
// If the datepicker flyout is not fully visible, scroll the viewport down to make it visible
|
|
if (scrollAmount > 0) {
|
|
window.scrollTo(0, scrollAmount + window.scrollY);
|
|
}
|
|
}, 50);
|
|
});
|
|
|
|
function focusInput() {
|
|
$inputTextBox.off("focus", openPicker);
|
|
$inputTextBox.focus();
|
|
$inputTextBox.on("focus", openPicker);
|
|
}
|
|
|
|
pickerObject.on('close', function() {
|
|
//Trigger the change event on the picker object's element.
|
|
window.parent.$(dateTextboxSelector).change();
|
|
$inputTextBox.attr("aria-expanded", "false");
|
|
focusInput();
|
|
});
|
|
|
|
// setDate is a helper function that should only be called from within this module.
|
|
function setDate(dateAsString, placeholder) {
|
|
if (dateAsString === '') {
|
|
pickerObject.set('clear', null, null);
|
|
} else {
|
|
if (pickerObject.component.settings.format == 'dd/mm/yyyy') {
|
|
var splitDate = autoCompleteDate(dateAsString, pickerObject.component.settings.format).split('/');
|
|
var date = splitDate[1] + '/' + splitDate[0] + '/' + splitDate[2];
|
|
var epochDate = Date.parse(date);
|
|
} else {
|
|
var epochDate = Date.parse(autoCompleteDate(dateAsString, pickerObject.component.settings.format));
|
|
}
|
|
if (!epochDate) {
|
|
return;
|
|
} else {
|
|
var parsedDate = new Date(epochDate);
|
|
pickerObject.set('select',
|
|
[parsedDate.getFullYear(), parsedDate.getMonth(), parsedDate.getDate()]);
|
|
}
|
|
}
|
|
}
|
|
|
|
var intervalID;
|
|
|
|
//Moves the date picker further up the DOM so it isn't overlapped https://civicplus.tpondemand.com/entity/22312
|
|
function adjustDTPicker() {
|
|
if (intervalID === 0) {
|
|
return;
|
|
}
|
|
|
|
var elemPicker = $('.picker--opened')[0];
|
|
if (elemPicker) {
|
|
$(elemPicker).appendTo('#cpDatePickerWrapper');
|
|
elemPicker.style.fontSize = 'inherit';
|
|
}
|
|
|
|
clearInterval(intervalID);
|
|
|
|
$('.cpDatePickerElevate').each(function() {
|
|
var $dateInput = $(this);
|
|
if ($dateInput.is(':focus')) {
|
|
adjustFlyoutPosition($dateInput, $('#cpDatePickerWrapper'));
|
|
}
|
|
});
|
|
|
|
intervalID = 0;
|
|
}
|
|
|
|
if ($('#cpDatePickerWrapper').length > 0) {
|
|
$(window).resize(function() {
|
|
$('.cpDatePickerElevate').each(function() {
|
|
var $dateInput = $(this);
|
|
if ($dateInput.is(':focus')) {
|
|
adjustFlyoutPosition($dateInput, $('#cpDatePickerWrapper'));
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
function openPicker() {
|
|
// We have to set the tab-index of the "today", "close", and "clear" buttons, so tabbing through fields works properly.
|
|
$('.picker__button--close').attr('tabindex', -1);
|
|
$('.picker__button--today').attr('tabindex', -1);
|
|
$('.picker__button--clear').attr('tabindex', -1);
|
|
pickerObject.$holder.find('[tabindex="-1"]').attr('tabindex', 0)
|
|
pickerObject.open(false);
|
|
if ($('#cpDatePickerWrapper').length > 0)
|
|
intervalID = setInterval(adjustDTPicker, 50);
|
|
}
|
|
|
|
$inputTextBox.on({
|
|
change: function() {
|
|
setDate(this.value);
|
|
},
|
|
focus: openPicker,
|
|
click: openPicker,
|
|
keydown: function(e) {
|
|
if (e.keyCode === 9) {
|
|
pickerObject.$holder.find('[tabindex="0"]').attr('tabindex', -1);
|
|
}
|
|
if (e.keyCode === 40) {
|
|
e.preventDefault();
|
|
pickerObject.open();
|
|
}
|
|
},
|
|
blur: function(e) {
|
|
pickerObject.close(true);
|
|
$inputTextBox.attr("aria-expanded", "false");
|
|
},
|
|
});
|
|
|
|
pickerObject.on('set', function() {
|
|
$inputTextBox.val(this.get('value'));
|
|
$inputTextBox.attr('value', this.get('value'));
|
|
});
|
|
|
|
// Priming initialization if there was a value specified for the input
|
|
if ($inputTextBox.val() !== '') {
|
|
setDate($inputTextBox.val());
|
|
}
|
|
|
|
|
|
return pickerObject;
|
|
}
|
|
|
|
function insertColonIfOverFourChars(inputString) {
|
|
if (inputString != null && inputString.length > 3 && inputString.indexOf(":") < 0) {
|
|
inputString = inputString.slice(0, 2) + ":" + inputString.slice(2, inputString.length);
|
|
}
|
|
return inputString;
|
|
}
|
|
|
|
function autoCompleteDate(dateAsString, formatString) {
|
|
try {
|
|
var fdts = null;
|
|
var currentDate = new Date();
|
|
|
|
//guard clauses
|
|
if (formatString == null || formatString.length === 0) {
|
|
formatString === "mm/dd/yyyy";
|
|
}
|
|
if ((dateAsString == null || dateAsString.length == 0 || dateAsString.indexOf(".") != -1) && formatString.toLowerCase == "dd/mm/yyyy") {
|
|
return ('0' + currentDate.getDate()).slice(-2) + '/' + ('0' + (currentDate.getMonth() + 1)).slice(-2) + '/' + currentDate.getFullYear();
|
|
} else if (dateAsString == null || dateAsString.length == 0 || dateAsString.indexOf(".") != -1) {
|
|
return ('0' + (currentDate.getMonth() + 1)).slice(-2) + '/' + ('0' + currentDate.getDate()).slice(-2) + '/' + currentDate.getFullYear();
|
|
}
|
|
|
|
fdts = dateAsString.split("/");
|
|
|
|
if (fdts != null) {
|
|
//switch mm/dd/yyyy to be dd/mm/yyyy for logic further down
|
|
if (formatString.toLowerCase() !== "dd/mm/yyyy") {
|
|
var day = fdts[1];
|
|
var month = fdts[0];
|
|
fdts[0] = day;
|
|
fdts[1] = month;
|
|
}
|
|
|
|
if (fdts[0] == null || fdts[0] === '0' || fdts[0].length < 1 || fdts[0].length > 2) {
|
|
fdts[0] = currentDate.getDate();
|
|
}
|
|
if (fdts[1] == null || fdts[1] === '0' || fdts[1].length < 1 || fdts[1].length > 2) {
|
|
fdts[1] = currentDate.getMonth() + 1;
|
|
}
|
|
if (fdts[2] == null || (fdts[2].length !== 2 && fdts[2].length !== 4)) {
|
|
//For last year's date it also adds time at the end of date so extracting year
|
|
fdts[2] = !isNaN(fdts[2].substr(0, 4)) ? fdts[2].substr(0, 4) : currentDate.getFullYear();
|
|
} else if (fdts[2].length === 2) {
|
|
var yr = fdts[2];
|
|
var crDate = new Date();
|
|
var crCtry = crDate.getFullYear();
|
|
var pstCnry = crCtry - 100;
|
|
crCtry = crCtry + ' ';
|
|
pstCnry = pstCnry + ' ';
|
|
crCtry = crCtry.substr(0, 2);
|
|
pstCnry = pstCnry.substr(0, 2);
|
|
if (yr >= 32)
|
|
yr = pstCnry + yr;
|
|
else
|
|
yr = crCtry + yr;
|
|
fdts[2] = yr;
|
|
}
|
|
}
|
|
if (formatString.toLowerCase() === "dd/mm/yyyy") {
|
|
dateAsString = ('0' + fdts[0]).slice(-2) + '/' + ('0' + fdts[1]).slice(-2) + '/' + fdts[2];
|
|
} else {
|
|
dateAsString = ('0' + fdts[1]).slice(-2) + '/' + ('0' + fdts[0]).slice(-2) + '/' + fdts[2];
|
|
}
|
|
return dateAsString;
|
|
} catch (e) {
|
|
return "";
|
|
}
|
|
}
|
|
|
|
function autoTimeFormat(inputString) {
|
|
|
|
const regex1 = /^(0?[1-9]|1[0-2]) ?([ap]m)$/i; //eg: 5AM -> 5:00 AM
|
|
const regex2 = /^(\d{1,2})(\d{2}) ?([ap]m)$/i; //eg: 500AM -> 5:00 AM
|
|
const regex3 = /^(\d{1,2})(?::(\d{2}))?\s*([ap]m)?$/i; //eg: 05:00 am or 5:00 am -> 5:00 AM
|
|
|
|
if (regex1.test(inputString)) {
|
|
return inputString.replace(regex1, (match, hour, period) => {
|
|
const hourNum = parseInt(hour);
|
|
const formattedHour = hourNum === 12 ? 12 : hourNum % 12; // Convert to 12-hour format
|
|
const formattedMinute = "00"; // Add 00 as minutes
|
|
const formattedPeriod = period.toUpperCase(); // Convert period to uppercase
|
|
return `${formattedHour}:${formattedMinute} ${formattedPeriod}`;
|
|
});
|
|
}
|
|
else if (regex2.test(inputString)) {
|
|
return inputString.replace(regex2, (match, hour, minute, period) => {
|
|
const hourNum = parseInt(hour);
|
|
const formattedHour = hourNum === 12 ? 12 : hourNum % 12; // Convert to 12-hour format
|
|
const formattedMinute = minute.padStart(2, "0"); // Pad minute with zero if necessary
|
|
const formattedPeriod = period.toUpperCase(); // Convert period to uppercase
|
|
return `${formattedHour}:${formattedMinute} ${formattedPeriod}`;
|
|
});
|
|
}
|
|
else if (regex3.test(inputString)) {
|
|
return inputString.replace(regex3, (match, hour, minute, period) => {
|
|
const hourNum = parseInt(hour);
|
|
const formattedHour = hourNum === 12 ? 12 : hourNum % 12; // Convert to 12-hour format
|
|
const formattedMinute = minute ? minute : "00"; // Set minute to "00" if not provided
|
|
const formattedPeriod = period ? period.toUpperCase() : ""; // Convert period to uppercase if provided
|
|
return `${formattedHour}:${formattedMinute} ${formattedPeriod}`;
|
|
});
|
|
}
|
|
else {
|
|
return inputString;
|
|
}
|
|
}
|
|
})(window.FeatureToggles.isActive("CMS.JqueryUpgrade.UpgradeTo224") && $('#hdnModuleEligibleForJquery224Upgrade').val() == "true" ? jQuery : typeof jQuery182 === "function" ? jQuery182 : null );
|
|
});
|
|
;
|
|
/*!
|
|
* jQuery Form Plugin
|
|
* version: 2.69 (06-APR-2011)
|
|
* @requires jQuery v1.3.2 or later
|
|
*
|
|
* Examples and documentation at: http://malsup.com/jquery/form/
|
|
* Dual licensed under the MIT and GPL licenses:
|
|
* http://www.opensource.org/licenses/mit-license.php
|
|
* http://www.gnu.org/licenses/gpl.html
|
|
*/
|
|
; (function ($) {
|
|
|
|
/*
|
|
Usage Note:
|
|
-----------
|
|
Do not use both ajaxSubmit and ajaxForm on the same form. These
|
|
functions are intended to be exclusive. Use ajaxSubmit if you want
|
|
to bind your own submit handler to the form. For example,
|
|
|
|
$(document).ready(function() {
|
|
$('#myForm').bind('submit', function(e) {
|
|
e.preventDefault(); // <-- important
|
|
$(this).ajaxSubmit({
|
|
target: '#output'
|
|
});
|
|
});
|
|
});
|
|
|
|
Use ajaxForm when you want the plugin to manage all the event binding
|
|
for you. For example,
|
|
|
|
$(document).ready(function() {
|
|
$('#myForm').ajaxForm({
|
|
target: '#output'
|
|
});
|
|
});
|
|
|
|
When using ajaxForm, the ajaxSubmit function will be invoked for you
|
|
at the appropriate time.
|
|
*/
|
|
|
|
/**
|
|
* ajaxSubmit() provides a mechanism for immediately submitting
|
|
* an HTML form using AJAX.
|
|
*/
|
|
$.fn.ajaxSubmit = function (options) {
|
|
// fast fail if nothing selected (http://dev.jquery.com/ticket/2752)
|
|
if (!this.length) {
|
|
log('ajaxSubmit: skipping submit process - no element selected');
|
|
return this;
|
|
}
|
|
|
|
if (typeof options == 'function') {
|
|
options = { success: options };
|
|
}
|
|
|
|
var action = this.attr('action');
|
|
var url = (typeof action === 'string') ? $.trim(action) : '';
|
|
if (url) {
|
|
// clean url (don't include hash vaue)
|
|
url = (url.match(/^([^#]+)/) || [])[1];
|
|
}
|
|
url = url || window.location.href || '';
|
|
|
|
options = $.extend(true, {
|
|
url: url,
|
|
success: $.ajaxSettings.success,
|
|
type: this[0].getAttribute('method') || 'GET', // IE7 massage (see issue 57)
|
|
iframeSrc: /^https/i.test(window.location.href || '') ? 'javascript:false' : 'about:blank'
|
|
}, options);
|
|
|
|
// hook for manipulating the form data before it is extracted;
|
|
// convenient for use with rich editors like tinyMCE or FCKEditor
|
|
var veto = {};
|
|
this.trigger('form-pre-serialize', [this, options, veto]);
|
|
if (veto.veto) {
|
|
log('ajaxSubmit: submit vetoed via form-pre-serialize trigger');
|
|
return this;
|
|
}
|
|
|
|
// provide opportunity to alter form data before it is serialized
|
|
if (options.beforeSerialize && options.beforeSerialize(this, options) === false) {
|
|
log('ajaxSubmit: submit aborted via beforeSerialize callback');
|
|
return this;
|
|
}
|
|
|
|
var n, v, a = this.formToArray(options.semantic);
|
|
if (options.data) {
|
|
options.extraData = options.data;
|
|
for (n in options.data) {
|
|
if (options.data[n] instanceof Array) {
|
|
for (var k in options.data[n]) {
|
|
a.push({ name: n, value: options.data[n][k] });
|
|
}
|
|
}
|
|
else {
|
|
v = options.data[n];
|
|
v = $.isFunction(v) ? v() : v; // if value is fn, invoke it
|
|
a.push({ name: n, value: v });
|
|
}
|
|
}
|
|
}
|
|
|
|
// give pre-submit callback an opportunity to abort the submit
|
|
if (options.beforeSubmit && options.beforeSubmit(a, this, options) === false) {
|
|
log('ajaxSubmit: submit aborted via beforeSubmit callback');
|
|
return this;
|
|
}
|
|
|
|
// fire vetoable 'validate' event
|
|
this.trigger('form-submit-validate', [a, this, options, veto]);
|
|
if (veto.veto) {
|
|
log('ajaxSubmit: submit vetoed via form-submit-validate trigger');
|
|
return this;
|
|
}
|
|
|
|
var q = $.param(a);
|
|
|
|
if (options.type.toUpperCase() == 'GET') {
|
|
options.url += (options.url.indexOf('?') >= 0 ? '&' : '?') + q;
|
|
options.data = null; // data is null for 'get'
|
|
}
|
|
else {
|
|
options.data = q; // data is the query string for 'post'
|
|
}
|
|
|
|
var $form = this, callbacks = [];
|
|
if (options.resetForm) {
|
|
callbacks.push(function () { $form.resetForm(); });
|
|
}
|
|
if (options.clearForm) {
|
|
callbacks.push(function () { $form.clearForm(); });
|
|
}
|
|
|
|
// perform a load on the target only if dataType is not provided
|
|
if (!options.dataType && options.target) {
|
|
var oldSuccess = options.success || function () { };
|
|
callbacks.push(function (data) {
|
|
var fn = options.replaceTarget ? 'replaceWith' : 'html';
|
|
$(options.target)[fn](data).each(oldSuccess, arguments);
|
|
});
|
|
}
|
|
else if (options.success) {
|
|
callbacks.push(options.success);
|
|
}
|
|
|
|
options.success = function (data, status, xhr) { // jQuery 1.4+ passes xhr as 3rd arg
|
|
var context = options.context || options; // jQuery 1.4+ supports scope context
|
|
for (var i = 0, max = callbacks.length; i < max; i++) {
|
|
callbacks[i].apply(context, [data, status, xhr || $form, $form]);
|
|
}
|
|
};
|
|
|
|
// are there files to upload?
|
|
var fileInputs = $('input:file', this).length > 0;
|
|
var mp = 'multipart/form-data';
|
|
var multipart = ($form.attr('enctype') == mp || $form.attr('encoding') == mp);
|
|
|
|
// options.iframe allows user to force iframe mode
|
|
// 06-NOV-09: now defaulting to iframe mode if file input is detected
|
|
if (options.iframe !== false && (fileInputs || options.iframe || multipart)) {
|
|
// hack to fix Safari hang (thanks to Tim Molendijk for this)
|
|
// see: http://groups.google.com/group/jquery-dev/browse_thread/thread/36395b7ab510dd5d
|
|
if (options.closeKeepAlive) {
|
|
$.get(options.closeKeepAlive, fileUpload);
|
|
}
|
|
else {
|
|
fileUpload();
|
|
}
|
|
}
|
|
else {
|
|
$.ajax(options);
|
|
}
|
|
|
|
// fire 'notify' event
|
|
this.trigger('form-submit-notify', [this, options]);
|
|
return this;
|
|
|
|
|
|
// private function for handling file uploads (hat tip to YAHOO!)
|
|
function fileUpload() {
|
|
var form = $form[0];
|
|
|
|
if ($(':input[name=submit],:input[id=submit]', form).length) {
|
|
// if there is an input with a name or id of 'submit' then we won't be
|
|
// able to invoke the submit fn on the form (at least not x-browser)
|
|
alert('Error: Form elements must not have name or id of "submit".');
|
|
return;
|
|
}
|
|
|
|
var s = $.extend(true, {}, $.ajaxSettings, options);
|
|
s.context = s.context || s;
|
|
var id = 'jqFormIO' + (new Date().getTime()), fn = '_' + id;
|
|
var $io = $('<iframe id="' + id + '" name="' + id + '" src="' + s.iframeSrc + '" />');
|
|
var io = $io[0];
|
|
|
|
$io.css({ position: 'absolute', top: '-1000px', left: '-1000px' });
|
|
|
|
var xhr = { // mock object
|
|
aborted: 0,
|
|
responseText: null,
|
|
responseXML: null,
|
|
status: 0,
|
|
statusText: 'n/a',
|
|
getAllResponseHeaders: function () { },
|
|
getResponseHeader: function () { },
|
|
setRequestHeader: function () { },
|
|
abort: function () {
|
|
log('aborting upload...');
|
|
var e = 'aborted';
|
|
this.aborted = 1;
|
|
$io.attr('src', s.iframeSrc); // abort op in progress
|
|
xhr.error = e;
|
|
s.error && s.error.call(s.context, xhr, 'error', e);
|
|
g && $.event.trigger("ajaxError", [xhr, s, e]);
|
|
s.complete && s.complete.call(s.context, xhr, 'error');
|
|
}
|
|
};
|
|
|
|
var g = s.global;
|
|
// trigger ajax global events so that activity/block indicators work like normal
|
|
if (g && !$.active++) {
|
|
$.event.trigger("ajaxStart");
|
|
}
|
|
if (g) {
|
|
$.event.trigger("ajaxSend", [xhr, s]);
|
|
}
|
|
|
|
if (s.beforeSend && s.beforeSend.call(s.context, xhr, s) === false) {
|
|
if (s.global) {
|
|
$.active--;
|
|
}
|
|
return;
|
|
}
|
|
if (xhr.aborted) {
|
|
return;
|
|
}
|
|
|
|
var timedOut = 0;
|
|
|
|
// add submitting element to data if we know it
|
|
var sub = form.clk;
|
|
if (sub) {
|
|
var n = sub.name;
|
|
if (n && !sub.disabled) {
|
|
s.extraData = s.extraData || {};
|
|
s.extraData[n] = sub.value;
|
|
if (sub.type == "image") {
|
|
s.extraData[n + '.x'] = form.clk_x;
|
|
s.extraData[n + '.y'] = form.clk_y;
|
|
}
|
|
}
|
|
}
|
|
|
|
// take a breath so that pending repaints get some cpu time before the upload starts
|
|
function doSubmit() {
|
|
// make sure form attrs are set
|
|
var t = $form.attr('target'), a = $form.attr('action');
|
|
|
|
// update form attrs in IE friendly way
|
|
form.setAttribute('target', id);
|
|
if (form.getAttribute('method') != 'POST') {
|
|
form.setAttribute('method', 'POST');
|
|
}
|
|
if (form.getAttribute('action') != s.url) {
|
|
form.setAttribute('action', s.url);
|
|
}
|
|
|
|
// ie borks in some cases when setting encoding
|
|
if (!s.skipEncodingOverride) {
|
|
$form.attr({
|
|
encoding: 'multipart/form-data',
|
|
enctype: 'multipart/form-data'
|
|
});
|
|
}
|
|
|
|
// support timout
|
|
if (s.timeout) {
|
|
setTimeout(function () { timedOut = true; cb(); }, s.timeout);
|
|
}
|
|
|
|
// add "extra" data to form if provided in options
|
|
var extraInputs = [];
|
|
try {
|
|
if (s.extraData) {
|
|
for (var n in s.extraData) {
|
|
extraInputs.push(
|
|
$('<input type="hidden" name="' + n + '" value="' + s.extraData[n] + '" />')
|
|
.appendTo(form)[0]);
|
|
}
|
|
}
|
|
|
|
// add iframe to doc and submit the form
|
|
$io.appendTo('body');
|
|
io.attachEvent ? io.attachEvent('onload', cb) : io.addEventListener('load', cb, false);
|
|
form.submit();
|
|
}
|
|
finally {
|
|
// reset attrs and remove "extra" input elements
|
|
form.setAttribute('action', a);
|
|
if (t) {
|
|
form.setAttribute('target', t);
|
|
} else {
|
|
$form.removeAttr('target');
|
|
}
|
|
$(extraInputs).remove();
|
|
}
|
|
}
|
|
|
|
if (s.forceSync) {
|
|
doSubmit();
|
|
}
|
|
else {
|
|
setTimeout(doSubmit, 10); // this lets dom updates render
|
|
}
|
|
|
|
var data, doc, domCheckCount = 50;
|
|
|
|
function cb() {
|
|
if (xhr.aborted) {
|
|
return;
|
|
}
|
|
|
|
var doc = io.contentWindow ? io.contentWindow.document : io.contentDocument ? io.contentDocument : io.document;
|
|
if (!doc || doc.location.href == s.iframeSrc) {
|
|
// response not received yet
|
|
if (!timedOut)
|
|
return;
|
|
}
|
|
io.detachEvent ? io.detachEvent('onload', cb) : io.removeEventListener('load', cb, false);
|
|
|
|
var ok = true;
|
|
try {
|
|
if (timedOut) {
|
|
throw 'timeout';
|
|
}
|
|
|
|
var isXml = s.dataType == 'xml' || doc.XMLDocument || $.isXMLDoc(doc);
|
|
log('isXml=' + isXml);
|
|
if (!isXml && window.opera && (doc.body == null || doc.body.innerHTML == '')) {
|
|
if (--domCheckCount) {
|
|
// in some browsers (Opera) the iframe DOM is not always traversable when
|
|
// the onload callback fires, so we loop a bit to accommodate
|
|
log('requeing onLoad callback, DOM not available');
|
|
setTimeout(cb, 250);
|
|
return;
|
|
}
|
|
// let this fall through because server response could be an empty document
|
|
//log('Could not access iframe DOM after mutiple tries.');
|
|
//throw 'DOMException: not available';
|
|
}
|
|
|
|
//log('response detected');
|
|
xhr.responseText = doc.body ? doc.body.innerHTML : doc.documentElement ? doc.documentElement.innerHTML : null;
|
|
xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc;
|
|
xhr.getResponseHeader = function (header) {
|
|
var headers = { 'content-type': s.dataType };
|
|
return headers[header];
|
|
};
|
|
|
|
var scr = /(json|script)/.test(s.dataType);
|
|
if (scr || s.textarea) {
|
|
// see if user embedded response in textarea
|
|
var ta = doc.getElementsByTagName('textarea')[0];
|
|
if (ta) {
|
|
xhr.responseText = ta.value;
|
|
}
|
|
else if (scr) {
|
|
// account for browsers injecting pre around json response
|
|
var pre = doc.getElementsByTagName('pre')[0];
|
|
var b = doc.getElementsByTagName('body')[0];
|
|
if (pre) {
|
|
xhr.responseText = pre.textContent;
|
|
}
|
|
else if (b) {
|
|
xhr.responseText = b.innerHTML;
|
|
}
|
|
}
|
|
}
|
|
else if (s.dataType == 'xml' && !xhr.responseXML && xhr.responseText != null) {
|
|
xhr.responseXML = toXml(xhr.responseText);
|
|
}
|
|
|
|
data = httpData(xhr, s.dataType, s);
|
|
}
|
|
catch (e) {
|
|
log('error caught:', e);
|
|
ok = false;
|
|
xhr.error = e;
|
|
s.error && s.error.call(s.context, xhr, 'error', e);
|
|
g && $.event.trigger("ajaxError", [xhr, s, e]);
|
|
}
|
|
|
|
if (xhr.aborted) {
|
|
log('upload aborted');
|
|
ok = false;
|
|
}
|
|
|
|
// ordering of these callbacks/triggers is odd, but that's how $.ajax does it
|
|
if (ok) {
|
|
s.success && s.success.call(s.context, data, 'success', xhr);
|
|
g && $.event.trigger("ajaxSuccess", [xhr, s]);
|
|
}
|
|
|
|
g && $.event.trigger("ajaxComplete", [xhr, s]);
|
|
|
|
if (g && ! --$.active) {
|
|
$.event.trigger("ajaxStop");
|
|
}
|
|
|
|
s.complete && s.complete.call(s.context, xhr, ok ? 'success' : 'error');
|
|
|
|
// clean up
|
|
setTimeout(function () {
|
|
$io.removeData('form-plugin-onload');
|
|
$io.remove();
|
|
xhr.responseXML = null;
|
|
}, 100);
|
|
}
|
|
|
|
var toXml = $.parseXML || function (s, doc) { // use parseXML if available (jQuery 1.5+)
|
|
if (window.ActiveXObject) {
|
|
doc = new ActiveXObject('Microsoft.XMLDOM');
|
|
doc.async = 'false';
|
|
doc.loadXML(s);
|
|
}
|
|
else {
|
|
doc = (new DOMParser()).parseFromString(s, 'text/xml');
|
|
}
|
|
return (doc && doc.documentElement && doc.documentElement.nodeName != 'parsererror') ? doc : null;
|
|
};
|
|
var parseJSON = $.parseJSON || function (s) {
|
|
return window['eval']('(' + s + ')');
|
|
};
|
|
|
|
var httpData = function (xhr, type, s) { // mostly lifted from jq1.4.4
|
|
var ct = xhr.getResponseHeader('content-type') || '',
|
|
xml = type === 'xml' || !type && ct.indexOf('xml') >= 0,
|
|
data = xml ? xhr.responseXML : xhr.responseText;
|
|
|
|
if (xml && data.documentElement.nodeName === 'parsererror') {
|
|
$.error && $.error('parsererror');
|
|
}
|
|
if (s && s.dataFilter) {
|
|
data = s.dataFilter(data, type);
|
|
}
|
|
if (typeof data === 'string') {
|
|
if (type === 'json' || !type && ct.indexOf('json') >= 0) {
|
|
data = parseJSON(data);
|
|
} else if (type === "script" || !type && ct.indexOf("javascript") >= 0) {
|
|
$.globalEval(data);
|
|
}
|
|
}
|
|
return data;
|
|
};
|
|
}
|
|
};
|
|
|
|
/**
|
|
* ajaxForm() provides a mechanism for fully automating form submission.
|
|
*
|
|
* The advantages of using this method instead of ajaxSubmit() are:
|
|
*
|
|
* 1: This method will include coordinates for <input type="image" /> elements (if the element
|
|
* is used to submit the form).
|
|
* 2. This method will include the submit element's name/value data (for the element that was
|
|
* used to submit the form).
|
|
* 3. This method binds the submit() method to the form for you.
|
|
*
|
|
* The options argument for ajaxForm works exactly as it does for ajaxSubmit. ajaxForm merely
|
|
* passes the options argument along after properly binding events for submit elements and
|
|
* the form itself.
|
|
*/
|
|
$.fn.ajaxForm = function (options) {
|
|
// in jQuery 1.3+ we can fix mistakes with the ready state
|
|
if (this.length === 0) {
|
|
var o = { s: this.selector, c: this.context };
|
|
if (!$.isReady && o.s) {
|
|
log('DOM not ready, queuing ajaxForm');
|
|
$(function () {
|
|
$(o.s, o.c).ajaxForm(options);
|
|
});
|
|
return this;
|
|
}
|
|
// is your DOM ready? http://docs.jquery.com/Tutorials:Introducing_$(document).ready()
|
|
log('terminating; zero elements found by selector' + ($.isReady ? '' : ' (DOM not ready)'));
|
|
return this;
|
|
}
|
|
|
|
return this.ajaxFormUnbind().bind('submit.form-plugin', function (e) {
|
|
if (!e.isDefaultPrevented()) { // if event has been canceled, don't proceed
|
|
e.preventDefault();
|
|
$(this).ajaxSubmit(options);
|
|
}
|
|
}).bind('click.form-plugin', function (e) {
|
|
var target = e.target;
|
|
var $el = $(target);
|
|
if (!($el.is(":submit,input:image"))) {
|
|
// is this a child element of the submit el? (ex: a span within a button)
|
|
var t = $el.closest(':submit');
|
|
if (t.length == 0) {
|
|
return;
|
|
}
|
|
target = t[0];
|
|
}
|
|
var form = this;
|
|
form.clk = target;
|
|
if (target.type == 'image') {
|
|
if (e.offsetX != undefined) {
|
|
form.clk_x = e.offsetX;
|
|
form.clk_y = e.offsetY;
|
|
} else if (typeof $.fn.offset == 'function') { // try to use dimensions plugin
|
|
var offset = $el.offset();
|
|
form.clk_x = e.pageX - offset.left;
|
|
form.clk_y = e.pageY - offset.top;
|
|
} else {
|
|
form.clk_x = e.pageX - target.offsetLeft;
|
|
form.clk_y = e.pageY - target.offsetTop;
|
|
}
|
|
}
|
|
// clear form vars
|
|
setTimeout(function () { form.clk = form.clk_x = form.clk_y = null; }, 100);
|
|
});
|
|
};
|
|
|
|
// ajaxFormUnbind unbinds the event handlers that were bound by ajaxForm
|
|
$.fn.ajaxFormUnbind = function () {
|
|
return this.unbind('submit.form-plugin click.form-plugin');
|
|
};
|
|
|
|
/**
|
|
* formToArray() gathers form element data into an array of objects that can
|
|
* be passed to any of the following ajax functions: $.get, $.post, or load.
|
|
* Each object in the array has both a 'name' and 'value' property. An example of
|
|
* an array for a simple login form might be:
|
|
*
|
|
* [ { name: 'username', value: 'jresig' }, { name: 'password', value: 'secret' } ]
|
|
*
|
|
* It is this array that is passed to pre-submit callback functions provided to the
|
|
* ajaxSubmit() and ajaxForm() methods.
|
|
*/
|
|
$.fn.formToArray = function (semantic) {
|
|
var a = [];
|
|
if (this.length === 0) {
|
|
return a;
|
|
}
|
|
|
|
var form = this[0];
|
|
var els = semantic ? form.getElementsByTagName('*') : form.elements;
|
|
if (!els) {
|
|
return a;
|
|
}
|
|
|
|
var i, j, n, v, el, max, jmax;
|
|
for (i = 0, max = els.length; i < max; i++) {
|
|
el = els[i];
|
|
n = el.name;
|
|
if (!n) {
|
|
continue;
|
|
}
|
|
|
|
if (semantic && form.clk && el.type == "image") {
|
|
// handle image inputs on the fly when semantic == true
|
|
if (!el.disabled && form.clk == el) {
|
|
a.push({ name: n, value: $(el).val() });
|
|
a.push({ name: n + '.x', value: form.clk_x }, { name: n + '.y', value: form.clk_y });
|
|
}
|
|
continue;
|
|
}
|
|
|
|
v = $.fieldValue(el, true);
|
|
if (v && v.constructor == Array) {
|
|
for (j = 0, jmax = v.length; j < jmax; j++) {
|
|
a.push({ name: n, value: v[j] });
|
|
}
|
|
}
|
|
else if (v !== null && typeof v != 'undefined') {
|
|
a.push({ name: n, value: v });
|
|
}
|
|
}
|
|
|
|
if (!semantic && form.clk) {
|
|
// input type=='image' are not found in elements array! handle it here
|
|
var $input = $(form.clk), input = $input[0];
|
|
n = input.name;
|
|
if (n && !input.disabled && input.type == 'image') {
|
|
a.push({ name: n, value: $input.val() });
|
|
a.push({ name: n + '.x', value: form.clk_x }, { name: n + '.y', value: form.clk_y });
|
|
}
|
|
}
|
|
return a;
|
|
};
|
|
|
|
/**
|
|
* Serializes form data into a 'submittable' string. This method will return a string
|
|
* in the format: name1=value1&name2=value2
|
|
*/
|
|
$.fn.formSerialize = function (semantic) {
|
|
//hand off to jQuery.param for proper encoding
|
|
return $.param(this.formToArray(semantic));
|
|
};
|
|
|
|
/**
|
|
* Serializes all field elements in the jQuery object into a query string.
|
|
* This method will return a string in the format: name1=value1&name2=value2
|
|
*/
|
|
$.fn.fieldSerialize = function (successful) {
|
|
var a = [];
|
|
this.each(function () {
|
|
var n = this.name;
|
|
if (!n) {
|
|
return;
|
|
}
|
|
var v = $.fieldValue(this, successful);
|
|
if (v && v.constructor == Array) {
|
|
for (var i = 0, max = v.length; i < max; i++) {
|
|
a.push({ name: n, value: v[i] });
|
|
}
|
|
}
|
|
else if (v !== null && typeof v != 'undefined') {
|
|
a.push({ name: this.name, value: v });
|
|
}
|
|
});
|
|
//hand off to jQuery.param for proper encoding
|
|
return $.param(a);
|
|
};
|
|
|
|
/**
|
|
* Returns the value(s) of the element in the matched set. For example, consider the following form:
|
|
*
|
|
* <form><fieldset>
|
|
* <input name="A" type="text" />
|
|
* <input name="A" type="text" />
|
|
* <input name="B" type="checkbox" value="B1" />
|
|
* <input name="B" type="checkbox" value="B2"/>
|
|
* <input name="C" type="radio" value="C1" />
|
|
* <input name="C" type="radio" value="C2" />
|
|
* </fieldset></form>
|
|
*
|
|
* var v = $(':text').fieldValue();
|
|
* // if no values are entered into the text inputs
|
|
* v == ['','']
|
|
* // if values entered into the text inputs are 'foo' and 'bar'
|
|
* v == ['foo','bar']
|
|
*
|
|
* var v = $(':checkbox').fieldValue();
|
|
* // if neither checkbox is checked
|
|
* v === undefined
|
|
* // if both checkboxes are checked
|
|
* v == ['B1', 'B2']
|
|
*
|
|
* var v = $(':radio').fieldValue();
|
|
* // if neither radio is checked
|
|
* v === undefined
|
|
* // if first radio is checked
|
|
* v == ['C1']
|
|
*
|
|
* The successful argument controls whether or not the field element must be 'successful'
|
|
* (per http://www.w3.org/TR/html4/interact/forms.html#successful-controls).
|
|
* The default value of the successful argument is true. If this value is false the value(s)
|
|
* for each element is returned.
|
|
*
|
|
* Note: This method *always* returns an array. If no valid value can be determined the
|
|
* array will be empty, otherwise it will contain one or more values.
|
|
*/
|
|
$.fn.fieldValue = function (successful) {
|
|
for (var val = [], i = 0, max = this.length; i < max; i++) {
|
|
var el = this[i];
|
|
var v = $.fieldValue(el, successful);
|
|
if (v === null || typeof v == 'undefined' || (v.constructor == Array && !v.length)) {
|
|
continue;
|
|
}
|
|
v.constructor == Array ? $.merge(val, v) : val.push(v);
|
|
}
|
|
return val;
|
|
};
|
|
|
|
/**
|
|
* Returns the value of the field element.
|
|
*/
|
|
$.fieldValue = function (el, successful) {
|
|
var n = el.name, t = el.type, tag = el.tagName.toLowerCase();
|
|
if (successful === undefined) {
|
|
successful = true;
|
|
}
|
|
|
|
if (successful && (!n || el.disabled || t == 'reset' || t == 'button' ||
|
|
(t == 'checkbox' || t == 'radio') && !el.checked ||
|
|
(t == 'submit' || t == 'image') && el.form && el.form.clk != el ||
|
|
tag == 'select' && el.selectedIndex == -1)) {
|
|
return null;
|
|
}
|
|
|
|
if (tag == 'select') {
|
|
var index = el.selectedIndex;
|
|
if (index < 0) {
|
|
return null;
|
|
}
|
|
var a = [], ops = el.options;
|
|
var one = (t == 'select-one');
|
|
var max = (one ? index + 1 : ops.length);
|
|
for (var i = (one ? index : 0); i < max; i++) {
|
|
var op = ops[i];
|
|
if (op.selected) {
|
|
var v = op.value;
|
|
if (!v) { // extra pain for IE...
|
|
v = (op.attributes && op.attributes['value'] && !(op.attributes['value'].specified)) ? op.text : op.value;
|
|
}
|
|
if (one) {
|
|
return v;
|
|
}
|
|
a.push(v);
|
|
}
|
|
}
|
|
return a;
|
|
}
|
|
return $(el).val();
|
|
};
|
|
|
|
/**
|
|
* Clears the form data. Takes the following actions on the form's input fields:
|
|
* - input text fields will have their 'value' property set to the empty string
|
|
* - select elements will have their 'selectedIndex' property set to -1
|
|
* - checkbox and radio inputs will have their 'checked' property set to false
|
|
* - inputs of type submit, button, reset, and hidden will *not* be effected
|
|
* - button elements will *not* be effected
|
|
*/
|
|
$.fn.clearForm = function () {
|
|
return this.each(function () {
|
|
$('input,select,textarea', this).clearFields();
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Clears the selected form elements.
|
|
*/
|
|
$.fn.clearFields = $.fn.clearInputs = function () {
|
|
return this.each(function () {
|
|
var t = this.type, tag = this.tagName.toLowerCase();
|
|
if (t == 'text' || t == 'password' || tag == 'textarea') {
|
|
this.value = '';
|
|
}
|
|
else if (t == 'checkbox' || t == 'radio') {
|
|
this.checked = false;
|
|
}
|
|
else if (tag == 'select') {
|
|
this.selectedIndex = -1;
|
|
}
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Resets the form data. Causes all form elements to be reset to their original value.
|
|
*/
|
|
$.fn.resetForm = function () {
|
|
return this.each(function () {
|
|
// guard against an input with the name of 'reset'
|
|
// note that IE reports the reset function as an 'object'
|
|
if (typeof this.reset == 'function' || (typeof this.reset == 'object' && !this.reset.nodeType)) {
|
|
this.reset();
|
|
}
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Enables or disables any matching elements.
|
|
*/
|
|
$.fn.enable = function (b) {
|
|
if (b === undefined) {
|
|
b = true;
|
|
}
|
|
return this.each(function () {
|
|
this.disabled = !b;
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Checks/unchecks any matching checkboxes or radio buttons and
|
|
* selects/deselects and matching option elements.
|
|
*/
|
|
$.fn.selected = function (select) {
|
|
if (select === undefined) {
|
|
select = true;
|
|
}
|
|
return this.each(function () {
|
|
var t = this.type;
|
|
if (t == 'checkbox' || t == 'radio') {
|
|
this.checked = select;
|
|
}
|
|
else if (this.tagName.toLowerCase() == 'option') {
|
|
var $sel = $(this).parent('select');
|
|
if (select && $sel[0] && $sel[0].type == 'select-one') {
|
|
// deselect all other options
|
|
$sel.find('option').selected(false);
|
|
}
|
|
this.selected = select;
|
|
}
|
|
});
|
|
};
|
|
|
|
// helper fn for console logging
|
|
// set $.fn.ajaxSubmit.debug to true to enable debug logging
|
|
function log() {
|
|
if ($.fn.ajaxSubmit.debug) {
|
|
var msg = '[jquery.form] ' + Array.prototype.join.call(arguments, '');
|
|
if (window.console && window.console.log) {
|
|
window.console.log(msg);
|
|
}
|
|
else if (window.opera && window.opera.postError) {
|
|
window.opera.postError(msg);
|
|
}
|
|
}
|
|
};
|
|
|
|
})(jQuery);
|
|
;
|
|
|