!function(n,r){"object"==typeof exports&&"undefined"!=typeof module?module.exports=r():"function"==typeof define&&define.amd?define("underscore",r):(n="undefined"!=typeof globalThis?globalThis:n||self,function(){var t=n._,e=n._=r();e.noConflict=function(){return n._=t,e}}())}(this,(function(){
//     Underscore.js 1.13.6
//     https://underscorejs.org
//     (c) 2009-2022 Jeremy Ashkenas, Julian Gonggrijp, and DocumentCloud and Investigative Reporters & Editors
//     Underscore may be freely distributed under the MIT license.
var n="1.13.6",r="object"==typeof self&&self.self===self&&self||"object"==typeof global&&global.global===global&&global||Function("return this")()||{},t=Array.prototype,e=Object.prototype,u="undefined"!=typeof Symbol?Symbol.prototype:null,o=t.push,i=t.slice,a=e.toString,f=e.hasOwnProperty,c="undefined"!=typeof ArrayBuffer,l="undefined"!=typeof DataView,s=Array.isArray,p=Object.keys,v=Object.create,h=c&&ArrayBuffer.isView,y=isNaN,d=isFinite,g=!{toString:null}.propertyIsEnumerable("toString"),b=["valueOf","isPrototypeOf","toString","propertyIsEnumerable","hasOwnProperty","toLocaleString"],m=Math.pow(2,53)-1;function j(n,r){return r=null==r?n.length-1:+r,function(){for(var t=Math.max(arguments.length-r,0),e=Array(t),u=0;u<t;u++)e[u]=arguments[u+r];switch(r){case 0:return n.call(this,e);case 1:return n.call(this,arguments[0],e);case 2:return n.call(this,arguments[0],arguments[1],e)}var o=Array(r+1);for(u=0;u<r;u++)o[u]=arguments[u];return o[r]=e,n.apply(this,o)}}function _(n){var r=typeof n;return"function"===r||"object"===r&&!!n}function w(n){return void 0===n}function A(n){return!0===n||!1===n||"[object Boolean]"===a.call(n)}function x(n){var r="[object "+n+"]";return function(n){return a.call(n)===r}}var S=x("String"),O=x("Number"),M=x("Date"),E=x("RegExp"),B=x("Error"),N=x("Symbol"),I=x("ArrayBuffer"),T=x("Function"),k=r.document&&r.document.childNodes;"function"!=typeof/./&&"object"!=typeof Int8Array&&"function"!=typeof k&&(T=function(n){return"function"==typeof n||!1});var D=T,R=x("Object"),F=l&&R(new DataView(new ArrayBuffer(8))),V="undefined"!=typeof Map&&R(new Map),P=x("DataView");var q=F?function(n){return null!=n&&D(n.getInt8)&&I(n.buffer)}:P,U=s||x("Array");function W(n,r){return null!=n&&f.call(n,r)}var z=x("Arguments");!function(){z(arguments)||(z=function(n){return W(n,"callee")})}();var L=z;function $(n){return O(n)&&y(n)}function C(n){return function(){return n}}function K(n){return function(r){var t=n(r);return"number"==typeof t&&t>=0&&t<=m}}function J(n){return function(r){return null==r?void 0:r[n]}}var G=J("byteLength"),H=K(G),Q=/\[object ((I|Ui)nt(8|16|32)|Float(32|64)|Uint8Clamped|Big(I|Ui)nt64)Array\]/;var X=c?function(n){return h?h(n)&&!q(n):H(n)&&Q.test(a.call(n))}:C(!1),Y=J("length");function Z(n,r){r=function(n){for(var r={},t=n.length,e=0;e<t;++e)r[n[e]]=!0;return{contains:function(n){return!0===r[n]},push:function(t){return r[t]=!0,n.push(t)}}}(r);var t=b.length,u=n.constructor,o=D(u)&&u.prototype||e,i="constructor";for(W(n,i)&&!r.contains(i)&&r.push(i);t--;)(i=b[t])in n&&n[i]!==o[i]&&!r.contains(i)&&r.push(i)}function nn(n){if(!_(n))return[];if(p)return p(n);var r=[];for(var t in n)W(n,t)&&r.push(t);return g&&Z(n,r),r}function rn(n,r){var t=nn(r),e=t.length;if(null==n)return!e;for(var u=Object(n),o=0;o<e;o++){var i=t[o];if(r[i]!==u[i]||!(i in u))return!1}return!0}function tn(n){return n instanceof tn?n:this instanceof tn?void(this._wrapped=n):new tn(n)}function en(n){return new Uint8Array(n.buffer||n,n.byteOffset||0,G(n))}tn.VERSION=n,tn.prototype.value=function(){return this._wrapped},tn.prototype.valueOf=tn.prototype.toJSON=tn.prototype.value,tn.prototype.toString=function(){return String(this._wrapped)};var un="[object DataView]";function on(n,r,t,e){if(n===r)return 0!==n||1/n==1/r;if(null==n||null==r)return!1;if(n!=n)return r!=r;var o=typeof n;return("function"===o||"object"===o||"object"==typeof r)&&function n(r,t,e,o){r instanceof tn&&(r=r._wrapped);t instanceof tn&&(t=t._wrapped);var i=a.call(r);if(i!==a.call(t))return!1;if(F&&"[object Object]"==i&&q(r)){if(!q(t))return!1;i=un}switch(i){case"[object RegExp]":case"[object String]":return""+r==""+t;case"[object Number]":return+r!=+r?+t!=+t:0==+r?1/+r==1/t:+r==+t;case"[object Date]":case"[object Boolean]":return+r==+t;case"[object Symbol]":return u.valueOf.call(r)===u.valueOf.call(t);case"[object ArrayBuffer]":case un:return n(en(r),en(t),e,o)}var f="[object Array]"===i;if(!f&&X(r)){if(G(r)!==G(t))return!1;if(r.buffer===t.buffer&&r.byteOffset===t.byteOffset)return!0;f=!0}if(!f){if("object"!=typeof r||"object"!=typeof t)return!1;var c=r.constructor,l=t.constructor;if(c!==l&&!(D(c)&&c instanceof c&&D(l)&&l instanceof l)&&"constructor"in r&&"constructor"in t)return!1}o=o||[];var s=(e=e||[]).length;for(;s--;)if(e[s]===r)return o[s]===t;if(e.push(r),o.push(t),f){if((s=r.length)!==t.length)return!1;for(;s--;)if(!on(r[s],t[s],e,o))return!1}else{var p,v=nn(r);if(s=v.length,nn(t).length!==s)return!1;for(;s--;)if(p=v[s],!W(t,p)||!on(r[p],t[p],e,o))return!1}return e.pop(),o.pop(),!0}(n,r,t,e)}function an(n){if(!_(n))return[];var r=[];for(var t in n)r.push(t);return g&&Z(n,r),r}function fn(n){var r=Y(n);return function(t){if(null==t)return!1;var e=an(t);if(Y(e))return!1;for(var u=0;u<r;u++)if(!D(t[n[u]]))return!1;return n!==hn||!D(t[cn])}}var cn="forEach",ln="has",sn=["clear","delete"],pn=["get",ln,"set"],vn=sn.concat(cn,pn),hn=sn.concat(pn),yn=["add"].concat(sn,cn,ln),dn=V?fn(vn):x("Map"),gn=V?fn(hn):x("WeakMap"),bn=V?fn(yn):x("Set"),mn=x("WeakSet");function jn(n){for(var r=nn(n),t=r.length,e=Array(t),u=0;u<t;u++)e[u]=n[r[u]];return e}function _n(n){for(var r={},t=nn(n),e=0,u=t.length;e<u;e++)r[n[t[e]]]=t[e];return r}function wn(n){var r=[];for(var t in n)D(n[t])&&r.push(t);return r.sort()}function An(n,r){return function(t){var e=arguments.length;if(r&&(t=Object(t)),e<2||null==t)return t;for(var u=1;u<e;u++)for(var o=arguments[u],i=n(o),a=i.length,f=0;f<a;f++){var c=i[f];r&&void 0!==t[c]||(t[c]=o[c])}return t}}var xn=An(an),Sn=An(nn),On=An(an,!0);function Mn(n){if(!_(n))return{};if(v)return v(n);var r=function(){};r.prototype=n;var t=new r;return r.prototype=null,t}function En(n){return U(n)?n:[n]}function Bn(n){return tn.toPath(n)}function Nn(n,r){for(var t=r.length,e=0;e<t;e++){if(null==n)return;n=n[r[e]]}return t?n:void 0}function In(n,r,t){var e=Nn(n,Bn(r));return w(e)?t:e}function Tn(n){return n}function kn(n){return n=Sn({},n),function(r){return rn(r,n)}}function Dn(n){return n=Bn(n),function(r){return Nn(r,n)}}function Rn(n,r,t){if(void 0===r)return n;switch(null==t?3:t){case 1:return function(t){return n.call(r,t)};case 3:return function(t,e,u){return n.call(r,t,e,u)};case 4:return function(t,e,u,o){return n.call(r,t,e,u,o)}}return function(){return n.apply(r,arguments)}}function Fn(n,r,t){return null==n?Tn:D(n)?Rn(n,r,t):_(n)&&!U(n)?kn(n):Dn(n)}function Vn(n,r){return Fn(n,r,1/0)}function Pn(n,r,t){return tn.iteratee!==Vn?tn.iteratee(n,r):Fn(n,r,t)}function qn(){}function Un(n,r){return null==r&&(r=n,n=0),n+Math.floor(Math.random()*(r-n+1))}tn.toPath=En,tn.iteratee=Vn;var Wn=Date.now||function(){return(new Date).getTime()};function zn(n){var r=function(r){return n[r]},t="(?:"+nn(n).join("|")+")",e=RegExp(t),u=RegExp(t,"g");return function(n){return n=null==n?"":""+n,e.test(n)?n.replace(u,r):n}}var Ln={"&":"&amp;","<":"&lt;",">":"&gt;",'"':"&quot;","'":"&#x27;","`":"&#x60;"},$n=zn(Ln),Cn=zn(_n(Ln)),Kn=tn.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g},Jn=/(.)^/,Gn={"'":"'","\\":"\\","\r":"r","\n":"n","\u2028":"u2028","\u2029":"u2029"},Hn=/\\|'|\r|\n|\u2028|\u2029/g;function Qn(n){return"\\"+Gn[n]}var Xn=/^\s*(\w|\$)+\s*$/;var Yn=0;function Zn(n,r,t,e,u){if(!(e instanceof r))return n.apply(t,u);var o=Mn(n.prototype),i=n.apply(o,u);return _(i)?i:o}var nr=j((function(n,r){var t=nr.placeholder,e=function(){for(var u=0,o=r.length,i=Array(o),a=0;a<o;a++)i[a]=r[a]===t?arguments[u++]:r[a];for(;u<arguments.length;)i.push(arguments[u++]);return Zn(n,e,this,this,i)};return e}));nr.placeholder=tn;var rr=j((function(n,r,t){if(!D(n))throw new TypeError("Bind must be called on a function");var e=j((function(u){return Zn(n,e,r,this,t.concat(u))}));return e})),tr=K(Y);function er(n,r,t,e){if(e=e||[],r||0===r){if(r<=0)return e.concat(n)}else r=1/0;for(var u=e.length,o=0,i=Y(n);o<i;o++){var a=n[o];if(tr(a)&&(U(a)||L(a)))if(r>1)er(a,r-1,t,e),u=e.length;else for(var f=0,c=a.length;f<c;)e[u++]=a[f++];else t||(e[u++]=a)}return e}var ur=j((function(n,r){var t=(r=er(r,!1,!1)).length;if(t<1)throw new Error("bindAll must be passed function names");for(;t--;){var e=r[t];n[e]=rr(n[e],n)}return n}));var or=j((function(n,r,t){return setTimeout((function(){return n.apply(null,t)}),r)})),ir=nr(or,tn,1);function ar(n){return function(){return!n.apply(this,arguments)}}function fr(n,r){var t;return function(){return--n>0&&(t=r.apply(this,arguments)),n<=1&&(r=null),t}}var cr=nr(fr,2);function lr(n,r,t){r=Pn(r,t);for(var e,u=nn(n),o=0,i=u.length;o<i;o++)if(r(n[e=u[o]],e,n))return e}function sr(n){return function(r,t,e){t=Pn(t,e);for(var u=Y(r),o=n>0?0:u-1;o>=0&&o<u;o+=n)if(t(r[o],o,r))return o;return-1}}var pr=sr(1),vr=sr(-1);function hr(n,r,t,e){for(var u=(t=Pn(t,e,1))(r),o=0,i=Y(n);o<i;){var a=Math.floor((o+i)/2);t(n[a])<u?o=a+1:i=a}return o}function yr(n,r,t){return function(e,u,o){var a=0,f=Y(e);if("number"==typeof o)n>0?a=o>=0?o:Math.max(o+f,a):f=o>=0?Math.min(o+1,f):o+f+1;else if(t&&o&&f)return e[o=t(e,u)]===u?o:-1;if(u!=u)return(o=r(i.call(e,a,f),$))>=0?o+a:-1;for(o=n>0?a:f-1;o>=0&&o<f;o+=n)if(e[o]===u)return o;return-1}}var dr=yr(1,pr,hr),gr=yr(-1,vr);function br(n,r,t){var e=(tr(n)?pr:lr)(n,r,t);if(void 0!==e&&-1!==e)return n[e]}function mr(n,r,t){var e,u;if(r=Rn(r,t),tr(n))for(e=0,u=n.length;e<u;e++)r(n[e],e,n);else{var o=nn(n);for(e=0,u=o.length;e<u;e++)r(n[o[e]],o[e],n)}return n}function jr(n,r,t){r=Pn(r,t);for(var e=!tr(n)&&nn(n),u=(e||n).length,o=Array(u),i=0;i<u;i++){var a=e?e[i]:i;o[i]=r(n[a],a,n)}return o}function _r(n){var r=function(r,t,e,u){var o=!tr(r)&&nn(r),i=(o||r).length,a=n>0?0:i-1;for(u||(e=r[o?o[a]:a],a+=n);a>=0&&a<i;a+=n){var f=o?o[a]:a;e=t(e,r[f],f,r)}return e};return function(n,t,e,u){var o=arguments.length>=3;return r(n,Rn(t,u,4),e,o)}}var wr=_r(1),Ar=_r(-1);function xr(n,r,t){var e=[];return r=Pn(r,t),mr(n,(function(n,t,u){r(n,t,u)&&e.push(n)})),e}function Sr(n,r,t){r=Pn(r,t);for(var e=!tr(n)&&nn(n),u=(e||n).length,o=0;o<u;o++){var i=e?e[o]:o;if(!r(n[i],i,n))return!1}return!0}function Or(n,r,t){r=Pn(r,t);for(var e=!tr(n)&&nn(n),u=(e||n).length,o=0;o<u;o++){var i=e?e[o]:o;if(r(n[i],i,n))return!0}return!1}function Mr(n,r,t,e){return tr(n)||(n=jn(n)),("number"!=typeof t||e)&&(t=0),dr(n,r,t)>=0}var Er=j((function(n,r,t){var e,u;return D(r)?u=r:(r=Bn(r),e=r.slice(0,-1),r=r[r.length-1]),jr(n,(function(n){var o=u;if(!o){if(e&&e.length&&(n=Nn(n,e)),null==n)return;o=n[r]}return null==o?o:o.apply(n,t)}))}));function Br(n,r){return jr(n,Dn(r))}function Nr(n,r,t){var e,u,o=-1/0,i=-1/0;if(null==r||"number"==typeof r&&"object"!=typeof n[0]&&null!=n)for(var a=0,f=(n=tr(n)?n:jn(n)).length;a<f;a++)null!=(e=n[a])&&e>o&&(o=e);else r=Pn(r,t),mr(n,(function(n,t,e){((u=r(n,t,e))>i||u===-1/0&&o===-1/0)&&(o=n,i=u)}));return o}var Ir=/[^\ud800-\udfff]|[\ud800-\udbff][\udc00-\udfff]|[\ud800-\udfff]/g;function Tr(n){return n?U(n)?i.call(n):S(n)?n.match(Ir):tr(n)?jr(n,Tn):jn(n):[]}function kr(n,r,t){if(null==r||t)return tr(n)||(n=jn(n)),n[Un(n.length-1)];var e=Tr(n),u=Y(e);r=Math.max(Math.min(r,u),0);for(var o=u-1,i=0;i<r;i++){var a=Un(i,o),f=e[i];e[i]=e[a],e[a]=f}return e.slice(0,r)}function Dr(n,r){return function(t,e,u){var o=r?[[],[]]:{};return e=Pn(e,u),mr(t,(function(r,u){var i=e(r,u,t);n(o,r,i)})),o}}var Rr=Dr((function(n,r,t){W(n,t)?n[t].push(r):n[t]=[r]})),Fr=Dr((function(n,r,t){n[t]=r})),Vr=Dr((function(n,r,t){W(n,t)?n[t]++:n[t]=1})),Pr=Dr((function(n,r,t){n[t?0:1].push(r)}),!0);function qr(n,r,t){return r in t}var Ur=j((function(n,r){var t={},e=r[0];if(null==n)return t;D(e)?(r.length>1&&(e=Rn(e,r[1])),r=an(n)):(e=qr,r=er(r,!1,!1),n=Object(n));for(var u=0,o=r.length;u<o;u++){var i=r[u],a=n[i];e(a,i,n)&&(t[i]=a)}return t})),Wr=j((function(n,r){var t,e=r[0];return D(e)?(e=ar(e),r.length>1&&(t=r[1])):(r=jr(er(r,!1,!1),String),e=function(n,t){return!Mr(r,t)}),Ur(n,e,t)}));function zr(n,r,t){return i.call(n,0,Math.max(0,n.length-(null==r||t?1:r)))}function Lr(n,r,t){return null==n||n.length<1?null==r||t?void 0:[]:null==r||t?n[0]:zr(n,n.length-r)}function $r(n,r,t){return i.call(n,null==r||t?1:r)}var Cr=j((function(n,r){return r=er(r,!0,!0),xr(n,(function(n){return!Mr(r,n)}))})),Kr=j((function(n,r){return Cr(n,r)}));function Jr(n,r,t,e){A(r)||(e=t,t=r,r=!1),null!=t&&(t=Pn(t,e));for(var u=[],o=[],i=0,a=Y(n);i<a;i++){var f=n[i],c=t?t(f,i,n):f;r&&!t?(i&&o===c||u.push(f),o=c):t?Mr(o,c)||(o.push(c),u.push(f)):Mr(u,f)||u.push(f)}return u}var Gr=j((function(n){return Jr(er(n,!0,!0))}));function Hr(n){for(var r=n&&Nr(n,Y).length||0,t=Array(r),e=0;e<r;e++)t[e]=Br(n,e);return t}var Qr=j(Hr);function Xr(n,r){return n._chain?tn(r).chain():r}function Yr(n){return mr(wn(n),(function(r){var t=tn[r]=n[r];tn.prototype[r]=function(){var n=[this._wrapped];return o.apply(n,arguments),Xr(this,t.apply(tn,n))}})),tn}mr(["pop","push","reverse","shift","sort","splice","unshift"],(function(n){var r=t[n];tn.prototype[n]=function(){var t=this._wrapped;return null!=t&&(r.apply(t,arguments),"shift"!==n&&"splice"!==n||0!==t.length||delete t[0]),Xr(this,t)}})),mr(["concat","join","slice"],(function(n){var r=t[n];tn.prototype[n]=function(){var n=this._wrapped;return null!=n&&(n=r.apply(n,arguments)),Xr(this,n)}}));var Zr=Yr({__proto__:null,VERSION:n,restArguments:j,isObject:_,isNull:function(n){return null===n},isUndefined:w,isBoolean:A,isElement:function(n){return!(!n||1!==n.nodeType)},isString:S,isNumber:O,isDate:M,isRegExp:E,isError:B,isSymbol:N,isArrayBuffer:I,isDataView:q,isArray:U,isFunction:D,isArguments:L,isFinite:function(n){return!N(n)&&d(n)&&!isNaN(parseFloat(n))},isNaN:$,isTypedArray:X,isEmpty:function(n){if(null==n)return!0;var r=Y(n);return"number"==typeof r&&(U(n)||S(n)||L(n))?0===r:0===Y(nn(n))},isMatch:rn,isEqual:function(n,r){return on(n,r)},isMap:dn,isWeakMap:gn,isSet:bn,isWeakSet:mn,keys:nn,allKeys:an,values:jn,pairs:function(n){for(var r=nn(n),t=r.length,e=Array(t),u=0;u<t;u++)e[u]=[r[u],n[r[u]]];return e},invert:_n,functions:wn,methods:wn,extend:xn,extendOwn:Sn,assign:Sn,defaults:On,create:function(n,r){var t=Mn(n);return r&&Sn(t,r),t},clone:function(n){return _(n)?U(n)?n.slice():xn({},n):n},tap:function(n,r){return r(n),n},get:In,has:function(n,r){for(var t=(r=Bn(r)).length,e=0;e<t;e++){var u=r[e];if(!W(n,u))return!1;n=n[u]}return!!t},mapObject:function(n,r,t){r=Pn(r,t);for(var e=nn(n),u=e.length,o={},i=0;i<u;i++){var a=e[i];o[a]=r(n[a],a,n)}return o},identity:Tn,constant:C,noop:qn,toPath:En,property:Dn,propertyOf:function(n){return null==n?qn:function(r){return In(n,r)}},matcher:kn,matches:kn,times:function(n,r,t){var e=Array(Math.max(0,n));r=Rn(r,t,1);for(var u=0;u<n;u++)e[u]=r(u);return e},random:Un,now:Wn,escape:$n,unescape:Cn,templateSettings:Kn,template:function(n,r,t){!r&&t&&(r=t),r=On({},r,tn.templateSettings);var e=RegExp([(r.escape||Jn).source,(r.interpolate||Jn).source,(r.evaluate||Jn).source].join("|")+"|$","g"),u=0,o="__p+='";n.replace(e,(function(r,t,e,i,a){return o+=n.slice(u,a).replace(Hn,Qn),u=a+r.length,t?o+="'+\n((__t=("+t+"))==null?'':_.escape(__t))+\n'":e?o+="'+\n((__t=("+e+"))==null?'':__t)+\n'":i&&(o+="';\n"+i+"\n__p+='"),r})),o+="';\n";var i,a=r.variable;if(a){if(!Xn.test(a))throw new Error("variable is not a bare identifier: "+a)}else o="with(obj||{}){\n"+o+"}\n",a="obj";o="var __t,__p='',__j=Array.prototype.join,"+"print=function(){__p+=__j.call(arguments,'');};\n"+o+"return __p;\n";try{i=new Function(a,"_",o)}catch(n){throw n.source=o,n}var f=function(n){return i.call(this,n,tn)};return f.source="function("+a+"){\n"+o+"}",f},result:function(n,r,t){var e=(r=Bn(r)).length;if(!e)return D(t)?t.call(n):t;for(var u=0;u<e;u++){var o=null==n?void 0:n[r[u]];void 0===o&&(o=t,u=e),n=D(o)?o.call(n):o}return n},uniqueId:function(n){var r=++Yn+"";return n?n+r:r},chain:function(n){var r=tn(n);return r._chain=!0,r},iteratee:Vn,partial:nr,bind:rr,bindAll:ur,memoize:function(n,r){var t=function(e){var u=t.cache,o=""+(r?r.apply(this,arguments):e);return W(u,o)||(u[o]=n.apply(this,arguments)),u[o]};return t.cache={},t},delay:or,defer:ir,throttle:function(n,r,t){var e,u,o,i,a=0;t||(t={});var f=function(){a=!1===t.leading?0:Wn(),e=null,i=n.apply(u,o),e||(u=o=null)},c=function(){var c=Wn();a||!1!==t.leading||(a=c);var l=r-(c-a);return u=this,o=arguments,l<=0||l>r?(e&&(clearTimeout(e),e=null),a=c,i=n.apply(u,o),e||(u=o=null)):e||!1===t.trailing||(e=setTimeout(f,l)),i};return c.cancel=function(){clearTimeout(e),a=0,e=u=o=null},c},debounce:function(n,r,t){var e,u,o,i,a,f=function(){var c=Wn()-u;r>c?e=setTimeout(f,r-c):(e=null,t||(i=n.apply(a,o)),e||(o=a=null))},c=j((function(c){return a=this,o=c,u=Wn(),e||(e=setTimeout(f,r),t&&(i=n.apply(a,o))),i}));return c.cancel=function(){clearTimeout(e),e=o=a=null},c},wrap:function(n,r){return nr(r,n)},negate:ar,compose:function(){var n=arguments,r=n.length-1;return function(){for(var t=r,e=n[r].apply(this,arguments);t--;)e=n[t].call(this,e);return e}},after:function(n,r){return function(){if(--n<1)return r.apply(this,arguments)}},before:fr,once:cr,findKey:lr,findIndex:pr,findLastIndex:vr,sortedIndex:hr,indexOf:dr,lastIndexOf:gr,find:br,detect:br,findWhere:function(n,r){return br(n,kn(r))},each:mr,forEach:mr,map:jr,collect:jr,reduce:wr,foldl:wr,inject:wr,reduceRight:Ar,foldr:Ar,filter:xr,select:xr,reject:function(n,r,t){return xr(n,ar(Pn(r)),t)},every:Sr,all:Sr,some:Or,any:Or,contains:Mr,includes:Mr,include:Mr,invoke:Er,pluck:Br,where:function(n,r){return xr(n,kn(r))},max:Nr,min:function(n,r,t){var e,u,o=1/0,i=1/0;if(null==r||"number"==typeof r&&"object"!=typeof n[0]&&null!=n)for(var a=0,f=(n=tr(n)?n:jn(n)).length;a<f;a++)null!=(e=n[a])&&e<o&&(o=e);else r=Pn(r,t),mr(n,(function(n,t,e){((u=r(n,t,e))<i||u===1/0&&o===1/0)&&(o=n,i=u)}));return o},shuffle:function(n){return kr(n,1/0)},sample:kr,sortBy:function(n,r,t){var e=0;return r=Pn(r,t),Br(jr(n,(function(n,t,u){return{value:n,index:e++,criteria:r(n,t,u)}})).sort((function(n,r){var t=n.criteria,e=r.criteria;if(t!==e){if(t>e||void 0===t)return 1;if(t<e||void 0===e)return-1}return n.index-r.index})),"value")},groupBy:Rr,indexBy:Fr,countBy:Vr,partition:Pr,toArray:Tr,size:function(n){return null==n?0:tr(n)?n.length:nn(n).length},pick:Ur,omit:Wr,first:Lr,head:Lr,take:Lr,initial:zr,last:function(n,r,t){return null==n||n.length<1?null==r||t?void 0:[]:null==r||t?n[n.length-1]:$r(n,Math.max(0,n.length-r))},rest:$r,tail:$r,drop:$r,compact:function(n){return xr(n,Boolean)},flatten:function(n,r){return er(n,r,!1)},without:Kr,uniq:Jr,unique:Jr,union:Gr,intersection:function(n){for(var r=[],t=arguments.length,e=0,u=Y(n);e<u;e++){var o=n[e];if(!Mr(r,o)){var i;for(i=1;i<t&&Mr(arguments[i],o);i++);i===t&&r.push(o)}}return r},difference:Cr,unzip:Hr,transpose:Hr,zip:Qr,object:function(n,r){for(var t={},e=0,u=Y(n);e<u;e++)r?t[n[e]]=r[e]:t[n[e][0]]=n[e][1];return t},range:function(n,r,t){null==r&&(r=n||0,n=0),t||(t=r<n?-1:1);for(var e=Math.max(Math.ceil((r-n)/t),0),u=Array(e),o=0;o<e;o++,n+=t)u[o]=n;return u},chunk:function(n,r){if(null==r||r<1)return[];for(var t=[],e=0,u=n.length;e<u;)t.push(i.call(n,e,e+=r));return t},mixin:Yr,default:tn});return Zr._=Zr,Zr}));
/**
* @preserve Copyright 2012 Twitter, Inc.
* @license http://www.apache.org/licenses/LICENSE-2.0.txt
*/
var Hogan={};!function(t){function n(t,n,e){var i;return n&&"object"==typeof n&&(void 0!==n[t]?i=n[t]:e&&n.get&&"function"==typeof n.get&&(i=n.get(t))),i}function e(t,n,e,i,r,s){function a(){}function o(){}a.prototype=t,o.prototype=t.subs;var u,c=new a;c.subs=new o,c.subsText={},c.buf="",i=i||{},c.stackSubs=i,c.subsText=s;for(u in n)i[u]||(i[u]=n[u]);for(u in i)c.subs[u]=i[u];r=r||{},c.stackPartials=r;for(u in e)r[u]||(r[u]=e[u]);for(u in r)c.partials[u]=r[u];return c}function i(t){return String(null===t||void 0===t?"":t)}function r(t){return t=i(t),l.test(t)?t.replace(s,"&amp;").replace(a,"&lt;").replace(o,"&gt;").replace(u,"&#39;").replace(c,"&quot;"):t}t.Template=function(t,n,e,i){t=t||{},this.r=t.code||this.r,this.c=e,this.options=i||{},this.text=n||"",this.partials=t.partials||{},this.subs=t.subs||{},this.buf=""},t.Template.prototype={r:function(){return""},v:r,t:i,render:function(t,n,e){return this.ri([t],n||{},e)},ri:function(t,n,e){return this.r(t,n,e)},ep:function(t,n){var i=this.partials[t],r=n[i.name];if(i.instance&&i.base==r)return i.instance;if("string"==typeof r){if(!this.c)throw new Error("No compiler available.");r=this.c.compile(r,this.options)}if(!r)return null;if(this.partials[t].base=r,i.subs){n.stackText||(n.stackText={});for(key in i.subs)n.stackText[key]||(n.stackText[key]=void 0!==this.activeSub&&n.stackText[this.activeSub]?n.stackText[this.activeSub]:this.text);r=e(r,i.subs,i.partials,this.stackSubs,this.stackPartials,n.stackText)}return this.partials[t].instance=r,r},rp:function(t,n,e,i){var r=this.ep(t,e);return r?r.ri(n,e,i):""},rs:function(t,n,e){var i=t[t.length-1];if(!f(i))return void e(t,n,this);for(var r=0;r<i.length;r++)t.push(i[r]),e(t,n,this),t.pop()},s:function(t,n,e,i,r,s,a){var o;return f(t)&&0===t.length?!1:("function"==typeof t&&(t=this.ms(t,n,e,i,r,s,a)),o=!!t,!i&&o&&n&&n.push("object"==typeof t?t:n[n.length-1]),o)},d:function(t,e,i,r){var s,a=t.split("."),o=this.f(a[0],e,i,r),u=this.options.modelGet,c=null;if("."===t&&f(e[e.length-2]))o=e[e.length-1];else for(var l=1;l<a.length;l++)s=n(a[l],o,u),void 0!==s?(c=o,o=s):o="";return r&&!o?!1:(r||"function"!=typeof o||(e.push(c),o=this.mv(o,e,i),e.pop()),o)},f:function(t,e,i,r){for(var s=!1,a=null,o=!1,u=this.options.modelGet,c=e.length-1;c>=0;c--)if(a=e[c],s=n(t,a,u),void 0!==s){o=!0;break}return o?(r||"function"!=typeof s||(s=this.mv(s,e,i)),s):r?!1:""},ls:function(t,n,e,r,s){var a=this.options.delimiters;return this.options.delimiters=s,this.b(this.ct(i(t.call(n,r)),n,e)),this.options.delimiters=a,!1},ct:function(t,n,e){if(this.options.disableLambda)throw new Error("Lambda features disabled.");return this.c.compile(t,this.options).render(n,e)},b:function(t){this.buf+=t},fl:function(){var t=this.buf;return this.buf="",t},ms:function(t,n,e,i,r,s,a){var o,u=n[n.length-1],c=t.call(u);return"function"==typeof c?i?!0:(o=this.activeSub&&this.subsText&&this.subsText[this.activeSub]?this.subsText[this.activeSub]:this.text,this.ls(c,u,e,o.substring(r,s),a)):c},mv:function(t,n,e){var r=n[n.length-1],s=t.call(r);return"function"==typeof s?this.ct(i(s.call(r)),r,e):s},sub:function(t,n,e,i){var r=this.subs[t];r&&(this.activeSub=t,r(n,e,this,i),this.activeSub=!1)}};var s=/&/g,a=/</g,o=/>/g,u=/\'/g,c=/\"/g,l=/[&<>\"\']/,f=Array.isArray||function(t){return"[object Array]"===Object.prototype.toString.call(t)}}("undefined"!=typeof exports?exports:Hogan),function(t){function n(t){"}"===t.n.substr(t.n.length-1)&&(t.n=t.n.substring(0,t.n.length-1))}function e(t){return t.trim?t.trim():t.replace(/^\s*|\s*$/g,"")}function i(t,n,e){if(n.charAt(e)!=t.charAt(0))return!1;for(var i=1,r=t.length;r>i;i++)if(n.charAt(e+i)!=t.charAt(i))return!1;return!0}function r(n,e,i,o){var u=[],c=null,l=null,f=null;for(l=i[i.length-1];n.length>0;){if(f=n.shift(),l&&"<"==l.tag&&!(f.tag in k))throw new Error("Illegal content in < super tag.");if(t.tags[f.tag]<=t.tags.$||s(f,o))i.push(f),f.nodes=r(n,f.tag,i,o);else{if("/"==f.tag){if(0===i.length)throw new Error("Closing tag without opener: /"+f.n);if(c=i.pop(),f.n!=c.n&&!a(f.n,c.n,o))throw new Error("Nesting error: "+c.n+" vs. "+f.n);return c.end=f.i,u}"\n"==f.tag&&(f.last=0==n.length||"\n"==n[0].tag)}u.push(f)}if(i.length>0)throw new Error("missing closing tag: "+i.pop().n);return u}function s(t,n){for(var e=0,i=n.length;i>e;e++)if(n[e].o==t.n)return t.tag="#",!0}function a(t,n,e){for(var i=0,r=e.length;r>i;i++)if(e[i].c==t&&e[i].o==n)return!0}function o(t){var n=[];for(var e in t)n.push('"'+c(e)+'": function(c,p,t,i) {'+t[e]+"}");return"{ "+n.join(",")+" }"}function u(t){var n=[];for(var e in t.partials)n.push('"'+c(e)+'":{name:"'+c(t.partials[e].name)+'", '+u(t.partials[e])+"}");return"partials: {"+n.join(",")+"}, subs: "+o(t.subs)}function c(t){return t.replace(m,"\\\\").replace(v,'\\"').replace(b,"\\n").replace(d,"\\r").replace(x,"\\u2028").replace(w,"\\u2029")}function l(t){return~t.indexOf(".")?"d":"f"}function f(t,n){var e="<"+(n.prefix||""),i=e+t.n+y++;return n.partials[i]={name:t.n,partials:{}},n.code+='t.b(t.rp("'+c(i)+'",c,p,"'+(t.indent||"")+'"));',i}function h(t,n){n.code+="t.b(t.t(t."+l(t.n)+'("'+c(t.n)+'",c,p,0)));'}function p(t){return"t.b("+t+");"}var g=/\S/,v=/\"/g,b=/\n/g,d=/\r/g,m=/\\/g,x=/\u2028/,w=/\u2029/;t.tags={"#":1,"^":2,"<":3,$:4,"/":5,"!":6,">":7,"=":8,_v:9,"{":10,"&":11,_t:12},t.scan=function(r,s){function a(){m.length>0&&(x.push({tag:"_t",text:new String(m)}),m="")}function o(){for(var n=!0,e=y;e<x.length;e++)if(n=t.tags[x[e].tag]<t.tags._v||"_t"==x[e].tag&&null===x[e].text.match(g),!n)return!1;return n}function u(t,n){if(a(),t&&o())for(var e,i=y;i<x.length;i++)x[i].text&&((e=x[i+1])&&">"==e.tag&&(e.indent=x[i].text.toString()),x.splice(i,1));else n||x.push({tag:"\n"});w=!1,y=x.length}function c(t,n){var i="="+S,r=t.indexOf(i,n),s=e(t.substring(t.indexOf("=",n)+1,r)).split(" ");return T=s[0],S=s[s.length-1],r+i.length-1}var l=r.length,f=0,h=1,p=2,v=f,b=null,d=null,m="",x=[],w=!1,k=0,y=0,T="{{",S="}}";for(s&&(s=s.split(" "),T=s[0],S=s[1]),k=0;l>k;k++)v==f?i(T,r,k)?(--k,a(),v=h):"\n"==r.charAt(k)?u(w):m+=r.charAt(k):v==h?(k+=T.length-1,d=t.tags[r.charAt(k+1)],b=d?r.charAt(k+1):"_v","="==b?(k=c(r,k),v=f):(d&&k++,v=p),w=k):i(S,r,k)?(x.push({tag:b,n:e(m),otag:T,ctag:S,i:"/"==b?w-T.length:k+S.length}),m="",k+=S.length-1,v=f,"{"==b&&("}}"==S?k++:n(x[x.length-1]))):m+=r.charAt(k);return u(w,!0),x};var k={_t:!0,"\n":!0,$:!0,"/":!0};t.stringify=function(n){return"{code: function (c,p,i) { "+t.wrapMain(n.code)+" },"+u(n)+"}"};var y=0;t.generate=function(n,e,i){y=0;var r={code:"",subs:{},partials:{}};return t.walk(n,r),i.asString?this.stringify(r,e,i):this.makeTemplate(r,e,i)},t.wrapMain=function(t){return'var t=this;t.b(i=i||"");'+t+"return t.fl();"},t.template=t.Template,t.makeTemplate=function(t,n,e){var i=this.makePartials(t);return i.code=new Function("c","p","i",this.wrapMain(t.code)),new this.template(i,n,this,e)},t.makePartials=function(t){var n,e={subs:{},partials:t.partials,name:t.name};for(n in e.partials)e.partials[n]=this.makePartials(e.partials[n]);for(n in t.subs)e.subs[n]=new Function("c","p","t","i",t.subs[n]);return e},t.codegen={"#":function(n,e){e.code+="if(t.s(t."+l(n.n)+'("'+c(n.n)+'",c,p,1),c,p,0,'+n.i+","+n.end+',"'+n.otag+" "+n.ctag+'")){t.rs(c,p,function(c,p,t){',t.walk(n.nodes,e),e.code+="});c.pop();}"},"^":function(n,e){e.code+="if(!t.s(t."+l(n.n)+'("'+c(n.n)+'",c,p,1),c,p,1,0,0,"")){',t.walk(n.nodes,e),e.code+="};"},">":f,"<":function(n,e){var i={partials:{},code:"",subs:{},inPartial:!0};t.walk(n.nodes,i);var r=e.partials[f(n,e)];r.subs=i.subs,r.partials=i.partials},$:function(n,e){var i={subs:{},code:"",partials:e.partials,prefix:n.n};t.walk(n.nodes,i),e.subs[n.n]=i.code,e.inPartial||(e.code+='t.sub("'+c(n.n)+'",c,p,i);')},"\n":function(t,n){n.code+=p('"\\n"'+(t.last?"":" + i"))},_v:function(t,n){n.code+="t.b(t.v(t."+l(t.n)+'("'+c(t.n)+'",c,p,0)));'},_t:function(t,n){n.code+=p('"'+c(t.text)+'"')},"{":h,"&":h},t.walk=function(n,e){for(var i,r=0,s=n.length;s>r;r++)i=t.codegen[n[r].tag],i&&i(n[r],e);return e},t.parse=function(t,n,e){return e=e||{},r(t,"",[],e.sectionTags||[])},t.cache={},t.cacheKey=function(t,n){return[t,!!n.asString,!!n.disableLambda,n.delimiters,!!n.modelGet].join("||")},t.compile=function(n,e){e=e||{};var i=t.cacheKey(n,e),r=this.cache[i];if(r){var s=r.partials;for(var a in s)delete s[a].instance;return r}return r=this.generate(this.parse(this.scan(n,e.delimiters),n,e),n,e),this.cache[i]=r}}("undefined"!=typeof exports?exports:Hogan);
'use strict';


/**
* Enum of PHOLIO area type IDs.
* @class AreaTypeIds
*/
var AreaTypeIds = {
    District: 1,
    Region: 6,
    Practice: 7,
    County: 9,
    AcuteTrust: 14,
    Country: 15,
    UnitaryAuthority: 16,
    GpShape: 18,
    DeprivationDecile: 23,
    Subregion: 46,
    DistrictUA: 101,
    CountyUA: 102,
    PheCentres2013: 103,
    PheCentres2015: 104,
    OnsClusterGroup2001: 110,
    OnsClusterGroup2011: 115,
    AcuteTrusts: 118,
    Stp: 219,
    CombinedAuthorities: 126,
    CCGPostApr2017: 152,
    CCGPreApr2017: 153,
    CCGSince2018: 154,
    Uk: 159,
    LAUnchanged: 160,
    LANew2019: 161,
    LAObsolete2019: 162,
    CcgSinceApr2019: 165,
    CcgSinceApr2021: 167,
    UAUnchanged: 170,
    UANew2019: 171,
    UAObsolete2019: 172,
    UAFutureAreas: 173,
    DistrictUASince2019: 201,
    CountyUASince2019: 202,
    AreaList: 30000,
    DistrictAndUnitaryAuthorityFromApr2023 : 501,
    CountyAndUnitaryAuthorityFromApr2023 : 502
};

/**
* Enum of PHOLIO category type IDs.
* @class CategoryTypeIds
*/
var CategoryTypeIds = {
    DeprivationDecileCountyUA2015: 39,
    DeprivationDecileDistrictUA2015: 40,
    HealthProfilesSSILimit: 5,
    DeprivationDecileCCG2010: 11,
    DeprivationDecileGp2015: 38,
    SocioeconomicGroup: 59,
    DeprivationDecileCountyUA2019: 113 
};

/**
* Enum of PHOLIO value type IDs.
* @class ValueTypeIds
*/
var ValueTypeIds = {
    IndirectlyStandardisedRatio: 2,
    IndirectlyStandardisedRate: 4,
    Ratio: 6,
    Count: 7,
    Score: 8,
    LifeExpectancy: 11
};

var NearestNeighbourTypeIds = {
    Cipfa2018: 1,
    Cipfa2019: 7
}

/**
* Enum of PHOLIO comparator method IDs.
* @class ComparatorMethodIds
*/
var ComparatorMethodIds = {
    SingleOverlappingCIsForOneCiLevel: 1,
    SuicidePlan: 14,
    Quintiles: 15,
    Quartiles: 16,
    SingleOverlappingCIsForTwoCiLevels: 17,
    SingleOverlappingCIsForSecondCiLevel : 18
};

/**
* Enum of PHOLIO polarity IDs.
* @class PolarityIds
*/
var PolarityIds = {
    NotApplicable: -1,
    RAGLowIsGood: 0,
    RAGHighIsGood: 1,
    BlueOrangeBlue: 99
}

/**
* Enum of PHOLIO sex IDs.
* @class SexIds
*/
var SexIds = {
    Male: 1,
    Female: 2,
    Person: 4
};

/**
* Enum of PHOLIO age IDs.
* @class AgeIds
*/
var AgeIds = {
    AllAges: 1
};


/**
* Enum of PHOLIO profile IDs.
* @class ProfileIds
*/
var ProfileIds = {
    SearchResults: 13,
    Tobacco:18,
    Phof: 19,
    PracticeProfiles: 20,
    PracticeProfilesForPatients: 167,
    Mortality: 22,
    HealthProfiles: 26,
    CommonMentalHealthDisorders: 40,
    SevereMentalIllness: 41,
    CommunityMentalHealth: 50,
    Diabetes: 51,
    Liver: 55,
    Hypertension: 67,
    Cancer: 71,
    Suicide: 73,
    DrugsAndAlcohol: 75,
    HealthChecks: 77,
    Dementia: 84,
    SuicidePrevention: 91,
    ChiMatWAY: 94,
    MentalHealthJsna: 98,
    ChildHealth: 105,
    ChildHealthBehaviours: 129,
    ChildrenYoungPeoplesWellBeing: 133,
    PublicHealthDashboard: 140
};

var TrendMarkerValue = {
    Up: 1,
    Down: 2,
    NoChange: 3,
    CannotCalculate: 4
};

var ValueNoteIds = {
    DataQualityIssue: 401
};

var IndicatorIds = {
    Deprivation: 338,
    SuicidePlan: 92607,
    LarcPrescribed: 92254,
    LivingInAqmas: 93384
}

var GroupIds = {
    PracticeProfiles: {
        Population: 1200006
    },
    HealthChecks: {
        HealthCheck: 1938132782,
        DiseaseAndDeath: 1938132785
    },
    Diabetes: {
        Complications: 1938132699
    },
    DrugsAndAlcohol: {
        PrevalenceAndRisks: 1938132771,
        TreatmentAndRecovery: 1938132772
    },
    Cancer: {
        IncidenceAndMortality: 1938132749
    },
    Suicide: {
        SuicideData: 1938132762
    }
}
'use strict';

/*
* File: common.js
* Purpose: provides functions used by every Fingertips profile
*/

function isDefined(obj) {
    return !_.isUndefined(obj) && obj !== null;
};

String.isNullOrEmpty = function (s) {
    return s === null || s === '';
};

var ajaxCache = {};

function ajaxGet(service, parameters, successFunction, errorFunction) {

    if (!isDefined(errorFunction)) {
        errorFunction = function (xhr) {
            unlock();
            ajaxError(xhr);
        };
    }

    var key = service + parameters;
    if (ajaxCache.hasOwnProperty(key)) {
        successFunction(ajaxCache[key]);
    } else {

        var ajaxConfig = {
            type: 'GET',
            url: FT.url.bridge + service,
            /* version added so calls can be cached between updates but are
            refreshed after an update */
            data: parameters + '&v=' + FT.version,
            cache: true,
            contentType: 'application/json; charset=utf-8',
            success: function (data) {
                ajaxCache[key] = data;
                successFunction(data);
            },
            dataType: 'json',
            error: errorFunction
        };

        $.ajax(ajaxConfig);
    }
};

function roundNumber(num, dec) {
    return Math.round(num * Math.pow(10, dec)) / Math.pow(10, dec);
};

var ajaxMonitor = {

    count: 0,
    errors: 0,
    state: null,

    setCalls: function (i) {
        var _this = this;
        _this.count = i;
        _this.errors = 0;
        _this.state = {};
    },

    // Enable state information to be accessed in the callback code
    setState: function (obj) {
        this.state = obj;
    },

    callCompleted: function () {
        this.count--;
    },

    registerError: function (xhr) {
        logError(xhr);
        this.errors++;
        this.callCompleted();
    },

    monitor: function (fn) {
        var _this = this;
        _this.errors = 0;
        if (_this.count === 0) {
            fn();
        } else {
            _this.fnFinished = fn;
            _this._pause();
        }
    },

    _pause: function () {
        setTimeout("ajaxMonitor.checkCompleted()", 10);
    },

    checkCompleted: function () {
        var _this = this, c;

        if (_this.count === 0) {
            if (_this.fnFinished !== null) {
                // Must set null before calling in case fnFinished uses this
                c = _this.fnFinished;
                _this.fnFinished = null;
                if (_this.errors > 0) {
                    handleAjaxFailure();
                } else {
                    c();
                }
            }
        } else {
            _this._pause();
        }
    },

    fnFinished: null
};

function ajaxError(xhr) {
    ajaxMonitor.registerError(xhr);
}

function logError(xhr) {
    var parameters = 'serviceAction=ex&errorMessage=' + xhr.statusText + '&errorStatus=' + xhr.status;

    var ajaxConfig = {
        type: 'POST',
        url: FT.url.bridge + 'log/exception?' + parameters,
        cache: false,
        contentType: 'application/json; charset=utf-8'
    };

    $.ajax(ajaxConfig);
};


function setUrl(url) {
    window.location.href = url;
}

//
// Builds a URL parameter string
//
function ParameterBuilder() {

    var keys = [],
        values = [];

    //
    // Add a key value pair to the parameter list
    //
    this.add = function (key, value) {
        keys.push(key);

        if (_.isArray(value)) {
            value = value.join(',');
        }

        values.push(value);
        return this;
    };

    // 
    // Generate the parameter string
    //
    this.build = function () {

        var url = [];
        for (var index in keys) {
            if (index > 0) {
                url.push('&');
            }

            url.push(keys[index], '=', values[index]);
        }

        // Do not prefix with '?' as JQuery ajax will do this
        return url.join('');
    };

    this.setNotToCache = function () {
        // Time stamp to beat network caching + no cache flag to indicate no caching to ajax bridge
        this.add('no_cache', new Date().getTime());
        return this;
    }
}

var ftHistory = (function () {

    var _blockNext = 0,
        _model,
        _hashChange = 'hashchange',
        _location = window.location;

    return {

        //
        // Either return the URL hash or null if none defined
        // 
        getHash: function () {
            var bits = _location.href.split('#');
            return bits.length === 2 ?
                bits[1] : null;
        },

        //
        // Initialises the history object.
        //
        init: function (model) {
            _model = model;

            $(window).bind(_hashChange, function () {
                ftHistory.hashChanged();
            });
        },

        //
        // Sets the URL hash according to the model
        //
        setHistory: function () {
            _blockNext = 1;
            _location.hash = _model.toString();
        },

        isParameterDefinedInHash: function (parameterName) {
            var hash = this.getKeyValuePairsFromHash();
            return !!hash[parameterName];
        },

        //
        // Parses the hash into key values pairs
        //
        getKeyValuePairsFromHash: function () {

            var hashString = this.getHash();
            return this.parseParameterString(hashString);
        },

        //
        // Parses the hash string into key values pairs
        //
        parseParameterString: function (hashString) {
            var obj = {};
            if (hashString) {
                var pairs = hashString.split(/[/,]/);
                for (var i = 0; i < pairs.length; i++) {
                    var key = pairs[i];
                    if (key && key !== '') {
                        var value = pairs[i + 1];
                        if (value && value !== '') {
                            obj[key] = value;
                        }
                    }
                    i++;
                }
            }
            return obj;
        },

        //
        // Fired when the URL hash changes
        //
        hashChanged: function () {
            if (_blockNext) {
                // Used to prevent model being restored when the hash is updated
                _blockNext--;
                if (_blockNext < 0) {
                    _blockNext = 0;
                }
                return;
            }

            if (!FT.ajaxLock) {
                lock();
            }

            // Restore model from hash state
            _model.restore();
        },

        /*
         * Sometimes necessary to increase block 
         */
        increaseBlock: function () {
            _blockNext++;
        }
    };
})();

function sortNumericAsc(a, b) {
    return a - b;
}

// Ensure calls to console.log don't break IE7/8
if (typeof (console) === 'undefined') {
    console = {
        log: function () { }
    };
}

// Log events to Google Analytics (Universal Analytics)
function logEvent(category, action, label) {

    //ga only initialised on the live site (Google Analytics)
    if (typeof (ga) !== 'undefined') {
        var _send = 'send';
        var _event = 'event';
        if (label) {
            ga(_send, _event, category, action, label);
        } else {
            ga(_send, _event, category, action);
        }
    }
}

function browserUpgradeMessage() {
    alert('Sorry this feature is not available for your browser. Please upgrade or use an alternative.');
}

function openFeedback() {
    $('#footer-feedback').hide();
    $('#footer-feedback-form').show();
    $('#what-were-you-doing').focus();
    return false;
}

function closeFeedback() {
    $('#footer-feedback').show();
    $('#footer-feedback-form').hide();
    clearFeedbackForm();
    return false;
}

function sendFeedback() {

    var whatWereYouDoing = $("#what-were-you-doing").val(),
        whatWentWrong = $("#what-went-wrong").val(),
        whichDeviceOrBrowser = $("#which-device-or-browser").val(),
        email = $("#email-for-user-feedback").val();

    if (whatWereYouDoing === '' && whatWentWrong === '') {
        $('#footer-feedback-error-summary').html('<div class="alert alert-warning">Please tell us what went wrong?</div>');
        return false;
    }

    var data = {
        url: window.location.href,
        whatUserWasDoing: whatWereYouDoing,
        whatWentWrong: whatWentWrong,
        email: email,
        whichDeviceOrBrowser: whichDeviceOrBrowser
    };

    var apiUrl = '/user_feedback';

    $.post(apiUrl, data)
        .success(function (obj) {
            $('#footer-feedback').show();
            $('#footer-feedback-form').hide();
            clearFeedbackForm();
            $('#feedback-message').html('<div style="color:white;">Thank you for your feedback</div>');
        })
        .error(function (error) {
            $('#footer-feedback-error-summary').html('<div class="alert alert-danger">Sorry, we are unable to receive your message right now.</div>');
        });
    return false;
}

function clearFeedbackForm() {
    $('#user-feedback-form').each(function () {
        this.reset();
    });
}


function SendPostForm(path, params, method) {
    method = method || "post"; // Set method to post by default if not specified.

    // The rest of this code assumes you are not using a library.
    // It can be made less wordy if you use one.
    var form = document.createElement("form");
    form.setAttribute("method", method);
    form.setAttribute("action", path);

    for (var key in params) {
        if (params.hasOwnProperty(key)) {
            var hiddenField = document.createElement("input");
            hiddenField.setAttribute("type", "hidden");
            hiddenField.setAttribute("name", key);
            hiddenField.setAttribute("value", params[key]);

            form.appendChild(hiddenField);
        }
    }

    document.body.appendChild(form);
    form.submit();
}

'use strict';

/**
* fingertips.js
* Code that is shared between Longer Lives and Fingertips.
*
* @module fingertips
*/

var FT = { menus: {}, data: {} };

/**
* Global hash containing data that has been fetched by AJAX.
* @class loaded
* @static
*/
var loaded = {
    areaLists: {},
    areaTypes: {},
    indicatorMetadata: {},
    yearTypes: {},
    comparatorMethods: {},
    ciMethods: {},
    ages: {},
    areaValues: {},
    areaMappings: {},
    parentAreaGroups: {},
    parentAreas: {},
    boundaries: {}, // Boundary coordinates for maps: Key -> areaTypeId, Value -> [] of area coordinates
    valueNotes: {},
    groupDataAtDataPoint: {},
    trendMarkers: {},
    groupingSubheadings: {}
};

function AreaTypes(areaTypesArray, areaTypesHash) {

    if (areaTypesHash) {
        // Initialise
        _.each(areaTypesArray, function (areaType) {
            areaTypesHash[areaType.Id] = areaType;
        });
    } else {
        // Already initialised
        areaTypesHash = loaded.areaTypes;
    }

    this.getAllIds = function () {
        return _.map(_(areaTypesHash).keys(), function (strAreaTypeId) {
            return parseInt(strAreaTypeId);
        });
    };

    this.getAreaTypes = function () {
        if (areaTypesArray) {
            return areaTypesArray;
        }

        return _(areaTypesHash).toArray();
    };
}


/**
* Defines the immutable configuration settings for the Fingertips data page.
* @class FT.config
* @static
*/
FT.config = {
    highChartsFileIncluded: false,
    displayLegend: true
}

/**
* Defines the state of the Fingertips data page.
* @class FT.model
* @static
*/
FT.model = {
    areaTypeId: null,
    groupId: null,
    parentCode: null,
    profileId: null,
    parentTypeId: null,
    areaCode: null,
    iid: null,
    ageId: null,
    sexId: null,
    nearestNeighbour: null,
    nearestNeighbourTypeId: null,
    groupRoots: null,
    filterIndicatorPeriod: null,
    yAxisSelectedIndicatorId: null,
    yAxisSelectedSexId: null,
    yAxisSelectedAgeId: null,
    comparatorId: null,
    targetBenchmark: null,
    pageOptions: null,
    areAnyIndicators: true,

    /**
    * Resets the model.
    * @method _reset
    */
    _reset: function () {
        var m = FT.model;
        m.groupId = FT.config.groupIds[0];
        m.areaTypeId = FT.config.defaultAreaType;
        m.comparatorId = NATIONAL_COMPARATOR_ID;
        m.targetBenchmark = '1';
        // Cannot define any more parameters until after web services calls have been made
    },

    /**
    * Restores the UI from the hash state
    * @method restore
    */
    restore: function () {
        this.update();

        // Must set up area options before page can be refreshed
        initAreaData();
        updateDomains();
    },

    /**
    * Updates the model from the current hash state
    * @method update
    */
    update: function () {
        this._reset();
        var m = this;
        var pairs = ftHistory.getKeyValuePairsFromHash();

        if (_.size(pairs)) {

            // Group ID
            var prop = pairs['gid'];
            if (prop) m.groupId = parseInt(prop);

            // Parent area code
            prop = pairs['par'];
            if (prop) m.parentCode = prop;

            // Area type ID
            prop = pairs['ati'];
            if (prop) m.areaTypeId = parseInt(prop);

            // Parent area type ID
            prop = pairs['pat'];
            if (prop) m.parentTypeId = parseInt(prop);

            // Area code
            prop = pairs['are'];
            m.areaCode = !!prop ? prop : null;

            // Indicator Id
            prop = pairs['iid'];
            if (prop) m.iid = parseInt(prop);

            // Age Id
            prop = pairs['age'];
            if (prop) m.ageId = parseInt(prop);

            // Sex Id
            prop = pairs['sex'];
            if (prop) m.sexId = parseInt(prop);

            // Nearest neighbours
            prop = pairs['nn'];
            m.nearestNeighbour = !!prop ? prop : null;

            // Comparator Id
            prop = pairs['cid'];
            if (prop) m.comparatorId = parseInt(prop);

            // Target benchmark
            prop = pairs['tbm'];
            m.targetBenchmark = !!prop ? prop : null;

            // Page options
            prop = pairs['page-options'];
            m.pageOptions = !!prop ? prop : null;

            // Page 
            prop = pairs['page'];
            var pageId = prop ?
                parseInt(prop) :
                pages.getDefault();
            pages.setCurrent(pageId);
        }
    },

    /**
    * Adds a model property to the parameters hash if it is defined
    * @method _addPropertyToParametersIfDefined
    */
    _addPropertyToParametersIfDefined: function (modelParameter, urlParameter, parameters) {
        if (this[modelParameter] && !String.isNullOrEmpty(this[modelParameter])) {
            parameters.push(urlParameter, this[modelParameter]);
        }
    },

    /**
    * Generates a string representation of the model to be used as the URL hash
    * @method toString
    */
    toString: function () {
        var m = this;

        // These parameters always included in serialisation
        var parameters = [
            'page', pages.getCurrent()
        ];

        // IMPORTANT: Only add parameters if they are defined (some browsers will not parse hash otherwise)
        m._addPropertyToParametersIfDefined('groupId', 'gid', parameters);
        m._addPropertyToParametersIfDefined('parentTypeId', 'pat', parameters);
        m._addPropertyToParametersIfDefined('parentCode', 'par', parameters);
        m._addPropertyToParametersIfDefined('areaTypeId', 'ati', parameters);
        m._addPropertyToParametersIfDefined('areaCode', 'are', parameters);
        m._addPropertyToParametersIfDefined('iid', 'iid', parameters);
        m._addPropertyToParametersIfDefined('ageId', 'age', parameters);
        m._addPropertyToParametersIfDefined('sexId', 'sex', parameters);
        m._addPropertyToParametersIfDefined('nearestNeighbour', 'nn', parameters);
        m._addPropertyToParametersIfDefined('comparatorId', 'cid', parameters);
        m._addPropertyToParametersIfDefined('targetBenchmark', 'tbm', parameters);
        m._addPropertyToParametersIfDefined('pageOptions', 'page-options', parameters);

        return parameters.join('/');
    }
};


/**
* Returns an array of all the relevant profile ids.
* @class getProfileIds
*/
function getProfileIds() {
    return [];
}

function SexAndAge() { }

SexAndAge.prototype = {

    getLabel: function (groupRoot) {

        var label = [];
        if (groupRoot.StateSex || groupRoot.StateAge) {

            var areBothLabelsRequired = groupRoot.StateSex && groupRoot.StateAge;

            label.push(' (');

            if (groupRoot.StateSex && groupRoot.Sex.Id !== -1) {
                label.push(groupRoot.Sex.Name);

                if (areBothLabelsRequired) {
                    label.push(', ');
                }
            }

            if (groupRoot.StateAge && groupRoot.Age.Id !== -1) {
                label.push(groupRoot.Age.Name);
            }

            label.push(')');

            // Neither sex nor age were defined
            if (label.length === 2) {
                return '';
            }
        }

        return label.join('');
    }
}

/**
* Results of an area search
* @class AreaSearchResults
*/
function AreaSearchResults(data) {
    var labels = [], parentAreaName, label, placeName;

    var caseInsensitiveAreEqual = function (s1, s2) {
        return s1.toLowerCase() === s2.toLowerCase();
    }

    /**
    * List of suggested area names.
    * @property suggestions
    */
    this.suggestions = $.map(data, function (searchResult) {

        placeName = searchResult.PlaceName;
        parentAreaName = searchResult.PolygonAreaName;

        // Do not want to repeated name
        label = parentAreaName === '' || caseInsensitiveAreEqual(placeName, parentAreaName) ?
            placeName :
            placeName + ', ' + parentAreaName;

        // Will this option already be displayed?
        if (!_.any(labels,
            function (option) {
                return caseInsensitiveAreEqual(option, label);
            })) {

            // Store that option will be displayed
            labels.push(label);

            // This result will be suggested to the user
            return {
                label: label,
                areaName: parentAreaName,
                result: searchResult
            };
        }
        return null;
    });
}

function navBarSearchIconClick() {
    setTimeout(function () {
        $($('.search-box')[0]).focus();
    }, 0);
}

/**
 * Event handler for when a user selects an option from area search results
 * @class SearchTextValidator
 */
function SearchTextValidator(textEnteredByUser) {
    this.isOk = textEnteredByUser !== 'Search for indicators';
    if (this.isOk) {
        this.text = textEnteredByUser.replace(
            /*valid word separators*/
            /[-]/g, ' ').replace(
                /*valid word combiners*/
                /[+&]/g, ' and ').replace(
                    /*nonalphanumerics*/
                    /[^A-Za-z0-9 ]/g, '').trim();
    }
}

function doSearch() {
    var isMobile = window.matchMedia("(max-width: 991px)").matches,
        searchBoxes = $('.search-box'),
        searchText = isMobile ? searchBoxes[0].value : searchBoxes[1].value;

    searchText = new SearchTextValidator(searchText);
    if (searchText.isOk) {
        var start = FT.url.search + encodeURIComponent(searchText.text);
        if (isDefined(FT.model.parentTypeId) && isDefined(FT.model.areaTypeId)) {
            setUrl(start + '#pat/' + FT.model.parentTypeId + '/ati/' + FT.model.areaTypeId + '/par/' + FT.model.parentCode);
        } else {
            setUrl(start);
        }
    }
}

function searchKeyPress(event) {
    var code = (window.event) ? event.keyCode : event.which;
    if (code == 13) {
        doSearch();
    }
}

function initBackToTop() {
    var $supportingInfo = $('.supporting-information');
    var $backToTop = $(".back-to-top");
    if ($supportingInfo.length && $supportingInfo.height() < 500) {
        // Hide for small pages
        $backToTop.hide();
    } else {
        $backToTop.click(function () { $(window).scrollTop(0); });
    }
}

function initProfileContentMenu() {
    var $menu = $('#profile-content-menu');
    if ($menu.length) {
        $menu.change(function () {
            var href = this.value;
            if (href) {
                location.href = href;
            }
        });
    }
}

// Constants
var REGIONAL_COMPARATOR_ID = 1;
var NATIONAL_COMPARATOR_ID = 4;
var TARGET_COMPARATOR_ID = 2;
var NATIONAL_CODE = 'E92000001';
var UK_CODE = 'K02000001';
var SEARCH_NO_RESULT_TOP_OFFSET = 0;
var SEARCH_NO_RESULT_LEFT_OFFSET = 0;

// Application state
var isSpinnerDisplayed = true;
FT.ajaxLock = null;
var mode = null;
/**
* SiteBaseLongerLives.js
* @module SiteBaseLongerLives
*/

MT = {};

function updateModelFromHash() {
    var model = MT.model;

    if (!model.isHashRestored) {
        model.isHashRestored = true;
        ftHistory.init(model);
        model.update();
    } else {
        ftHistory.setHistory();
    }
}

function handleAjaxFailure(e) {

    //var href = window.location.href;

    // Only reset URL if it contains a hash state string
    //if (href.indexOf('#') > 0) {
    //  alert("The data could not be loaded. Please try again.");
    //  PP.model.reset();
    //}
}


// Wrapper for Hogan.js mustache template engine
var templates = (function () {

    var compiledTemplates = {};
    var rawTemplates = {};

    return {

        renderOnce: function (template, parameters) {
            return Hogan.compile(template).render(parameters);
        },

        render: function (name, parameters) {

            var compiled = compiledTemplates[name];

            if (!isDefined(compiled)) {
                compiled = Hogan.compile(rawTemplates[name]);
                compiledTemplates[name] = compiled;
            }

            return compiled.render(parameters);
        },

        add: function (name, mustache) {
            // Do not overwrite templates that are already added
            if (!isDefined(rawTemplates[name])) {
                rawTemplates[name] = mustache;
            }
        }
    };
})();

function getAreaTypeNamePlural() {
    return 'Counties & Unitary Authorities';
}

function getAreaTypeNameSingular() {
    return 'County & Unitary Authority';
}

function getSimpleAreaTypeName() {
    return 'County/UA';
}

function getSimpleAreaTypeNamePlural() {
    return 'areas';
}

function getSimpleAreaTypeNameSingular() {
    return 'area';
}

function getSimilarAreaTooltipText() {

    return 'This view shows your chosen local authority in comparison to the other local authorities with similar levels of socioeconomic deprivation';
}

function getCardinal(rank) {

    var rankString = rank.toString();
    var rankPenultimateChar = (rank < 10) ?
        '' :
        rankString.charAt(rankString.length - 2);

    if (rankPenultimateChar !== '1') {
        var lastChar = rankString.charAt(rankString.length - 1);
        switch (lastChar) {
            case '1':
                return 'st';
            case '2':
                return 'nd';
            case '3':
                return 'rd';
        }
    }

    return 'th';
}

function getAllAreas(model) {

    if (!loaded.areaLists[model.areaTypeId]) {

        var parameters = new ParameterBuilder();

        parameters.add('area_type_id', model.areaTypeId);
        parameters.add('profile_id', model.profileId);
        parameters.add('retrieve_ignored_areas', 'yes');
        parameters.add('no_cache', 'true');

        ajaxGet('api/areas/by_area_type',
            parameters.build(), function (obj) {

                // Ensure hash is created for the area type
                if (!loaded.areaLists[model.areaTypeId]) {
                    loaded.areaLists[model.areaTypeId] = {};
                }

                // Flatten hash
                var nationalAreaHash = loaded.areaLists[model.areaTypeId];
                _.each(obj, function (a) { nationalAreaHash[a.Code] = a; });

                ajaxMonitor.callCompleted();
            });

    } else {
        ajaxMonitor.callCompleted();
    }
}

/**
* AJAX call to fetch the area types for a profile.
* @class getAreaTypes
*/
function getAreaTypes() {

    var parameters = new ParameterBuilder();
    parameters.add('profile_ids', MT.model.profileId);

    ajaxGet('api/area_types',
        parameters.build(),
        function (areaTypes) {
            loaded.areaTypes = areaTypes;
            ajaxMonitor.callCompleted();
        });
}

function AreaFilterModelBuilder(areaTypes, selectedAreaTypeId) {

    _.each(areaTypes, function (a) {
        if (a.Id === selectedAreaTypeId) {
            a['class'] = 'active';
        }

        a.Short = getAreaTypeNamePlural(a.Id);
    });

    //Ensure GP area types are never shown in the Longer Lives area filter.
    //This is need because the web service has been altered to now include GP area types as it is supported in Fingertips.
    areaTypes = areaTypes.filter(function (a) {
        return a.Id !== AreaTypeIds.Practice && a.Id !== AreaTypeIds.Country;
    });

    areaTypes.sort(sortAreasByShort);

    this.getModel = function () {
        return {
            types: areaTypes,
            isOneType: areaTypes.length === 1
        };
    }
}

function setAreaTypeOptionHtml(templateName) {

    var areaFilterModel = new AreaFilterModelBuilder(loaded.areaTypes, MT.model.areaTypeId).getModel();

    $('#area-filter').html(templates.render(templateName, areaFilterModel));
}

function getIndicatorMetadata(groupId, getDefinition, getSystemContent) {

    if (!isDefined(getDefinition)) {
        getDefinition = 'no';
    }

    if (!isDefined(getSystemContent)) {
        getSystemContent = 'no';
    }

    if (loaded.indicatorMetadata[groupId]) {
        // Data already loaded
        ajaxMonitor.callCompleted();
    } else {

        var parameters = new ParameterBuilder(
        ).add('include_system_content', getSystemContent
        ).add('group_ids', groupId
        ).add('include_definition', getDefinition);

        ajaxGet('api/indicator_metadata/by_group_id', parameters.build(),
            function (obj) {
                loaded.indicatorMetadata[groupId] = obj;
                ajaxMonitor.callCompleted();
            });
    }
};

function getAreaValuesCallback(obj) {

    loaded.areaValues[ajaxMonitor.state.indicatorKey] = obj;

    if (typeof (rootIndexesToGet) !== 'undefined' &&
        rootIndexesToGet.length) {
        // Remove the index that has been fetched
        rootIndexesToGet = _.without(rootIndexesToGet, rootIndexesToGet[0]);
    }

    ajaxMonitor.callCompleted();
}

/*
* Provide national area
*/
function getCurrentComparator() {

    var area = {};
    area.Code = MT.model.parentCode;
    return area;
}


//TODO: text should come from database
function highlightSelectedTopic() {

    var id = null;

    switch (MT.model.profileId) {
        case ProfileIds.Diabetes:
            id = 'Diabetes';
            break;
        case ProfileIds.Suicide:
            id = 'Suicide Prevention';
            break;
        case ProfileIds.Mortality:
            id = 'Premature Mortality';
            break;
        case ProfileIds.HealthChecks:
            id = 'NHS Health Checks';
            break;
        case ProfileIds.Hypertension:
            id = 'High Blood Pressure';
            break;
        case ProfileIds.DrugsAndAlcohol:
            id = 'Alcohol And Drugs';
            break;
        case ProfileIds.Cancer:
            id = 'Cancer';
            break;
        case ProfileIds.PublicHealthDashboard:
            id = "Public Health Dashboard";
            break;
    }

    if (id) {
        $('#selected-topic').text(id);
    }
}

function highlightSelectedTab() {
    var words = document.title.split(' '),
        lastWord = words[words.length - 1].toLowerCase();

    var lookUp = {
        'home': 'home-nav',
        'found': 'error-nav',
        'data': 'about-data-nav',
        'connect': 'connect-nav',
        'map': 'map-with-data'
    }

    var id = lookUp[lastWord] ?
        lookUp[lastWord] :
        'rankings-nav';

    if (id !== 'error-nav') {
        $('#' + id).addClass('selected');
    }
}

function getDecileCategoryTypeId() {
    return CategoryTypeIds.DeprivationDecileCountyUA2019;
}

/*
* Get deprivation decile of each area. Response is key/value pair list of 
* area code -> decile number
*/
function getDecileData(model) {
    if (!loaded.categories[AreaTypeIds.DeprivationDecile]) {

        var categoryTypeId = getDecileCategoryTypeId(model.areaTypeId);

        var parameters = new ParameterBuilder(
        ).add('profile_id', model.profileId
        ).add('category_type_id', categoryTypeId
        ).add('child_area_type_id', model.areaTypeId);

        ajaxGet('api/area_categories', parameters.build(),
            function (obj) {
                loaded.categories[AreaTypeIds.DeprivationDecile] = obj;
                ajaxMonitor.callCompleted();
            });

    } else {
        ajaxMonitor.callCompleted();
    }
}

function getDecileInfo(decile) {

    var suffix = ' deprived';

    if (decile > 8) {
        return {
            text: 'Least' + suffix,
            quintile: 1,
            decile: decile,
            color: quintileColors[0]
        };
    }

    if (decile > 6) {
        return {
            text: 'Less' + suffix,
            quintile: 2,
            decile: decile,
            color: quintileColors[1]
        };
    }

    if (decile > 4) {
        return {
            text: 'Average',
            quintile: 3,
            decile: decile,
            color: quintileColors[2]
        };
    }

    if (decile > 2) {
        return {
            text: 'More' + suffix,
            quintile: 4,
            decile: decile,
            color: quintileColors[3]
        };
    }

    return {
        text: 'Most' + suffix,
        quintile: 5,
        decile: decile,
        color: quintileColors[4]
    };
}

function displayAreaRangeElements(isNational) {

    new MutuallyExclusiveDisplay({
        a: $('.national'),
        b: $('.similar')
    }).showA(isNational);
}

function isSimilarAreas() {
    return !!MT.model.similarAreaCode;
}

function getUrlParameterString() {
    var href = window.location.href;
    var bits = href.split('?');
    return bits.length > 1 ?
        bits[1] :
        '';
}

function getParentDetails(parentCode, areaCode, index) {
    if (!isDefined(areaCode)) {
        areaCode = NATIONAL_CODE;
    }
    var details = loaded.areaDetails.getData({ areaCode: parentCode });
    var ranks = details.Ranks[areaCode];
    return ranks[index].AreaRank;
}

function getParentPeriod(parentCode, areaCode, index) {
    if (!isDefined(areaCode)) {
        areaCode = NATIONAL_CODE;
    }
    var ranks = loaded.areaDetails.getData({ areaCode: parentCode }).Ranks[areaCode];
    return ranks[index].Period.replace(/ /g, '');
}

function lock() {
    FT.ajaxLock = 1;
}

function showLoadingSpinner() {
    var over;
    if (SPINNER_STATE) {
        over = '<div class="loading"></div>';
        $(over).appendTo('body');
    } else {
        SPINNER_STATE = true;
        over = '<div id="overlay"><div class="loading"></div></div>';
        $(over).appendTo('body');
    }
}

function removeLoadingSpinner() {
    $('#overlay').remove();
    $('.loading').remove();
}

function bindOverlayMenuEvents() {
    $(".header_summary_tag").hover(
        function () {
            var pos = $(this).position();
            $("#overlay-hover-menu").css({ top: pos.top + "px", left: (pos.left) + "px" }).show();

            $("#overlay-hover-menu").mouseleave(function () {
                $("#overlay-hover-menu").hide();
            });
        }
    );
}

function ajaxAreaSearch(text, successFunction, excludeParentAreasFromSearchResults) {

    var model = MT.model;
    var areaTypeId = model.areaTypeId === AreaTypeIds.Practice
        ? model.parentAreaType
        : model.areaTypeId;

    var parentAreas = [];
    if (!excludeParentAreasFromSearchResults) {
        parentAreas.push(areaTypeId);
    }

    getAreaSearchResults(text, successFunction, areaTypeId, shouldSearchRetreiveCoordinates, parentAreas);
}

function replacePercentageWithArialFont(text) {
    text = text.replace(/%/g, '<span class="arial">%</span>');
    return text;
}

function getGpPracticesNearbyCallback(obj) {
    loaded.nearbyPractices = obj;
    ajaxMonitor.callCompleted();
}

/**
* Retrieves and manages area details data.
* @class AreaDetailsDataManager
*/
function AreaDetailsDataManager() {

    var data = {};
    var _this = this;

    /**
    * Data is only publically accessible for debugging.
    * @property data
    */
    _this.data = data;

    var getDataKey = function (modelForKey) {
        return getKey(modelForKey.groupId, modelForKey.areaCode);
    }

    var setData = function (modelForKey, newData) {
        var key = getDataKey(modelForKey);
        data[key] = newData;
    };

    var getDataFromModel = function (modelForKey) {
        var key = getDataKey(modelForKey);
        return data[key];
    }

    var getModel = function (alternativeModel) {
        var modelCopy = _.clone(MT.model);
        $.extend(modelCopy, alternativeModel);
        return modelCopy;
    }

    /**
    * Gets complex data object that was retrieved by AJAX
    * @method getData
    */
    _this.getData = function (alternativeModel) {
        var modelForKey = getModel(alternativeModel);
        return getDataFromModel(modelForKey);
    };

    /**
    * Fetches data by AJAX.
    * @method fetchDataByAjax
    */
    _this.fetchDataByAjax = function (alternativeModel) {

        var modelCopy = getModel(alternativeModel);

        if (!getDataFromModel(modelCopy)) {

            var parameters = new ParameterBuilder(
            ).add('profile_id', modelCopy.profileId
            ).add('group_id', modelCopy.groupId
            ).add('area_code', modelCopy.areaCode
            ).add('child_area_type_id', modelCopy.areaTypeId);

            ajaxGet('api/area_details',
                parameters.build(),
                function (obj) {
                    setData(modelCopy, obj);
                    ajaxMonitor.callCompleted();
                });
        } else {
            // Do not need to load data
            ajaxMonitor.callCompleted();
        }
    }
}

function doesAreaTypeCompareToOnsCluster() {
    return MT.model.areaTypeId === AreaTypeIds.DistrictUA;
}

function getOnsClusterName(onsClusterCode) {
    return loaded.areaDetails.getData({ areaCode: onsClusterCode }).Area.Name;
}

function getOnsClusterAreaTypeId() {
    if (MT.model.areaTypeId === AreaTypeIds.CCGPreApr2017) {
        return AreaTypeIds.OnsClusterGroup2001;
    }
    return AreaTypeIds.OnsClusterGroup2011;
}

function getOnsClusterCode(model) {
    var parameters = new ParameterBuilder();
    parameters.add('profile_id', model.profileId);
    parameters.add('child_area_type_id', model.areaTypeId);
    parameters.add('parent_area_type_id', getOnsClusterAreaTypeId());

    // Get parent areas
    ajaxGet('api/parent_to_child_areas', parameters.build(), getOnsClusterCodeCallback);
}

function getOnsClusterCodeCallback(onsCodeToChildAreas) {
    loaded.onsClusterCodes = onsCodeToChildAreas;
    ajaxMonitor.callCompleted();
}

function getOnsCodeForArea(areaCode) {
    var onsCodeForArea = null;
    var onsCodeToChildAreas = loaded.onsClusterCodes;
    for (var onsCode in onsCodeToChildAreas) {
        var childAreas = onsCodeToChildAreas[onsCode];
        _.each(childAreas,
            function (area) {
                if (area === areaCode) {
                    onsCodeForArea = onsCode;
                }
            });
        if (onsCodeForArea) break;
    };
    return onsCodeForArea;
}

quintileColors = ['#DED3EC', '#BEA7DA', '#9E7CC8', '#7E50B6', '#5E25A4'];

$(document).ready(function () {

    // Do not proceed if test page
    if (!MT.model) {
        return;
    }

    // Intercept search text parameter for devices where autocomplete does not work
    var parameter = getUrlParameterString();
    if (parameter.indexOf('search_text') === 0) {
        MT.nav.home();
    }

    bindOverlayMenuEvents();
    highlightSelectedTopic();
    highlightSelectedTab();

    initAreaSearch('#search_text', false);
    initSite();

    // Page specific initialisation
    if (typeof initPage !== 'undefined') {
        initPage();
    }
});

function initAreaSearch(jquerySelector, excludeParentAreasFromSearchResults, extraParentAreaTypeIds) {

    var $searchBox = $(jquerySelector),
        $noMatches = $('#no-matches-found'),
        cssSearchWatermark = 'search-watermark',
        title = 'title';

    // Initialize auto-hint fields
    $searchBox.each(function (index) {

        var $sb = $($searchBox[index]);

        $sb.focus(function () {
            $sb.val('').removeClass(cssSearchWatermark);
        });

        $sb.blur(function () {
            if ($sb.val() === '' && $sb.attr(title) !== '') {
                // Display watermark
                $sb.val($sb.attr(title)).addClass(cssSearchWatermark);
            }
            $noMatches.hide();
        });

        $sb.keydown(function (e) {
            var code = (e.keyCode ? e.keyCode : e.which);
            if ($noMatches.is(':visible') && code === 13) {
                e.preventDefault();
            }
        });

        $sb.keyup(function () {
            // Hide no matches if search box empty
            if (!$sb.val().length) {
                $noMatches.hide();
            }
        });

        // No results match
        var showSearchNotFound = function () {
            var position = $sb.position();
            $noMatches.width($sb.width()).css(
                {
                    top: position.top + SEARCH_NO_RESULT_TOP_OFFSET,
                    left: position.left + SEARCH_NO_RESULT_LEFT_OFFSET
                }).show();

            // Hide suggestions
            $('#ui-id-1').hide();
        }

        $sb.autocomplete({
            source: function (request, response) {
                ajaxAreaSearch($sb.val(),
                    function (data) {
                        if (data.length > 0) {
                            $noMatches.hide();
                            response(new AreaSearchResults(data).suggestions);
                        } else {
                            showSearchNotFound();
                        }
                    },
                    excludeParentAreasFromSearchResults,
                    extraParentAreaTypeIds
                );
            },
            delay: 0,
            autoFocus: true,
            minLength: 3,
            select: function (event, ui) {

                lightbox.hide();
                event.preventDefault();
                areaSearchResultSelected($noMatches, ui.item.result);

                logEvent('Search', 'AreaSearch', ui.item.result.PlaceName);
            },
            open: function () {
                $sb.removeClass('ui-corner-all').addClass('ui-corner-top');
            },
            close: function () {
                $sb.removeClass('ui-corner-top').addClass('ui-corner-all');
            }
        });
    });
}

// Returns text string from sig for given indicator data
function getSignificanceText(sig, groupRoot) {

    var comparatorMethodId = groupRoot.ComparatorMethodId;

    if (sig === 0) {
        return '';
    }

    var suffix = '<br>fifth';
    if (comparatorMethodId === ComparatorMethodIds.Quintiles) {
        switch (sig) {
            case 1:
                return 'Lowest' + suffix;
            case 2:
                return 'second<br>lowest' + suffix;
            case 3:
                return 'middle' + suffix;
            case 4:
                return 'second<br>highest' + suffix;
            case 5:
                return 'highest' + suffix;
        }
    }

    suffix = ' average';
    if (comparatorMethodId === ComparatorMethodIds.Quartiles) {
        switch (sig) {
            case 1:
                return 'best';
            case 2:
                return 'better than average rank';
            case 3:
                return 'worse than average rank';
            case 4:
                return 'worst';
        }
    }

    suffix = ' than average';
    switch (groupRoot.PolarityId) {
        case PolarityIds.BlueOrangeBlue:
            switch (sig) {
                case 1:
                    return 'lower' + suffix;
                case 2:
                    return 'consistent with average'; // Amber;
                case 3:
                    return 'higher' + suffix;
            }
        default:
            // Polarity handled in web services re: HighIsGood/LowIsGood
            switch (sig) {
                case 1:
                    return 'worse' + suffix; // Red
                case 2:
                    return 'consistent with average'; // Amber
                case 3:
                    return 'better' + suffix; // Green
            }
    }

    return '';
}

function isNearestNeighbour() {
    return MT.model.similarAreaCode && MT.model.similarAreaCode.indexOf('nn-') === 0;
}

function unlock() {
    FT.ajaxLock = null;
}

GET_METADATA_DEFINITION = GET_METADATA_SYSTEM_CONTENT = 'yes';
SEARCH_NO_RESULT_TOP_OFFSET = 38;

SPINNER_STATE = false;

loaded.areaDetails = new AreaDetailsDataManager();
loaded.categories = {};
loaded.neighbours = {};
loaded.areaLists = {};

/**
* Call out box for map.
* @class CallOutBox
*/
CallOutBox = {};

AnalyticsCategories = {
    DETAILS: 'AreaDetailsPage',
    RANKINGS: 'RankingsPage',
    MAP: 'MapPage'
}

AnalyticsAction = {
    ALL_AREAS: 'AllAreasSelected',
    SIMILAR_AREAS: 'SimilarAreasSelected',
    NEAREST_NEIGHBOURS: 'NearestNeighboursSelected'
}

/**
* Sort function for sorting strings.
* @class sortString
*/
function sortString(a, b) {
    var nameA = a.toLowerCase(),
        nameB = b.toLowerCase();
    if (nameA < nameB) return -1;
    if (nameA > nameB) return 1;
    return 0;
}

/**
* Sort function for sorting areas alphabetically by short name.
* @class sortAreasByShort
*/
function sortAreasByShort(a, b) {
    return sortString(a.Short, b.Short);
};

/**
* Sort function for sorting areas alphabetically by name.
* @class sortAreasByName
*/
function sortAreasByName(a, b) {
    return sortString(a.Name, b.Name);
};

/**
* Gets a basic Google Map object with controls displayed as appropriate.
* @class getGoogleMap
*/
function getGoogleMap() {

    var gm = google.maps;
    return new gm.Map($('#map-canvas')[0], {
        zoom: 6,
        mapTypeId: gm.MapTypeId.ROADMAP,
        panControl: false,
        zoomControl: true,
        scaleControl: false,
        streetViewControl: false,
        mapTypeControl: false
    });
}

/**
* Fits a Google Map to specified LatLngBounds.
* @class fitMapToPracticeResults
*/
function fitMapToPracticeResults(map, bounds) {

    map.fitBounds(bounds);

    var googleMapsEvent = google.maps.event;

    // Add bounds changed listener
    var zoomChangeBoundsListener =
        googleMapsEvent.addListenerOnce(map, 'bounds_changed', function () {

            var maximumZoom = 13;

            // Zoom out if to close
            if (map.getZoom() > maximumZoom) {
                map.setZoom(maximumZoom);
            }
            googleMapsEvent.removeListener(zoomChangeBoundsListener);
        });
}

/**
* Provides information about a CoreDataSet object
* @class CoreDataSetInfo
*/
function CoreDataSetInfo(coreDataSet) {
    this.data = coreDataSet;
}

CoreDataSetInfo.prototype = {

    /**
    * Is the CoreDataSet object defined
    * @method isDefined
    */
    isDefined: function () {
        return !!this.data;
    },

    /**
    * Is there are value note associated with the CoreDataSet object
    * @method isNote
    */
    isNote: function () {
        var data = this.data;
        return data ?
            !!data.NoteId :
            false;
    },

    /**
    * Gets the ID of value note
    * @method getNoteId
    */
    getNoteId: function () {
        return this.isNote() ?
            this.data.NoteId :
            null;
    },

    /**
    * Is the value defined
    * @method isValue
    */
    isValue: function () {
        var data = this.data;
        return data ?
            data.ValF !== '-' :
            false;
    },

    /**
    * Gets the formatted value
    * @method getValF
    */
    getValF: function () {
        return this.data.ValF;
    },

    /**
    * Is the count defined
    * @method isCount
    */
    isCount: function () {
        var data = this.data;
        if (!data) return false;
        return data.Count !== null && data.Count !== -1;
    },

    /**
    * Are confidence intervals defined
    * @method areCIs
    */
    areCIs: function () {
        var data = this.data;
        if (!this.isValue()) return false;
        return data.hasOwnProperty('LoCI') && data.LoCI !== -1;
    },

    /**
    * Are the value and both the confidence intervals equal to zero
    * @method areValueAndCIsZero
    */
    areValueAndCIsZero: function () {

        if (!this.areCIs()) return false;

        var rep = function (valF) {
            return isDefined(valF)
                ? parseInt(valF.replace('.', '').replace('-', ''))
                : -1;
        }

        var data = this.data;
        var up = rep(data.UpCIF);
        var lo = rep(data.LoCIF);
        var val = rep(data.ValF);

        return up === 0 && lo === 0 && val === 0;
    }
};

function MutuallyExclusiveDisplay(jquerySelectors) {

    this.A = jquerySelectors.a;
    this.B = jquerySelectors.b;
}

MutuallyExclusiveDisplay.prototype = {

    showA: function (showAHideB) {

        var a = this.A,
            b = this.B;

        (showAHideB ? a : b).show();
        (showAHideB ? b : a).hide();
    },

    showB: function () {
        this.showA(false);
    }

}

function getColourFromSignificance(significance, useRag, colours, useQuintileColouring, indicatorId, sexId, ageId) {

    if (isEnglandAreaType() && pages.getDefault() === PAGE_MODES.AREA_TRENDS) {
        return colours.comparator;
    }

    if (useQuintileColouring) {
        if (significance > 0 && significance < 6) {
            var quintile = 'quintile' + significance;
            var groupRoot = getGroupRootByIndicatorSexAndAge(indicatorId, sexId, ageId);
            if (groupRoot.PolarityId === PolarityIds.NotApplicable) {
                switch (quintile) {
                    case 'quintile1':
                        return colours['bobQuintile1'];
                    case 'quintile2':
                        return colours['bobQuintile2'];
                    case 'quintile3':
                        return colours['bobQuintile3'];
                    case 'quintile4':
                        return colours['bobQuintile4'];
                    case 'quintile5':
                        return colours['bobQuintile5'];
                }
            } else {
                switch (quintile) {
                    case 'quintile1':
                        return colours['ragQuintile1'];
                    case 'quintile2':
                        return colours['ragQuintile2'];
                    case 'quintile3':
                        return colours['ragQuintile3'];
                    case 'quintile4':
                        return colours['ragQuintile4'];
                    case 'quintile5':
                        return colours['ragQuintile5'];
                }
            }
        }
    } else {
        if (useRag) {
            switch (significance) {
                case 1:
                    return colours.worse;
                case 2:
                    return colours.same;
                case 3:
                    return colours.better;
                case 4:
                    return colours.worst;
                case 5:
                    return colours.best;
            }
        } else {
            switch (significance) {
                case 1:
                    return colours.bobLower;
                case 2:
                    return colours.same;
                case 3:
                    return colours.bobHigher;
                case 4:
                    return colours.bobLowest;
                case 5:
                    return colours.bobHighest;
            }
        }
    }

    return colours.none;
}

/**
* Creates a key/value pair list of area code to coredataset
* where: Key -> area code, Value -> coredataset
* @class getAreaCodeToCoreDataHash
*/
function getAreaCodeToCoreDataHash(dataList) {

    var hash = {};
    _.each(dataList, function (data) {
        hash[data.AreaCode] = data;
    });
    return hash;
}

/**
* AJAX call to fetch the value notes.
* @class getValueNotesCall
*/
function getValueNotesCall() {
    ajaxGet('api/value_notes', '', getValueNotesCallback);
}

function getAreaValues(root, model, code) {

    var parentCode = !!code
        ? code
        : getCurrentComparator().Code;
    var key = getIndicatorKey(root, model, parentCode);

    ajaxMonitor.state.indicatorKey = key;

    var areaValues = loaded.areaValues;
    if (areaValues.hasOwnProperty(key)) {
        // Data already loaded
        getAreaValuesCallback(areaValues[key]);
    } else {

        var parameters = new ParameterBuilder(
        ).add('group_id', model.groupId
        ).add('area_type_id', model.areaTypeId
        ).add('parent_area_code', parentCode
        ).add('profile_id', model.profileId
        ).add('comparator_id', comparatorId
        ).add('indicator_id', root.IID
        ).add('year_range', root.YearRange
        ).add('sex_id', root.Sex.Id
        ).add('age_id', root.Age.Id);
        ajaxGet('api/latest_data/single_indicator_for_all_areas', parameters.build(), getAreaValuesCallback);
    }
}

function getAreaValuesCallback(coreDataList) {

    var hash = {};
    for (var i in coreDataList) {
        var data = coreDataList[i];
        hash[data.AreaCode] = data;
    }

    loaded.areaValues[ajaxMonitor.state.indicatorKey] = hash;

    ajaxMonitor.callCompleted();
}

function getIndicatorKey(root, model, comparatorCode) {
    return getKey(
        model.groupId,
        root.IID,
        root.Sex.Id,
        root.Age.Id,
        model.areaTypeId,
        comparatorCode
    );
}

/**
* AJAX call to fetch indicator metadata for a domain.
* @class getIndicatorMetadata
*/
function getIndicatorMetadata(groupId, getSystemContent) {
    // Establish whether metadata is already loaded
    var loadedMetadata = loaded.indicatorMetadata;
    if (isDefined(loadedMetadata[groupId])) {
        ajaxMonitor.callCompleted();
    } else {
        if (!isDefined(getSystemContent)) {
            getSystemContent = 'no';
        }

        var parameters = new ParameterBuilder(
        ).add('include_system_content', getSystemContent
        ).add('include_definition', 'yes');


        var method = 'by_group_id';
        parameters.add('group_ids', groupId);

        ajaxGet('api/indicator_metadata/' + method, parameters.build(),
            function (metadata) {
                loadedMetadata[groupId] = metadata;
                ajaxMonitor.callCompleted();
            });
    }
};

function getKey() {
    return _.toArray(arguments).join('-');
};


/**
* Trims a string to the specified character limit and appends "...".
* @class trimName
*/
function trimName(name, limit) {

    if (name.length > limit) {

        name = $.trim(name.substr(0, limit)) + '...';
    }
    return name;
};

function getCategoryAreaCode() {
    if (isCategoryAreacode()) {
        return FT.model.parentCode;
    }
    return null;
}

function isCategoryAreacode() {
    return FT.model.parentCode.includes("cat-");
}

function MinMaxFinder(arrayOfNumbers) {
    this.setValues(arrayOfNumbers);
}

MinMaxFinder.prototype = {
    setValues: function (arrayOfNumbers) {

        var areValues = arrayOfNumbers.length > 0;

        if (areValues) {
            this.min = _.min(arrayOfNumbers);
            this.max = _.max(arrayOfNumbers);
        }
        this.isValid = areValues;
    }

};


/*
* Formats a number to include commas, e.g. 100000 to 100,000
* @class CommaNumber
*/
function CommaNumber(n) {
    this.n = n;
}

CommaNumber.prototype = {

    _commarate: function (number) {
        return number.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
    },

    /**
    * Rounds the number to 0 decimal places before formatting with commass
    * @method rounded
    */
    rounded: function () {
        return this._commarate(Math.round(this.n, 0));
    },

    unrounded: function () {
        var commaNumber = this.n.toString();
        if (commaNumber.indexOf('.') > -1) {
            var bits = commaNumber.split('.');
            return this._commarate(bits[0]) + '.' + bits[1];
        }
        return this._commarate(this.n);
    }
};

/**
* Gets an element ID from the argument of a JQuery event.
* @class getElementIdFromJQueryEvent
*/
function getElementIdFromJQueryEvent(e) {

    // Target may be child of element that event is registered to
    var target = e.target,
        targetId = target.id;
    while (targetId === '') {
        target = $(target).parent()[0];
        targetId = target.id;
    }
    return targetId;
}

var tooltipManager = new function () {

    var tooltipId = '#tooltip',
        tooltipElement = null,
        manager = this,
        isVisible = false,
        isInitialised = false,
        _tooltipProvider;

    this.init = function () {

        if (!isInitialised) {
            // Add necessary elements to page
            $('body').append(lightbox.HTML +
                /* Tooltip HTML */'<div id="tooltip" style="display: none;" onclick="tooltipManager.hide();"></div>'
            );

            if (!tooltipElement) {
                tooltipElement = $(tooltipId);
            }

            isInitialised = true;
        }
    };

    // Make an element trigger a tooltip
    this.initElement = function (id) {

        var $element = $('#' + id);

        $element.mouseenter(function (e) {
            manager.show(e);
        });
        $element.mouseleave(function (e) {
            manager.hide();
        });
        $element.mousemove(function (e) {
            manager.move(e);
        });
    };

    // Display the tooltip
    this.show = function (e, html) {

        isVisible = true;

        this.positionByEvent(e);

        var targetId = getElementIdFromJQueryEvent(e);

        var text = isDefined(html) ?
            html :
            this.getTooltipHtml(targetId);

        if (text !== '') {
            tooltipElement.html(text);
            tooltipElement.show();
        }
    };

    this.hide = function () {
        if (tooltipElement !== null) {
            tooltipElement.hide();
            isVisible = false;
        }
    };

    // Move the tooltip with the mouse pointer
    this.move = function (e) {
        this.positionByEvent(e);
    };

    this.positionByEvent = function (e) {
        this.positionXY(e.pageX + 10, e.pageY + 15);
    };

    this.positionXY = function (x, y) {
        tooltipElement.css('top', y);
        tooltipElement.css('left', x);
    };

    // Display a fixed position pop-up
    this.showText = function (jq, text, x, y) {

        isVisible = true;

        var pos = jq.position();
        this.positionXY(pos.left + x, pos.top + y);
        tooltipElement.html(text);
        tooltipElement.show();
    };

    this.setHtml = function (text) {
        tooltipElement.html(text);
    };

    this.showOnly = function () {
        tooltipElement.show();
    };

    this.showHtmlInPosition = function (text, x, y) {

        isVisible = true;

        this.positionXY(x, y);
        tooltipElement.html(text);
        tooltipElement.show();
    };

    this.getTooltipHtml = function (id) {
        return _tooltipProvider ?
            _tooltipProvider.getHtml(id) :
            '';
    };

    this.setTooltipProvider = function (tooltipProvider) {
        _tooltipProvider = tooltipProvider;
    };
};

lightbox = (function () {

    // Private members
    fadeIn = true;
    var jqInfo = null;
    var jqLightbox = null;

    var init = function () {
        if (jqInfo === null) {
            jqLightbox = $('#lightBox');
            jqInfo = $('#infoBox');
        }
    };

    var CLICK_HIDE = ' onclick="lightbox.hide();"';
    var leafletClass = '.leaflet-control';

    return {

        HTML: '<div id="lightBox"' + CLICK_HIDE +
            '></div><div id="infoBox" style="display: none;"></div>',

        // Some older browsers do not fade in gracefully
        setFadeIn: function (b) {
            fadeIn = b;
        },

        // pre functions facilitate cross-browser support
        preHide: function () { },
        preShow: function () { },

        setHtml: function (html) {
            jqInfo.html(html + '<div class="close"' + CLICK_HIDE + '></div>');
        },

        // The left position for a pop-up to appear in the middle of the window
        getLeftForCenteredPopup: function (width) {
            return ($(window).width() - width) / 2;
        },

        show: function (html, top, left, width) {

            init();

            this.preShow();

            var height = $(document).height();
            if (height < 1000) {
                height = 1000;
            }
            jqLightbox.height(height);

            // Show transparent background
            if (fadeIn) {
                jqLightbox.fadeIn(400);
            } else {
                jqLightbox.show();
            }

            // Default width
            if (!width) {
                width = 500;
            }

            // Default centered
            if (!left) {
                left = this.getLeftForCenteredPopup(width);
            }

            // Default top
            if (!top) {
                top = 200;
            }

            // Show content
            this.setHtml(html);
            jqInfo.css({ 'top': top, 'left': left, 'width': width });
            jqInfo.show();

            // Hide map controls
            $(leafletClass).hide();

            // Final readjustment
            var finalHeight = $(document).height();
            if (finalHeight > height) {
                jqLightbox.height(finalHeight + 20);
            }
        },


        hide: function () {
            this.preHide();

            if (jqInfo !== null) {
                jqInfo.hide();
                jqLightbox.hide();
            }

            // Show map controls
            $(leafletClass).show();
        }
    }
})();

tooltipManager.init();

/**
* AJAX call to fetch results of an area search
* @class getAreaSearchResults
*/
function getAreaSearchResults(text, successFunction, areaTypeId, shouldSearchRetreiveCoordinates, parentAreasToIncludeInResults) {

    var parameters = new ParameterBuilder();
    parameters.add('search_text', text);
    parameters.setNotToCache();
    parameters.add('polygon_area_type_id', areaTypeId);
    parameters.add('include_coordinates', shouldSearchRetreiveCoordinates);
    parameters.add('parent_areas_to_include_in_results', parentAreasToIncludeInResults.join(','));

    ajaxGet('api/place_postcode_search', parameters.build(), successFunction);
}



