/*! minerva.js */document.addEventListener("click", function () {
    var element = document.getElementById("dhtmltooltip")
    if (element != undefined) { element.style.display = 'none'; }
});


var timer;
var caller;
// MOUSE LEAVE EVENT CALLBACK (CALLED FROM ANGULAR DIRECTIVE BUT IT CAN BE USED EVERYWHERE IN THE HTML CODE)
function tooltipMouseEnter(e) {

    // if tooltip text inserted inside the next .tooltiptext div is null or empty return
    if ($(this).next("DIV .tooltiptext") == undefined) return;
    var html = $(this).next("DIV .tooltiptext").html();
    if (html == undefined || html == '') {
        if ($(this).is("input") && $(this).attr("autocomplete") != undefined) {
            html = $(this).next().next("DIV .tooltiptext").html();
            if (html == undefined || html == '') return;
        }
        else return;
    }

    var showTopBottom = true;
    if ($(this).is("select")
        || ($(this).is("input") && $(this).attr("autocomplete") != undefined)
        || $(this).attr("horizontal") != undefined)
    { showTopBottom = false; }

    // get how many pixels are scrolled from top
    var srollTop = $(document).scrollTop();

    // get window width 
    var winw = $(window).width();
    var winh = $(window).height();

    // get mouse left position
    var mouseleft = e.pageX;

    // get offset left and top of the caller control
    var offset = $(this).offset();
    // get caller element width and height
    var h = $(this).height();
    var w = $(this).width();


    // tooltip padding must set as 20 total (top+bottom or right+left)
    var tooltippadding = 22;

    // set tooltip 
    var dhtmltooltip = $("#dhtmltooltip");
    $(dhtmltooltip).html(html);

    if (html.length > 450)
    { $(dhtmltooltip).css({ width: '600px' }); }
    else
    { $(dhtmltooltip).css({ width: '300px' }); }

    // get tooltip element width and height
    var tooltipwidth = $(dhtmltooltip).width();
    var tooltipheight = $(dhtmltooltip).height();
    // if tolltip width is more than window one, set tooltip width = window width
    if (tooltipwidth > winw)
        tooltipwidth = winw;
    // alternative the tolltip could be not showed if (winw < tooltipwidth) return;

    // TOOLTIP POSITIONING ON LEFT MOUSE ENTERING POSITION (WITH SPECIAL CASE FOR AUTOCOMPLETE AND SELECT)
    var left = 0;
    var top = offset.top;
    if (!showTopBottom) {
        left = offset.left + w + (tooltippadding / 2);
        top = top;
    }
    else {
        left = mouseleft - (tooltipwidth / 2);

        if ((top + h + tooltipheight + tooltippadding) > (winh + srollTop))
        {
            top = top - tooltipheight;
            if ($(this).is("input") || $(this).is("textarea")) {
                top = top - tooltippadding;
            }
        }
        else
        {
            top = top + h;
            if ($(this).is("input") || $(this).is("textarea")) {
                top = top + 10;
            }
        }

        if ($(this).is("input") || $(this).is("button")) {
            top = top - 10;
        }

    }
    left = (left > 0) ? left : 0;

    if ((left + tooltipwidth + tooltippadding) > winw) {
        if (!showTopBottom) {
            if ((tooltipwidth + tooltippadding) > offset.left) {
                left = winw - tooltipwidth - tooltippadding;
                top = top - tooltipheight - tooltippadding;
            }
            else { left = offset.left - tooltipwidth - tooltippadding; }
        }
        else {
            left = winw - tooltipwidth - tooltippadding;
        }
    }
    
    $(dhtmltooltip).css({ top: top, left: left });
    
   
    if (caller != this) {
        $(dhtmltooltip).hide();
    }

    caller = this;
    clearTimeout(timer);
    timer = setTimeout(function () {
        $(dhtmltooltip).stop(true, false).fadeIn('fast');        
    }, 1000);
    


    /* // ALTERNATIVE TOOLTIP POSITIONING ON CALLER CONTROL CENTER
    if ($(this).is("select")) {
        var left = offset.left + w + tooltippadding;
        var top = offset.top;
    }
    else {
        var left = offset.left - ((tooltipwidth - w) / 2)
        var top = offset.top + h;
    }
    left = (left > 0) ? left : 0;
    if ((left + tooltipwidth + tooltippadding) > winw) left = winw - tooltipwidth - tooltippadding;
    */

}

// MOUSE LEAVE EVENT CALLBACK (CALLED FROM ANGULAR DIRECTIVE BUT IT CAN BE USED EVERYWHERE IN THE HTML CODE)
function tooltipMouseLeave(e) {
    var dhtmltooltip = $("#dhtmltooltip");
    
    clearTimeout(timer);
    timer = setTimeout(function () {
        $(dhtmltooltip).stop(true, false).fadeOut('fast');
    }, 3500);
}

// SET TOOLTIP BOX MOUSE EVENTS
$(function () {
    $("#dhtmltooltip").on("mouseover", function (e) {
        clearTimeout(timer);
        timer = setTimeout(function () {
            $(dhtmltooltip).stop(true, false).fadeIn('fast');
        }, 1000);

    }).on("mouseout", function () {
        clearTimeout(timer);
        timer = setTimeout(function () {
            $(dhtmltooltip).stop(true, false).fadeOut('fast');
        }, 500);


        //$(this).clearQueue().finish().delay(1000).fadeOut(100);
    });
});


// GET THE PERCENTAGE WITH ONLY 1 DECIMAL
function getGraphicPercent(perc) {
    perc = perc.toString();
    var split = perc.split(',');
    if (split.length > 1)
        perc = perc.split(',')[0] + "," + perc.split(',')[1].substring(0, 1);
    split = perc.split('.');
    if (split.length > 1)
        perc = perc.split('.')[0] + "," + perc.split('.')[1].substring(0, 1);
    return perc;
}


function stringIsNullOrEmpty(value) {
    if (value == undefined)
        return 'true';
    if (value == '')
        return 'true';

    return 'false';
}

function numberIsNullOrLessThanZero(value) {
    if (value == undefined)
        return true;
    if (value < 1)
        return true;

    return false;
}

function numberIsNullOrZero(value) {
    if (value == undefined)
        return true;
    if (value == 0)
        return true;

    return false;
}


/* SCRIPT FOR MENU LEFT FRONT END */
$(function () {
    
    $("li:has(.sub) .sub_menu_plus").click(function () {
        $(this).parent().siblings("ul:first").toggle();
        if ($(this).parent().siblings("ul:first").is(":visible")) {
            $(this).text("-");
        }
        else {
            $(this).text("+");
        }
    });
    //$("li").click(function (event) {
    //    //$('#menu ul li a').removeClass('selected');
    //    //$(this).find('.menu_element a:first').addClass('selected');
    //    event.stopPropagation();
    //});
});


/*Returns the month names in language. Format can be 'short' or 'long'*/
function getMonthName(lang, format) {
    var ret = [];
    for(var i = 0; i < 12; i++) {
        var objDate = new Date(2000, i, 1);
        ret.push(objDate.toLocaleString(lang, { month: format }));
    }
    return ret;
};
/*! leaflet.js */!function(t,e,i){var n=t.L,o={};o.version="0.7.7","object"==typeof module&&"object"==typeof module.exports?module.exports=o:"function"==typeof define&&define.amd&&define(o),o.noConflict=function(){return t.L=n,this},t.L=o,o.Util={extend:function(t){var e,i,n,o,s=Array.prototype.slice.call(arguments,1);for(i=0,n=s.length;n>i;i++){o=s[i]||{};for(e in o)o.hasOwnProperty(e)&&(t[e]=o[e])}return t},bind:function(t,e){var i=arguments.length>2?Array.prototype.slice.call(arguments,2):null;return function(){return t.apply(e,i||arguments)}},stamp:function(){var t=0,e="_leaflet_id";return function(i){return i[e]=i[e]||++t,i[e]}}(),invokeEach:function(t,e,i){var n,o;if("object"==typeof t){o=Array.prototype.slice.call(arguments,3);for(n in t)e.apply(i,[n,t[n]].concat(o));return!0}return!1},limitExecByInterval:function(t,e,i){var n,o;return function s(){var a=arguments;return n?void(o=!0):(n=!0,setTimeout(function(){n=!1,o&&(s.apply(i,a),o=!1)},e),void t.apply(i,a))}},falseFn:function(){return!1},formatNum:function(t,e){var i=Math.pow(10,e||5);return Math.round(t*i)/i},trim:function(t){return t.trim?t.trim():t.replace(/^\s+|\s+$/g,"")},splitWords:function(t){return o.Util.trim(t).split(/\s+/)},setOptions:function(t,e){return t.options=o.extend({},t.options,e),t.options},getParamString:function(t,e,i){var n=[];for(var o in t)n.push(encodeURIComponent(i?o.toUpperCase():o)+"="+encodeURIComponent(t[o]));return(e&&-1!==e.indexOf("?")?"&":"?")+n.join("&")},template:function(t,e){return t.replace(/\{ *([\w_]+) *\}/g,function(t,n){var o=e[n];if(o===i)throw new Error("No value provided for variable "+t);return"function"==typeof o&&(o=o(e)),o})},isArray:Array.isArray||function(t){return"[object Array]"===Object.prototype.toString.call(t)},emptyImageUrl:"data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs="},function(){function e(e){var i,n,o=["webkit","moz","o","ms"];for(i=0;i<o.length&&!n;i++)n=t[o[i]+e];return n}function i(e){var i=+new Date,o=Math.max(0,16-(i-n));return n=i+o,t.setTimeout(e,o)}var n=0,s=t.requestAnimationFrame||e("RequestAnimationFrame")||i,a=t.cancelAnimationFrame||e("CancelAnimationFrame")||e("CancelRequestAnimationFrame")||function(e){t.clearTimeout(e)};o.Util.requestAnimFrame=function(e,n,a,r){return e=o.bind(e,n),a&&s===i?void e():s.call(t,e,r)},o.Util.cancelAnimFrame=function(e){e&&a.call(t,e)}}(),o.extend=o.Util.extend,o.bind=o.Util.bind,o.stamp=o.Util.stamp,o.setOptions=o.Util.setOptions,o.Class=function(){},o.Class.extend=function(t){var e=function(){this.initialize&&this.initialize.apply(this,arguments),this._initHooks&&this.callInitHooks()},i=function(){};i.prototype=this.prototype;var n=new i;n.constructor=e,e.prototype=n;for(var s in this)this.hasOwnProperty(s)&&"prototype"!==s&&(e[s]=this[s]);t.statics&&(o.extend(e,t.statics),delete t.statics),t.includes&&(o.Util.extend.apply(null,[n].concat(t.includes)),delete t.includes),t.options&&n.options&&(t.options=o.extend({},n.options,t.options)),o.extend(n,t),n._initHooks=[];var a=this;return e.__super__=a.prototype,n.callInitHooks=function(){if(!this._initHooksCalled){a.prototype.callInitHooks&&a.prototype.callInitHooks.call(this),this._initHooksCalled=!0;for(var t=0,e=n._initHooks.length;e>t;t++)n._initHooks[t].call(this)}},e},o.Class.include=function(t){o.extend(this.prototype,t)},o.Class.mergeOptions=function(t){o.extend(this.prototype.options,t)},o.Class.addInitHook=function(t){var e=Array.prototype.slice.call(arguments,1),i="function"==typeof t?t:function(){this[t].apply(this,e)};this.prototype._initHooks=this.prototype._initHooks||[],this.prototype._initHooks.push(i)};var s="_leaflet_events";o.Mixin={},o.Mixin.Events={addEventListener:function(t,e,i){if(o.Util.invokeEach(t,this.addEventListener,this,e,i))return this;var n,a,r,h,l,u,c,d=this[s]=this[s]||{},p=i&&i!==this&&o.stamp(i);for(t=o.Util.splitWords(t),n=0,a=t.length;a>n;n++)r={action:e,context:i||this},h=t[n],p?(l=h+"_idx",u=l+"_len",c=d[l]=d[l]||{},c[p]||(c[p]=[],d[u]=(d[u]||0)+1),c[p].push(r)):(d[h]=d[h]||[],d[h].push(r));return this},hasEventListeners:function(t){var e=this[s];return!!e&&(t in e&&e[t].length>0||t+"_idx"in e&&e[t+"_idx_len"]>0)},removeEventListener:function(t,e,i){if(!this[s])return this;if(!t)return this.clearAllEventListeners();if(o.Util.invokeEach(t,this.removeEventListener,this,e,i))return this;var n,a,r,h,l,u,c,d,p,_=this[s],m=i&&i!==this&&o.stamp(i);for(t=o.Util.splitWords(t),n=0,a=t.length;a>n;n++)if(r=t[n],u=r+"_idx",c=u+"_len",d=_[u],e){if(h=m&&d?d[m]:_[r]){for(l=h.length-1;l>=0;l--)h[l].action!==e||i&&h[l].context!==i||(p=h.splice(l,1),p[0].action=o.Util.falseFn);i&&d&&0===h.length&&(delete d[m],_[c]--)}}else delete _[r],delete _[u],delete _[c];return this},clearAllEventListeners:function(){return delete this[s],this},fireEvent:function(t,e){if(!this.hasEventListeners(t))return this;var i,n,a,r,h,l=o.Util.extend({},e,{type:t,target:this}),u=this[s];if(u[t])for(i=u[t].slice(),n=0,a=i.length;a>n;n++)i[n].action.call(i[n].context,l);r=u[t+"_idx"];for(h in r)if(i=r[h].slice())for(n=0,a=i.length;a>n;n++)i[n].action.call(i[n].context,l);return this},addOneTimeEventListener:function(t,e,i){if(o.Util.invokeEach(t,this.addOneTimeEventListener,this,e,i))return this;var n=o.bind(function(){this.removeEventListener(t,e,i).removeEventListener(t,n,i)},this);return this.addEventListener(t,e,i).addEventListener(t,n,i)}},o.Mixin.Events.on=o.Mixin.Events.addEventListener,o.Mixin.Events.off=o.Mixin.Events.removeEventListener,o.Mixin.Events.once=o.Mixin.Events.addOneTimeEventListener,o.Mixin.Events.fire=o.Mixin.Events.fireEvent,function(){var n="ActiveXObject"in t,s=n&&!e.addEventListener,a=navigator.userAgent.toLowerCase(),r=-1!==a.indexOf("webkit"),h=-1!==a.indexOf("chrome"),l=-1!==a.indexOf("phantom"),u=-1!==a.indexOf("android"),c=-1!==a.search("android [23]"),d=-1!==a.indexOf("gecko"),p=typeof orientation!=i+"",_=!t.PointerEvent&&t.MSPointerEvent,m=t.PointerEvent&&t.navigator.pointerEnabled||_,f="devicePixelRatio"in t&&t.devicePixelRatio>1||"matchMedia"in t&&t.matchMedia("(min-resolution:144dpi)")&&t.matchMedia("(min-resolution:144dpi)").matches,g=e.documentElement,v=n&&"transition"in g.style,y="WebKitCSSMatrix"in t&&"m11"in new t.WebKitCSSMatrix&&!c,P="MozPerspective"in g.style,L="OTransition"in g.style,x=!t.L_DISABLE_3D&&(v||y||P||L)&&!l,w=!t.L_NO_TOUCH&&!l&&(m||"ontouchstart"in t||t.DocumentTouch&&e instanceof t.DocumentTouch);o.Browser={ie:n,ielt9:s,webkit:r,gecko:d&&!r&&!t.opera&&!n,android:u,android23:c,chrome:h,ie3d:v,webkit3d:y,gecko3d:P,opera3d:L,any3d:x,mobile:p,mobileWebkit:p&&r,mobileWebkit3d:p&&y,mobileOpera:p&&t.opera,touch:w,msPointer:_,pointer:m,retina:f}}(),o.Point=function(t,e,i){this.x=i?Math.round(t):t,this.y=i?Math.round(e):e},o.Point.prototype={clone:function(){return new o.Point(this.x,this.y)},add:function(t){return this.clone()._add(o.point(t))},_add:function(t){return this.x+=t.x,this.y+=t.y,this},subtract:function(t){return this.clone()._subtract(o.point(t))},_subtract:function(t){return this.x-=t.x,this.y-=t.y,this},divideBy:function(t){return this.clone()._divideBy(t)},_divideBy:function(t){return this.x/=t,this.y/=t,this},multiplyBy:function(t){return this.clone()._multiplyBy(t)},_multiplyBy:function(t){return this.x*=t,this.y*=t,this},round:function(){return this.clone()._round()},_round:function(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this},floor:function(){return this.clone()._floor()},_floor:function(){return this.x=Math.floor(this.x),this.y=Math.floor(this.y),this},distanceTo:function(t){t=o.point(t);var e=t.x-this.x,i=t.y-this.y;return Math.sqrt(e*e+i*i)},equals:function(t){return t=o.point(t),t.x===this.x&&t.y===this.y},contains:function(t){return t=o.point(t),Math.abs(t.x)<=Math.abs(this.x)&&Math.abs(t.y)<=Math.abs(this.y)},toString:function(){return"Point("+o.Util.formatNum(this.x)+", "+o.Util.formatNum(this.y)+")"}},o.point=function(t,e,n){return t instanceof o.Point?t:o.Util.isArray(t)?new o.Point(t[0],t[1]):t===i||null===t?t:new o.Point(t,e,n)},o.Bounds=function(t,e){if(t)for(var i=e?[t,e]:t,n=0,o=i.length;o>n;n++)this.extend(i[n])},o.Bounds.prototype={extend:function(t){return t=o.point(t),this.min||this.max?(this.min.x=Math.min(t.x,this.min.x),this.max.x=Math.max(t.x,this.max.x),this.min.y=Math.min(t.y,this.min.y),this.max.y=Math.max(t.y,this.max.y)):(this.min=t.clone(),this.max=t.clone()),this},getCenter:function(t){return new o.Point((this.min.x+this.max.x)/2,(this.min.y+this.max.y)/2,t)},getBottomLeft:function(){return new o.Point(this.min.x,this.max.y)},getTopRight:function(){return new o.Point(this.max.x,this.min.y)},getSize:function(){return this.max.subtract(this.min)},contains:function(t){var e,i;return t="number"==typeof t[0]||t instanceof o.Point?o.point(t):o.bounds(t),t instanceof o.Bounds?(e=t.min,i=t.max):e=i=t,e.x>=this.min.x&&i.x<=this.max.x&&e.y>=this.min.y&&i.y<=this.max.y},intersects:function(t){t=o.bounds(t);var e=this.min,i=this.max,n=t.min,s=t.max,a=s.x>=e.x&&n.x<=i.x,r=s.y>=e.y&&n.y<=i.y;return a&&r},isValid:function(){return!(!this.min||!this.max)}},o.bounds=function(t,e){return!t||t instanceof o.Bounds?t:new o.Bounds(t,e)},o.Transformation=function(t,e,i,n){this._a=t,this._b=e,this._c=i,this._d=n},o.Transformation.prototype={transform:function(t,e){return this._transform(t.clone(),e)},_transform:function(t,e){return e=e||1,t.x=e*(this._a*t.x+this._b),t.y=e*(this._c*t.y+this._d),t},untransform:function(t,e){return e=e||1,new o.Point((t.x/e-this._b)/this._a,(t.y/e-this._d)/this._c)}},o.DomUtil={get:function(t){return"string"==typeof t?e.getElementById(t):t},getStyle:function(t,i){var n=t.style[i];if(!n&&t.currentStyle&&(n=t.currentStyle[i]),(!n||"auto"===n)&&e.defaultView){var o=e.defaultView.getComputedStyle(t,null);n=o?o[i]:null}return"auto"===n?null:n},getViewportOffset:function(t){var i,n=0,s=0,a=t,r=e.body,h=e.documentElement;do{if(n+=a.offsetTop||0,s+=a.offsetLeft||0,n+=parseInt(o.DomUtil.getStyle(a,"borderTopWidth"),10)||0,s+=parseInt(o.DomUtil.getStyle(a,"borderLeftWidth"),10)||0,i=o.DomUtil.getStyle(a,"position"),a.offsetParent===r&&"absolute"===i)break;if("fixed"===i){n+=r.scrollTop||h.scrollTop||0,s+=r.scrollLeft||h.scrollLeft||0;break}if("relative"===i&&!a.offsetLeft){var l=o.DomUtil.getStyle(a,"width"),u=o.DomUtil.getStyle(a,"max-width"),c=a.getBoundingClientRect();("none"!==l||"none"!==u)&&(s+=c.left+a.clientLeft),n+=c.top+(r.scrollTop||h.scrollTop||0);break}a=a.offsetParent}while(a);a=t;do{if(a===r)break;n-=a.scrollTop||0,s-=a.scrollLeft||0,a=a.parentNode}while(a);return new o.Point(s,n)},documentIsLtr:function(){return o.DomUtil._docIsLtrCached||(o.DomUtil._docIsLtrCached=!0,o.DomUtil._docIsLtr="ltr"===o.DomUtil.getStyle(e.body,"direction")),o.DomUtil._docIsLtr},create:function(t,i,n){var o=e.createElement(t);return o.className=i,n&&n.appendChild(o),o},hasClass:function(t,e){if(t.classList!==i)return t.classList.contains(e);var n=o.DomUtil._getClass(t);return n.length>0&&new RegExp("(^|\\s)"+e+"(\\s|$)").test(n)},addClass:function(t,e){if(t.classList!==i)for(var n=o.Util.splitWords(e),s=0,a=n.length;a>s;s++)t.classList.add(n[s]);else if(!o.DomUtil.hasClass(t,e)){var r=o.DomUtil._getClass(t);o.DomUtil._setClass(t,(r?r+" ":"")+e)}},removeClass:function(t,e){t.classList!==i?t.classList.remove(e):o.DomUtil._setClass(t,o.Util.trim((" "+o.DomUtil._getClass(t)+" ").replace(" "+e+" "," ")))},_setClass:function(t,e){t.className.baseVal===i?t.className=e:t.className.baseVal=e},_getClass:function(t){return t.className.baseVal===i?t.className:t.className.baseVal},setOpacity:function(t,e){if("opacity"in t.style)t.style.opacity=e;else if("filter"in t.style){var i=!1,n="DXImageTransform.Microsoft.Alpha";try{i=t.filters.item(n)}catch(o){if(1===e)return}e=Math.round(100*e),i?(i.Enabled=100!==e,i.Opacity=e):t.style.filter+=" progid:"+n+"(opacity="+e+")"}},testProp:function(t){for(var i=e.documentElement.style,n=0;n<t.length;n++)if(t[n]in i)return t[n];return!1},getTranslateString:function(t){var e=o.Browser.webkit3d,i="translate"+(e?"3d":"")+"(",n=(e?",0":"")+")";return i+t.x+"px,"+t.y+"px"+n},getScaleString:function(t,e){var i=o.DomUtil.getTranslateString(e.add(e.multiplyBy(-1*t))),n=" scale("+t+") ";return i+n},setPosition:function(t,e,i){t._leaflet_pos=e,!i&&o.Browser.any3d?t.style[o.DomUtil.TRANSFORM]=o.DomUtil.getTranslateString(e):(t.style.left=e.x+"px",t.style.top=e.y+"px")},getPosition:function(t){return t._leaflet_pos}},o.DomUtil.TRANSFORM=o.DomUtil.testProp(["transform","WebkitTransform","OTransform","MozTransform","msTransform"]),o.DomUtil.TRANSITION=o.DomUtil.testProp(["webkitTransition","transition","OTransition","MozTransition","msTransition"]),o.DomUtil.TRANSITION_END="webkitTransition"===o.DomUtil.TRANSITION||"OTransition"===o.DomUtil.TRANSITION?o.DomUtil.TRANSITION+"End":"transitionend",function(){if("onselectstart"in e)o.extend(o.DomUtil,{disableTextSelection:function(){o.DomEvent.on(t,"selectstart",o.DomEvent.preventDefault)},enableTextSelection:function(){o.DomEvent.off(t,"selectstart",o.DomEvent.preventDefault)}});else{var i=o.DomUtil.testProp(["userSelect","WebkitUserSelect","OUserSelect","MozUserSelect","msUserSelect"]);o.extend(o.DomUtil,{disableTextSelection:function(){if(i){var t=e.documentElement.style;this._userSelect=t[i],t[i]="none"}},enableTextSelection:function(){i&&(e.documentElement.style[i]=this._userSelect,delete this._userSelect)}})}o.extend(o.DomUtil,{disableImageDrag:function(){o.DomEvent.on(t,"dragstart",o.DomEvent.preventDefault)},enableImageDrag:function(){o.DomEvent.off(t,"dragstart",o.DomEvent.preventDefault)}})}(),o.LatLng=function(t,e,n){if(t=parseFloat(t),e=parseFloat(e),isNaN(t)||isNaN(e))throw new Error("Invalid LatLng object: ("+t+", "+e+")");this.lat=t,this.lng=e,n!==i&&(this.alt=parseFloat(n))},o.extend(o.LatLng,{DEG_TO_RAD:Math.PI/180,RAD_TO_DEG:180/Math.PI,MAX_MARGIN:1e-9}),o.LatLng.prototype={equals:function(t){if(!t)return!1;t=o.latLng(t);var e=Math.max(Math.abs(this.lat-t.lat),Math.abs(this.lng-t.lng));return e<=o.LatLng.MAX_MARGIN},toString:function(t){return"LatLng("+o.Util.formatNum(this.lat,t)+", "+o.Util.formatNum(this.lng,t)+")"},distanceTo:function(t){t=o.latLng(t);var e=6378137,i=o.LatLng.DEG_TO_RAD,n=(t.lat-this.lat)*i,s=(t.lng-this.lng)*i,a=this.lat*i,r=t.lat*i,h=Math.sin(n/2),l=Math.sin(s/2),u=h*h+l*l*Math.cos(a)*Math.cos(r);return 2*e*Math.atan2(Math.sqrt(u),Math.sqrt(1-u))},wrap:function(t,e){var i=this.lng;return t=t||-180,e=e||180,i=(i+e)%(e-t)+(t>i||i===e?e:t),new o.LatLng(this.lat,i)}},o.latLng=function(t,e){return t instanceof o.LatLng?t:o.Util.isArray(t)?"number"==typeof t[0]||"string"==typeof t[0]?new o.LatLng(t[0],t[1],t[2]):null:t===i||null===t?t:"object"==typeof t&&"lat"in t?new o.LatLng(t.lat,"lng"in t?t.lng:t.lon):e===i?null:new o.LatLng(t,e)},o.LatLngBounds=function(t,e){if(t)for(var i=e?[t,e]:t,n=0,o=i.length;o>n;n++)this.extend(i[n])},o.LatLngBounds.prototype={extend:function(t){if(!t)return this;var e=o.latLng(t);return t=null!==e?e:o.latLngBounds(t),t instanceof o.LatLng?this._southWest||this._northEast?(this._southWest.lat=Math.min(t.lat,this._southWest.lat),this._southWest.lng=Math.min(t.lng,this._southWest.lng),this._northEast.lat=Math.max(t.lat,this._northEast.lat),this._northEast.lng=Math.max(t.lng,this._northEast.lng)):(this._southWest=new o.LatLng(t.lat,t.lng),this._northEast=new o.LatLng(t.lat,t.lng)):t instanceof o.LatLngBounds&&(this.extend(t._southWest),this.extend(t._northEast)),this},pad:function(t){var e=this._southWest,i=this._northEast,n=Math.abs(e.lat-i.lat)*t,s=Math.abs(e.lng-i.lng)*t;return new o.LatLngBounds(new o.LatLng(e.lat-n,e.lng-s),new o.LatLng(i.lat+n,i.lng+s))},getCenter:function(){return new o.LatLng((this._southWest.lat+this._northEast.lat)/2,(this._southWest.lng+this._northEast.lng)/2)},getSouthWest:function(){return this._southWest},getNorthEast:function(){return this._northEast},getNorthWest:function(){return new o.LatLng(this.getNorth(),this.getWest())},getSouthEast:function(){return new o.LatLng(this.getSouth(),this.getEast())},getWest:function(){return this._southWest.lng},getSouth:function(){return this._southWest.lat},getEast:function(){return this._northEast.lng},getNorth:function(){return this._northEast.lat},contains:function(t){t="number"==typeof t[0]||t instanceof o.LatLng?o.latLng(t):o.latLngBounds(t);var e,i,n=this._southWest,s=this._northEast;return t instanceof o.LatLngBounds?(e=t.getSouthWest(),i=t.getNorthEast()):e=i=t,e.lat>=n.lat&&i.lat<=s.lat&&e.lng>=n.lng&&i.lng<=s.lng},intersects:function(t){t=o.latLngBounds(t);var e=this._southWest,i=this._northEast,n=t.getSouthWest(),s=t.getNorthEast(),a=s.lat>=e.lat&&n.lat<=i.lat,r=s.lng>=e.lng&&n.lng<=i.lng;return a&&r},toBBoxString:function(){return[this.getWest(),this.getSouth(),this.getEast(),this.getNorth()].join(",")},equals:function(t){return t?(t=o.latLngBounds(t),this._southWest.equals(t.getSouthWest())&&this._northEast.equals(t.getNorthEast())):!1},isValid:function(){return!(!this._southWest||!this._northEast)}},o.latLngBounds=function(t,e){return!t||t instanceof o.LatLngBounds?t:new o.LatLngBounds(t,e)},o.Projection={},o.Projection.SphericalMercator={MAX_LATITUDE:85.0511287798,project:function(t){var e=o.LatLng.DEG_TO_RAD,i=this.MAX_LATITUDE,n=Math.max(Math.min(i,t.lat),-i),s=t.lng*e,a=n*e;return a=Math.log(Math.tan(Math.PI/4+a/2)),new o.Point(s,a)},unproject:function(t){var e=o.LatLng.RAD_TO_DEG,i=t.x*e,n=(2*Math.atan(Math.exp(t.y))-Math.PI/2)*e;return new o.LatLng(n,i)}},o.Projection.LonLat={project:function(t){return new o.Point(t.lng,t.lat)},unproject:function(t){return new o.LatLng(t.y,t.x)}},o.CRS={latLngToPoint:function(t,e){var i=this.projection.project(t),n=this.scale(e);return this.transformation._transform(i,n)},pointToLatLng:function(t,e){var i=this.scale(e),n=this.transformation.untransform(t,i);return this.projection.unproject(n)},project:function(t){return this.projection.project(t)},scale:function(t){return 256*Math.pow(2,t)},getSize:function(t){var e=this.scale(t);return o.point(e,e)}},o.CRS.Simple=o.extend({},o.CRS,{projection:o.Projection.LonLat,transformation:new o.Transformation(1,0,-1,0),scale:function(t){return Math.pow(2,t)}}),o.CRS.EPSG3857=o.extend({},o.CRS,{code:"EPSG:3857",projection:o.Projection.SphericalMercator,transformation:new o.Transformation(.5/Math.PI,.5,-.5/Math.PI,.5),project:function(t){var e=this.projection.project(t),i=6378137;return e.multiplyBy(i)}}),o.CRS.EPSG900913=o.extend({},o.CRS.EPSG3857,{code:"EPSG:900913"}),o.CRS.EPSG4326=o.extend({},o.CRS,{code:"EPSG:4326",projection:o.Projection.LonLat,transformation:new o.Transformation(1/360,.5,-1/360,.5)}),o.Map=o.Class.extend({includes:o.Mixin.Events,options:{crs:o.CRS.EPSG3857,fadeAnimation:o.DomUtil.TRANSITION&&!o.Browser.android23,trackResize:!0,markerZoomAnimation:o.DomUtil.TRANSITION&&o.Browser.any3d},initialize:function(t,e){e=o.setOptions(this,e),this._initContainer(t),this._initLayout(),this._onResize=o.bind(this._onResize,this),this._initEvents(),e.maxBounds&&this.setMaxBounds(e.maxBounds),e.center&&e.zoom!==i&&this.setView(o.latLng(e.center),e.zoom,{reset:!0}),this._handlers=[],this._layers={},this._zoomBoundLayers={},this._tileLayersNum=0,this.callInitHooks(),this._addLayers(e.layers)},setView:function(t,e){return e=e===i?this.getZoom():e,this._resetView(o.latLng(t),this._limitZoom(e)),this},setZoom:function(t,e){return this._loaded?this.setView(this.getCenter(),t,{zoom:e}):(this._zoom=this._limitZoom(t),this)},zoomIn:function(t,e){return this.setZoom(this._zoom+(t||1),e)},zoomOut:function(t,e){return this.setZoom(this._zoom-(t||1),e)},setZoomAround:function(t,e,i){var n=this.getZoomScale(e),s=this.getSize().divideBy(2),a=t instanceof o.Point?t:this.latLngToContainerPoint(t),r=a.subtract(s).multiplyBy(1-1/n),h=this.containerPointToLatLng(s.add(r));return this.setView(h,e,{zoom:i})},fitBounds:function(t,e){e=e||{},t=t.getBounds?t.getBounds():o.latLngBounds(t);var i=o.point(e.paddingTopLeft||e.padding||[0,0]),n=o.point(e.paddingBottomRight||e.padding||[0,0]),s=this.getBoundsZoom(t,!1,i.add(n));s=e.maxZoom?Math.min(e.maxZoom,s):s;var a=n.subtract(i).divideBy(2),r=this.project(t.getSouthWest(),s),h=this.project(t.getNorthEast(),s),l=this.unproject(r.add(h).divideBy(2).add(a),s);return this.setView(l,s,e)},fitWorld:function(t){return this.fitBounds([[-90,-180],[90,180]],t)},panTo:function(t,e){return this.setView(t,this._zoom,{pan:e})},panBy:function(t){return this.fire("movestart"),this._rawPanBy(o.point(t)),this.fire("move"),this.fire("moveend")},setMaxBounds:function(t){return t=o.latLngBounds(t),this.options.maxBounds=t,t?(this._loaded&&this._panInsideMaxBounds(),this.on("moveend",this._panInsideMaxBounds,this)):this.off("moveend",this._panInsideMaxBounds,this)},panInsideBounds:function(t,e){var i=this.getCenter(),n=this._limitCenter(i,this._zoom,t);return i.equals(n)?this:this.panTo(n,e)},addLayer:function(t){var e=o.stamp(t);return this._layers[e]?this:(this._layers[e]=t,!t.options||isNaN(t.options.maxZoom)&&isNaN(t.options.minZoom)||(this._zoomBoundLayers[e]=t,this._updateZoomLevels()),this.options.zoomAnimation&&o.TileLayer&&t instanceof o.TileLayer&&(this._tileLayersNum++,this._tileLayersToLoad++,t.on("load",this._onTileLayerLoad,this)),this._loaded&&this._layerAdd(t),this)},removeLayer:function(t){var e=o.stamp(t);return this._layers[e]?(this._loaded&&t.onRemove(this),delete this._layers[e],this._loaded&&this.fire("layerremove",{layer:t}),this._zoomBoundLayers[e]&&(delete this._zoomBoundLayers[e],this._updateZoomLevels()),this.options.zoomAnimation&&o.TileLayer&&t instanceof o.TileLayer&&(this._tileLayersNum--,this._tileLayersToLoad--,t.off("load",this._onTileLayerLoad,this)),this):this},hasLayer:function(t){return t?o.stamp(t)in this._layers:!1},eachLayer:function(t,e){for(var i in this._layers)t.call(e,this._layers[i]);return this},invalidateSize:function(t){if(!this._loaded)return this;t=o.extend({animate:!1,pan:!0},t===!0?{animate:!0}:t);var e=this.getSize();this._sizeChanged=!0,this._initialCenter=null;var i=this.getSize(),n=e.divideBy(2).round(),s=i.divideBy(2).round(),a=n.subtract(s);return a.x||a.y?(t.animate&&t.pan?this.panBy(a):(t.pan&&this._rawPanBy(a),this.fire("move"),t.debounceMoveend?(clearTimeout(this._sizeTimer),this._sizeTimer=setTimeout(o.bind(this.fire,this,"moveend"),200)):this.fire("moveend")),this.fire("resize",{oldSize:e,newSize:i})):this},addHandler:function(t,e){if(!e)return this;var i=this[t]=new e(this);return this._handlers.push(i),this.options[t]&&i.enable(),this},remove:function(){this._loaded&&this.fire("unload"),this._initEvents("off");try{delete this._container._leaflet}catch(t){this._container._leaflet=i}return this._clearPanes(),this._clearControlPos&&this._clearControlPos(),this._clearHandlers(),this},getCenter:function(){return this._checkIfLoaded(),this._initialCenter&&!this._moved()?this._initialCenter:this.layerPointToLatLng(this._getCenterLayerPoint())},getZoom:function(){return this._zoom},getBounds:function(){var t=this.getPixelBounds(),e=this.unproject(t.getBottomLeft()),i=this.unproject(t.getTopRight());return new o.LatLngBounds(e,i)},getMinZoom:function(){return this.options.minZoom===i?this._layersMinZoom===i?0:this._layersMinZoom:this.options.minZoom},getMaxZoom:function(){return this.options.maxZoom===i?this._layersMaxZoom===i?1/0:this._layersMaxZoom:this.options.maxZoom},getBoundsZoom:function(t,e,i){t=o.latLngBounds(t);var n,s=this.getMinZoom()-(e?1:0),a=this.getMaxZoom(),r=this.getSize(),h=t.getNorthWest(),l=t.getSouthEast(),u=!0;i=o.point(i||[0,0]);do s++,n=this.project(l,s).subtract(this.project(h,s)).add(i),u=e?n.x<r.x||n.y<r.y:r.contains(n);while(u&&a>=s);return u&&e?null:e?s:s-1},getSize:function(){return(!this._size||this._sizeChanged)&&(this._size=new o.Point(this._container.clientWidth,this._container.clientHeight),this._sizeChanged=!1),this._size.clone()},getPixelBounds:function(){var t=this._getTopLeftPoint();return new o.Bounds(t,t.add(this.getSize()))},getPixelOrigin:function(){return this._checkIfLoaded(),this._initialTopLeftPoint},getPanes:function(){return this._panes},getContainer:function(){return this._container},getZoomScale:function(t){var e=this.options.crs;return e.scale(t)/e.scale(this._zoom)},getScaleZoom:function(t){return this._zoom+Math.log(t)/Math.LN2},project:function(t,e){return e=e===i?this._zoom:e,this.options.crs.latLngToPoint(o.latLng(t),e)},unproject:function(t,e){return e=e===i?this._zoom:e,this.options.crs.pointToLatLng(o.point(t),e)},layerPointToLatLng:function(t){var e=o.point(t).add(this.getPixelOrigin());return this.unproject(e)},latLngToLayerPoint:function(t){var e=this.project(o.latLng(t))._round();return e._subtract(this.getPixelOrigin())},containerPointToLayerPoint:function(t){return o.point(t).subtract(this._getMapPanePos())},layerPointToContainerPoint:function(t){return o.point(t).add(this._getMapPanePos())},containerPointToLatLng:function(t){var e=this.containerPointToLayerPoint(o.point(t));return this.layerPointToLatLng(e)},latLngToContainerPoint:function(t){return this.layerPointToContainerPoint(this.latLngToLayerPoint(o.latLng(t)))},mouseEventToContainerPoint:function(t){return o.DomEvent.getMousePosition(t,this._container)},mouseEventToLayerPoint:function(t){return this.containerPointToLayerPoint(this.mouseEventToContainerPoint(t))},mouseEventToLatLng:function(t){return this.layerPointToLatLng(this.mouseEventToLayerPoint(t))},_initContainer:function(t){var e=this._container=o.DomUtil.get(t);if(!e)throw new Error("Map container not found.");if(e._leaflet)throw new Error("Map container is already initialized.");e._leaflet=!0},_initLayout:function(){var t=this._container;o.DomUtil.addClass(t,"leaflet-container"+(o.Browser.touch?" leaflet-touch":"")+(o.Browser.retina?" leaflet-retina":"")+(o.Browser.ielt9?" leaflet-oldie":"")+(this.options.fadeAnimation?" leaflet-fade-anim":""));var e=o.DomUtil.getStyle(t,"position");"absolute"!==e&&"relative"!==e&&"fixed"!==e&&(t.style.position="relative"),this._initPanes(),this._initControlPos&&this._initControlPos()},_initPanes:function(){var t=this._panes={};this._mapPane=t.mapPane=this._createPane("leaflet-map-pane",this._container),this._tilePane=t.tilePane=this._createPane("leaflet-tile-pane",this._mapPane),t.objectsPane=this._createPane("leaflet-objects-pane",this._mapPane),t.shadowPane=this._createPane("leaflet-shadow-pane"),t.overlayPane=this._createPane("leaflet-overlay-pane"),t.markerPane=this._createPane("leaflet-marker-pane"),t.popupPane=this._createPane("leaflet-popup-pane");var e=" leaflet-zoom-hide";this.options.markerZoomAnimation||(o.DomUtil.addClass(t.markerPane,e),o.DomUtil.addClass(t.shadowPane,e),o.DomUtil.addClass(t.popupPane,e))},_createPane:function(t,e){return o.DomUtil.create("div",t,e||this._panes.objectsPane)},_clearPanes:function(){this._container.removeChild(this._mapPane)},_addLayers:function(t){t=t?o.Util.isArray(t)?t:[t]:[];for(var e=0,i=t.length;i>e;e++)this.addLayer(t[e])},_resetView:function(t,e,i,n){var s=this._zoom!==e;n||(this.fire("movestart"),s&&this.fire("zoomstart")),this._zoom=e,this._initialCenter=t,this._initialTopLeftPoint=this._getNewTopLeftPoint(t),i?this._initialTopLeftPoint._add(this._getMapPanePos()):o.DomUtil.setPosition(this._mapPane,new o.Point(0,0)),this._tileLayersToLoad=this._tileLayersNum;var a=!this._loaded;this._loaded=!0,this.fire("viewreset",{hard:!i}),a&&(this.fire("load"),this.eachLayer(this._layerAdd,this)),this.fire("move"),(s||n)&&this.fire("zoomend"),this.fire("moveend",{hard:!i})},_rawPanBy:function(t){o.DomUtil.setPosition(this._mapPane,this._getMapPanePos().subtract(t))},_getZoomSpan:function(){return this.getMaxZoom()-this.getMinZoom()},_updateZoomLevels:function(){var t,e=1/0,n=-(1/0),o=this._getZoomSpan();for(t in this._zoomBoundLayers){var s=this._zoomBoundLayers[t];isNaN(s.options.minZoom)||(e=Math.min(e,s.options.minZoom)),isNaN(s.options.maxZoom)||(n=Math.max(n,s.options.maxZoom))}t===i?this._layersMaxZoom=this._layersMinZoom=i:(this._layersMaxZoom=n,this._layersMinZoom=e),o!==this._getZoomSpan()&&this.fire("zoomlevelschange")},_panInsideMaxBounds:function(){this.panInsideBounds(this.options.maxBounds)},_checkIfLoaded:function(){if(!this._loaded)throw new Error("Set map center and zoom first.")},_initEvents:function(e){if(o.DomEvent){e=e||"on",o.DomEvent[e](this._container,"click",this._onMouseClick,this);var i,n,s=["dblclick","mousedown","mouseup","mouseenter","mouseleave","mousemove","contextmenu"];for(i=0,n=s.length;n>i;i++)o.DomEvent[e](this._container,s[i],this._fireMouseEvent,this);this.options.trackResize&&o.DomEvent[e](t,"resize",this._onResize,this)}},_onResize:function(){o.Util.cancelAnimFrame(this._resizeRequest),this._resizeRequest=o.Util.requestAnimFrame(function(){this.invalidateSize({debounceMoveend:!0})},this,!1,this._container)},_onMouseClick:function(t){!this._loaded||!t._simulated&&(this.dragging&&this.dragging.moved()||this.boxZoom&&this.boxZoom.moved())||o.DomEvent._skipped(t)||(this.fire("preclick"),this._fireMouseEvent(t))},_fireMouseEvent:function(t){if(this._loaded&&!o.DomEvent._skipped(t)){var e=t.type;if(e="mouseenter"===e?"mouseover":"mouseleave"===e?"mouseout":e,this.hasEventListeners(e)){"contextmenu"===e&&o.DomEvent.preventDefault(t);var i=this.mouseEventToContainerPoint(t),n=this.containerPointToLayerPoint(i),s=this.layerPointToLatLng(n);this.fire(e,{latlng:s,layerPoint:n,containerPoint:i,originalEvent:t})}}},_onTileLayerLoad:function(){this._tileLayersToLoad--,this._tileLayersNum&&!this._tileLayersToLoad&&this.fire("tilelayersload")},_clearHandlers:function(){for(var t=0,e=this._handlers.length;e>t;t++)this._handlers[t].disable()},whenReady:function(t,e){return this._loaded?t.call(e||this,this):this.on("load",t,e),this},_layerAdd:function(t){t.onAdd(this),this.fire("layeradd",{layer:t})},_getMapPanePos:function(){return o.DomUtil.getPosition(this._mapPane)},_moved:function(){var t=this._getMapPanePos();return t&&!t.equals([0,0])},_getTopLeftPoint:function(){return this.getPixelOrigin().subtract(this._getMapPanePos())},_getNewTopLeftPoint:function(t,e){var i=this.getSize()._divideBy(2);return this.project(t,e)._subtract(i)._round()},_latLngToNewLayerPoint:function(t,e,i){var n=this._getNewTopLeftPoint(i,e).add(this._getMapPanePos());return this.project(t,e)._subtract(n)},_getCenterLayerPoint:function(){return this.containerPointToLayerPoint(this.getSize()._divideBy(2))},_getCenterOffset:function(t){return this.latLngToLayerPoint(t).subtract(this._getCenterLayerPoint())},_limitCenter:function(t,e,i){if(!i)return t;var n=this.project(t,e),s=this.getSize().divideBy(2),a=new o.Bounds(n.subtract(s),n.add(s)),r=this._getBoundsOffset(a,i,e);return this.unproject(n.add(r),e)},_limitOffset:function(t,e){if(!e)return t;var i=this.getPixelBounds(),n=new o.Bounds(i.min.add(t),i.max.add(t));return t.add(this._getBoundsOffset(n,e))},_getBoundsOffset:function(t,e,i){var n=this.project(e.getNorthWest(),i).subtract(t.min),s=this.project(e.getSouthEast(),i).subtract(t.max),a=this._rebound(n.x,-s.x),r=this._rebound(n.y,-s.y);return new o.Point(a,r)},_rebound:function(t,e){return t+e>0?Math.round(t-e)/2:Math.max(0,Math.ceil(t))-Math.max(0,Math.floor(e))},_limitZoom:function(t){var e=this.getMinZoom(),i=this.getMaxZoom();return Math.max(e,Math.min(i,t))}}),o.map=function(t,e){return new o.Map(t,e)},o.Projection.Mercator={MAX_LATITUDE:85.0840591556,R_MINOR:6356752.314245179,R_MAJOR:6378137,project:function(t){var e=o.LatLng.DEG_TO_RAD,i=this.MAX_LATITUDE,n=Math.max(Math.min(i,t.lat),-i),s=this.R_MAJOR,a=this.R_MINOR,r=t.lng*e*s,h=n*e,l=a/s,u=Math.sqrt(1-l*l),c=u*Math.sin(h);c=Math.pow((1-c)/(1+c),.5*u);var d=Math.tan(.5*(.5*Math.PI-h))/c;return h=-s*Math.log(d),new o.Point(r,h)},unproject:function(t){for(var e,i=o.LatLng.RAD_TO_DEG,n=this.R_MAJOR,s=this.R_MINOR,a=t.x*i/n,r=s/n,h=Math.sqrt(1-r*r),l=Math.exp(-t.y/n),u=Math.PI/2-2*Math.atan(l),c=15,d=1e-7,p=c,_=.1;Math.abs(_)>d&&--p>0;)e=h*Math.sin(u),_=Math.PI/2-2*Math.atan(l*Math.pow((1-e)/(1+e),.5*h))-u,u+=_;return new o.LatLng(u*i,a)}},o.CRS.EPSG3395=o.extend({},o.CRS,{code:"EPSG:3395",projection:o.Projection.Mercator,
transformation:function(){var t=o.Projection.Mercator,e=t.R_MAJOR,i=.5/(Math.PI*e);return new o.Transformation(i,.5,-i,.5)}()}),o.TileLayer=o.Class.extend({includes:o.Mixin.Events,options:{minZoom:0,maxZoom:18,tileSize:256,subdomains:"abc",errorTileUrl:"",attribution:"",zoomOffset:0,opacity:1,unloadInvisibleTiles:o.Browser.mobile,updateWhenIdle:o.Browser.mobile},initialize:function(t,e){e=o.setOptions(this,e),e.detectRetina&&o.Browser.retina&&e.maxZoom>0&&(e.tileSize=Math.floor(e.tileSize/2),e.zoomOffset++,e.minZoom>0&&e.minZoom--,this.options.maxZoom--),e.bounds&&(e.bounds=o.latLngBounds(e.bounds)),this._url=t;var i=this.options.subdomains;"string"==typeof i&&(this.options.subdomains=i.split(""))},onAdd:function(t){this._map=t,this._animated=t._zoomAnimated,this._initContainer(),t.on({viewreset:this._reset,moveend:this._update},this),this._animated&&t.on({zoomanim:this._animateZoom,zoomend:this._endZoomAnim},this),this.options.updateWhenIdle||(this._limitedUpdate=o.Util.limitExecByInterval(this._update,150,this),t.on("move",this._limitedUpdate,this)),this._reset(),this._update()},addTo:function(t){return t.addLayer(this),this},onRemove:function(t){this._container.parentNode.removeChild(this._container),t.off({viewreset:this._reset,moveend:this._update},this),this._animated&&t.off({zoomanim:this._animateZoom,zoomend:this._endZoomAnim},this),this.options.updateWhenIdle||t.off("move",this._limitedUpdate,this),this._container=null,this._map=null},bringToFront:function(){var t=this._map._panes.tilePane;return this._container&&(t.appendChild(this._container),this._setAutoZIndex(t,Math.max)),this},bringToBack:function(){var t=this._map._panes.tilePane;return this._container&&(t.insertBefore(this._container,t.firstChild),this._setAutoZIndex(t,Math.min)),this},getAttribution:function(){return this.options.attribution},getContainer:function(){return this._container},setOpacity:function(t){return this.options.opacity=t,this._map&&this._updateOpacity(),this},setZIndex:function(t){return this.options.zIndex=t,this._updateZIndex(),this},setUrl:function(t,e){return this._url=t,e||this.redraw(),this},redraw:function(){return this._map&&(this._reset({hard:!0}),this._update()),this},_updateZIndex:function(){this._container&&this.options.zIndex!==i&&(this._container.style.zIndex=this.options.zIndex)},_setAutoZIndex:function(t,e){var i,n,o,s=t.children,a=-e(1/0,-(1/0));for(n=0,o=s.length;o>n;n++)s[n]!==this._container&&(i=parseInt(s[n].style.zIndex,10),isNaN(i)||(a=e(a,i)));this.options.zIndex=this._container.style.zIndex=(isFinite(a)?a:0)+e(1,-1)},_updateOpacity:function(){var t,e=this._tiles;if(o.Browser.ielt9)for(t in e)o.DomUtil.setOpacity(e[t],this.options.opacity);else o.DomUtil.setOpacity(this._container,this.options.opacity)},_initContainer:function(){var t=this._map._panes.tilePane;if(!this._container){if(this._container=o.DomUtil.create("div","leaflet-layer"),this._updateZIndex(),this._animated){var e="leaflet-tile-container";this._bgBuffer=o.DomUtil.create("div",e,this._container),this._tileContainer=o.DomUtil.create("div",e,this._container)}else this._tileContainer=this._container;t.appendChild(this._container),this.options.opacity<1&&this._updateOpacity()}},_reset:function(t){for(var e in this._tiles)this.fire("tileunload",{tile:this._tiles[e]});this._tiles={},this._tilesToLoad=0,this.options.reuseTiles&&(this._unusedTiles=[]),this._tileContainer.innerHTML="",this._animated&&t&&t.hard&&this._clearBgBuffer(),this._initContainer()},_getTileSize:function(){var t=this._map,e=t.getZoom()+this.options.zoomOffset,i=this.options.maxNativeZoom,n=this.options.tileSize;return i&&e>i&&(n=Math.round(t.getZoomScale(e)/t.getZoomScale(i)*n)),n},_update:function(){if(this._map){var t=this._map,e=t.getPixelBounds(),i=t.getZoom(),n=this._getTileSize();if(!(i>this.options.maxZoom||i<this.options.minZoom)){var s=o.bounds(e.min.divideBy(n)._floor(),e.max.divideBy(n)._floor());this._addTilesFromCenterOut(s),(this.options.unloadInvisibleTiles||this.options.reuseTiles)&&this._removeOtherTiles(s)}}},_addTilesFromCenterOut:function(t){var i,n,s,a=[],r=t.getCenter();for(i=t.min.y;i<=t.max.y;i++)for(n=t.min.x;n<=t.max.x;n++)s=new o.Point(n,i),this._tileShouldBeLoaded(s)&&a.push(s);var h=a.length;if(0!==h){a.sort(function(t,e){return t.distanceTo(r)-e.distanceTo(r)});var l=e.createDocumentFragment();for(this._tilesToLoad||this.fire("loading"),this._tilesToLoad+=h,n=0;h>n;n++)this._addTile(a[n],l);this._tileContainer.appendChild(l)}},_tileShouldBeLoaded:function(t){if(t.x+":"+t.y in this._tiles)return!1;var e=this.options;if(!e.continuousWorld){var i=this._getWrapTileNum();if(e.noWrap&&(t.x<0||t.x>=i.x)||t.y<0||t.y>=i.y)return!1}if(e.bounds){var n=this._getTileSize(),o=t.multiplyBy(n),s=o.add([n,n]),a=this._map.unproject(o),r=this._map.unproject(s);if(e.continuousWorld||e.noWrap||(a=a.wrap(),r=r.wrap()),!e.bounds.intersects([a,r]))return!1}return!0},_removeOtherTiles:function(t){var e,i,n,o;for(o in this._tiles)e=o.split(":"),i=parseInt(e[0],10),n=parseInt(e[1],10),(i<t.min.x||i>t.max.x||n<t.min.y||n>t.max.y)&&this._removeTile(o)},_removeTile:function(t){var e=this._tiles[t];this.fire("tileunload",{tile:e,url:e.src}),this.options.reuseTiles?(o.DomUtil.removeClass(e,"leaflet-tile-loaded"),this._unusedTiles.push(e)):e.parentNode===this._tileContainer&&this._tileContainer.removeChild(e),o.Browser.android||(e.onload=null,e.src=o.Util.emptyImageUrl),delete this._tiles[t]},_addTile:function(t,e){var i=this._getTilePos(t),n=this._getTile();o.DomUtil.setPosition(n,i,o.Browser.chrome),this._tiles[t.x+":"+t.y]=n,this._loadTile(n,t),n.parentNode!==this._tileContainer&&e.appendChild(n)},_getZoomForUrl:function(){var t=this.options,e=this._map.getZoom();return t.zoomReverse&&(e=t.maxZoom-e),e+=t.zoomOffset,t.maxNativeZoom?Math.min(e,t.maxNativeZoom):e},_getTilePos:function(t){var e=this._map.getPixelOrigin(),i=this._getTileSize();return t.multiplyBy(i).subtract(e)},getTileUrl:function(t){return o.Util.template(this._url,o.extend({s:this._getSubdomain(t),z:t.z,x:t.x,y:t.y},this.options))},_getWrapTileNum:function(){var t=this._map.options.crs,e=t.getSize(this._map.getZoom());return e.divideBy(this._getTileSize())._floor()},_adjustTilePoint:function(t){var e=this._getWrapTileNum();this.options.continuousWorld||this.options.noWrap||(t.x=(t.x%e.x+e.x)%e.x),this.options.tms&&(t.y=e.y-t.y-1),t.z=this._getZoomForUrl()},_getSubdomain:function(t){var e=Math.abs(t.x+t.y)%this.options.subdomains.length;return this.options.subdomains[e]},_getTile:function(){if(this.options.reuseTiles&&this._unusedTiles.length>0){var t=this._unusedTiles.pop();return this._resetTile(t),t}return this._createTile()},_resetTile:function(){},_createTile:function(){var t=o.DomUtil.create("img","leaflet-tile");return t.style.width=t.style.height=this._getTileSize()+"px",t.galleryimg="no",t.onselectstart=t.onmousemove=o.Util.falseFn,o.Browser.ielt9&&this.options.opacity!==i&&o.DomUtil.setOpacity(t,this.options.opacity),o.Browser.mobileWebkit3d&&(t.style.WebkitBackfaceVisibility="hidden"),t},_loadTile:function(t,e){t._layer=this,t.onload=this._tileOnLoad,t.onerror=this._tileOnError,this._adjustTilePoint(e),t.src=this.getTileUrl(e),this.fire("tileloadstart",{tile:t,url:t.src})},_tileLoaded:function(){this._tilesToLoad--,this._animated&&o.DomUtil.addClass(this._tileContainer,"leaflet-zoom-animated"),this._tilesToLoad||(this.fire("load"),this._animated&&(clearTimeout(this._clearBgBufferTimer),this._clearBgBufferTimer=setTimeout(o.bind(this._clearBgBuffer,this),500)))},_tileOnLoad:function(){var t=this._layer;this.src!==o.Util.emptyImageUrl&&(o.DomUtil.addClass(this,"leaflet-tile-loaded"),t.fire("tileload",{tile:this,url:this.src})),t._tileLoaded()},_tileOnError:function(){var t=this._layer;t.fire("tileerror",{tile:this,url:this.src});var e=t.options.errorTileUrl;e&&(this.src=e),t._tileLoaded()}}),o.tileLayer=function(t,e){return new o.TileLayer(t,e)},o.TileLayer.WMS=o.TileLayer.extend({defaultWmsParams:{service:"WMS",request:"GetMap",version:"1.1.1",layers:"",styles:"",format:"image/jpeg",transparent:!1},initialize:function(t,e){this._url=t;var i=o.extend({},this.defaultWmsParams),n=e.tileSize||this.options.tileSize;e.detectRetina&&o.Browser.retina?i.width=i.height=2*n:i.width=i.height=n;for(var s in e)this.options.hasOwnProperty(s)||"crs"===s||(i[s]=e[s]);this.wmsParams=i,o.setOptions(this,e)},onAdd:function(t){this._crs=this.options.crs||t.options.crs,this._wmsVersion=parseFloat(this.wmsParams.version);var e=this._wmsVersion>=1.3?"crs":"srs";this.wmsParams[e]=this._crs.code,o.TileLayer.prototype.onAdd.call(this,t)},getTileUrl:function(t){var e=this._map,i=this.options.tileSize,n=t.multiplyBy(i),s=n.add([i,i]),a=this._crs.project(e.unproject(n,t.z)),r=this._crs.project(e.unproject(s,t.z)),h=this._wmsVersion>=1.3&&this._crs===o.CRS.EPSG4326?[r.y,a.x,a.y,r.x].join(","):[a.x,r.y,r.x,a.y].join(","),l=o.Util.template(this._url,{s:this._getSubdomain(t)});return l+o.Util.getParamString(this.wmsParams,l,!0)+"&BBOX="+h},setParams:function(t,e){return o.extend(this.wmsParams,t),e||this.redraw(),this}}),o.tileLayer.wms=function(t,e){return new o.TileLayer.WMS(t,e)},o.TileLayer.Canvas=o.TileLayer.extend({options:{async:!1},initialize:function(t){o.setOptions(this,t)},redraw:function(){this._map&&(this._reset({hard:!0}),this._update());for(var t in this._tiles)this._redrawTile(this._tiles[t]);return this},_redrawTile:function(t){this.drawTile(t,t._tilePoint,this._map._zoom)},_createTile:function(){var t=o.DomUtil.create("canvas","leaflet-tile");return t.width=t.height=this.options.tileSize,t.onselectstart=t.onmousemove=o.Util.falseFn,t},_loadTile:function(t,e){t._layer=this,t._tilePoint=e,this._redrawTile(t),this.options.async||this.tileDrawn(t)},drawTile:function(){},tileDrawn:function(t){this._tileOnLoad.call(t)}}),o.tileLayer.canvas=function(t){return new o.TileLayer.Canvas(t)},o.ImageOverlay=o.Class.extend({includes:o.Mixin.Events,options:{opacity:1},initialize:function(t,e,i){this._url=t,this._bounds=o.latLngBounds(e),o.setOptions(this,i)},onAdd:function(t){this._map=t,this._image||this._initImage(),t._panes.overlayPane.appendChild(this._image),t.on("viewreset",this._reset,this),t.options.zoomAnimation&&o.Browser.any3d&&t.on("zoomanim",this._animateZoom,this),this._reset()},onRemove:function(t){t.getPanes().overlayPane.removeChild(this._image),t.off("viewreset",this._reset,this),t.options.zoomAnimation&&t.off("zoomanim",this._animateZoom,this)},addTo:function(t){return t.addLayer(this),this},setOpacity:function(t){return this.options.opacity=t,this._updateOpacity(),this},bringToFront:function(){return this._image&&this._map._panes.overlayPane.appendChild(this._image),this},bringToBack:function(){var t=this._map._panes.overlayPane;return this._image&&t.insertBefore(this._image,t.firstChild),this},setUrl:function(t){this._url=t,this._image.src=this._url},getAttribution:function(){return this.options.attribution},_initImage:function(){this._image=o.DomUtil.create("img","leaflet-image-layer"),this._map.options.zoomAnimation&&o.Browser.any3d?o.DomUtil.addClass(this._image,"leaflet-zoom-animated"):o.DomUtil.addClass(this._image,"leaflet-zoom-hide"),this._updateOpacity(),o.extend(this._image,{galleryimg:"no",onselectstart:o.Util.falseFn,onmousemove:o.Util.falseFn,onload:o.bind(this._onImageLoad,this),src:this._url})},_animateZoom:function(t){var e=this._map,i=this._image,n=e.getZoomScale(t.zoom),s=this._bounds.getNorthWest(),a=this._bounds.getSouthEast(),r=e._latLngToNewLayerPoint(s,t.zoom,t.center),h=e._latLngToNewLayerPoint(a,t.zoom,t.center)._subtract(r),l=r._add(h._multiplyBy(.5*(1-1/n)));i.style[o.DomUtil.TRANSFORM]=o.DomUtil.getTranslateString(l)+" scale("+n+") "},_reset:function(){var t=this._image,e=this._map.latLngToLayerPoint(this._bounds.getNorthWest()),i=this._map.latLngToLayerPoint(this._bounds.getSouthEast())._subtract(e);o.DomUtil.setPosition(t,e),t.style.width=i.x+"px",t.style.height=i.y+"px"},_onImageLoad:function(){this.fire("load")},_updateOpacity:function(){o.DomUtil.setOpacity(this._image,this.options.opacity)}}),o.imageOverlay=function(t,e,i){return new o.ImageOverlay(t,e,i)},o.Icon=o.Class.extend({options:{className:""},initialize:function(t){o.setOptions(this,t)},createIcon:function(t){return this._createIcon("icon",t)},createShadow:function(t){return this._createIcon("shadow",t)},_createIcon:function(t,e){var i=this._getIconUrl(t);if(!i){if("icon"===t)throw new Error("iconUrl not set in Icon options (see the docs).");return null}var n;return n=e&&"IMG"===e.tagName?this._createImg(i,e):this._createImg(i),this._setIconStyles(n,t),n},_setIconStyles:function(t,e){var i,n=this.options,s=o.point(n[e+"Size"]);i="shadow"===e?o.point(n.shadowAnchor||n.iconAnchor):o.point(n.iconAnchor),!i&&s&&(i=s.divideBy(2,!0)),t.className="leaflet-marker-"+e+" "+n.className,i&&(t.style.marginLeft=-i.x+"px",t.style.marginTop=-i.y+"px"),s&&(t.style.width=s.x+"px",t.style.height=s.y+"px")},_createImg:function(t,i){return i=i||e.createElement("img"),i.src=t,i},_getIconUrl:function(t){return o.Browser.retina&&this.options[t+"RetinaUrl"]?this.options[t+"RetinaUrl"]:this.options[t+"Url"]}}),o.icon=function(t){return new o.Icon(t)},o.Icon.Default=o.Icon.extend({options:{iconSize:[25,41],iconAnchor:[12,41],popupAnchor:[1,-34],shadowSize:[41,41]},_getIconUrl:function(t){var e=t+"Url";if(this.options[e])return this.options[e];o.Browser.retina&&"icon"===t&&(t+="-2x");var i=o.Icon.Default.imagePath;if(!i)throw new Error("Couldn't autodetect L.Icon.Default.imagePath, set it manually.");return i+"/marker-"+t+".png"}}),o.Icon.Default.imagePath=function(){var t,i,n,o,s,a=e.getElementsByTagName("script"),r=/[\/^]leaflet[\-\._]?([\w\-\._]*)\.js\??/;for(t=0,i=a.length;i>t;t++)if(n=a[t].src,o=n.match(r))return s=n.split(r)[0],(s?s+"/":"")+"images"}(),o.Marker=o.Class.extend({includes:o.Mixin.Events,options:{icon:new o.Icon.Default,title:"",alt:"",clickable:!0,draggable:!1,keyboard:!0,zIndexOffset:0,opacity:1,riseOnHover:!1,riseOffset:250},initialize:function(t,e){o.setOptions(this,e),this._latlng=o.latLng(t)},onAdd:function(t){this._map=t,t.on("viewreset",this.update,this),this._initIcon(),this.update(),this.fire("add"),t.options.zoomAnimation&&t.options.markerZoomAnimation&&t.on("zoomanim",this._animateZoom,this)},addTo:function(t){return t.addLayer(this),this},onRemove:function(t){this.dragging&&this.dragging.disable(),this._removeIcon(),this._removeShadow(),this.fire("remove"),t.off({viewreset:this.update,zoomanim:this._animateZoom},this),this._map=null},getLatLng:function(){return this._latlng},setLatLng:function(t){return this._latlng=o.latLng(t),this.update(),this.fire("move",{latlng:this._latlng})},setZIndexOffset:function(t){return this.options.zIndexOffset=t,this.update(),this},setIcon:function(t){return this.options.icon=t,this._map&&(this._initIcon(),this.update()),this._popup&&this.bindPopup(this._popup),this},update:function(){return this._icon&&this._setPos(this._map.latLngToLayerPoint(this._latlng).round()),this},_initIcon:function(){var t=this.options,e=this._map,i=e.options.zoomAnimation&&e.options.markerZoomAnimation,n=i?"leaflet-zoom-animated":"leaflet-zoom-hide",s=t.icon.createIcon(this._icon),a=!1;s!==this._icon&&(this._icon&&this._removeIcon(),a=!0,t.title&&(s.title=t.title),t.alt&&(s.alt=t.alt)),o.DomUtil.addClass(s,n),t.keyboard&&(s.tabIndex="0"),this._icon=s,this._initInteraction(),t.riseOnHover&&o.DomEvent.on(s,"mouseover",this._bringToFront,this).on(s,"mouseout",this._resetZIndex,this);var r=t.icon.createShadow(this._shadow),h=!1;r!==this._shadow&&(this._removeShadow(),h=!0),r&&o.DomUtil.addClass(r,n),this._shadow=r,t.opacity<1&&this._updateOpacity();var l=this._map._panes;a&&l.markerPane.appendChild(this._icon),r&&h&&l.shadowPane.appendChild(this._shadow)},_removeIcon:function(){this.options.riseOnHover&&o.DomEvent.off(this._icon,"mouseover",this._bringToFront).off(this._icon,"mouseout",this._resetZIndex),this._map._panes.markerPane.removeChild(this._icon),this._icon=null},_removeShadow:function(){this._shadow&&this._map._panes.shadowPane.removeChild(this._shadow),this._shadow=null},_setPos:function(t){o.DomUtil.setPosition(this._icon,t),this._shadow&&o.DomUtil.setPosition(this._shadow,t),this._zIndex=t.y+this.options.zIndexOffset,this._resetZIndex()},_updateZIndex:function(t){this._icon.style.zIndex=this._zIndex+t},_animateZoom:function(t){var e=this._map._latLngToNewLayerPoint(this._latlng,t.zoom,t.center).round();this._setPos(e)},_initInteraction:function(){if(this.options.clickable){var t=this._icon,e=["dblclick","mousedown","mouseover","mouseout","contextmenu"];o.DomUtil.addClass(t,"leaflet-clickable"),o.DomEvent.on(t,"click",this._onMouseClick,this),o.DomEvent.on(t,"keypress",this._onKeyPress,this);for(var i=0;i<e.length;i++)o.DomEvent.on(t,e[i],this._fireMouseEvent,this);o.Handler.MarkerDrag&&(this.dragging=new o.Handler.MarkerDrag(this),this.options.draggable&&this.dragging.enable())}},_onMouseClick:function(t){var e=this.dragging&&this.dragging.moved();(this.hasEventListeners(t.type)||e)&&o.DomEvent.stopPropagation(t),e||(this.dragging&&this.dragging._enabled||!this._map.dragging||!this._map.dragging.moved())&&this.fire(t.type,{originalEvent:t,latlng:this._latlng})},_onKeyPress:function(t){13===t.keyCode&&this.fire("click",{originalEvent:t,latlng:this._latlng})},_fireMouseEvent:function(t){this.fire(t.type,{originalEvent:t,latlng:this._latlng}),"contextmenu"===t.type&&this.hasEventListeners(t.type)&&o.DomEvent.preventDefault(t),"mousedown"!==t.type?o.DomEvent.stopPropagation(t):o.DomEvent.preventDefault(t)},setOpacity:function(t){return this.options.opacity=t,this._map&&this._updateOpacity(),this},_updateOpacity:function(){o.DomUtil.setOpacity(this._icon,this.options.opacity),this._shadow&&o.DomUtil.setOpacity(this._shadow,this.options.opacity)},_bringToFront:function(){this._updateZIndex(this.options.riseOffset)},_resetZIndex:function(){this._updateZIndex(0)}}),o.marker=function(t,e){return new o.Marker(t,e)},o.DivIcon=o.Icon.extend({options:{iconSize:[12,12],className:"leaflet-div-icon",html:!1},createIcon:function(t){var i=t&&"DIV"===t.tagName?t:e.createElement("div"),n=this.options;return n.html!==!1?i.innerHTML=n.html:i.innerHTML="",n.bgPos&&(i.style.backgroundPosition=-n.bgPos.x+"px "+-n.bgPos.y+"px"),this._setIconStyles(i,"icon"),i},createShadow:function(){return null}}),o.divIcon=function(t){return new o.DivIcon(t)},o.Map.mergeOptions({closePopupOnClick:!0}),o.Popup=o.Class.extend({includes:o.Mixin.Events,options:{minWidth:50,maxWidth:300,autoPan:!0,closeButton:!0,offset:[0,7],autoPanPadding:[5,5],keepInView:!1,className:"",zoomAnimation:!0},initialize:function(t,e){o.setOptions(this,t),this._source=e,this._animated=o.Browser.any3d&&this.options.zoomAnimation,this._isOpen=!1},onAdd:function(t){this._map=t,this._container||this._initLayout();var e=t.options.fadeAnimation;e&&o.DomUtil.setOpacity(this._container,0),t._panes.popupPane.appendChild(this._container),t.on(this._getEvents(),this),this.update(),e&&o.DomUtil.setOpacity(this._container,1),this.fire("open"),t.fire("popupopen",{popup:this}),this._source&&this._source.fire("popupopen",{popup:this})},addTo:function(t){return t.addLayer(this),this},openOn:function(t){return t.openPopup(this),this},onRemove:function(t){t._panes.popupPane.removeChild(this._container),o.Util.falseFn(this._container.offsetWidth),t.off(this._getEvents(),this),t.options.fadeAnimation&&o.DomUtil.setOpacity(this._container,0),this._map=null,this.fire("close"),t.fire("popupclose",{popup:this}),this._source&&this._source.fire("popupclose",{popup:this})},getLatLng:function(){return this._latlng},setLatLng:function(t){return this._latlng=o.latLng(t),this._map&&(this._updatePosition(),this._adjustPan()),this},getContent:function(){return this._content},setContent:function(t){return this._content=t,this.update(),this},update:function(){this._map&&(this._container.style.visibility="hidden",this._updateContent(),this._updateLayout(),this._updatePosition(),this._container.style.visibility="",this._adjustPan())},_getEvents:function(){var t={viewreset:this._updatePosition};return this._animated&&(t.zoomanim=this._zoomAnimation),("closeOnClick"in this.options?this.options.closeOnClick:this._map.options.closePopupOnClick)&&(t.preclick=this._close),this.options.keepInView&&(t.moveend=this._adjustPan),t},_close:function(){this._map&&this._map.closePopup(this)},_initLayout:function(){var t,e="leaflet-popup",i=e+" "+this.options.className+" leaflet-zoom-"+(this._animated?"animated":"hide"),n=this._container=o.DomUtil.create("div",i);this.options.closeButton&&(t=this._closeButton=o.DomUtil.create("a",e+"-close-button",n),t.href="#close",t.innerHTML="&#215;",o.DomEvent.disableClickPropagation(t),o.DomEvent.on(t,"click",this._onCloseButtonClick,this));var s=this._wrapper=o.DomUtil.create("div",e+"-content-wrapper",n);o.DomEvent.disableClickPropagation(s),this._contentNode=o.DomUtil.create("div",e+"-content",s),o.DomEvent.disableScrollPropagation(this._contentNode),o.DomEvent.on(s,"contextmenu",o.DomEvent.stopPropagation),this._tipContainer=o.DomUtil.create("div",e+"-tip-container",n),this._tip=o.DomUtil.create("div",e+"-tip",this._tipContainer)},_updateContent:function(){if(this._content){if("string"==typeof this._content)this._contentNode.innerHTML=this._content;else{for(;this._contentNode.hasChildNodes();)this._contentNode.removeChild(this._contentNode.firstChild);this._contentNode.appendChild(this._content)}this.fire("contentupdate")}},_updateLayout:function(){var t=this._contentNode,e=t.style;e.width="",e.whiteSpace="nowrap";var i=t.offsetWidth;i=Math.min(i,this.options.maxWidth),i=Math.max(i,this.options.minWidth),e.width=i+1+"px",e.whiteSpace="",e.height="";var n=t.offsetHeight,s=this.options.maxHeight,a="leaflet-popup-scrolled";s&&n>s?(e.height=s+"px",o.DomUtil.addClass(t,a)):o.DomUtil.removeClass(t,a),this._containerWidth=this._container.offsetWidth},_updatePosition:function(){if(this._map){var t=this._map.latLngToLayerPoint(this._latlng),e=this._animated,i=o.point(this.options.offset);e&&o.DomUtil.setPosition(this._container,t),this._containerBottom=-i.y-(e?0:t.y),this._containerLeft=-Math.round(this._containerWidth/2)+i.x+(e?0:t.x),this._container.style.bottom=this._containerBottom+"px",this._container.style.left=this._containerLeft+"px"}},_zoomAnimation:function(t){var e=this._map._latLngToNewLayerPoint(this._latlng,t.zoom,t.center);o.DomUtil.setPosition(this._container,e)},_adjustPan:function(){if(this.options.autoPan){var t=this._map,e=this._container.offsetHeight,i=this._containerWidth,n=new o.Point(this._containerLeft,-e-this._containerBottom);this._animated&&n._add(o.DomUtil.getPosition(this._container));var s=t.layerPointToContainerPoint(n),a=o.point(this.options.autoPanPadding),r=o.point(this.options.autoPanPaddingTopLeft||a),h=o.point(this.options.autoPanPaddingBottomRight||a),l=t.getSize(),u=0,c=0;s.x+i+h.x>l.x&&(u=s.x+i-l.x+h.x),s.x-u-r.x<0&&(u=s.x-r.x),s.y+e+h.y>l.y&&(c=s.y+e-l.y+h.y),s.y-c-r.y<0&&(c=s.y-r.y),(u||c)&&t.fire("autopanstart").panBy([u,c])}},_onCloseButtonClick:function(t){this._close(),o.DomEvent.stop(t)}}),o.popup=function(t,e){return new o.Popup(t,e)},o.Map.include({openPopup:function(t,e,i){if(this.closePopup(),!(t instanceof o.Popup)){var n=t;t=new o.Popup(i).setLatLng(e).setContent(n)}return t._isOpen=!0,this._popup=t,this.addLayer(t)},closePopup:function(t){return t&&t!==this._popup||(t=this._popup,this._popup=null),t&&(this.removeLayer(t),t._isOpen=!1),this}}),o.Marker.include({openPopup:function(){return this._popup&&this._map&&!this._map.hasLayer(this._popup)&&(this._popup.setLatLng(this._latlng),this._map.openPopup(this._popup)),this},closePopup:function(){return this._popup&&this._popup._close(),this},togglePopup:function(){return this._popup&&(this._popup._isOpen?this.closePopup():this.openPopup()),this},bindPopup:function(t,e){var i=o.point(this.options.icon.options.popupAnchor||[0,0]);return i=i.add(o.Popup.prototype.options.offset),e&&e.offset&&(i=i.add(e.offset)),e=o.extend({offset:i},e),this._popupHandlersAdded||(this.on("click",this.togglePopup,this).on("remove",this.closePopup,this).on("move",this._movePopup,this),this._popupHandlersAdded=!0),t instanceof o.Popup?(o.setOptions(t,e),this._popup=t,t._source=this):this._popup=new o.Popup(e,this).setContent(t),this},setPopupContent:function(t){return this._popup&&this._popup.setContent(t),this},unbindPopup:function(){return this._popup&&(this._popup=null,this.off("click",this.togglePopup,this).off("remove",this.closePopup,this).off("move",this._movePopup,this),this._popupHandlersAdded=!1),this},getPopup:function(){return this._popup},_movePopup:function(t){this._popup.setLatLng(t.latlng)}}),o.LayerGroup=o.Class.extend({initialize:function(t){this._layers={};var e,i;if(t)for(e=0,i=t.length;i>e;e++)this.addLayer(t[e])},addLayer:function(t){var e=this.getLayerId(t);return this._layers[e]=t,this._map&&this._map.addLayer(t),this},removeLayer:function(t){var e=t in this._layers?t:this.getLayerId(t);return this._map&&this._layers[e]&&this._map.removeLayer(this._layers[e]),delete this._layers[e],this},hasLayer:function(t){return t?t in this._layers||this.getLayerId(t)in this._layers:!1},clearLayers:function(){return this.eachLayer(this.removeLayer,this),this},invoke:function(t){var e,i,n=Array.prototype.slice.call(arguments,1);for(e in this._layers)i=this._layers[e],i[t]&&i[t].apply(i,n);return this},onAdd:function(t){this._map=t,this.eachLayer(t.addLayer,t)},onRemove:function(t){this.eachLayer(t.removeLayer,t),this._map=null},addTo:function(t){return t.addLayer(this),this},eachLayer:function(t,e){for(var i in this._layers)t.call(e,this._layers[i]);return this},getLayer:function(t){return this._layers[t]},getLayers:function(){var t=[];for(var e in this._layers)t.push(this._layers[e]);return t},setZIndex:function(t){return this.invoke("setZIndex",t)},getLayerId:function(t){return o.stamp(t)}}),o.layerGroup=function(t){return new o.LayerGroup(t)},o.FeatureGroup=o.LayerGroup.extend({includes:o.Mixin.Events,statics:{EVENTS:"click dblclick mouseover mouseout mousemove contextmenu popupopen popupclose"},addLayer:function(t){return this.hasLayer(t)?this:("on"in t&&t.on(o.FeatureGroup.EVENTS,this._propagateEvent,this),o.LayerGroup.prototype.addLayer.call(this,t),this._popupContent&&t.bindPopup&&t.bindPopup(this._popupContent,this._popupOptions),this.fire("layeradd",{layer:t}))},removeLayer:function(t){return this.hasLayer(t)?(t in this._layers&&(t=this._layers[t]),"off"in t&&t.off(o.FeatureGroup.EVENTS,this._propagateEvent,this),o.LayerGroup.prototype.removeLayer.call(this,t),this._popupContent&&this.invoke("unbindPopup"),this.fire("layerremove",{layer:t})):this},bindPopup:function(t,e){return this._popupContent=t,this._popupOptions=e,this.invoke("bindPopup",t,e)},openPopup:function(t){for(var e in this._layers){this._layers[e].openPopup(t);break}return this},setStyle:function(t){return this.invoke("setStyle",t)},bringToFront:function(){return this.invoke("bringToFront")},bringToBack:function(){return this.invoke("bringToBack")},getBounds:function(){var t=new o.LatLngBounds;return this.eachLayer(function(e){t.extend(e instanceof o.Marker?e.getLatLng():e.getBounds())}),t},_propagateEvent:function(t){t=o.extend({layer:t.target,target:this},t),this.fire(t.type,t)}}),o.featureGroup=function(t){return new o.FeatureGroup(t)},o.Path=o.Class.extend({includes:[o.Mixin.Events],statics:{CLIP_PADDING:function(){var e=o.Browser.mobile?1280:2e3,i=(e/Math.max(t.outerWidth,t.outerHeight)-1)/2;return Math.max(0,Math.min(.5,i))}()},options:{stroke:!0,color:"#0033ff",dashArray:null,lineCap:null,lineJoin:null,weight:5,opacity:.5,fill:!1,fillColor:null,fillOpacity:.2,clickable:!0},initialize:function(t){o.setOptions(this,t)},onAdd:function(t){this._map=t,this._container||(this._initElements(),this._initEvents()),this.projectLatlngs(),this._updatePath(),this._container&&this._map._pathRoot.appendChild(this._container),this.fire("add"),t.on({viewreset:this.projectLatlngs,moveend:this._updatePath},this)},addTo:function(t){return t.addLayer(this),this},onRemove:function(t){t._pathRoot.removeChild(this._container),this.fire("remove"),this._map=null,o.Browser.vml&&(this._container=null,this._stroke=null,this._fill=null),t.off({viewreset:this.projectLatlngs,moveend:this._updatePath},this)},projectLatlngs:function(){},setStyle:function(t){return o.setOptions(this,t),this._container&&this._updateStyle(),this},redraw:function(){return this._map&&(this.projectLatlngs(),this._updatePath()),this}}),o.Map.include({_updatePathViewport:function(){var t=o.Path.CLIP_PADDING,e=this.getSize(),i=o.DomUtil.getPosition(this._mapPane),n=i.multiplyBy(-1)._subtract(e.multiplyBy(t)._round()),s=n.add(e.multiplyBy(1+2*t)._round());this._pathViewport=new o.Bounds(n,s)}}),o.Path.SVG_NS="http://www.w3.org/2000/svg",o.Browser.svg=!(!e.createElementNS||!e.createElementNS(o.Path.SVG_NS,"svg").createSVGRect),o.Path=o.Path.extend({statics:{SVG:o.Browser.svg},bringToFront:function(){var t=this._map._pathRoot,e=this._container;return e&&t.lastChild!==e&&t.appendChild(e),this},bringToBack:function(){var t=this._map._pathRoot,e=this._container,i=t.firstChild;return e&&i!==e&&t.insertBefore(e,i),this},getPathString:function(){},_createElement:function(t){return e.createElementNS(o.Path.SVG_NS,t)},_initElements:function(){this._map._initPathRoot(),this._initPath(),this._initStyle()},_initPath:function(){this._container=this._createElement("g"),this._path=this._createElement("path"),this.options.className&&o.DomUtil.addClass(this._path,this.options.className),this._container.appendChild(this._path)},_initStyle:function(){this.options.stroke&&(this._path.setAttribute("stroke-linejoin","round"),this._path.setAttribute("stroke-linecap","round")),this.options.fill&&this._path.setAttribute("fill-rule","evenodd"),this.options.pointerEvents&&this._path.setAttribute("pointer-events",this.options.pointerEvents),this.options.clickable||this.options.pointerEvents||this._path.setAttribute("pointer-events","none"),this._updateStyle()},_updateStyle:function(){this.options.stroke?(this._path.setAttribute("stroke",this.options.color),this._path.setAttribute("stroke-opacity",this.options.opacity),this._path.setAttribute("stroke-width",this.options.weight),this.options.dashArray?this._path.setAttribute("stroke-dasharray",this.options.dashArray):this._path.removeAttribute("stroke-dasharray"),this.options.lineCap&&this._path.setAttribute("stroke-linecap",this.options.lineCap),this.options.lineJoin&&this._path.setAttribute("stroke-linejoin",this.options.lineJoin)):this._path.setAttribute("stroke","none"),this.options.fill?(this._path.setAttribute("fill",this.options.fillColor||this.options.color),this._path.setAttribute("fill-opacity",this.options.fillOpacity)):this._path.setAttribute("fill","none")},_updatePath:function(){var t=this.getPathString();t||(t="M0 0"),this._path.setAttribute("d",t)},_initEvents:function(){if(this.options.clickable){(o.Browser.svg||!o.Browser.vml)&&o.DomUtil.addClass(this._path,"leaflet-clickable"),o.DomEvent.on(this._container,"click",this._onMouseClick,this);for(var t=["dblclick","mousedown","mouseover","mouseout","mousemove","contextmenu"],e=0;e<t.length;e++)o.DomEvent.on(this._container,t[e],this._fireMouseEvent,this)}},_onMouseClick:function(t){this._map.dragging&&this._map.dragging.moved()||this._fireMouseEvent(t)},_fireMouseEvent:function(t){if(this._map&&this.hasEventListeners(t.type)){var e=this._map,i=e.mouseEventToContainerPoint(t),n=e.containerPointToLayerPoint(i),s=e.layerPointToLatLng(n);this.fire(t.type,{latlng:s,layerPoint:n,containerPoint:i,originalEvent:t}),"contextmenu"===t.type&&o.DomEvent.preventDefault(t),"mousemove"!==t.type&&o.DomEvent.stopPropagation(t)}}}),o.Map.include({_initPathRoot:function(){this._pathRoot||(this._pathRoot=o.Path.prototype._createElement("svg"),this._panes.overlayPane.appendChild(this._pathRoot),this.options.zoomAnimation&&o.Browser.any3d?(o.DomUtil.addClass(this._pathRoot,"leaflet-zoom-animated"),
this.on({zoomanim:this._animatePathZoom,zoomend:this._endPathZoom})):o.DomUtil.addClass(this._pathRoot,"leaflet-zoom-hide"),this.on("moveend",this._updateSvgViewport),this._updateSvgViewport())},_animatePathZoom:function(t){var e=this.getZoomScale(t.zoom),i=this._getCenterOffset(t.center)._multiplyBy(-e)._add(this._pathViewport.min);this._pathRoot.style[o.DomUtil.TRANSFORM]=o.DomUtil.getTranslateString(i)+" scale("+e+") ",this._pathZooming=!0},_endPathZoom:function(){this._pathZooming=!1},_updateSvgViewport:function(){if(!this._pathZooming){this._updatePathViewport();var t=this._pathViewport,e=t.min,i=t.max,n=i.x-e.x,s=i.y-e.y,a=this._pathRoot,r=this._panes.overlayPane;o.Browser.mobileWebkit&&r.removeChild(a),o.DomUtil.setPosition(a,e),a.setAttribute("width",n),a.setAttribute("height",s),a.setAttribute("viewBox",[e.x,e.y,n,s].join(" ")),o.Browser.mobileWebkit&&r.appendChild(a)}}}),o.Path.include({bindPopup:function(t,e){return t instanceof o.Popup?this._popup=t:((!this._popup||e)&&(this._popup=new o.Popup(e,this)),this._popup.setContent(t)),this._popupHandlersAdded||(this.on("click",this._openPopup,this).on("remove",this.closePopup,this),this._popupHandlersAdded=!0),this},unbindPopup:function(){return this._popup&&(this._popup=null,this.off("click",this._openPopup).off("remove",this.closePopup),this._popupHandlersAdded=!1),this},openPopup:function(t){return this._popup&&(t=t||this._latlng||this._latlngs[Math.floor(this._latlngs.length/2)],this._openPopup({latlng:t})),this},closePopup:function(){return this._popup&&this._popup._close(),this},_openPopup:function(t){this._popup.setLatLng(t.latlng),this._map.openPopup(this._popup)}}),o.Browser.vml=!o.Browser.svg&&function(){try{var t=e.createElement("div");t.innerHTML='<v:shape adj="1"/>';var i=t.firstChild;return i.style.behavior="url(#default#VML)",i&&"object"==typeof i.adj}catch(n){return!1}}(),o.Path=o.Browser.svg||!o.Browser.vml?o.Path:o.Path.extend({statics:{VML:!0,CLIP_PADDING:.02},_createElement:function(){try{return e.namespaces.add("lvml","urn:schemas-microsoft-com:vml"),function(t){return e.createElement("<lvml:"+t+' class="lvml">')}}catch(t){return function(t){return e.createElement("<"+t+' xmlns="urn:schemas-microsoft.com:vml" class="lvml">')}}}(),_initPath:function(){var t=this._container=this._createElement("shape");o.DomUtil.addClass(t,"leaflet-vml-shape"+(this.options.className?" "+this.options.className:"")),this.options.clickable&&o.DomUtil.addClass(t,"leaflet-clickable"),t.coordsize="1 1",this._path=this._createElement("path"),t.appendChild(this._path),this._map._pathRoot.appendChild(t)},_initStyle:function(){this._updateStyle()},_updateStyle:function(){var t=this._stroke,e=this._fill,i=this.options,n=this._container;n.stroked=i.stroke,n.filled=i.fill,i.stroke?(t||(t=this._stroke=this._createElement("stroke"),t.endcap="round",n.appendChild(t)),t.weight=i.weight+"px",t.color=i.color,t.opacity=i.opacity,i.dashArray?t.dashStyle=o.Util.isArray(i.dashArray)?i.dashArray.join(" "):i.dashArray.replace(/( *, *)/g," "):t.dashStyle="",i.lineCap&&(t.endcap=i.lineCap.replace("butt","flat")),i.lineJoin&&(t.joinstyle=i.lineJoin)):t&&(n.removeChild(t),this._stroke=null),i.fill?(e||(e=this._fill=this._createElement("fill"),n.appendChild(e)),e.color=i.fillColor||i.color,e.opacity=i.fillOpacity):e&&(n.removeChild(e),this._fill=null)},_updatePath:function(){var t=this._container.style;t.display="none",this._path.v=this.getPathString()+" ",t.display=""}}),o.Map.include(o.Browser.svg||!o.Browser.vml?{}:{_initPathRoot:function(){if(!this._pathRoot){var t=this._pathRoot=e.createElement("div");t.className="leaflet-vml-container",this._panes.overlayPane.appendChild(t),this.on("moveend",this._updatePathViewport),this._updatePathViewport()}}}),o.Browser.canvas=function(){return!!e.createElement("canvas").getContext}(),o.Path=o.Path.SVG&&!t.L_PREFER_CANVAS||!o.Browser.canvas?o.Path:o.Path.extend({statics:{CANVAS:!0,SVG:!1},redraw:function(){return this._map&&(this.projectLatlngs(),this._requestUpdate()),this},setStyle:function(t){return o.setOptions(this,t),this._map&&(this._updateStyle(),this._requestUpdate()),this},onRemove:function(t){t.off("viewreset",this.projectLatlngs,this).off("moveend",this._updatePath,this),this.options.clickable&&(this._map.off("click",this._onClick,this),this._map.off("mousemove",this._onMouseMove,this)),this._requestUpdate(),this.fire("remove"),this._map=null},_requestUpdate:function(){this._map&&!o.Path._updateRequest&&(o.Path._updateRequest=o.Util.requestAnimFrame(this._fireMapMoveEnd,this._map))},_fireMapMoveEnd:function(){o.Path._updateRequest=null,this.fire("moveend")},_initElements:function(){this._map._initPathRoot(),this._ctx=this._map._canvasCtx},_updateStyle:function(){var t=this.options;t.stroke&&(this._ctx.lineWidth=t.weight,this._ctx.strokeStyle=t.color),t.fill&&(this._ctx.fillStyle=t.fillColor||t.color),t.lineCap&&(this._ctx.lineCap=t.lineCap),t.lineJoin&&(this._ctx.lineJoin=t.lineJoin)},_drawPath:function(){var t,e,i,n,s,a;for(this._ctx.beginPath(),t=0,i=this._parts.length;i>t;t++){for(e=0,n=this._parts[t].length;n>e;e++)s=this._parts[t][e],a=(0===e?"move":"line")+"To",this._ctx[a](s.x,s.y);this instanceof o.Polygon&&this._ctx.closePath()}},_checkIfEmpty:function(){return!this._parts.length},_updatePath:function(){if(!this._checkIfEmpty()){var t=this._ctx,e=this.options;this._drawPath(),t.save(),this._updateStyle(),e.fill&&(t.globalAlpha=e.fillOpacity,t.fill(e.fillRule||"evenodd")),e.stroke&&(t.globalAlpha=e.opacity,t.stroke()),t.restore()}},_initEvents:function(){this.options.clickable&&(this._map.on("mousemove",this._onMouseMove,this),this._map.on("click dblclick contextmenu",this._fireMouseEvent,this))},_fireMouseEvent:function(t){this._containsPoint(t.layerPoint)&&this.fire(t.type,t)},_onMouseMove:function(t){this._map&&!this._map._animatingZoom&&(this._containsPoint(t.layerPoint)?(this._ctx.canvas.style.cursor="pointer",this._mouseInside=!0,this.fire("mouseover",t)):this._mouseInside&&(this._ctx.canvas.style.cursor="",this._mouseInside=!1,this.fire("mouseout",t)))}}),o.Map.include(o.Path.SVG&&!t.L_PREFER_CANVAS||!o.Browser.canvas?{}:{_initPathRoot:function(){var t,i=this._pathRoot;i||(i=this._pathRoot=e.createElement("canvas"),i.style.position="absolute",t=this._canvasCtx=i.getContext("2d"),t.lineCap="round",t.lineJoin="round",this._panes.overlayPane.appendChild(i),this.options.zoomAnimation&&(this._pathRoot.className="leaflet-zoom-animated",this.on("zoomanim",this._animatePathZoom),this.on("zoomend",this._endPathZoom)),this.on("moveend",this._updateCanvasViewport),this._updateCanvasViewport())},_updateCanvasViewport:function(){if(!this._pathZooming){this._updatePathViewport();var t=this._pathViewport,e=t.min,i=t.max.subtract(e),n=this._pathRoot;o.DomUtil.setPosition(n,e),n.width=i.x,n.height=i.y,n.getContext("2d").translate(-e.x,-e.y)}}}),o.LineUtil={simplify:function(t,e){if(!e||!t.length)return t.slice();var i=e*e;return t=this._reducePoints(t,i),t=this._simplifyDP(t,i)},pointToSegmentDistance:function(t,e,i){return Math.sqrt(this._sqClosestPointOnSegment(t,e,i,!0))},closestPointOnSegment:function(t,e,i){return this._sqClosestPointOnSegment(t,e,i)},_simplifyDP:function(t,e){var n=t.length,o=typeof Uint8Array!=i+""?Uint8Array:Array,s=new o(n);s[0]=s[n-1]=1,this._simplifyDPStep(t,s,e,0,n-1);var a,r=[];for(a=0;n>a;a++)s[a]&&r.push(t[a]);return r},_simplifyDPStep:function(t,e,i,n,o){var s,a,r,h=0;for(a=n+1;o-1>=a;a++)r=this._sqClosestPointOnSegment(t[a],t[n],t[o],!0),r>h&&(s=a,h=r);h>i&&(e[s]=1,this._simplifyDPStep(t,e,i,n,s),this._simplifyDPStep(t,e,i,s,o))},_reducePoints:function(t,e){for(var i=[t[0]],n=1,o=0,s=t.length;s>n;n++)this._sqDist(t[n],t[o])>e&&(i.push(t[n]),o=n);return s-1>o&&i.push(t[s-1]),i},clipSegment:function(t,e,i,n){var o,s,a,r=n?this._lastCode:this._getBitCode(t,i),h=this._getBitCode(e,i);for(this._lastCode=h;;){if(!(r|h))return[t,e];if(r&h)return!1;o=r||h,s=this._getEdgeIntersection(t,e,o,i),a=this._getBitCode(s,i),o===r?(t=s,r=a):(e=s,h=a)}},_getEdgeIntersection:function(t,e,i,n){var s=e.x-t.x,a=e.y-t.y,r=n.min,h=n.max;return 8&i?new o.Point(t.x+s*(h.y-t.y)/a,h.y):4&i?new o.Point(t.x+s*(r.y-t.y)/a,r.y):2&i?new o.Point(h.x,t.y+a*(h.x-t.x)/s):1&i?new o.Point(r.x,t.y+a*(r.x-t.x)/s):void 0},_getBitCode:function(t,e){var i=0;return t.x<e.min.x?i|=1:t.x>e.max.x&&(i|=2),t.y<e.min.y?i|=4:t.y>e.max.y&&(i|=8),i},_sqDist:function(t,e){var i=e.x-t.x,n=e.y-t.y;return i*i+n*n},_sqClosestPointOnSegment:function(t,e,i,n){var s,a=e.x,r=e.y,h=i.x-a,l=i.y-r,u=h*h+l*l;return u>0&&(s=((t.x-a)*h+(t.y-r)*l)/u,s>1?(a=i.x,r=i.y):s>0&&(a+=h*s,r+=l*s)),h=t.x-a,l=t.y-r,n?h*h+l*l:new o.Point(a,r)}},o.Polyline=o.Path.extend({initialize:function(t,e){o.Path.prototype.initialize.call(this,e),this._latlngs=this._convertLatLngs(t)},options:{smoothFactor:1,noClip:!1},projectLatlngs:function(){this._originalPoints=[];for(var t=0,e=this._latlngs.length;e>t;t++)this._originalPoints[t]=this._map.latLngToLayerPoint(this._latlngs[t])},getPathString:function(){for(var t=0,e=this._parts.length,i="";e>t;t++)i+=this._getPathPartStr(this._parts[t]);return i},getLatLngs:function(){return this._latlngs},setLatLngs:function(t){return this._latlngs=this._convertLatLngs(t),this.redraw()},addLatLng:function(t){return this._latlngs.push(o.latLng(t)),this.redraw()},spliceLatLngs:function(){var t=[].splice.apply(this._latlngs,arguments);return this._convertLatLngs(this._latlngs,!0),this.redraw(),t},closestLayerPoint:function(t){for(var e,i,n=1/0,s=this._parts,a=null,r=0,h=s.length;h>r;r++)for(var l=s[r],u=1,c=l.length;c>u;u++){e=l[u-1],i=l[u];var d=o.LineUtil._sqClosestPointOnSegment(t,e,i,!0);n>d&&(n=d,a=o.LineUtil._sqClosestPointOnSegment(t,e,i))}return a&&(a.distance=Math.sqrt(n)),a},getBounds:function(){return new o.LatLngBounds(this.getLatLngs())},_convertLatLngs:function(t,e){var i,n,s=e?t:[];for(i=0,n=t.length;n>i;i++){if(o.Util.isArray(t[i])&&"number"!=typeof t[i][0])return;s[i]=o.latLng(t[i])}return s},_initEvents:function(){o.Path.prototype._initEvents.call(this)},_getPathPartStr:function(t){for(var e,i=o.Path.VML,n=0,s=t.length,a="";s>n;n++)e=t[n],i&&e._round(),a+=(n?"L":"M")+e.x+" "+e.y;return a},_clipPoints:function(){var t,e,i,n=this._originalPoints,s=n.length;if(this.options.noClip)return void(this._parts=[n]);this._parts=[];var a=this._parts,r=this._map._pathViewport,h=o.LineUtil;for(t=0,e=0;s-1>t;t++)i=h.clipSegment(n[t],n[t+1],r,t),i&&(a[e]=a[e]||[],a[e].push(i[0]),(i[1]!==n[t+1]||t===s-2)&&(a[e].push(i[1]),e++))},_simplifyPoints:function(){for(var t=this._parts,e=o.LineUtil,i=0,n=t.length;n>i;i++)t[i]=e.simplify(t[i],this.options.smoothFactor)},_updatePath:function(){this._map&&(this._clipPoints(),this._simplifyPoints(),o.Path.prototype._updatePath.call(this))}}),o.polyline=function(t,e){return new o.Polyline(t,e)},o.PolyUtil={},o.PolyUtil.clipPolygon=function(t,e){var i,n,s,a,r,h,l,u,c,d=[1,4,2,8],p=o.LineUtil;for(n=0,l=t.length;l>n;n++)t[n]._code=p._getBitCode(t[n],e);for(a=0;4>a;a++){for(u=d[a],i=[],n=0,l=t.length,s=l-1;l>n;s=n++)r=t[n],h=t[s],r._code&u?h._code&u||(c=p._getEdgeIntersection(h,r,u,e),c._code=p._getBitCode(c,e),i.push(c)):(h._code&u&&(c=p._getEdgeIntersection(h,r,u,e),c._code=p._getBitCode(c,e),i.push(c)),i.push(r));t=i}return t},o.Polygon=o.Polyline.extend({options:{fill:!0},initialize:function(t,e){o.Polyline.prototype.initialize.call(this,t,e),this._initWithHoles(t)},_initWithHoles:function(t){var e,i,n;if(t&&o.Util.isArray(t[0])&&"number"!=typeof t[0][0])for(this._latlngs=this._convertLatLngs(t[0]),this._holes=t.slice(1),e=0,i=this._holes.length;i>e;e++)n=this._holes[e]=this._convertLatLngs(this._holes[e]),n[0].equals(n[n.length-1])&&n.pop();t=this._latlngs,t.length>=2&&t[0].equals(t[t.length-1])&&t.pop()},projectLatlngs:function(){if(o.Polyline.prototype.projectLatlngs.call(this),this._holePoints=[],this._holes){var t,e,i,n;for(t=0,i=this._holes.length;i>t;t++)for(this._holePoints[t]=[],e=0,n=this._holes[t].length;n>e;e++)this._holePoints[t][e]=this._map.latLngToLayerPoint(this._holes[t][e])}},setLatLngs:function(t){return t&&o.Util.isArray(t[0])&&"number"!=typeof t[0][0]?(this._initWithHoles(t),this.redraw()):o.Polyline.prototype.setLatLngs.call(this,t)},_clipPoints:function(){var t=this._originalPoints,e=[];if(this._parts=[t].concat(this._holePoints),!this.options.noClip){for(var i=0,n=this._parts.length;n>i;i++){var s=o.PolyUtil.clipPolygon(this._parts[i],this._map._pathViewport);s.length&&e.push(s)}this._parts=e}},_getPathPartStr:function(t){var e=o.Polyline.prototype._getPathPartStr.call(this,t);return e+(o.Browser.svg?"z":"x")}}),o.polygon=function(t,e){return new o.Polygon(t,e)},function(){function t(t){return o.FeatureGroup.extend({initialize:function(t,e){this._layers={},this._options=e,this.setLatLngs(t)},setLatLngs:function(e){var i=0,n=e.length;for(this.eachLayer(function(t){n>i?t.setLatLngs(e[i++]):this.removeLayer(t)},this);n>i;)this.addLayer(new t(e[i++],this._options));return this},getLatLngs:function(){var t=[];return this.eachLayer(function(e){t.push(e.getLatLngs())}),t}})}o.MultiPolyline=t(o.Polyline),o.MultiPolygon=t(o.Polygon),o.multiPolyline=function(t,e){return new o.MultiPolyline(t,e)},o.multiPolygon=function(t,e){return new o.MultiPolygon(t,e)}}(),o.Rectangle=o.Polygon.extend({initialize:function(t,e){o.Polygon.prototype.initialize.call(this,this._boundsToLatLngs(t),e)},setBounds:function(t){this.setLatLngs(this._boundsToLatLngs(t))},_boundsToLatLngs:function(t){return t=o.latLngBounds(t),[t.getSouthWest(),t.getNorthWest(),t.getNorthEast(),t.getSouthEast()]}}),o.rectangle=function(t,e){return new o.Rectangle(t,e)},o.Circle=o.Path.extend({initialize:function(t,e,i){o.Path.prototype.initialize.call(this,i),this._latlng=o.latLng(t),this._mRadius=e},options:{fill:!0},setLatLng:function(t){return this._latlng=o.latLng(t),this.redraw()},setRadius:function(t){return this._mRadius=t,this.redraw()},projectLatlngs:function(){var t=this._getLngRadius(),e=this._latlng,i=this._map.latLngToLayerPoint([e.lat,e.lng-t]);this._point=this._map.latLngToLayerPoint(e),this._radius=Math.max(this._point.x-i.x,1)},getBounds:function(){var t=this._getLngRadius(),e=this._mRadius/40075017*360,i=this._latlng;return new o.LatLngBounds([i.lat-e,i.lng-t],[i.lat+e,i.lng+t])},getLatLng:function(){return this._latlng},getPathString:function(){var t=this._point,e=this._radius;return this._checkIfEmpty()?"":o.Browser.svg?"M"+t.x+","+(t.y-e)+"A"+e+","+e+",0,1,1,"+(t.x-.1)+","+(t.y-e)+" z":(t._round(),e=Math.round(e),"AL "+t.x+","+t.y+" "+e+","+e+" 0,23592600")},getRadius:function(){return this._mRadius},_getLatRadius:function(){return this._mRadius/40075017*360},_getLngRadius:function(){return this._getLatRadius()/Math.cos(o.LatLng.DEG_TO_RAD*this._latlng.lat)},_checkIfEmpty:function(){if(!this._map)return!1;var t=this._map._pathViewport,e=this._radius,i=this._point;return i.x-e>t.max.x||i.y-e>t.max.y||i.x+e<t.min.x||i.y+e<t.min.y}}),o.circle=function(t,e,i){return new o.Circle(t,e,i)},o.CircleMarker=o.Circle.extend({options:{radius:10,weight:2},initialize:function(t,e){o.Circle.prototype.initialize.call(this,t,null,e),this._radius=this.options.radius},projectLatlngs:function(){this._point=this._map.latLngToLayerPoint(this._latlng)},_updateStyle:function(){o.Circle.prototype._updateStyle.call(this),this.setRadius(this.options.radius)},setLatLng:function(t){return o.Circle.prototype.setLatLng.call(this,t),this._popup&&this._popup._isOpen&&this._popup.setLatLng(t),this},setRadius:function(t){return this.options.radius=this._radius=t,this.redraw()},getRadius:function(){return this._radius}}),o.circleMarker=function(t,e){return new o.CircleMarker(t,e)},o.Polyline.include(o.Path.CANVAS?{_containsPoint:function(t,e){var i,n,s,a,r,h,l,u=this.options.weight/2;for(o.Browser.touch&&(u+=10),i=0,a=this._parts.length;a>i;i++)for(l=this._parts[i],n=0,r=l.length,s=r-1;r>n;s=n++)if((e||0!==n)&&(h=o.LineUtil.pointToSegmentDistance(t,l[s],l[n]),u>=h))return!0;return!1}}:{}),o.Polygon.include(o.Path.CANVAS?{_containsPoint:function(t){var e,i,n,s,a,r,h,l,u=!1;if(o.Polyline.prototype._containsPoint.call(this,t,!0))return!0;for(s=0,h=this._parts.length;h>s;s++)for(e=this._parts[s],a=0,l=e.length,r=l-1;l>a;r=a++)i=e[a],n=e[r],i.y>t.y!=n.y>t.y&&t.x<(n.x-i.x)*(t.y-i.y)/(n.y-i.y)+i.x&&(u=!u);return u}}:{}),o.Circle.include(o.Path.CANVAS?{_drawPath:function(){var t=this._point;this._ctx.beginPath(),this._ctx.arc(t.x,t.y,this._radius,0,2*Math.PI,!1)},_containsPoint:function(t){var e=this._point,i=this.options.stroke?this.options.weight/2:0;return t.distanceTo(e)<=this._radius+i}}:{}),o.CircleMarker.include(o.Path.CANVAS?{_updateStyle:function(){o.Path.prototype._updateStyle.call(this)}}:{}),o.GeoJSON=o.FeatureGroup.extend({initialize:function(t,e){o.setOptions(this,e),this._layers={},t&&this.addData(t)},addData:function(t){var e,i,n,s=o.Util.isArray(t)?t:t.features;if(s){for(e=0,i=s.length;i>e;e++)n=s[e],(n.geometries||n.geometry||n.features||n.coordinates)&&this.addData(s[e]);return this}var a=this.options;if(!a.filter||a.filter(t)){var r=o.GeoJSON.geometryToLayer(t,a.pointToLayer,a.coordsToLatLng,a);return r.feature=o.GeoJSON.asFeature(t),r.defaultOptions=r.options,this.resetStyle(r),a.onEachFeature&&a.onEachFeature(t,r),this.addLayer(r)}},resetStyle:function(t){var e=this.options.style;e&&(o.Util.extend(t.options,t.defaultOptions),this._setLayerStyle(t,e))},setStyle:function(t){this.eachLayer(function(e){this._setLayerStyle(e,t)},this)},_setLayerStyle:function(t,e){"function"==typeof e&&(e=e(t.feature)),t.setStyle&&t.setStyle(e)}}),o.extend(o.GeoJSON,{geometryToLayer:function(t,e,i,n){var s,a,r,h,l="Feature"===t.type?t.geometry:t,u=l.coordinates,c=[];switch(i=i||this.coordsToLatLng,l.type){case"Point":return s=i(u),e?e(t,s):new o.Marker(s);case"MultiPoint":for(r=0,h=u.length;h>r;r++)s=i(u[r]),c.push(e?e(t,s):new o.Marker(s));return new o.FeatureGroup(c);case"LineString":return a=this.coordsToLatLngs(u,0,i),new o.Polyline(a,n);case"Polygon":if(2===u.length&&!u[1].length)throw new Error("Invalid GeoJSON object.");return a=this.coordsToLatLngs(u,1,i),new o.Polygon(a,n);case"MultiLineString":return a=this.coordsToLatLngs(u,1,i),new o.MultiPolyline(a,n);case"MultiPolygon":return a=this.coordsToLatLngs(u,2,i),new o.MultiPolygon(a,n);case"GeometryCollection":for(r=0,h=l.geometries.length;h>r;r++)c.push(this.geometryToLayer({geometry:l.geometries[r],type:"Feature",properties:t.properties},e,i,n));return new o.FeatureGroup(c);default:throw new Error("Invalid GeoJSON object.")}},coordsToLatLng:function(t){return new o.LatLng(t[1],t[0],t[2])},coordsToLatLngs:function(t,e,i){var n,o,s,a=[];for(o=0,s=t.length;s>o;o++)n=e?this.coordsToLatLngs(t[o],e-1,i):(i||this.coordsToLatLng)(t[o]),a.push(n);return a},latLngToCoords:function(t){var e=[t.lng,t.lat];return t.alt!==i&&e.push(t.alt),e},latLngsToCoords:function(t){for(var e=[],i=0,n=t.length;n>i;i++)e.push(o.GeoJSON.latLngToCoords(t[i]));return e},getFeature:function(t,e){return t.feature?o.extend({},t.feature,{geometry:e}):o.GeoJSON.asFeature(e)},asFeature:function(t){return"Feature"===t.type?t:{type:"Feature",properties:{},geometry:t}}});var a={toGeoJSON:function(){return o.GeoJSON.getFeature(this,{type:"Point",coordinates:o.GeoJSON.latLngToCoords(this.getLatLng())})}};o.Marker.include(a),o.Circle.include(a),o.CircleMarker.include(a),o.Polyline.include({toGeoJSON:function(){return o.GeoJSON.getFeature(this,{type:"LineString",coordinates:o.GeoJSON.latLngsToCoords(this.getLatLngs())})}}),o.Polygon.include({toGeoJSON:function(){var t,e,i,n=[o.GeoJSON.latLngsToCoords(this.getLatLngs())];if(n[0].push(n[0][0]),this._holes)for(t=0,e=this._holes.length;e>t;t++)i=o.GeoJSON.latLngsToCoords(this._holes[t]),i.push(i[0]),n.push(i);return o.GeoJSON.getFeature(this,{type:"Polygon",coordinates:n})}}),function(){function t(t){return function(){var e=[];return this.eachLayer(function(t){e.push(t.toGeoJSON().geometry.coordinates)}),o.GeoJSON.getFeature(this,{type:t,coordinates:e})}}o.MultiPolyline.include({toGeoJSON:t("MultiLineString")}),o.MultiPolygon.include({toGeoJSON:t("MultiPolygon")}),o.LayerGroup.include({toGeoJSON:function(){var e,i=this.feature&&this.feature.geometry,n=[];if(i&&"MultiPoint"===i.type)return t("MultiPoint").call(this);var s=i&&"GeometryCollection"===i.type;return this.eachLayer(function(t){t.toGeoJSON&&(e=t.toGeoJSON(),n.push(s?e.geometry:o.GeoJSON.asFeature(e)))}),s?o.GeoJSON.getFeature(this,{geometries:n,type:"GeometryCollection"}):{type:"FeatureCollection",features:n}}})}(),o.geoJson=function(t,e){return new o.GeoJSON(t,e)},o.DomEvent={addListener:function(t,e,i,n){var s,a,r,h=o.stamp(i),l="_leaflet_"+e+h;return t[l]?this:(s=function(e){return i.call(n||t,e||o.DomEvent._getEvent())},o.Browser.pointer&&0===e.indexOf("touch")?this.addPointerListener(t,e,s,h):(o.Browser.touch&&"dblclick"===e&&this.addDoubleTapListener&&this.addDoubleTapListener(t,s,h),"addEventListener"in t?"mousewheel"===e?(t.addEventListener("DOMMouseScroll",s,!1),t.addEventListener(e,s,!1)):"mouseenter"===e||"mouseleave"===e?(a=s,r="mouseenter"===e?"mouseover":"mouseout",s=function(e){return o.DomEvent._checkMouse(t,e)?a(e):void 0},t.addEventListener(r,s,!1)):"click"===e&&o.Browser.android?(a=s,s=function(t){return o.DomEvent._filterClick(t,a)},t.addEventListener(e,s,!1)):t.addEventListener(e,s,!1):"attachEvent"in t&&t.attachEvent("on"+e,s),t[l]=s,this))},removeListener:function(t,e,i){var n=o.stamp(i),s="_leaflet_"+e+n,a=t[s];return a?(o.Browser.pointer&&0===e.indexOf("touch")?this.removePointerListener(t,e,n):o.Browser.touch&&"dblclick"===e&&this.removeDoubleTapListener?this.removeDoubleTapListener(t,n):"removeEventListener"in t?"mousewheel"===e?(t.removeEventListener("DOMMouseScroll",a,!1),t.removeEventListener(e,a,!1)):"mouseenter"===e||"mouseleave"===e?t.removeEventListener("mouseenter"===e?"mouseover":"mouseout",a,!1):t.removeEventListener(e,a,!1):"detachEvent"in t&&t.detachEvent("on"+e,a),t[s]=null,this):this},stopPropagation:function(t){return t.stopPropagation?t.stopPropagation():t.cancelBubble=!0,o.DomEvent._skipped(t),this},disableScrollPropagation:function(t){var e=o.DomEvent.stopPropagation;return o.DomEvent.on(t,"mousewheel",e).on(t,"MozMousePixelScroll",e)},disableClickPropagation:function(t){for(var e=o.DomEvent.stopPropagation,i=o.Draggable.START.length-1;i>=0;i--)o.DomEvent.on(t,o.Draggable.START[i],e);return o.DomEvent.on(t,"click",o.DomEvent._fakeStop).on(t,"dblclick",e)},preventDefault:function(t){return t.preventDefault?t.preventDefault():t.returnValue=!1,this},stop:function(t){return o.DomEvent.preventDefault(t).stopPropagation(t)},getMousePosition:function(t,e){if(!e)return new o.Point(t.clientX,t.clientY);var i=e.getBoundingClientRect();return new o.Point(t.clientX-i.left-e.clientLeft,t.clientY-i.top-e.clientTop)},getWheelDelta:function(t){var e=0;return t.wheelDelta&&(e=t.wheelDelta/120),t.detail&&(e=-t.detail/3),e},_skipEvents:{},_fakeStop:function(t){o.DomEvent._skipEvents[t.type]=!0},_skipped:function(t){var e=this._skipEvents[t.type];return this._skipEvents[t.type]=!1,e},_checkMouse:function(t,e){var i=e.relatedTarget;if(!i)return!0;try{for(;i&&i!==t;)i=i.parentNode}catch(n){return!1}return i!==t},_getEvent:function(){var e=t.event;if(!e)for(var i=arguments.callee.caller;i&&(e=i.arguments[0],!e||t.Event!==e.constructor);)i=i.caller;return e},_filterClick:function(t,e){var i=t.timeStamp||t.originalEvent.timeStamp,n=o.DomEvent._lastClick&&i-o.DomEvent._lastClick;return n&&n>100&&500>n||t.target._simulatedClick&&!t._simulated?void o.DomEvent.stop(t):(o.DomEvent._lastClick=i,e(t))}},o.DomEvent.on=o.DomEvent.addListener,o.DomEvent.off=o.DomEvent.removeListener,o.Draggable=o.Class.extend({includes:o.Mixin.Events,statics:{START:o.Browser.touch?["touchstart","mousedown"]:["mousedown"],END:{mousedown:"mouseup",touchstart:"touchend",pointerdown:"touchend",MSPointerDown:"touchend"},MOVE:{mousedown:"mousemove",touchstart:"touchmove",pointerdown:"touchmove",MSPointerDown:"touchmove"}},initialize:function(t,e){this._element=t,this._dragStartTarget=e||t},enable:function(){if(!this._enabled){for(var t=o.Draggable.START.length-1;t>=0;t--)o.DomEvent.on(this._dragStartTarget,o.Draggable.START[t],this._onDown,this);this._enabled=!0}},disable:function(){if(this._enabled){for(var t=o.Draggable.START.length-1;t>=0;t--)o.DomEvent.off(this._dragStartTarget,o.Draggable.START[t],this._onDown,this);this._enabled=!1,this._moved=!1}},_onDown:function(t){if(this._moved=!1,!t.shiftKey&&(1===t.which||1===t.button||t.touches)&&(o.DomEvent.stopPropagation(t),!o.Draggable._disabled&&(o.DomUtil.disableImageDrag(),o.DomUtil.disableTextSelection(),!this._moving))){var i=t.touches?t.touches[0]:t;this._startPoint=new o.Point(i.clientX,i.clientY),this._startPos=this._newPos=o.DomUtil.getPosition(this._element),o.DomEvent.on(e,o.Draggable.MOVE[t.type],this._onMove,this).on(e,o.Draggable.END[t.type],this._onUp,this)}},_onMove:function(t){if(t.touches&&t.touches.length>1)return void(this._moved=!0);var i=t.touches&&1===t.touches.length?t.touches[0]:t,n=new o.Point(i.clientX,i.clientY),s=n.subtract(this._startPoint);(s.x||s.y)&&(o.Browser.touch&&Math.abs(s.x)+Math.abs(s.y)<3||(o.DomEvent.preventDefault(t),this._moved||(this.fire("dragstart"),this._moved=!0,this._startPos=o.DomUtil.getPosition(this._element).subtract(s),o.DomUtil.addClass(e.body,"leaflet-dragging"),this._lastTarget=t.target||t.srcElement,o.DomUtil.addClass(this._lastTarget,"leaflet-drag-target")),this._newPos=this._startPos.add(s),this._moving=!0,o.Util.cancelAnimFrame(this._animRequest),this._animRequest=o.Util.requestAnimFrame(this._updatePosition,this,!0,this._dragStartTarget)))},_updatePosition:function(){this.fire("predrag"),o.DomUtil.setPosition(this._element,this._newPos),this.fire("drag")},_onUp:function(){o.DomUtil.removeClass(e.body,"leaflet-dragging"),this._lastTarget&&(o.DomUtil.removeClass(this._lastTarget,"leaflet-drag-target"),this._lastTarget=null);for(var t in o.Draggable.MOVE)o.DomEvent.off(e,o.Draggable.MOVE[t],this._onMove).off(e,o.Draggable.END[t],this._onUp);o.DomUtil.enableImageDrag(),o.DomUtil.enableTextSelection(),this._moved&&this._moving&&(o.Util.cancelAnimFrame(this._animRequest),this.fire("dragend",{distance:this._newPos.distanceTo(this._startPos)})),this._moving=!1}}),o.Handler=o.Class.extend({initialize:function(t){this._map=t},enable:function(){this._enabled||(this._enabled=!0,this.addHooks())},disable:function(){this._enabled&&(this._enabled=!1,this.removeHooks())},enabled:function(){return!!this._enabled}}),o.Map.mergeOptions({dragging:!0,inertia:!o.Browser.android23,inertiaDeceleration:3400,inertiaMaxSpeed:1/0,inertiaThreshold:o.Browser.touch?32:18,easeLinearity:.25,worldCopyJump:!1}),o.Map.Drag=o.Handler.extend({addHooks:function(){if(!this._draggable){var t=this._map;this._draggable=new o.Draggable(t._mapPane,t._container),this._draggable.on({dragstart:this._onDragStart,drag:this._onDrag,dragend:this._onDragEnd},this),t.options.worldCopyJump&&(this._draggable.on("predrag",this._onPreDrag,this),t.on("viewreset",this._onViewReset,this),t.whenReady(this._onViewReset,this))}this._draggable.enable()},removeHooks:function(){this._draggable.disable()},moved:function(){return this._draggable&&this._draggable._moved},_onDragStart:function(){var t=this._map;t._panAnim&&t._panAnim.stop(),t.fire("movestart").fire("dragstart"),t.options.inertia&&(this._positions=[],this._times=[])},_onDrag:function(){if(this._map.options.inertia){var t=this._lastTime=+new Date,e=this._lastPos=this._draggable._newPos;this._positions.push(e),this._times.push(t),t-this._times[0]>200&&(this._positions.shift(),this._times.shift())}this._map.fire("move").fire("drag")},_onViewReset:function(){var t=this._map.getSize()._divideBy(2),e=this._map.latLngToLayerPoint([0,0]);this._initialWorldOffset=e.subtract(t).x,this._worldWidth=this._map.project([0,180]).x},_onPreDrag:function(){var t=this._worldWidth,e=Math.round(t/2),i=this._initialWorldOffset,n=this._draggable._newPos.x,o=(n-e+i)%t+e-i,s=(n+e+i)%t-e-i,a=Math.abs(o+i)<Math.abs(s+i)?o:s;this._draggable._newPos.x=a},_onDragEnd:function(t){var e=this._map,i=e.options,n=+new Date-this._lastTime,s=!i.inertia||n>i.inertiaThreshold||!this._positions[0];if(e.fire("dragend",t),s)e.fire("moveend");else{var a=this._lastPos.subtract(this._positions[0]),r=(this._lastTime+n-this._times[0])/1e3,h=i.easeLinearity,l=a.multiplyBy(h/r),u=l.distanceTo([0,0]),c=Math.min(i.inertiaMaxSpeed,u),d=l.multiplyBy(c/u),p=c/(i.inertiaDeceleration*h),_=d.multiplyBy(-p/2).round();_.x&&_.y?(_=e._limitOffset(_,e.options.maxBounds),o.Util.requestAnimFrame(function(){e.panBy(_,{duration:p,easeLinearity:h,noMoveStart:!0})})):e.fire("moveend")}}}),o.Map.addInitHook("addHandler","dragging",o.Map.Drag),o.Map.mergeOptions({doubleClickZoom:!0}),o.Map.DoubleClickZoom=o.Handler.extend({addHooks:function(){this._map.on("dblclick",this._onDoubleClick,this)},removeHooks:function(){this._map.off("dblclick",this._onDoubleClick,this)},_onDoubleClick:function(t){var e=this._map,i=e.getZoom()+(t.originalEvent.shiftKey?-1:1);"center"===e.options.doubleClickZoom?e.setZoom(i):e.setZoomAround(t.containerPoint,i)}}),o.Map.addInitHook("addHandler","doubleClickZoom",o.Map.DoubleClickZoom),o.Map.mergeOptions({scrollWheelZoom:!0}),o.Map.ScrollWheelZoom=o.Handler.extend({addHooks:function(){o.DomEvent.on(this._map._container,"mousewheel",this._onWheelScroll,this),o.DomEvent.on(this._map._container,"MozMousePixelScroll",o.DomEvent.preventDefault),this._delta=0},removeHooks:function(){o.DomEvent.off(this._map._container,"mousewheel",this._onWheelScroll),o.DomEvent.off(this._map._container,"MozMousePixelScroll",o.DomEvent.preventDefault)},_onWheelScroll:function(t){var e=o.DomEvent.getWheelDelta(t);this._delta+=e,this._lastMousePos=this._map.mouseEventToContainerPoint(t),this._startTime||(this._startTime=+new Date);var i=Math.max(40-(+new Date-this._startTime),0);clearTimeout(this._timer),this._timer=setTimeout(o.bind(this._performZoom,this),i),o.DomEvent.preventDefault(t),o.DomEvent.stopPropagation(t)},_performZoom:function(){var t=this._map,e=this._delta,i=t.getZoom();e=e>0?Math.ceil(e):Math.floor(e),e=Math.max(Math.min(e,4),-4),e=t._limitZoom(i+e)-i,this._delta=0,this._startTime=null,e&&("center"===t.options.scrollWheelZoom?t.setZoom(i+e):t.setZoomAround(this._lastMousePos,i+e))}}),o.Map.addInitHook("addHandler","scrollWheelZoom",o.Map.ScrollWheelZoom),o.extend(o.DomEvent,{_touchstart:o.Browser.msPointer?"MSPointerDown":o.Browser.pointer?"pointerdown":"touchstart",_touchend:o.Browser.msPointer?"MSPointerUp":o.Browser.pointer?"pointerup":"touchend",addDoubleTapListener:function(t,i,n){function s(t){var e;if(o.Browser.pointer?(_.push(t.pointerId),e=_.length):e=t.touches.length,!(e>1)){var i=Date.now(),n=i-(r||i);h=t.touches?t.touches[0]:t,l=n>0&&u>=n,r=i}}function a(t){if(o.Browser.pointer){var e=_.indexOf(t.pointerId);if(-1===e)return;_.splice(e,1)}if(l){if(o.Browser.pointer){var n,s={};for(var a in h)n=h[a],"function"==typeof n?s[a]=n.bind(h):s[a]=n;h=s}h.type="dblclick",i(h),r=null}}var r,h,l=!1,u=250,c="_leaflet_",d=this._touchstart,p=this._touchend,_=[];t[c+d+n]=s,t[c+p+n]=a;var m=o.Browser.pointer?e.documentElement:t;return t.addEventListener(d,s,!1),m.addEventListener(p,a,!1),o.Browser.pointer&&m.addEventListener(o.DomEvent.POINTER_CANCEL,a,!1),this},removeDoubleTapListener:function(t,i){var n="_leaflet_";return t.removeEventListener(this._touchstart,t[n+this._touchstart+i],!1),(o.Browser.pointer?e.documentElement:t).removeEventListener(this._touchend,t[n+this._touchend+i],!1),o.Browser.pointer&&e.documentElement.removeEventListener(o.DomEvent.POINTER_CANCEL,t[n+this._touchend+i],!1),this}}),o.extend(o.DomEvent,{POINTER_DOWN:o.Browser.msPointer?"MSPointerDown":"pointerdown",POINTER_MOVE:o.Browser.msPointer?"MSPointerMove":"pointermove",POINTER_UP:o.Browser.msPointer?"MSPointerUp":"pointerup",POINTER_CANCEL:o.Browser.msPointer?"MSPointerCancel":"pointercancel",_pointers:[],_pointerDocumentListener:!1,addPointerListener:function(t,e,i,n){switch(e){case"touchstart":return this.addPointerListenerStart(t,e,i,n);
case"touchend":return this.addPointerListenerEnd(t,e,i,n);case"touchmove":return this.addPointerListenerMove(t,e,i,n);default:throw"Unknown touch event type"}},addPointerListenerStart:function(t,i,n,s){var a="_leaflet_",r=this._pointers,h=function(t){"mouse"!==t.pointerType&&t.pointerType!==t.MSPOINTER_TYPE_MOUSE&&o.DomEvent.preventDefault(t);for(var e=!1,i=0;i<r.length;i++)if(r[i].pointerId===t.pointerId){e=!0;break}e||r.push(t),t.touches=r.slice(),t.changedTouches=[t],n(t)};if(t[a+"touchstart"+s]=h,t.addEventListener(this.POINTER_DOWN,h,!1),!this._pointerDocumentListener){var l=function(t){for(var e=0;e<r.length;e++)if(r[e].pointerId===t.pointerId){r.splice(e,1);break}};e.documentElement.addEventListener(this.POINTER_UP,l,!1),e.documentElement.addEventListener(this.POINTER_CANCEL,l,!1),this._pointerDocumentListener=!0}return this},addPointerListenerMove:function(t,e,i,n){function o(t){if(t.pointerType!==t.MSPOINTER_TYPE_MOUSE&&"mouse"!==t.pointerType||0!==t.buttons){for(var e=0;e<a.length;e++)if(a[e].pointerId===t.pointerId){a[e]=t;break}t.touches=a.slice(),t.changedTouches=[t],i(t)}}var s="_leaflet_",a=this._pointers;return t[s+"touchmove"+n]=o,t.addEventListener(this.POINTER_MOVE,o,!1),this},addPointerListenerEnd:function(t,e,i,n){var o="_leaflet_",s=this._pointers,a=function(t){for(var e=0;e<s.length;e++)if(s[e].pointerId===t.pointerId){s.splice(e,1);break}t.touches=s.slice(),t.changedTouches=[t],i(t)};return t[o+"touchend"+n]=a,t.addEventListener(this.POINTER_UP,a,!1),t.addEventListener(this.POINTER_CANCEL,a,!1),this},removePointerListener:function(t,e,i){var n="_leaflet_",o=t[n+e+i];switch(e){case"touchstart":t.removeEventListener(this.POINTER_DOWN,o,!1);break;case"touchmove":t.removeEventListener(this.POINTER_MOVE,o,!1);break;case"touchend":t.removeEventListener(this.POINTER_UP,o,!1),t.removeEventListener(this.POINTER_CANCEL,o,!1)}return this}}),o.Map.mergeOptions({touchZoom:o.Browser.touch&&!o.Browser.android23,bounceAtZoomLimits:!0}),o.Map.TouchZoom=o.Handler.extend({addHooks:function(){o.DomEvent.on(this._map._container,"touchstart",this._onTouchStart,this)},removeHooks:function(){o.DomEvent.off(this._map._container,"touchstart",this._onTouchStart,this)},_onTouchStart:function(t){var i=this._map;if(t.touches&&2===t.touches.length&&!i._animatingZoom&&!this._zooming){var n=i.mouseEventToLayerPoint(t.touches[0]),s=i.mouseEventToLayerPoint(t.touches[1]),a=i._getCenterLayerPoint();this._startCenter=n.add(s)._divideBy(2),this._startDist=n.distanceTo(s),this._moved=!1,this._zooming=!0,this._centerOffset=a.subtract(this._startCenter),i._panAnim&&i._panAnim.stop(),o.DomEvent.on(e,"touchmove",this._onTouchMove,this).on(e,"touchend",this._onTouchEnd,this),o.DomEvent.preventDefault(t)}},_onTouchMove:function(t){var e=this._map;if(t.touches&&2===t.touches.length&&this._zooming){var i=e.mouseEventToLayerPoint(t.touches[0]),n=e.mouseEventToLayerPoint(t.touches[1]);this._scale=i.distanceTo(n)/this._startDist,this._delta=i._add(n)._divideBy(2)._subtract(this._startCenter),1!==this._scale&&(e.options.bounceAtZoomLimits||!(e.getZoom()===e.getMinZoom()&&this._scale<1||e.getZoom()===e.getMaxZoom()&&this._scale>1))&&(this._moved||(o.DomUtil.addClass(e._mapPane,"leaflet-touching"),e.fire("movestart").fire("zoomstart"),this._moved=!0),o.Util.cancelAnimFrame(this._animRequest),this._animRequest=o.Util.requestAnimFrame(this._updateOnMove,this,!0,this._map._container),o.DomEvent.preventDefault(t))}},_updateOnMove:function(){var t=this._map,e=this._getScaleOrigin(),i=t.layerPointToLatLng(e),n=t.getScaleZoom(this._scale);t._animateZoom(i,n,this._startCenter,this._scale,this._delta,!1,!0)},_onTouchEnd:function(){if(!this._moved||!this._zooming)return void(this._zooming=!1);var t=this._map;this._zooming=!1,o.DomUtil.removeClass(t._mapPane,"leaflet-touching"),o.Util.cancelAnimFrame(this._animRequest),o.DomEvent.off(e,"touchmove",this._onTouchMove).off(e,"touchend",this._onTouchEnd);var i=this._getScaleOrigin(),n=t.layerPointToLatLng(i),s=t.getZoom(),a=t.getScaleZoom(this._scale)-s,r=a>0?Math.ceil(a):Math.floor(a),h=t._limitZoom(s+r),l=t.getZoomScale(h)/this._scale;t._animateZoom(n,h,i,l)},_getScaleOrigin:function(){var t=this._centerOffset.subtract(this._delta).divideBy(this._scale);return this._startCenter.add(t)}}),o.Map.addInitHook("addHandler","touchZoom",o.Map.TouchZoom),o.Map.mergeOptions({tap:!0,tapTolerance:15}),o.Map.Tap=o.Handler.extend({addHooks:function(){o.DomEvent.on(this._map._container,"touchstart",this._onDown,this)},removeHooks:function(){o.DomEvent.off(this._map._container,"touchstart",this._onDown,this)},_onDown:function(t){if(t.touches){if(o.DomEvent.preventDefault(t),this._fireClick=!0,t.touches.length>1)return this._fireClick=!1,void clearTimeout(this._holdTimeout);var i=t.touches[0],n=i.target;this._startPos=this._newPos=new o.Point(i.clientX,i.clientY),n.tagName&&"a"===n.tagName.toLowerCase()&&o.DomUtil.addClass(n,"leaflet-active"),this._holdTimeout=setTimeout(o.bind(function(){this._isTapValid()&&(this._fireClick=!1,this._onUp(),this._simulateEvent("contextmenu",i))},this),1e3),o.DomEvent.on(e,"touchmove",this._onMove,this).on(e,"touchend",this._onUp,this)}},_onUp:function(t){if(clearTimeout(this._holdTimeout),o.DomEvent.off(e,"touchmove",this._onMove,this).off(e,"touchend",this._onUp,this),this._fireClick&&t&&t.changedTouches){var i=t.changedTouches[0],n=i.target;n&&n.tagName&&"a"===n.tagName.toLowerCase()&&o.DomUtil.removeClass(n,"leaflet-active"),this._isTapValid()&&this._simulateEvent("click",i)}},_isTapValid:function(){return this._newPos.distanceTo(this._startPos)<=this._map.options.tapTolerance},_onMove:function(t){var e=t.touches[0];this._newPos=new o.Point(e.clientX,e.clientY)},_simulateEvent:function(i,n){var o=e.createEvent("MouseEvents");o._simulated=!0,n.target._simulatedClick=!0,o.initMouseEvent(i,!0,!0,t,1,n.screenX,n.screenY,n.clientX,n.clientY,!1,!1,!1,!1,0,null),n.target.dispatchEvent(o)}}),o.Browser.touch&&!o.Browser.pointer&&o.Map.addInitHook("addHandler","tap",o.Map.Tap),o.Map.mergeOptions({boxZoom:!0}),o.Map.BoxZoom=o.Handler.extend({initialize:function(t){this._map=t,this._container=t._container,this._pane=t._panes.overlayPane,this._moved=!1},addHooks:function(){o.DomEvent.on(this._container,"mousedown",this._onMouseDown,this)},removeHooks:function(){o.DomEvent.off(this._container,"mousedown",this._onMouseDown),this._moved=!1},moved:function(){return this._moved},_onMouseDown:function(t){return this._moved=!1,!t.shiftKey||1!==t.which&&1!==t.button?!1:(o.DomUtil.disableTextSelection(),o.DomUtil.disableImageDrag(),this._startLayerPoint=this._map.mouseEventToLayerPoint(t),void o.DomEvent.on(e,"mousemove",this._onMouseMove,this).on(e,"mouseup",this._onMouseUp,this).on(e,"keydown",this._onKeyDown,this))},_onMouseMove:function(t){this._moved||(this._box=o.DomUtil.create("div","leaflet-zoom-box",this._pane),o.DomUtil.setPosition(this._box,this._startLayerPoint),this._container.style.cursor="crosshair",this._map.fire("boxzoomstart"));var e=this._startLayerPoint,i=this._box,n=this._map.mouseEventToLayerPoint(t),s=n.subtract(e),a=new o.Point(Math.min(n.x,e.x),Math.min(n.y,e.y));o.DomUtil.setPosition(i,a),this._moved=!0,i.style.width=Math.max(0,Math.abs(s.x)-4)+"px",i.style.height=Math.max(0,Math.abs(s.y)-4)+"px"},_finish:function(){this._moved&&(this._pane.removeChild(this._box),this._container.style.cursor=""),o.DomUtil.enableTextSelection(),o.DomUtil.enableImageDrag(),o.DomEvent.off(e,"mousemove",this._onMouseMove).off(e,"mouseup",this._onMouseUp).off(e,"keydown",this._onKeyDown)},_onMouseUp:function(t){this._finish();var e=this._map,i=e.mouseEventToLayerPoint(t);if(!this._startLayerPoint.equals(i)){var n=new o.LatLngBounds(e.layerPointToLatLng(this._startLayerPoint),e.layerPointToLatLng(i));e.fitBounds(n),e.fire("boxzoomend",{boxZoomBounds:n})}},_onKeyDown:function(t){27===t.keyCode&&this._finish()}}),o.Map.addInitHook("addHandler","boxZoom",o.Map.BoxZoom),o.Map.mergeOptions({keyboard:!0,keyboardPanOffset:80,keyboardZoomOffset:1}),o.Map.Keyboard=o.Handler.extend({keyCodes:{left:[37],right:[39],down:[40],up:[38],zoomIn:[187,107,61,171],zoomOut:[189,109,173]},initialize:function(t){this._map=t,this._setPanOffset(t.options.keyboardPanOffset),this._setZoomOffset(t.options.keyboardZoomOffset)},addHooks:function(){var t=this._map._container;-1===t.tabIndex&&(t.tabIndex="0"),o.DomEvent.on(t,"focus",this._onFocus,this).on(t,"blur",this._onBlur,this).on(t,"mousedown",this._onMouseDown,this),this._map.on("focus",this._addHooks,this).on("blur",this._removeHooks,this)},removeHooks:function(){this._removeHooks();var t=this._map._container;o.DomEvent.off(t,"focus",this._onFocus,this).off(t,"blur",this._onBlur,this).off(t,"mousedown",this._onMouseDown,this),this._map.off("focus",this._addHooks,this).off("blur",this._removeHooks,this)},_onMouseDown:function(){if(!this._focused){var i=e.body,n=e.documentElement,o=i.scrollTop||n.scrollTop,s=i.scrollLeft||n.scrollLeft;this._map._container.focus(),t.scrollTo(s,o)}},_onFocus:function(){this._focused=!0,this._map.fire("focus")},_onBlur:function(){this._focused=!1,this._map.fire("blur")},_setPanOffset:function(t){var e,i,n=this._panKeys={},o=this.keyCodes;for(e=0,i=o.left.length;i>e;e++)n[o.left[e]]=[-1*t,0];for(e=0,i=o.right.length;i>e;e++)n[o.right[e]]=[t,0];for(e=0,i=o.down.length;i>e;e++)n[o.down[e]]=[0,t];for(e=0,i=o.up.length;i>e;e++)n[o.up[e]]=[0,-1*t]},_setZoomOffset:function(t){var e,i,n=this._zoomKeys={},o=this.keyCodes;for(e=0,i=o.zoomIn.length;i>e;e++)n[o.zoomIn[e]]=t;for(e=0,i=o.zoomOut.length;i>e;e++)n[o.zoomOut[e]]=-t},_addHooks:function(){o.DomEvent.on(e,"keydown",this._onKeyDown,this)},_removeHooks:function(){o.DomEvent.off(e,"keydown",this._onKeyDown,this)},_onKeyDown:function(t){var e=t.keyCode,i=this._map;if(e in this._panKeys){if(i._panAnim&&i._panAnim._inProgress)return;i.panBy(this._panKeys[e]),i.options.maxBounds&&i.panInsideBounds(i.options.maxBounds)}else{if(!(e in this._zoomKeys))return;i.setZoom(i.getZoom()+this._zoomKeys[e])}o.DomEvent.stop(t)}}),o.Map.addInitHook("addHandler","keyboard",o.Map.Keyboard),o.Handler.MarkerDrag=o.Handler.extend({initialize:function(t){this._marker=t},addHooks:function(){var t=this._marker._icon;this._draggable||(this._draggable=new o.Draggable(t,t)),this._draggable.on("dragstart",this._onDragStart,this).on("drag",this._onDrag,this).on("dragend",this._onDragEnd,this),this._draggable.enable(),o.DomUtil.addClass(this._marker._icon,"leaflet-marker-draggable")},removeHooks:function(){this._draggable.off("dragstart",this._onDragStart,this).off("drag",this._onDrag,this).off("dragend",this._onDragEnd,this),this._draggable.disable(),o.DomUtil.removeClass(this._marker._icon,"leaflet-marker-draggable")},moved:function(){return this._draggable&&this._draggable._moved},_onDragStart:function(){this._marker.closePopup().fire("movestart").fire("dragstart")},_onDrag:function(){var t=this._marker,e=t._shadow,i=o.DomUtil.getPosition(t._icon),n=t._map.layerPointToLatLng(i);e&&o.DomUtil.setPosition(e,i),t._latlng=n,t.fire("move",{latlng:n}).fire("drag")},_onDragEnd:function(t){this._marker.fire("moveend").fire("dragend",t)}}),o.Control=o.Class.extend({options:{position:"topright"},initialize:function(t){o.setOptions(this,t)},getPosition:function(){return this.options.position},setPosition:function(t){var e=this._map;return e&&e.removeControl(this),this.options.position=t,e&&e.addControl(this),this},getContainer:function(){return this._container},addTo:function(t){this._map=t;var e=this._container=this.onAdd(t),i=this.getPosition(),n=t._controlCorners[i];return o.DomUtil.addClass(e,"leaflet-control"),-1!==i.indexOf("bottom")?n.insertBefore(e,n.firstChild):n.appendChild(e),this},removeFrom:function(t){var e=this.getPosition(),i=t._controlCorners[e];return i.removeChild(this._container),this._map=null,this.onRemove&&this.onRemove(t),this},_refocusOnMap:function(){this._map&&this._map.getContainer().focus()}}),o.control=function(t){return new o.Control(t)},o.Map.include({addControl:function(t){return t.addTo(this),this},removeControl:function(t){return t.removeFrom(this),this},_initControlPos:function(){function t(t,s){var a=i+t+" "+i+s;e[t+s]=o.DomUtil.create("div",a,n)}var e=this._controlCorners={},i="leaflet-",n=this._controlContainer=o.DomUtil.create("div",i+"control-container",this._container);t("top","left"),t("top","right"),t("bottom","left"),t("bottom","right")},_clearControlPos:function(){this._container.removeChild(this._controlContainer)}}),o.Control.Zoom=o.Control.extend({options:{position:"topleft",zoomInText:"+",zoomInTitle:"Zoom in",zoomOutText:"-",zoomOutTitle:"Zoom out"},onAdd:function(t){var e="leaflet-control-zoom",i=o.DomUtil.create("div",e+" leaflet-bar");return this._map=t,this._zoomInButton=this._createButton(this.options.zoomInText,this.options.zoomInTitle,e+"-in",i,this._zoomIn,this),this._zoomOutButton=this._createButton(this.options.zoomOutText,this.options.zoomOutTitle,e+"-out",i,this._zoomOut,this),this._updateDisabled(),t.on("zoomend zoomlevelschange",this._updateDisabled,this),i},onRemove:function(t){t.off("zoomend zoomlevelschange",this._updateDisabled,this)},_zoomIn:function(t){this._map.zoomIn(t.shiftKey?3:1)},_zoomOut:function(t){this._map.zoomOut(t.shiftKey?3:1)},_createButton:function(t,e,i,n,s,a){var r=o.DomUtil.create("a",i,n);r.innerHTML=t,r.href="#",r.title=e;var h=o.DomEvent.stopPropagation;return o.DomEvent.on(r,"click",h).on(r,"mousedown",h).on(r,"dblclick",h).on(r,"click",o.DomEvent.preventDefault).on(r,"click",s,a).on(r,"click",this._refocusOnMap,a),r},_updateDisabled:function(){var t=this._map,e="leaflet-disabled";o.DomUtil.removeClass(this._zoomInButton,e),o.DomUtil.removeClass(this._zoomOutButton,e),t._zoom===t.getMinZoom()&&o.DomUtil.addClass(this._zoomOutButton,e),t._zoom===t.getMaxZoom()&&o.DomUtil.addClass(this._zoomInButton,e)}}),o.Map.mergeOptions({zoomControl:!0}),o.Map.addInitHook(function(){this.options.zoomControl&&(this.zoomControl=new o.Control.Zoom,this.addControl(this.zoomControl))}),o.control.zoom=function(t){return new o.Control.Zoom(t)},o.Control.Attribution=o.Control.extend({options:{position:"bottomright",prefix:'<a href="http://leafletjs.com" title="A JS library for interactive maps">Leaflet</a>'},initialize:function(t){o.setOptions(this,t),this._attributions={}},onAdd:function(t){this._container=o.DomUtil.create("div","leaflet-control-attribution"),o.DomEvent.disableClickPropagation(this._container);for(var e in t._layers)t._layers[e].getAttribution&&this.addAttribution(t._layers[e].getAttribution());return t.on("layeradd",this._onLayerAdd,this).on("layerremove",this._onLayerRemove,this),this._update(),this._container},onRemove:function(t){t.off("layeradd",this._onLayerAdd).off("layerremove",this._onLayerRemove)},setPrefix:function(t){return this.options.prefix=t,this._update(),this},addAttribution:function(t){return t?(this._attributions[t]||(this._attributions[t]=0),this._attributions[t]++,this._update(),this):void 0},removeAttribution:function(t){return t?(this._attributions[t]&&(this._attributions[t]--,this._update()),this):void 0},_update:function(){if(this._map){var t=[];for(var e in this._attributions)this._attributions[e]&&t.push(e);var i=[];this.options.prefix&&i.push(this.options.prefix),t.length&&i.push(t.join(", ")),this._container.innerHTML=i.join(" | ")}},_onLayerAdd:function(t){t.layer.getAttribution&&this.addAttribution(t.layer.getAttribution())},_onLayerRemove:function(t){t.layer.getAttribution&&this.removeAttribution(t.layer.getAttribution())}}),o.Map.mergeOptions({attributionControl:!0}),o.Map.addInitHook(function(){this.options.attributionControl&&(this.attributionControl=(new o.Control.Attribution).addTo(this))}),o.control.attribution=function(t){return new o.Control.Attribution(t)},o.Control.Scale=o.Control.extend({options:{position:"bottomleft",maxWidth:100,metric:!0,imperial:!0,updateWhenIdle:!1},onAdd:function(t){this._map=t;var e="leaflet-control-scale",i=o.DomUtil.create("div",e),n=this.options;return this._addScales(n,e,i),t.on(n.updateWhenIdle?"moveend":"move",this._update,this),t.whenReady(this._update,this),i},onRemove:function(t){t.off(this.options.updateWhenIdle?"moveend":"move",this._update,this)},_addScales:function(t,e,i){t.metric&&(this._mScale=o.DomUtil.create("div",e+"-line",i)),t.imperial&&(this._iScale=o.DomUtil.create("div",e+"-line",i))},_update:function(){var t=this._map.getBounds(),e=t.getCenter().lat,i=6378137*Math.PI*Math.cos(e*Math.PI/180),n=i*(t.getNorthEast().lng-t.getSouthWest().lng)/180,o=this._map.getSize(),s=this.options,a=0;o.x>0&&(a=n*(s.maxWidth/o.x)),this._updateScales(s,a)},_updateScales:function(t,e){t.metric&&e&&this._updateMetric(e),t.imperial&&e&&this._updateImperial(e)},_updateMetric:function(t){var e=this._getRoundNum(t);this._mScale.style.width=this._getScaleWidth(e/t)+"px",this._mScale.innerHTML=1e3>e?e+" m":e/1e3+" km"},_updateImperial:function(t){var e,i,n,o=3.2808399*t,s=this._iScale;o>5280?(e=o/5280,i=this._getRoundNum(e),s.style.width=this._getScaleWidth(i/e)+"px",s.innerHTML=i+" mi"):(n=this._getRoundNum(o),s.style.width=this._getScaleWidth(n/o)+"px",s.innerHTML=n+" ft")},_getScaleWidth:function(t){return Math.round(this.options.maxWidth*t)-10},_getRoundNum:function(t){var e=Math.pow(10,(Math.floor(t)+"").length-1),i=t/e;return i=i>=10?10:i>=5?5:i>=3?3:i>=2?2:1,e*i}}),o.control.scale=function(t){return new o.Control.Scale(t)},o.Control.Layers=o.Control.extend({options:{collapsed:!0,position:"topright",autoZIndex:!0},initialize:function(t,e,i){o.setOptions(this,i),this._layers={},this._lastZIndex=0,this._handlingClick=!1;for(var n in t)this._addLayer(t[n],n);for(n in e)this._addLayer(e[n],n,!0)},onAdd:function(t){return this._initLayout(),this._update(),t.on("layeradd",this._onLayerChange,this).on("layerremove",this._onLayerChange,this),this._container},onRemove:function(t){t.off("layeradd",this._onLayerChange,this).off("layerremove",this._onLayerChange,this)},addBaseLayer:function(t,e){return this._addLayer(t,e),this._update(),this},addOverlay:function(t,e){return this._addLayer(t,e,!0),this._update(),this},removeLayer:function(t){var e=o.stamp(t);return delete this._layers[e],this._update(),this},_initLayout:function(){var t="leaflet-control-layers",e=this._container=o.DomUtil.create("div",t);e.setAttribute("aria-haspopup",!0),o.Browser.touch?o.DomEvent.on(e,"click",o.DomEvent.stopPropagation):o.DomEvent.disableClickPropagation(e).disableScrollPropagation(e);var i=this._form=o.DomUtil.create("form",t+"-list");if(this.options.collapsed){o.Browser.android||o.DomEvent.on(e,"mouseover",this._expand,this).on(e,"mouseout",this._collapse,this);var n=this._layersLink=o.DomUtil.create("a",t+"-toggle",e);n.href="#",n.title="Layers",o.Browser.touch?o.DomEvent.on(n,"click",o.DomEvent.stop).on(n,"click",this._expand,this):o.DomEvent.on(n,"focus",this._expand,this),o.DomEvent.on(i,"click",function(){setTimeout(o.bind(this._onInputClick,this),0)},this),this._map.on("click",this._collapse,this)}else this._expand();this._baseLayersList=o.DomUtil.create("div",t+"-base",i),this._separator=o.DomUtil.create("div",t+"-separator",i),this._overlaysList=o.DomUtil.create("div",t+"-overlays",i),e.appendChild(i)},_addLayer:function(t,e,i){var n=o.stamp(t);this._layers[n]={layer:t,name:e,overlay:i},this.options.autoZIndex&&t.setZIndex&&(this._lastZIndex++,t.setZIndex(this._lastZIndex))},_update:function(){if(this._container){this._baseLayersList.innerHTML="",this._overlaysList.innerHTML="";var t,e,i=!1,n=!1;for(t in this._layers)e=this._layers[t],this._addItem(e),n=n||e.overlay,i=i||!e.overlay;this._separator.style.display=n&&i?"":"none"}},_onLayerChange:function(t){var e=this._layers[o.stamp(t.layer)];if(e){this._handlingClick||this._update();var i=e.overlay?"layeradd"===t.type?"overlayadd":"overlayremove":"layeradd"===t.type?"baselayerchange":null;i&&this._map.fire(i,e)}},_createRadioElement:function(t,i){var n='<input type="radio" class="leaflet-control-layers-selector" name="'+t+'"';i&&(n+=' checked="checked"'),n+="/>";var o=e.createElement("div");return o.innerHTML=n,o.firstChild},_addItem:function(t){var i,n=e.createElement("label"),s=this._map.hasLayer(t.layer);t.overlay?(i=e.createElement("input"),i.type="checkbox",i.className="leaflet-control-layers-selector",i.defaultChecked=s):i=this._createRadioElement("leaflet-base-layers",s),i.layerId=o.stamp(t.layer),o.DomEvent.on(i,"click",this._onInputClick,this);var a=e.createElement("span");a.innerHTML=" "+t.name,n.appendChild(i),n.appendChild(a);var r=t.overlay?this._overlaysList:this._baseLayersList;return r.appendChild(n),n},_onInputClick:function(){var t,e,i,n=this._form.getElementsByTagName("input"),o=n.length;for(this._handlingClick=!0,t=0;o>t;t++)e=n[t],i=this._layers[e.layerId],e.checked&&!this._map.hasLayer(i.layer)?this._map.addLayer(i.layer):!e.checked&&this._map.hasLayer(i.layer)&&this._map.removeLayer(i.layer);this._handlingClick=!1,this._refocusOnMap()},_expand:function(){o.DomUtil.addClass(this._container,"leaflet-control-layers-expanded")},_collapse:function(){this._container.className=this._container.className.replace(" leaflet-control-layers-expanded","")}}),o.control.layers=function(t,e,i){return new o.Control.Layers(t,e,i)},o.PosAnimation=o.Class.extend({includes:o.Mixin.Events,run:function(t,e,i,n){this.stop(),this._el=t,this._inProgress=!0,this._newPos=e,this.fire("start"),t.style[o.DomUtil.TRANSITION]="all "+(i||.25)+"s cubic-bezier(0,0,"+(n||.5)+",1)",o.DomEvent.on(t,o.DomUtil.TRANSITION_END,this._onTransitionEnd,this),o.DomUtil.setPosition(t,e),o.Util.falseFn(t.offsetWidth),this._stepTimer=setInterval(o.bind(this._onStep,this),50)},stop:function(){this._inProgress&&(o.DomUtil.setPosition(this._el,this._getPos()),this._onTransitionEnd(),o.Util.falseFn(this._el.offsetWidth))},_onStep:function(){var t=this._getPos();return t?(this._el._leaflet_pos=t,void this.fire("step")):void this._onTransitionEnd()},_transformRe:/([-+]?(?:\d*\.)?\d+)\D*, ([-+]?(?:\d*\.)?\d+)\D*\)/,_getPos:function(){var e,i,n,s=this._el,a=t.getComputedStyle(s);if(o.Browser.any3d){if(n=a[o.DomUtil.TRANSFORM].match(this._transformRe),!n)return;e=parseFloat(n[1]),i=parseFloat(n[2])}else e=parseFloat(a.left),i=parseFloat(a.top);return new o.Point(e,i,!0)},_onTransitionEnd:function(){o.DomEvent.off(this._el,o.DomUtil.TRANSITION_END,this._onTransitionEnd,this),this._inProgress&&(this._inProgress=!1,this._el.style[o.DomUtil.TRANSITION]="",this._el._leaflet_pos=this._newPos,clearInterval(this._stepTimer),this.fire("step").fire("end"))}}),o.Map.include({setView:function(t,e,n){if(e=e===i?this._zoom:this._limitZoom(e),t=this._limitCenter(o.latLng(t),e,this.options.maxBounds),n=n||{},this._panAnim&&this._panAnim.stop(),this._loaded&&!n.reset&&n!==!0){n.animate!==i&&(n.zoom=o.extend({animate:n.animate},n.zoom),n.pan=o.extend({animate:n.animate},n.pan));var s=this._zoom!==e?this._tryAnimatedZoom&&this._tryAnimatedZoom(t,e,n.zoom):this._tryAnimatedPan(t,n.pan);if(s)return clearTimeout(this._sizeTimer),this}return this._resetView(t,e),this},panBy:function(t,e){if(t=o.point(t).round(),e=e||{},!t.x&&!t.y)return this;if(this._panAnim||(this._panAnim=new o.PosAnimation,this._panAnim.on({step:this._onPanTransitionStep,end:this._onPanTransitionEnd},this)),e.noMoveStart||this.fire("movestart"),e.animate!==!1){o.DomUtil.addClass(this._mapPane,"leaflet-pan-anim");var i=this._getMapPanePos().subtract(t);this._panAnim.run(this._mapPane,i,e.duration||.25,e.easeLinearity)}else this._rawPanBy(t),this.fire("move").fire("moveend");return this},_onPanTransitionStep:function(){this.fire("move")},_onPanTransitionEnd:function(){o.DomUtil.removeClass(this._mapPane,"leaflet-pan-anim"),this.fire("moveend")},_tryAnimatedPan:function(t,e){var i=this._getCenterOffset(t)._floor();return(e&&e.animate)===!0||this.getSize().contains(i)?(this.panBy(i,e),!0):!1}}),o.PosAnimation=o.DomUtil.TRANSITION?o.PosAnimation:o.PosAnimation.extend({run:function(t,e,i,n){this.stop(),this._el=t,this._inProgress=!0,this._duration=i||.25,this._easeOutPower=1/Math.max(n||.5,.2),this._startPos=o.DomUtil.getPosition(t),this._offset=e.subtract(this._startPos),this._startTime=+new Date,this.fire("start"),this._animate()},stop:function(){this._inProgress&&(this._step(),this._complete())},_animate:function(){this._animId=o.Util.requestAnimFrame(this._animate,this),this._step()},_step:function(){var t=+new Date-this._startTime,e=1e3*this._duration;e>t?this._runFrame(this._easeOut(t/e)):(this._runFrame(1),this._complete())},_runFrame:function(t){var e=this._startPos.add(this._offset.multiplyBy(t));o.DomUtil.setPosition(this._el,e),this.fire("step")},_complete:function(){o.Util.cancelAnimFrame(this._animId),this._inProgress=!1,this.fire("end")},_easeOut:function(t){return 1-Math.pow(1-t,this._easeOutPower)}}),o.Map.mergeOptions({zoomAnimation:!0,zoomAnimationThreshold:4}),o.DomUtil.TRANSITION&&o.Map.addInitHook(function(){this._zoomAnimated=this.options.zoomAnimation&&o.DomUtil.TRANSITION&&o.Browser.any3d&&!o.Browser.android23&&!o.Browser.mobileOpera,this._zoomAnimated&&o.DomEvent.on(this._mapPane,o.DomUtil.TRANSITION_END,this._catchTransitionEnd,this)}),o.Map.include(o.DomUtil.TRANSITION?{_catchTransitionEnd:function(t){this._animatingZoom&&t.propertyName.indexOf("transform")>=0&&this._onZoomTransitionEnd()},_nothingToAnimate:function(){return!this._container.getElementsByClassName("leaflet-zoom-animated").length},_tryAnimatedZoom:function(t,e,i){if(this._animatingZoom)return!0;if(i=i||{},!this._zoomAnimated||i.animate===!1||this._nothingToAnimate()||Math.abs(e-this._zoom)>this.options.zoomAnimationThreshold)return!1;var n=this.getZoomScale(e),o=this._getCenterOffset(t)._divideBy(1-1/n),s=this._getCenterLayerPoint()._add(o);return i.animate===!0||this.getSize().contains(o)?(this.fire("movestart").fire("zoomstart"),this._animateZoom(t,e,s,n,null,!0),!0):!1},_animateZoom:function(t,e,i,n,s,a,r){r||(this._animatingZoom=!0),o.DomUtil.addClass(this._mapPane,"leaflet-zoom-anim"),this._animateToCenter=t,this._animateToZoom=e,o.Draggable&&(o.Draggable._disabled=!0),o.Util.requestAnimFrame(function(){this.fire("zoomanim",{center:t,zoom:e,origin:i,scale:n,delta:s,backwards:a}),setTimeout(o.bind(this._onZoomTransitionEnd,this),250)},this)},_onZoomTransitionEnd:function(){this._animatingZoom&&(this._animatingZoom=!1,o.DomUtil.removeClass(this._mapPane,"leaflet-zoom-anim"),o.Util.requestAnimFrame(function(){this._resetView(this._animateToCenter,this._animateToZoom,!0,!0),o.Draggable&&(o.Draggable._disabled=!1)},this))}}:{}),o.TileLayer.include({_animateZoom:function(t){this._animating||(this._animating=!0,this._prepareBgBuffer());var e=this._bgBuffer,i=o.DomUtil.TRANSFORM,n=t.delta?o.DomUtil.getTranslateString(t.delta):e.style[i],s=o.DomUtil.getScaleString(t.scale,t.origin);e.style[i]=t.backwards?s+" "+n:n+" "+s},_endZoomAnim:function(){var t=this._tileContainer,e=this._bgBuffer;t.style.visibility="",t.parentNode.appendChild(t),o.Util.falseFn(e.offsetWidth);var i=this._map.getZoom();(i>this.options.maxZoom||i<this.options.minZoom)&&this._clearBgBuffer(),this._animating=!1},_clearBgBuffer:function(){var t=this._map;!t||t._animatingZoom||t.touchZoom._zooming||(this._bgBuffer.innerHTML="",this._bgBuffer.style[o.DomUtil.TRANSFORM]="")},_prepareBgBuffer:function(){var t=this._tileContainer,e=this._bgBuffer,i=this._getLoadedTilesPercentage(e),n=this._getLoadedTilesPercentage(t);return e&&i>.5&&.5>n?(t.style.visibility="hidden",void this._stopLoadingImages(t)):(e.style.visibility="hidden",e.style[o.DomUtil.TRANSFORM]="",this._tileContainer=e,e=this._bgBuffer=t,this._stopLoadingImages(e),void clearTimeout(this._clearBgBufferTimer))},_getLoadedTilesPercentage:function(t){var e,i,n=t.getElementsByTagName("img"),o=0;for(e=0,i=n.length;i>e;e++)n[e].complete&&o++;return o/i},_stopLoadingImages:function(t){var e,i,n,s=Array.prototype.slice.call(t.getElementsByTagName("img"));for(e=0,i=s.length;i>e;e++)n=s[e],n.complete||(n.onload=o.Util.falseFn,n.onerror=o.Util.falseFn,n.src=o.Util.emptyImageUrl,n.parentNode.removeChild(n))}}),o.Map.include({_defaultLocateOptions:{watch:!1,setView:!1,maxZoom:1/0,timeout:1e4,maximumAge:0,enableHighAccuracy:!1},locate:function(t){if(t=this._locateOptions=o.extend(this._defaultLocateOptions,t),!navigator.geolocation)return this._handleGeolocationError({code:0,message:"Geolocation not supported."}),this;var e=o.bind(this._handleGeolocationResponse,this),i=o.bind(this._handleGeolocationError,this);return t.watch?this._locationWatchId=navigator.geolocation.watchPosition(e,i,t):navigator.geolocation.getCurrentPosition(e,i,t),this},stopLocate:function(){return navigator.geolocation&&navigator.geolocation.clearWatch(this._locationWatchId),this._locateOptions&&(this._locateOptions.setView=!1),this},_handleGeolocationError:function(t){var e=t.code,i=t.message||(1===e?"permission denied":2===e?"position unavailable":"timeout");this._locateOptions.setView&&!this._loaded&&this.fitWorld(),this.fire("locationerror",{code:e,message:"Geolocation error: "+i+"."})},_handleGeolocationResponse:function(t){var e=t.coords.latitude,i=t.coords.longitude,n=new o.LatLng(e,i),s=180*t.coords.accuracy/40075017,a=s/Math.cos(o.LatLng.DEG_TO_RAD*e),r=o.latLngBounds([e-s,i-a],[e+s,i+a]),h=this._locateOptions;if(h.setView){var l=Math.min(this.getBoundsZoom(r),h.maxZoom);this.setView(n,l)}var u={latlng:n,bounds:r,timestamp:t.timestamp};for(var c in t.coords)"number"==typeof t.coords[c]&&(u[c]=t.coords[c]);this.fire("locationfound",u)}})}(window,document);;
/*! leaflet.markercluster-src.js */(function (window, document, undefined) {/*
 * L.MarkerClusterGroup extends L.FeatureGroup by clustering the markers contained within
 */

    L.MarkerClusterGroup = L.FeatureGroup.extend({

        options: {
            maxClusterRadius: 80, //A cluster will cover at most this many pixels from its center
            iconCreateFunction: null,

            spiderfyOnMaxZoom: true,
            showCoverageOnHover: true,
            zoomToBoundsOnClick: true,
            singleMarkerMode: false,

            disableClusteringAtZoom: null,

            // Setting this to false prevents the removal of any clusters outside of the viewpoint, which
            // is the default behaviour for performance reasons.
            removeOutsideVisibleBounds: true,

            // Set to false to disable all animations (zoom and spiderfy).
            // If false, option animateAddingMarkers below has no effect.
            // If L.DomUtil.TRANSITION is falsy, this option has no effect.
            animate: true,

            //Whether to animate adding markers after adding the MarkerClusterGroup to the map
            // If you are adding individual markers set to true, if adding bulk markers leave false for massive performance gains.
            animateAddingMarkers: false,

            //Increase to increase the distance away that spiderfied markers appear from the center
            spiderfyDistanceMultiplier: 1,

            // Make it possible to specify a polyline options on a spider leg
            spiderLegPolylineOptions: { weight: 1.5, color: '#222', opacity: 0.5 },

            // When bulk adding layers, adds markers in chunks. Means addLayers may not add all the layers in the call, others will be loaded during setTimeouts
            chunkedLoading: false,
            chunkInterval: 200, // process markers for a maximum of ~ n milliseconds (then trigger the chunkProgress callback)
            chunkDelay: 50, // at the end of each interval, give n milliseconds back to system/browser
            chunkProgress: null, // progress callback: function(processed, total, elapsed) (e.g. for a progress indicator)

            //Options to pass to the L.Polygon constructor
            polygonOptions: {}
        },

        initialize: function (options) {
            L.Util.setOptions(this, options);
            if (!this.options.iconCreateFunction) {
                this.options.iconCreateFunction = this._defaultIconCreateFunction;
            }

            this._featureGroup = L.featureGroup();
            this._featureGroup.on(L.FeatureGroup.EVENTS, this._propagateEvent, this);

            this._nonPointGroup = L.featureGroup();
            this._nonPointGroup.on(L.FeatureGroup.EVENTS, this._propagateEvent, this);

            this._inZoomAnimation = 0;
            this._needsClustering = [];
            this._needsRemoving = []; //Markers removed while we aren't on the map need to be kept track of
            //The bounds of the currently shown area (from _getExpandedVisibleBounds) Updated on zoom/move
            this._currentShownBounds = null;

            this._queue = [];

            // Hook the appropriate animation methods.
            var animate = L.DomUtil.TRANSITION && this.options.animate;
            L.extend(this, animate ? this._withAnimation : this._noAnimation);
            // Remember which MarkerCluster class to instantiate (animated or not).
            this._markerCluster = animate ? L.MarkerCluster : L.MarkerClusterNonAnimated;
        },

        addLayer: function (layer) {

            if (layer instanceof L.LayerGroup) {
                var array = [];
                for (var i in layer._layers) {
                    array.push(layer._layers[i]);
                }
                return this.addLayers(array);
            }

            //Don't cluster non point data
            if (!layer.getLatLng) {
                this._nonPointGroup.addLayer(layer);
                return this;
            }

            if (!this._map) {
                this._needsClustering.push(layer);
                return this;
            }

            if (this.hasLayer(layer)) {
                return this;
            }


            //If we have already clustered we'll need to add this one to a cluster

            if (this._unspiderfy) {
                this._unspiderfy();
            }

            this._addLayer(layer, this._maxZoom);

            // Refresh bounds and weighted positions.
            this._topClusterLevel._recalculateBounds();

            //Work out what is visible
            var visibleLayer = layer,
                currentZoom = this._map.getZoom();
            if (layer.__parent) {
                while (visibleLayer.__parent._zoom >= currentZoom) {
                    visibleLayer = visibleLayer.__parent;
                }
            }

            if (this._currentShownBounds.contains(visibleLayer.getLatLng())) {
                if (this.options.animateAddingMarkers) {
                    this._animationAddLayer(layer, visibleLayer);
                } else {
                    this._animationAddLayerNonAnimated(layer, visibleLayer);
                }
            }
            return this;
        },

        removeLayer: function (layer) {

            if (layer instanceof L.LayerGroup) {
                var array = [];
                for (var i in layer._layers) {
                    array.push(layer._layers[i]);
                }
                return this.removeLayers(array);
            }

            //Non point layers
            if (!layer.getLatLng) {
                this._nonPointGroup.removeLayer(layer);
                return this;
            }

            if (!this._map) {
                if (!this._arraySplice(this._needsClustering, layer) && this.hasLayer(layer)) {
                    this._needsRemoving.push(layer);
                }
                return this;
            }

            if (!layer.__parent) {
                return this;
            }

            if (this._unspiderfy) {
                this._unspiderfy();
                this._unspiderfyLayer(layer);
            }

            //Remove the marker from clusters
            this._removeLayer(layer, true);

            // Refresh bounds and weighted positions.
            this._topClusterLevel._recalculateBounds();

            if (this._featureGroup.hasLayer(layer)) {
                this._featureGroup.removeLayer(layer);
                if (layer.clusterShow) {
                    layer.clusterShow();
                }
            }

            return this;
        },

        //Takes an array of markers and adds them in bulk
        addLayers: function (layersArray) {
            var fg = this._featureGroup,
                npg = this._nonPointGroup,
                chunked = this.options.chunkedLoading,
                chunkInterval = this.options.chunkInterval,
                chunkProgress = this.options.chunkProgress,
                newMarkers, i, l, m;

            if (this._map) {
                var offset = 0,
                    started = (new Date()).getTime();
                var process = L.bind(function () {
                    var start = (new Date()).getTime();
                    for (; offset < layersArray.length; offset++) {
                        if (chunked && offset % 200 === 0) {
                            // every couple hundred markers, instrument the time elapsed since processing started:
                            var elapsed = (new Date()).getTime() - start;
                            if (elapsed > chunkInterval) {
                                break; // been working too hard, time to take a break :-)
                            }
                        }

                        m = layersArray[offset];

                        //Not point data, can't be clustered
                        if (!m.getLatLng) {
                            npg.addLayer(m);
                            continue;
                        }

                        if (this.hasLayer(m)) {
                            continue;
                        }

                        this._addLayer(m, this._maxZoom);

                        //If we just made a cluster of size 2 then we need to remove the other marker from the map (if it is) or we never will
                        if (m.__parent) {
                            if (m.__parent.getChildCount() === 2) {
                                var markers = m.__parent.getAllChildMarkers(),
                                    otherMarker = markers[0] === m ? markers[1] : markers[0];
                                fg.removeLayer(otherMarker);
                            }
                        }
                    }

                    if (chunkProgress) {
                        // report progress and time elapsed:
                        chunkProgress(offset, layersArray.length, (new Date()).getTime() - started);
                    }

                    // Completed processing all markers.
                    if (offset === layersArray.length) {

                        // Refresh bounds and weighted positions.
                        this._topClusterLevel._recalculateBounds();

                        //Update the icons of all those visible clusters that were affected
                        this._featureGroup.eachLayer(function (c) {
                            if (c instanceof L.MarkerCluster && c._iconNeedsUpdate) {
                                c._updateIcon();
                            }
                        });

                        this._topClusterLevel._recursivelyAddChildrenToMap(null, this._zoom, this._currentShownBounds);
                    } else {
                        setTimeout(process, this.options.chunkDelay);
                    }
                }, this);

                process();
            } else {
                newMarkers = [];
                for (i = 0, l = layersArray.length; i < l; i++) {
                    m = layersArray[i];

                    //Not point data, can't be clustered
                    if (!m.getLatLng) {
                        npg.addLayer(m);
                        continue;
                    }

                    if (this.hasLayer(m)) {
                        continue;
                    }

                    newMarkers.push(m);
                }
                this._needsClustering = this._needsClustering.concat(newMarkers);
            }
            return this;
        },

        //Takes an array of markers and removes them in bulk
        removeLayers: function (layersArray) {
            var i, l, m,
                fg = this._featureGroup,
                npg = this._nonPointGroup;

            if (!this._map) {
                for (i = 0, l = layersArray.length; i < l; i++) {
                    m = layersArray[i];
                    this._arraySplice(this._needsClustering, m);
                    npg.removeLayer(m);
                    if (this.hasLayer(m)) {
                        this._needsRemoving.push(m);
                    }
                }
                return this;
            }

            if (this._unspiderfy) {
                this._unspiderfy();
                for (i = 0, l = layersArray.length; i < l; i++) {
                    m = layersArray[i];
                    this._unspiderfyLayer(m);
                }
            }

            for (i = 0, l = layersArray.length; i < l; i++) {
                m = layersArray[i];

                if (!m.__parent) {
                    npg.removeLayer(m);
                    continue;
                }

                this._removeLayer(m, true, true);

                if (fg.hasLayer(m)) {
                    fg.removeLayer(m);
                    if (m.clusterShow) {
                        m.clusterShow();
                    }
                }
            }

            // Refresh bounds and weighted positions.
            this._topClusterLevel._recalculateBounds();

            //Fix up the clusters and markers on the map
            this._topClusterLevel._recursivelyAddChildrenToMap(null, this._zoom, this._currentShownBounds);

            fg.eachLayer(function (c) {
                if (c instanceof L.MarkerCluster) {
                    c._updateIcon();
                }
            });

            return this;
        },

        //Removes all layers from the MarkerClusterGroup
        clearLayers: function () {
            //Need our own special implementation as the LayerGroup one doesn't work for us

            //If we aren't on the map (yet), blow away the markers we know of
            if (!this._map) {
                this._needsClustering = [];
                delete this._gridClusters;
                delete this._gridUnclustered;
            }

            if (this._noanimationUnspiderfy) {
                this._noanimationUnspiderfy();
            }

            //Remove all the visible layers
            this._featureGroup.clearLayers();
            this._nonPointGroup.clearLayers();

            this.eachLayer(function (marker) {
                delete marker.__parent;
            });

            if (this._map) {
                //Reset _topClusterLevel and the DistanceGrids
                this._generateInitialClusters();
            }

            return this;
        },

        //Override FeatureGroup.getBounds as it doesn't work
        getBounds: function () {
            var bounds = new L.LatLngBounds();

            if (this._topClusterLevel) {
                bounds.extend(this._topClusterLevel._bounds);
            }

            for (var i = this._needsClustering.length - 1; i >= 0; i--) {
                bounds.extend(this._needsClustering[i].getLatLng());
            }

            bounds.extend(this._nonPointGroup.getBounds());

            return bounds;
        },

        //Overrides LayerGroup.eachLayer
        eachLayer: function (method, context) {
            var markers = this._needsClustering.slice(),
                i;

            if (this._topClusterLevel) {
                this._topClusterLevel.getAllChildMarkers(markers);
            }

            for (i = markers.length - 1; i >= 0; i--) {
                method.call(context, markers[i]);
            }

            this._nonPointGroup.eachLayer(method, context);
        },

        //Overrides LayerGroup.getLayers
        getLayers: function () {
            var layers = [];
            this.eachLayer(function (l) {
                layers.push(l);
            });
            return layers;
        },

        //Overrides LayerGroup.getLayer, WARNING: Really bad performance
        getLayer: function (id) {
            var result = null;

            id = parseInt(id, 10);

            this.eachLayer(function (l) {
                if (L.stamp(l) === id) {
                    result = l;
                }
            });

            return result;
        },

        //Returns true if the given layer is in this MarkerClusterGroup
        hasLayer: function (layer) {
            if (!layer) {
                return false;
            }

            var i, anArray = this._needsClustering;

            for (i = anArray.length - 1; i >= 0; i--) {
                if (anArray[i] === layer) {
                    return true;
                }
            }

            anArray = this._needsRemoving;
            for (i = anArray.length - 1; i >= 0; i--) {
                if (anArray[i] === layer) {
                    return false;
                }
            }

            return !!(layer.__parent && layer.__parent._group === this) || this._nonPointGroup.hasLayer(layer);
        },

        //Zoom down to show the given layer (spiderfying if necessary) then calls the callback
        zoomToShowLayer: function (layer, callback) {

            if (typeof callback !== 'function') {
                callback = function () { };
            }

            var showMarker = function () {
                if ((layer._icon || layer.__parent._icon) && !this._inZoomAnimation) {
                    this._map.off('moveend', showMarker, this);
                    this.off('animationend', showMarker, this);

                    if (layer._icon) {
                        callback();
                    } else if (layer.__parent._icon) {
                        this.once('spiderfied', callback, this);
                        layer.__parent.spiderfy();
                    }
                }
            };

            if (layer._icon && this._map.getBounds().contains(layer.getLatLng())) {
                //Layer is visible ond on screen, immediate return
                callback();
            } else if (layer.__parent._zoom < this._map.getZoom()) {
                //Layer should be visible at this zoom level. It must not be on screen so just pan over to it
                this._map.on('moveend', showMarker, this);
                this._map.panTo(layer.getLatLng());
            } else {
                var moveStart = function () {
                    this._map.off('movestart', moveStart, this);
                    moveStart = null;
                };

                this._map.on('movestart', moveStart, this);
                this._map.on('moveend', showMarker, this);
                this.on('animationend', showMarker, this);
                layer.__parent.zoomToBounds();

                if (moveStart) {
                    //Never started moving, must already be there, probably need clustering however
                    showMarker.call(this);
                }
            }
        },

        //Overrides FeatureGroup.onAdd
        onAdd: function (map) {
            this._map = map;
            var i, l, layer;

            if (!isFinite(this._map.getMaxZoom())) {
                throw "Map has no maxZoom specified";
            }

            this._featureGroup.onAdd(map);
            this._nonPointGroup.onAdd(map);

            if (!this._gridClusters) {
                this._generateInitialClusters();
            }

            this._maxLat = map.options.crs.projection.MAX_LATITUDE;

            for (i = 0, l = this._needsRemoving.length; i < l; i++) {
                layer = this._needsRemoving[i];
                this._removeLayer(layer, true);
            }
            this._needsRemoving = [];

            //Remember the current zoom level and bounds
            this._zoom = this._map.getZoom();
            this._currentShownBounds = this._getExpandedVisibleBounds();

            this._map.on('zoomend', this._zoomEnd, this);
            this._map.on('moveend', this._moveEnd, this);

            if (this._spiderfierOnAdd) { //TODO FIXME: Not sure how to have spiderfier add something on here nicely
                this._spiderfierOnAdd();
            }

            this._bindEvents();

            //Actually add our markers to the map:
            l = this._needsClustering;
            this._needsClustering = [];
            this.addLayers(l);
        },

        //Overrides FeatureGroup.onRemove
        onRemove: function (map) {
            map.off('zoomend', this._zoomEnd, this);
            map.off('moveend', this._moveEnd, this);

            this._unbindEvents();

            //In case we are in a cluster animation
            this._map._mapPane.className = this._map._mapPane.className.replace(' leaflet-cluster-anim', '');

            if (this._spiderfierOnRemove) { //TODO FIXME: Not sure how to have spiderfier add something on here nicely
                this._spiderfierOnRemove();
            }

            delete this._maxLat;

            //Clean up all the layers we added to the map
            this._hideCoverage();
            this._featureGroup.onRemove(map);
            this._nonPointGroup.onRemove(map);

            this._featureGroup.clearLayers();

            this._map = null;
        },

        getVisibleParent: function (marker) {
            var vMarker = marker;
            while (vMarker && !vMarker._icon) {
                vMarker = vMarker.__parent;
            }
            return vMarker || null;
        },

        //Remove the given object from the given array
        _arraySplice: function (anArray, obj) {
            for (var i = anArray.length - 1; i >= 0; i--) {
                if (anArray[i] === obj) {
                    anArray.splice(i, 1);
                    return true;
                }
            }
        },

        /**
         * Removes a marker from all _gridUnclustered zoom levels, starting at the supplied zoom.
         * @param marker to be removed from _gridUnclustered.
         * @param z integer bottom start zoom level (included)
         * @private
         */
        _removeFromGridUnclustered: function (marker, z) {
            var map = this._map,
                gridUnclustered = this._gridUnclustered;

            for (; z >= 0; z--) {
                if (!gridUnclustered[z].removeObject(marker, map.project(marker.getLatLng(), z))) {
                    break;
                }
            }
        },

        //Internal function for removing a marker from everything.
        //dontUpdateMap: set to true if you will handle updating the map manually (for bulk functions)
        _removeLayer: function (marker, removeFromDistanceGrid, dontUpdateMap) {
            var gridClusters = this._gridClusters,
                gridUnclustered = this._gridUnclustered,
                fg = this._featureGroup,
                map = this._map;

            //Remove the marker from distance clusters it might be in
            if (removeFromDistanceGrid) {
                this._removeFromGridUnclustered(marker, this._maxZoom);
            }

            //Work our way up the clusters removing them as we go if required
            var cluster = marker.__parent,
                markers = cluster._markers,
                otherMarker;

            //Remove the marker from the immediate parents marker list
            this._arraySplice(markers, marker);

            while (cluster) {
                cluster._childCount--;
                cluster._boundsNeedUpdate = true;

                if (cluster._zoom < 0) {
                    //Top level, do nothing
                    break;
                } else if (removeFromDistanceGrid && cluster._childCount <= 1) { //Cluster no longer required
                    //We need to push the other marker up to the parent
                    otherMarker = cluster._markers[0] === marker ? cluster._markers[1] : cluster._markers[0];

                    //Update distance grid
                    gridClusters[cluster._zoom].removeObject(cluster, map.project(cluster._cLatLng, cluster._zoom));
                    gridUnclustered[cluster._zoom].addObject(otherMarker, map.project(otherMarker.getLatLng(), cluster._zoom));

                    //Move otherMarker up to parent
                    this._arraySplice(cluster.__parent._childClusters, cluster);
                    cluster.__parent._markers.push(otherMarker);
                    otherMarker.__parent = cluster.__parent;

                    if (cluster._icon) {
                        //Cluster is currently on the map, need to put the marker on the map instead
                        fg.removeLayer(cluster);
                        if (!dontUpdateMap) {
                            fg.addLayer(otherMarker);
                        }
                    }
                } else {
                    if (!dontUpdateMap || !cluster._icon) {
                        cluster._updateIcon();
                    }
                }

                cluster = cluster.__parent;
            }

            delete marker.__parent;
        },

        _isOrIsParent: function (el, oel) {
            while (oel) {
                if (el === oel) {
                    return true;
                }
                oel = oel.parentNode;
            }
            return false;
        },

        _propagateEvent: function (e) {
            if (e.layer instanceof L.MarkerCluster) {
                //Prevent multiple clustermouseover/off events if the icon is made up of stacked divs (Doesn't work in ie <= 8, no relatedTarget)
                if (e.originalEvent && this._isOrIsParent(e.layer._icon, e.originalEvent.relatedTarget)) {
                    return;
                }
                e.type = 'cluster' + e.type;
            }

            this.fire(e.type, e);
        },

        //Default functionality
        _defaultIconCreateFunction: function (cluster) {
            var childCount = cluster.getChildCount();

            var c = ' marker-cluster-';
            if (childCount < 10) {
                c += 'small';
            } else if (childCount < 100) {
                c += 'medium';
            } else {
                c += 'large';
            }

            return new L.DivIcon({ html: '<div><span>' + childCount + '</span></div>', className: 'marker-cluster' + c, iconSize: new L.Point(40, 40) });
        },

        _bindEvents: function () {
            var map = this._map,
                spiderfyOnMaxZoom = this.options.spiderfyOnMaxZoom,
                showCoverageOnHover = this.options.showCoverageOnHover,
                zoomToBoundsOnClick = this.options.zoomToBoundsOnClick;

            //Zoom on cluster click or spiderfy if we are at the lowest level
            if (spiderfyOnMaxZoom || zoomToBoundsOnClick) {
                this.on('clusterclick', this._zoomOrSpiderfy, this);
            }

            //Show convex hull (boundary) polygon on mouse over
            if (showCoverageOnHover) {
                this.on('clustermouseover', this._showCoverage, this);
                this.on('clustermouseout', this._hideCoverage, this);
                map.on('zoomend', this._hideCoverage, this);
            }
        },

        _zoomOrSpiderfy: function (e) {
            var cluster = e.layer,
                bottomCluster = cluster;

            while (bottomCluster._childClusters.length === 1) {
                bottomCluster = bottomCluster._childClusters[0];
            }

            if (bottomCluster._zoom === this._maxZoom && bottomCluster._childCount === cluster._childCount) {
                // All child markers are contained in a single cluster from this._maxZoom to this cluster.
                if (this.options.spiderfyOnMaxZoom) {
                    cluster.spiderfy();
                }
            } else if (this.options.zoomToBoundsOnClick) {
                cluster.zoomToBounds();
            }

            // Focus the map again for keyboard users.
            if (e.originalEvent && e.originalEvent.keyCode === 13) {
                this._map._container.focus();
            }
        },

        _showCoverage: function (e) {
            var map = this._map;
            if (this._inZoomAnimation) {
                return;
            }
            if (this._shownPolygon) {
                map.removeLayer(this._shownPolygon);
            }
            if (e.layer.getChildCount() > 2 && e.layer !== this._spiderfied) {
                this._shownPolygon = new L.Polygon(e.layer.getConvexHull(), this.options.polygonOptions);
                map.addLayer(this._shownPolygon);
            }
        },

        _hideCoverage: function () {
            if (this._shownPolygon) {
                this._map.removeLayer(this._shownPolygon);
                this._shownPolygon = null;
            }
        },

        _unbindEvents: function () {
            var spiderfyOnMaxZoom = this.options.spiderfyOnMaxZoom,
                showCoverageOnHover = this.options.showCoverageOnHover,
                zoomToBoundsOnClick = this.options.zoomToBoundsOnClick,
                map = this._map;

            if (spiderfyOnMaxZoom || zoomToBoundsOnClick) {
                this.off('clusterclick', this._zoomOrSpiderfy, this);
            }
            if (showCoverageOnHover) {
                this.off('clustermouseover', this._showCoverage, this);
                this.off('clustermouseout', this._hideCoverage, this);
                map.off('zoomend', this._hideCoverage, this);
            }
        },

        _zoomEnd: function () {
            if (!this._map) { //May have been removed from the map by a zoomEnd handler
                return;
            }
            this._mergeSplitClusters();

            this._zoom = this._map._zoom;
            this._currentShownBounds = this._getExpandedVisibleBounds();
        },

        _moveEnd: function () {
            if (this._inZoomAnimation) {
                return;
            }

            var newBounds = this._getExpandedVisibleBounds();

            this._topClusterLevel._recursivelyRemoveChildrenFromMap(this._currentShownBounds, this._zoom, newBounds);
            this._topClusterLevel._recursivelyAddChildrenToMap(null, this._map._zoom, newBounds);

            this._currentShownBounds = newBounds;
            return;
        },

        _generateInitialClusters: function () {
            var maxZoom = this._map.getMaxZoom(),
                radius = this.options.maxClusterRadius,
                radiusFn = radius;

            //If we just set maxClusterRadius to a single number, we need to create
            //a simple function to return that number. Otherwise, we just have to
            //use the function we've passed in.
            if (typeof radius !== "function") {
                radiusFn = function () { return radius; };
            }

            if (this.options.disableClusteringAtZoom) {
                maxZoom = this.options.disableClusteringAtZoom - 1;
            }
            this._maxZoom = maxZoom;
            this._gridClusters = {};
            this._gridUnclustered = {};

            //Set up DistanceGrids for each zoom
            for (var zoom = maxZoom; zoom >= 0; zoom--) {
                this._gridClusters[zoom] = new L.DistanceGrid(radiusFn(zoom));
                this._gridUnclustered[zoom] = new L.DistanceGrid(radiusFn(zoom));
            }

            // Instantiate the appropriate L.MarkerCluster class (animated or not).
            this._topClusterLevel = new this._markerCluster(this, -1);
        },

        //Zoom: Zoom to start adding at (Pass this._maxZoom to start at the bottom)
        _addLayer: function (layer, zoom) {
            var gridClusters = this._gridClusters,
                gridUnclustered = this._gridUnclustered,
                markerPoint, z;

            if (this.options.singleMarkerMode) {
                this._overrideMarkerIcon(layer);
            }

            //Find the lowest zoom level to slot this one in
            for (; zoom >= 0; zoom--) {
                markerPoint = this._map.project(layer.getLatLng(), zoom); // calculate pixel position

                //Try find a cluster close by
                var closest = gridClusters[zoom].getNearObject(markerPoint);
                if (closest) {
                    closest._addChild(layer);
                    layer.__parent = closest;
                    return;
                }

                //Try find a marker close by to form a new cluster with
                closest = gridUnclustered[zoom].getNearObject(markerPoint);
                if (closest) {
                    var parent = closest.__parent;
                    if (parent) {
                        this._removeLayer(closest, false);
                    }

                    //Create new cluster with these 2 in it

                    var newCluster = new this._markerCluster(this, zoom, closest, layer);
                    gridClusters[zoom].addObject(newCluster, this._map.project(newCluster._cLatLng, zoom));
                    closest.__parent = newCluster;
                    layer.__parent = newCluster;

                    //First create any new intermediate parent clusters that don't exist
                    var lastParent = newCluster;
                    for (z = zoom - 1; z > parent._zoom; z--) {
                        lastParent = new this._markerCluster(this, z, lastParent);
                        gridClusters[z].addObject(lastParent, this._map.project(closest.getLatLng(), z));
                    }
                    parent._addChild(lastParent);

                    //Remove closest from this zoom level and any above that it is in, replace with newCluster
                    this._removeFromGridUnclustered(closest, zoom);

                    return;
                }

                //Didn't manage to cluster in at this zoom, record us as a marker here and continue upwards
                gridUnclustered[zoom].addObject(layer, markerPoint);
            }

            //Didn't get in anything, add us to the top
            this._topClusterLevel._addChild(layer);
            layer.__parent = this._topClusterLevel;
            return;
        },

        //Enqueue code to fire after the marker expand/contract has happened
        _enqueue: function (fn) {
            this._queue.push(fn);
            if (!this._queueTimeout) {
                this._queueTimeout = setTimeout(L.bind(this._processQueue, this), 300);
            }
        },
        _processQueue: function () {
            for (var i = 0; i < this._queue.length; i++) {
                this._queue[i].call(this);
            }
            this._queue.length = 0;
            clearTimeout(this._queueTimeout);
            this._queueTimeout = null;
        },

        //Merge and split any existing clusters that are too big or small
        _mergeSplitClusters: function () {

            //Incase we are starting to split before the animation finished
            this._processQueue();

            if (this._zoom < this._map._zoom && this._currentShownBounds.intersects(this._getExpandedVisibleBounds())) { //Zoom in, split
                this._animationStart();
                //Remove clusters now off screen
                this._topClusterLevel._recursivelyRemoveChildrenFromMap(this._currentShownBounds, this._zoom, this._getExpandedVisibleBounds());

                this._animationZoomIn(this._zoom, this._map._zoom);

            } else if (this._zoom > this._map._zoom) { //Zoom out, merge
                this._animationStart();

                this._animationZoomOut(this._zoom, this._map._zoom);
            } else {
                this._moveEnd();
            }
        },

        //Gets the maps visible bounds expanded in each direction by the size of the screen (so the user cannot see an area we do not cover in one pan)
        _getExpandedVisibleBounds: function () {
            if (!this.options.removeOutsideVisibleBounds) {
                return this._mapBoundsInfinite;
            } else if (L.Browser.mobile) {
                return this._checkBoundsMaxLat(this._map.getBounds());
            }

            return this._checkBoundsMaxLat(this._map.getBounds().pad(1)); // Padding expands the bounds by its own dimensions but scaled with the given factor.
        },

        /**
         * Expands the latitude to Infinity (or -Infinity) if the input bounds reach the map projection maximum defined latitude
         * (in the case of Web/Spherical Mercator, it is 85.0511287798 / see https://en.wikipedia.org/wiki/Web_Mercator#Formulas).
         * Otherwise, the removeOutsideVisibleBounds option will remove markers beyond that limit, whereas the same markers without
         * this option (or outside MCG) will have their position floored (ceiled) by the projection and rendered at that limit,
         * making the user think that MCG "eats" them and never displays them again.
         * @param bounds L.LatLngBounds
         * @returns {L.LatLngBounds}
         * @private
         */
        _checkBoundsMaxLat: function (bounds) {
            var maxLat = this._maxLat;

            if (maxLat !== undefined) {
                if (bounds.getNorth() >= maxLat) {
                    bounds._northEast.lat = Infinity;
                }
                if (bounds.getSouth() <= -maxLat) {
                    bounds._southWest.lat = -Infinity;
                }
            }

            return bounds;
        },

        //Shared animation code
        _animationAddLayerNonAnimated: function (layer, newCluster) {
            if (newCluster === layer) {
                this._featureGroup.addLayer(layer);
            } else if (newCluster._childCount === 2) {
                newCluster._addToMap();

                var markers = newCluster.getAllChildMarkers();
                this._featureGroup.removeLayer(markers[0]);
                this._featureGroup.removeLayer(markers[1]);
            } else {
                newCluster._updateIcon();
            }
        },

        /**
         * Implements the singleMarkerMode option.
         * @param layer Marker to re-style using the Clusters iconCreateFunction.
         * @returns {L.Icon} The newly created icon.
         * @private
         */
        _overrideMarkerIcon: function (layer) {
            var icon = layer.options.icon = this.options.iconCreateFunction({
                getChildCount: function () {
                    return 1;
                },
                getAllChildMarkers: function () {
                    return [layer];
                }
            });

            return icon;
        }
    });

    // Constant bounds used in case option "removeOutsideVisibleBounds" is set to false.
    L.MarkerClusterGroup.include({
        _mapBoundsInfinite: new L.LatLngBounds(new L.LatLng(-Infinity, -Infinity), new L.LatLng(Infinity, Infinity))
    });

    L.MarkerClusterGroup.include({
        _noAnimation: {
            //Non Animated versions of everything
            _animationStart: function () {
                //Do nothing...
            },
            _animationZoomIn: function (previousZoomLevel, newZoomLevel) {
                this._topClusterLevel._recursivelyRemoveChildrenFromMap(this._currentShownBounds, previousZoomLevel);
                this._topClusterLevel._recursivelyAddChildrenToMap(null, newZoomLevel, this._getExpandedVisibleBounds());

                //We didn't actually animate, but we use this event to mean "clustering animations have finished"
                this.fire('animationend');
            },
            _animationZoomOut: function (previousZoomLevel, newZoomLevel) {
                this._topClusterLevel._recursivelyRemoveChildrenFromMap(this._currentShownBounds, previousZoomLevel);
                this._topClusterLevel._recursivelyAddChildrenToMap(null, newZoomLevel, this._getExpandedVisibleBounds());

                //We didn't actually animate, but we use this event to mean "clustering animations have finished"
                this.fire('animationend');
            },
            _animationAddLayer: function (layer, newCluster) {
                this._animationAddLayerNonAnimated(layer, newCluster);
            }
        },
        _withAnimation: {
            //Animated versions here
            _animationStart: function () {
                this._map._mapPane.className += ' leaflet-cluster-anim';
                this._inZoomAnimation++;
            },
            _animationZoomIn: function (previousZoomLevel, newZoomLevel) {
                var bounds = this._getExpandedVisibleBounds(),
                    fg = this._featureGroup,
                    i;

                //Add all children of current clusters to map and remove those clusters from map
                this._topClusterLevel._recursively(bounds, previousZoomLevel, 0, function (c) {
                    var startPos = c._latlng,
                        markers = c._markers,
                        m;

                    if (!bounds.contains(startPos)) {
                        startPos = null;
                    }

                    if (c._isSingleParent() && previousZoomLevel + 1 === newZoomLevel) { //Immediately add the new child and remove us
                        fg.removeLayer(c);
                        c._recursivelyAddChildrenToMap(null, newZoomLevel, bounds);
                    } else {
                        //Fade out old cluster
                        c.clusterHide();
                        c._recursivelyAddChildrenToMap(startPos, newZoomLevel, bounds);
                    }

                    //Remove all markers that aren't visible any more
                    //TODO: Do we actually need to do this on the higher levels too?
                    for (i = markers.length - 1; i >= 0; i--) {
                        m = markers[i];
                        if (!bounds.contains(m._latlng)) {
                            fg.removeLayer(m);
                        }
                    }

                });

                this._forceLayout();

                //Update opacities
                this._topClusterLevel._recursivelyBecomeVisible(bounds, newZoomLevel);
                //TODO Maybe? Update markers in _recursivelyBecomeVisible
                fg.eachLayer(function (n) {
                    if (!(n instanceof L.MarkerCluster) && n._icon) {
                        n.clusterShow();
                    }
                });

                //update the positions of the just added clusters/markers
                this._topClusterLevel._recursively(bounds, previousZoomLevel, newZoomLevel, function (c) {
                    c._recursivelyRestoreChildPositions(newZoomLevel);
                });

                //Remove the old clusters and close the zoom animation
                this._enqueue(function () {
                    //update the positions of the just added clusters/markers
                    this._topClusterLevel._recursively(bounds, previousZoomLevel, 0, function (c) {
                        fg.removeLayer(c);
                        c.clusterShow();
                    });

                    this._animationEnd();
                });
            },

            _animationZoomOut: function (previousZoomLevel, newZoomLevel) {
                this._animationZoomOutSingle(this._topClusterLevel, previousZoomLevel - 1, newZoomLevel);

                //Need to add markers for those that weren't on the map before but are now
                this._topClusterLevel._recursivelyAddChildrenToMap(null, newZoomLevel, this._getExpandedVisibleBounds());
                //Remove markers that were on the map before but won't be now
                this._topClusterLevel._recursivelyRemoveChildrenFromMap(this._currentShownBounds, previousZoomLevel, this._getExpandedVisibleBounds());
            },
            _animationAddLayer: function (layer, newCluster) {
                var me = this,
                    fg = this._featureGroup;

                fg.addLayer(layer);
                if (newCluster !== layer) {
                    if (newCluster._childCount > 2) { //Was already a cluster

                        newCluster._updateIcon();
                        this._forceLayout();
                        this._animationStart();

                        layer._setPos(this._map.latLngToLayerPoint(newCluster.getLatLng()));
                        layer.clusterHide();

                        this._enqueue(function () {
                            fg.removeLayer(layer);
                            layer.clusterShow();

                            me._animationEnd();
                        });

                    } else { //Just became a cluster
                        this._forceLayout();

                        me._animationStart();
                        me._animationZoomOutSingle(newCluster, this._map.getMaxZoom(), this._map.getZoom());
                    }
                }
            }
        },

        // Private methods for animated versions.
        _animationZoomOutSingle: function (cluster, previousZoomLevel, newZoomLevel) {
            var bounds = this._getExpandedVisibleBounds();

            //Animate all of the markers in the clusters to move to their cluster center point
            cluster._recursivelyAnimateChildrenInAndAddSelfToMap(bounds, previousZoomLevel + 1, newZoomLevel);

            var me = this;

            //Update the opacity (If we immediately set it they won't animate)
            this._forceLayout();
            cluster._recursivelyBecomeVisible(bounds, newZoomLevel);

            //TODO: Maybe use the transition timing stuff to make this more reliable
            //When the animations are done, tidy up
            this._enqueue(function () {

                //This cluster stopped being a cluster before the timeout fired
                if (cluster._childCount === 1) {
                    var m = cluster._markers[0];
                    //If we were in a cluster animation at the time then the opacity and position of our child could be wrong now, so fix it
                    m.setLatLng(m.getLatLng());
                    if (m.clusterShow) {
                        m.clusterShow();
                    }
                } else {
                    cluster._recursively(bounds, newZoomLevel, 0, function (c) {
                        c._recursivelyRemoveChildrenFromMap(bounds, previousZoomLevel + 1);
                    });
                }
                me._animationEnd();
            });
        },

        _animationEnd: function () {
            if (this._map) {
                this._map._mapPane.className = this._map._mapPane.className.replace(' leaflet-cluster-anim', '');
            }
            this._inZoomAnimation--;
            this.fire('animationend');
        },

        //Force a browser layout of stuff in the map
        // Should apply the current opacity and location to all elements so we can update them again for an animation
        _forceLayout: function () {
            //In my testing this works, infact offsetWidth of any element seems to work.
            //Could loop all this._layers and do this for each _icon if it stops working

            L.Util.falseFn(document.body.offsetWidth);
        }
    });

    L.markerClusterGroup = function (options) {
        return new L.MarkerClusterGroup(options);
    };


    L.MarkerCluster = L.Marker.extend({
        initialize: function (group, zoom, a, b) {

            L.Marker.prototype.initialize.call(this, a ? (a._cLatLng || a.getLatLng()) : new L.LatLng(0, 0), { icon: this });


            this._group = group;
            this._zoom = zoom;

            this._markers = [];
            this._childClusters = [];
            this._childCount = 0;
            this._iconNeedsUpdate = true;
            this._boundsNeedUpdate = true;

            this._bounds = new L.LatLngBounds();

            if (a) {
                this._addChild(a);
            }
            if (b) {
                this._addChild(b);
            }
        },

        //Recursively retrieve all child markers of this cluster
        getAllChildMarkers: function (storageArray) {
            storageArray = storageArray || [];

            for (var i = this._childClusters.length - 1; i >= 0; i--) {
                this._childClusters[i].getAllChildMarkers(storageArray);
            }

            for (var j = this._markers.length - 1; j >= 0; j--) {
                storageArray.push(this._markers[j]);
            }

            return storageArray;
        },

        //Returns the count of how many child markers we have
        getChildCount: function () {
            return this._childCount;
        },

        //Zoom to the minimum of showing all of the child markers, or the extents of this cluster
        zoomToBounds: function () {
            var childClusters = this._childClusters.slice(),
                map = this._group._map,
                boundsZoom = map.getBoundsZoom(this._bounds),
                zoom = this._zoom + 1,
                mapZoom = map.getZoom(),
                i;

            //calculate how far we need to zoom down to see all of the markers
            while (childClusters.length > 0 && boundsZoom > zoom) {
                zoom++;
                var newClusters = [];
                for (i = 0; i < childClusters.length; i++) {
                    newClusters = newClusters.concat(childClusters[i]._childClusters);
                }
                childClusters = newClusters;
            }

            if (boundsZoom > zoom) {
                this._group._map.setView(this._latlng, zoom);
            } else if (boundsZoom <= mapZoom) { //If fitBounds wouldn't zoom us down, zoom us down instead
                this._group._map.setView(this._latlng, mapZoom + 1);
            } else {
                this._group._map.fitBounds(this._bounds);
            }
        },

        getBounds: function () {
            var bounds = new L.LatLngBounds();
            bounds.extend(this._bounds);
            return bounds;
        },

        _updateIcon: function () {
            this._iconNeedsUpdate = true;
            if (this._icon) {
                this.setIcon(this);
            }
        },

        //Cludge for Icon, we pretend to be an icon for performance
        createIcon: function () {
            if (this._iconNeedsUpdate) {
                this._iconObj = this._group.options.iconCreateFunction(this);
                this._iconNeedsUpdate = false;
            }
            return this._iconObj.createIcon();
        },
        createShadow: function () {
            return this._iconObj.createShadow();
        },


        _addChild: function (new1, isNotificationFromChild) {

            this._iconNeedsUpdate = true;

            this._boundsNeedUpdate = true;
            this._setClusterCenter(new1);

            if (new1 instanceof L.MarkerCluster) {
                if (!isNotificationFromChild) {
                    this._childClusters.push(new1);
                    new1.__parent = this;
                }
                this._childCount += new1._childCount;
            } else {
                if (!isNotificationFromChild) {
                    this._markers.push(new1);
                }
                this._childCount++;
            }

            if (this.__parent) {
                this.__parent._addChild(new1, true);
            }
        },

        /**
         * Makes sure the cluster center is set. If not, uses the child center if it is a cluster, or the marker position.
         * @param child L.MarkerCluster|L.Marker that will be used as cluster center if not defined yet.
         * @private
         */
        _setClusterCenter: function (child) {
            if (!this._cLatLng) {
                // when clustering, take position of the first point as the cluster center
                this._cLatLng = child._cLatLng || child._latlng;
            }
        },

        /**
         * Assigns impossible bounding values so that the next extend entirely determines the new bounds.
         * This method avoids having to trash the previous L.LatLngBounds object and to create a new one, which is much slower for this class.
         * As long as the bounds are not extended, most other methods would probably fail, as they would with bounds initialized but not extended.
         * @private
         */
        _resetBounds: function () {
            var bounds = this._bounds;

            if (bounds._southWest) {
                bounds._southWest.lat = Infinity;
                bounds._southWest.lng = Infinity;
            }
            if (bounds._northEast) {
                bounds._northEast.lat = -Infinity;
                bounds._northEast.lng = -Infinity;
            }
        },

        _recalculateBounds: function () {
            var markers = this._markers,
                childClusters = this._childClusters,
                latSum = 0,
                lngSum = 0,
                totalCount = this._childCount,
                i, child, childLatLng, childCount;

            // Case where all markers are removed from the map and we are left with just an empty _topClusterLevel.
            if (totalCount === 0) {
                return;
            }

            // Reset rather than creating a new object, for performance.
            this._resetBounds();

            // Child markers.
            for (i = 0; i < markers.length; i++) {
                childLatLng = markers[i]._latlng;

                this._bounds.extend(childLatLng);

                latSum += childLatLng.lat;
                lngSum += childLatLng.lng;
            }

            // Child clusters.
            for (i = 0; i < childClusters.length; i++) {
                child = childClusters[i];

                // Re-compute child bounds and weighted position first if necessary.
                if (child._boundsNeedUpdate) {
                    child._recalculateBounds();
                }

                this._bounds.extend(child._bounds);

                childLatLng = child._wLatLng;
                childCount = child._childCount;

                latSum += childLatLng.lat * childCount;
                lngSum += childLatLng.lng * childCount;
            }

            this._latlng = this._wLatLng = new L.LatLng(latSum / totalCount, lngSum / totalCount);

            // Reset dirty flag.
            this._boundsNeedUpdate = false;
        },

        //Set our markers position as given and add it to the map
        _addToMap: function (startPos) {
            if (startPos) {
                this._backupLatlng = this._latlng;
                this.setLatLng(startPos);
            }
            this._group._featureGroup.addLayer(this);
        },

        _recursivelyAnimateChildrenIn: function (bounds, center, maxZoom) {
            this._recursively(bounds, 0, maxZoom - 1,
                function (c) {
                    var markers = c._markers,
                        i, m;
                    for (i = markers.length - 1; i >= 0; i--) {
                        m = markers[i];

                        //Only do it if the icon is still on the map
                        if (m._icon) {
                            m._setPos(center);
                            m.clusterHide();
                        }
                    }
                },
                function (c) {
                    var childClusters = c._childClusters,
                        j, cm;
                    for (j = childClusters.length - 1; j >= 0; j--) {
                        cm = childClusters[j];
                        if (cm._icon) {
                            cm._setPos(center);
                            cm.clusterHide();
                        }
                    }
                }
            );
        },

        _recursivelyAnimateChildrenInAndAddSelfToMap: function (bounds, previousZoomLevel, newZoomLevel) {
            this._recursively(bounds, newZoomLevel, 0,
                function (c) {
                    c._recursivelyAnimateChildrenIn(bounds, c._group._map.latLngToLayerPoint(c.getLatLng()).round(), previousZoomLevel);

                    //TODO: depthToAnimateIn affects _isSingleParent, if there is a multizoom we may/may not be.
                    //As a hack we only do a animation free zoom on a single level zoom, if someone does multiple levels then we always animate
                    if (c._isSingleParent() && previousZoomLevel - 1 === newZoomLevel) {
                        c.clusterShow();
                        c._recursivelyRemoveChildrenFromMap(bounds, previousZoomLevel); //Immediately remove our children as we are replacing them. TODO previousBounds not bounds
                    } else {
                        c.clusterHide();
                    }

                    c._addToMap();
                }
            );
        },

        _recursivelyBecomeVisible: function (bounds, zoomLevel) {
            this._recursively(bounds, 0, zoomLevel, null, function (c) {
                c.clusterShow();
            });
        },

        _recursivelyAddChildrenToMap: function (startPos, zoomLevel, bounds) {
            this._recursively(bounds, -1, zoomLevel,
                function (c) {
                    if (zoomLevel === c._zoom) {
                        return;
                    }

                    //Add our child markers at startPos (so they can be animated out)
                    for (var i = c._markers.length - 1; i >= 0; i--) {
                        var nm = c._markers[i];

                        if (!bounds.contains(nm._latlng)) {
                            continue;
                        }

                        if (startPos) {
                            nm._backupLatlng = nm.getLatLng();

                            nm.setLatLng(startPos);
                            if (nm.clusterHide) {
                                nm.clusterHide();
                            }
                        }

                        c._group._featureGroup.addLayer(nm);
                    }
                },
                function (c) {
                    c._addToMap(startPos);
                }
            );
        },

        _recursivelyRestoreChildPositions: function (zoomLevel) {
            //Fix positions of child markers
            for (var i = this._markers.length - 1; i >= 0; i--) {
                var nm = this._markers[i];
                if (nm._backupLatlng) {
                    nm.setLatLng(nm._backupLatlng);
                    delete nm._backupLatlng;
                }
            }

            if (zoomLevel - 1 === this._zoom) {
                //Reposition child clusters
                for (var j = this._childClusters.length - 1; j >= 0; j--) {
                    this._childClusters[j]._restorePosition();
                }
            } else {
                for (var k = this._childClusters.length - 1; k >= 0; k--) {
                    this._childClusters[k]._recursivelyRestoreChildPositions(zoomLevel);
                }
            }
        },

        _restorePosition: function () {
            if (this._backupLatlng) {
                this.setLatLng(this._backupLatlng);
                delete this._backupLatlng;
            }
        },

        //exceptBounds: If set, don't remove any markers/clusters in it
        _recursivelyRemoveChildrenFromMap: function (previousBounds, zoomLevel, exceptBounds) {
            var m, i;
            this._recursively(previousBounds, -1, zoomLevel - 1,
                function (c) {
                    //Remove markers at every level
                    for (i = c._markers.length - 1; i >= 0; i--) {
                        m = c._markers[i];
                        if (!exceptBounds || !exceptBounds.contains(m._latlng)) {
                            c._group._featureGroup.removeLayer(m);
                            if (m.clusterShow) {
                                m.clusterShow();
                            }
                        }
                    }
                },
                function (c) {
                    //Remove child clusters at just the bottom level
                    for (i = c._childClusters.length - 1; i >= 0; i--) {
                        m = c._childClusters[i];
                        if (!exceptBounds || !exceptBounds.contains(m._latlng)) {
                            c._group._featureGroup.removeLayer(m);
                            if (m.clusterShow) {
                                m.clusterShow();
                            }
                        }
                    }
                }
            );
        },

        //Run the given functions recursively to this and child clusters
        // boundsToApplyTo: a L.LatLngBounds representing the bounds of what clusters to recurse in to
        // zoomLevelToStart: zoom level to start running functions (inclusive)
        // zoomLevelToStop: zoom level to stop running functions (inclusive)
        // runAtEveryLevel: function that takes an L.MarkerCluster as an argument that should be applied on every level
        // runAtBottomLevel: function that takes an L.MarkerCluster as an argument that should be applied at only the bottom level
        _recursively: function (boundsToApplyTo, zoomLevelToStart, zoomLevelToStop, runAtEveryLevel, runAtBottomLevel) {
            var childClusters = this._childClusters,
                zoom = this._zoom,
                i, c;

            if (zoomLevelToStart > zoom) { //Still going down to required depth, just recurse to child clusters
                for (i = childClusters.length - 1; i >= 0; i--) {
                    c = childClusters[i];
                    if (boundsToApplyTo.intersects(c._bounds)) {
                        c._recursively(boundsToApplyTo, zoomLevelToStart, zoomLevelToStop, runAtEveryLevel, runAtBottomLevel);
                    }
                }
            } else { //In required depth

                if (runAtEveryLevel) {
                    runAtEveryLevel(this);
                }
                if (runAtBottomLevel && this._zoom === zoomLevelToStop) {
                    runAtBottomLevel(this);
                }

                //TODO: This loop is almost the same as above
                if (zoomLevelToStop > zoom) {
                    for (i = childClusters.length - 1; i >= 0; i--) {
                        c = childClusters[i];
                        if (boundsToApplyTo.intersects(c._bounds)) {
                            c._recursively(boundsToApplyTo, zoomLevelToStart, zoomLevelToStop, runAtEveryLevel, runAtBottomLevel);
                        }
                    }
                }
            }
        },

        //Returns true if we are the parent of only one cluster and that cluster is the same as us
        _isSingleParent: function () {
            //Don't need to check this._markers as the rest won't work if there are any
            return this._childClusters.length > 0 && this._childClusters[0]._childCount === this._childCount;
        }
    });



    /*
    * Extends L.Marker to include two extra methods: clusterHide and clusterShow.
    * 
    * They work as setOpacity(0) and setOpacity(1) respectively, but
    * they will remember the marker's opacity when hiding and showing it again.
    * 
    */


    L.Marker.include({

        clusterHide: function () {
            this.options.opacityWhenUnclustered = this.options.opacity || 1;
            return this.setOpacity(0);
        },

        clusterShow: function () {
            var ret = this.setOpacity(this.options.opacity || this.options.opacityWhenUnclustered);
            delete this.options.opacityWhenUnclustered;
            return ret;
        }

    });





    L.DistanceGrid = function (cellSize) {
        this._cellSize = cellSize;
        this._sqCellSize = cellSize * cellSize;
        this._grid = {};
        this._objectPoint = {};
    };

    L.DistanceGrid.prototype = {

        addObject: function (obj, point) {
            var x = this._getCoord(point.x),
                y = this._getCoord(point.y),
                grid = this._grid,
                row = grid[y] = grid[y] || {},
                cell = row[x] = row[x] || [],
                stamp = L.Util.stamp(obj);

            this._objectPoint[stamp] = point;

            cell.push(obj);
        },

        updateObject: function (obj, point) {
            this.removeObject(obj);
            this.addObject(obj, point);
        },

        //Returns true if the object was found
        removeObject: function (obj, point) {
            var x = this._getCoord(point.x),
                y = this._getCoord(point.y),
                grid = this._grid,
                row = grid[y] = grid[y] || {},
                cell = row[x] = row[x] || [],
                i, len;

            delete this._objectPoint[L.Util.stamp(obj)];

            for (i = 0, len = cell.length; i < len; i++) {
                if (cell[i] === obj) {

                    cell.splice(i, 1);

                    if (len === 1) {
                        delete row[x];
                    }

                    return true;
                }
            }

        },

        eachObject: function (fn, context) {
            var i, j, k, len, row, cell, removed,
                grid = this._grid;

            for (i in grid) {
                row = grid[i];

                for (j in row) {
                    cell = row[j];

                    for (k = 0, len = cell.length; k < len; k++) {
                        removed = fn.call(context, cell[k]);
                        if (removed) {
                            k--;
                            len--;
                        }
                    }
                }
            }
        },

        getNearObject: function (point) {
            var x = this._getCoord(point.x),
                y = this._getCoord(point.y),
                i, j, k, row, cell, len, obj, dist,
                objectPoint = this._objectPoint,
                closestDistSq = this._sqCellSize,
                closest = null;

            for (i = y - 1; i <= y + 1; i++) {
                row = this._grid[i];
                if (row) {

                    for (j = x - 1; j <= x + 1; j++) {
                        cell = row[j];
                        if (cell) {

                            for (k = 0, len = cell.length; k < len; k++) {
                                obj = cell[k];
                                dist = this._sqDist(objectPoint[L.Util.stamp(obj)], point);
                                if (dist < closestDistSq) {
                                    closestDistSq = dist;
                                    closest = obj;
                                }
                            }
                        }
                    }
                }
            }
            return closest;
        },

        _getCoord: function (x) {
            return Math.floor(x / this._cellSize);
        },

        _sqDist: function (p, p2) {
            var dx = p2.x - p.x,
                dy = p2.y - p.y;
            return dx * dx + dy * dy;
        }
    };


    /* Copyright (c) 2012 the authors listed at the following URL, and/or
    the authors of referenced articles or incorporated external code:
    http://en.literateprograms.org/Quickhull_(Javascript)?action=history&offset=20120410175256
    
    Permission is hereby granted, free of charge, to any person obtaining
    a copy of this software and associated documentation files (the
    "Software"), to deal in the Software without restriction, including
    without limitation the rights to use, copy, modify, merge, publish,
    distribute, sublicense, and/or sell copies of the Software, and to
    permit persons to whom the Software is furnished to do so, subject to
    the following conditions:
    
    The above copyright notice and this permission notice shall be
    included in all copies or substantial portions of the Software.
    
    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
    IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
    CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
    TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
    SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
    
    Retrieved from: http://en.literateprograms.org/Quickhull_(Javascript)?oldid=18434
    */

    (function () {
        L.QuickHull = {

            /*
             * @param {Object} cpt a point to be measured from the baseline
             * @param {Array} bl the baseline, as represented by a two-element
             *   array of latlng objects.
             * @returns {Number} an approximate distance measure
             */
            getDistant: function (cpt, bl) {
                var vY = bl[1].lat - bl[0].lat,
                    vX = bl[0].lng - bl[1].lng;
                return (vX * (cpt.lat - bl[0].lat) + vY * (cpt.lng - bl[0].lng));
            },

            /*
             * @param {Array} baseLine a two-element array of latlng objects
             *   representing the baseline to project from
             * @param {Array} latLngs an array of latlng objects
             * @returns {Object} the maximum point and all new points to stay
             *   in consideration for the hull.
             */
            findMostDistantPointFromBaseLine: function (baseLine, latLngs) {
                var maxD = 0,
                    maxPt = null,
                    newPoints = [],
                    i, pt, d;

                for (i = latLngs.length - 1; i >= 0; i--) {
                    pt = latLngs[i];
                    d = this.getDistant(pt, baseLine);

                    if (d > 0) {
                        newPoints.push(pt);
                    } else {
                        continue;
                    }

                    if (d > maxD) {
                        maxD = d;
                        maxPt = pt;
                    }
                }

                return { maxPoint: maxPt, newPoints: newPoints };
            },


            /*
             * Given a baseline, compute the convex hull of latLngs as an array
             * of latLngs.
             *
             * @param {Array} latLngs
             * @returns {Array}
             */
            buildConvexHull: function (baseLine, latLngs) {
                var convexHullBaseLines = [],
                    t = this.findMostDistantPointFromBaseLine(baseLine, latLngs);

                if (t.maxPoint) { // if there is still a point "outside" the base line
                    convexHullBaseLines =
                        convexHullBaseLines.concat(
                            this.buildConvexHull([baseLine[0], t.maxPoint], t.newPoints)
                        );
                    convexHullBaseLines =
                        convexHullBaseLines.concat(
                            this.buildConvexHull([t.maxPoint, baseLine[1]], t.newPoints)
                        );
                    return convexHullBaseLines;
                } else {  // if there is no more point "outside" the base line, the current base line is part of the convex hull
                    return [baseLine[0]];
                }
            },

            /*
             * Given an array of latlngs, compute a convex hull as an array
             * of latlngs
             *
             * @param {Array} latLngs
             * @returns {Array}
             */
            getConvexHull: function (latLngs) {
                // find first baseline
                var maxLat = false, minLat = false,
                    maxLng = false, minLng = false,
                    maxLatPt = null, minLatPt = null,
                    maxLngPt = null, minLngPt = null,
                    maxPt = null, minPt = null,
                    i;

                for (i = latLngs.length - 1; i >= 0; i--) {
                    var pt = latLngs[i];
                    if (maxLat === false || pt.lat > maxLat) {
                        maxLatPt = pt;
                        maxLat = pt.lat;
                    }
                    if (minLat === false || pt.lat < minLat) {
                        minLatPt = pt;
                        minLat = pt.lat;
                    }
                    if (maxLng === false || pt.lng > maxLng) {
                        maxLngPt = pt;
                        maxLng = pt.lng;
                    }
                    if (minLng === false || pt.lng < minLng) {
                        minLngPt = pt;
                        minLng = pt.lng;
                    }
                }

                if (minLat !== maxLat) {
                    minPt = minLatPt;
                    maxPt = maxLatPt;
                } else {
                    minPt = minLngPt;
                    maxPt = maxLngPt;
                }

                var ch = [].concat(this.buildConvexHull([minPt, maxPt], latLngs),
                                    this.buildConvexHull([maxPt, minPt], latLngs));
                return ch;
            }
        };
    }());

    L.MarkerCluster.include({
        getConvexHull: function () {
            var childMarkers = this.getAllChildMarkers(),
                points = [],
                p, i;

            for (i = childMarkers.length - 1; i >= 0; i--) {
                p = childMarkers[i].getLatLng();
                points.push(p);
            }

            return L.QuickHull.getConvexHull(points);
        }
    });


    //This code is 100% based on https://github.com/jawj/OverlappingMarkerSpiderfier-Leaflet
    //Huge thanks to jawj for implementing it first to make my job easy :-)

    L.MarkerCluster.include({

        _2PI: Math.PI * 2,
        _circleFootSeparation: 25, //related to circumference of circle
        _circleStartAngle: Math.PI / 6,

        _spiralFootSeparation: 28, //related to size of spiral (experiment!)
        _spiralLengthStart: 11,
        _spiralLengthFactor: 5,

        _circleSpiralSwitchover: 9, //show spiral instead of circle from this marker count upwards.
        // 0 -> always spiral; Infinity -> always circle

        spiderfy: function () {
            if (this._group._spiderfied === this || this._group._inZoomAnimation) {
                return;
            }

            var childMarkers = this.getAllChildMarkers(),
                group = this._group,
                map = group._map,
                center = map.latLngToLayerPoint(this._latlng),
                positions;

            this._group._unspiderfy();
            this._group._spiderfied = this;

            //TODO Maybe: childMarkers order by distance to center

            if (childMarkers.length >= this._circleSpiralSwitchover) {
                positions = this._generatePointsSpiral(childMarkers.length, center);
            } else {
                center.y += 10; // Otherwise circles look wrong => hack for standard blue icon, renders differently for other icons.
                positions = this._generatePointsCircle(childMarkers.length, center);
            }

            this._animationSpiderfy(childMarkers, positions);
        },

        unspiderfy: function (zoomDetails) {
            /// <param Name="zoomDetails">Argument from zoomanim if being called in a zoom animation or null otherwise</param>
            if (this._group._inZoomAnimation) {
                return;
            }
            this._animationUnspiderfy(zoomDetails);

            this._group._spiderfied = null;
        },

        _generatePointsCircle: function (count, centerPt) {
            var circumference = this._group.options.spiderfyDistanceMultiplier * this._circleFootSeparation * (2 + count),
                legLength = circumference / this._2PI,  //radius from circumference
                angleStep = this._2PI / count,
                res = [],
                i, angle;

            res.length = count;

            for (i = count - 1; i >= 0; i--) {
                angle = this._circleStartAngle + i * angleStep;
                res[i] = new L.Point(centerPt.x + legLength * Math.cos(angle), centerPt.y + legLength * Math.sin(angle))._round();
            }

            return res;
        },

        _generatePointsSpiral: function (count, centerPt) {
            var spiderfyDistanceMultiplier = this._group.options.spiderfyDistanceMultiplier,
                legLength = spiderfyDistanceMultiplier * this._spiralLengthStart,
                separation = spiderfyDistanceMultiplier * this._spiralFootSeparation,
                lengthFactor = spiderfyDistanceMultiplier * this._spiralLengthFactor * this._2PI,
                angle = 0,
                res = [],
                i;

            res.length = count;

            // Higher index, closer position to cluster center.
            for (i = count - 1; i >= 0; i--) {
                angle += separation / legLength + i * 0.0005;
                res[i] = new L.Point(centerPt.x + legLength * Math.cos(angle), centerPt.y + legLength * Math.sin(angle))._round();
                legLength += lengthFactor / angle;
            }
            return res;
        },

        _noanimationUnspiderfy: function () {
            var group = this._group,
                map = group._map,
                fg = group._featureGroup,
                childMarkers = this.getAllChildMarkers(),
                m, i;

            this.setOpacity(1);
            for (i = childMarkers.length - 1; i >= 0; i--) {
                m = childMarkers[i];

                fg.removeLayer(m);

                if (m._preSpiderfyLatlng) {
                    m.setLatLng(m._preSpiderfyLatlng);
                    delete m._preSpiderfyLatlng;
                }
                if (m.setZIndexOffset) {
                    m.setZIndexOffset(0);
                }

                if (m._spiderLeg) {
                    map.removeLayer(m._spiderLeg);
                    delete m._spiderLeg;
                }
            }

            group.fire('unspiderfied', {
                cluster: this,
                markers: childMarkers
            });
            group._spiderfied = null;
        }
    });

    //Non Animated versions of everything
    L.MarkerClusterNonAnimated = L.MarkerCluster.extend({
        _animationSpiderfy: function (childMarkers, positions) {
            var group = this._group,
                map = group._map,
                fg = group._featureGroup,
                legOptions = this._group.options.spiderLegPolylineOptions,
                i, m, leg, newPos;

            // Traverse in ascending order to make sure that inner circleMarkers are on top of further legs. Normal markers are re-ordered by newPosition.
            // The reverse order trick no longer improves performance on modern browsers.
            for (i = 0; i < childMarkers.length; i++) {
                newPos = map.layerPointToLatLng(positions[i]);
                m = childMarkers[i];

                // Add the leg before the marker, so that in case the latter is a circleMarker, the leg is behind it.
                leg = new L.Polyline([this._latlng, newPos], legOptions);
                map.addLayer(leg);
                m._spiderLeg = leg;

                // Now add the marker.
                m._preSpiderfyLatlng = m._latlng;
                m.setLatLng(newPos);
                if (m.setZIndexOffset) {
                    m.setZIndexOffset(1000000); //Make these appear on top of EVERYTHING
                }

                fg.addLayer(m);
            }
            this.setOpacity(0.3);
            group.fire('spiderfied', {
                cluster: this,
                markers: childMarkers
            });
        },

        _animationUnspiderfy: function () {
            this._noanimationUnspiderfy();
        }
    });

    //Animated versions here
    L.MarkerCluster.include({

        _animationSpiderfy: function (childMarkers, positions) {
            var me = this,
                group = this._group,
                map = group._map,
                fg = group._featureGroup,
                thisLayerLatLng = this._latlng,
                thisLayerPos = map.latLngToLayerPoint(thisLayerLatLng),
                svg = L.Path.SVG,
                legOptions = L.extend({}, this._group.options.spiderLegPolylineOptions), // Copy the options so that we can modify them for animation.
                finalLegOpacity = legOptions.opacity,
                i, m, leg, legPath, legLength, newPos;

            if (finalLegOpacity === undefined) {
                finalLegOpacity = L.MarkerClusterGroup.prototype.options.spiderLegPolylineOptions.opacity;
            }

            if (svg) {
                // If the initial opacity of the spider leg is not 0 then it appears before the animation starts.
                legOptions.opacity = 0;

                // Add the class for CSS transitions.
                legOptions.className = (legOptions.className || '') + ' leaflet-cluster-spider-leg';
            } else {
                // Make sure we have a defined opacity.
                legOptions.opacity = finalLegOpacity;
            }

            // Add markers and spider legs to map, hidden at our center point.
            // Traverse in ascending order to make sure that inner circleMarkers are on top of further legs. Normal markers are re-ordered by newPosition.
            // The reverse order trick no longer improves performance on modern browsers.
            for (i = 0; i < childMarkers.length; i++) {
                m = childMarkers[i];

                newPos = map.layerPointToLatLng(positions[i]);

                // Add the leg before the marker, so that in case the latter is a circleMarker, the leg is behind it.
                leg = new L.Polyline([thisLayerLatLng, newPos], legOptions);
                map.addLayer(leg);
                m._spiderLeg = leg;

                // Explanations: https://jakearchibald.com/2013/animated-line-drawing-svg/
                // In our case the transition property is declared in the CSS file.
                if (svg) {
                    legPath = leg._path;
                    legLength = legPath.getTotalLength() + 0.1; // Need a small extra length to avoid remaining dot in Firefox.
                    legPath.style.strokeDasharray = legLength; // Just 1 length is enough, it will be duplicated.
                    legPath.style.strokeDashoffset = legLength;
                }

                // If it is a marker, add it now and we'll animate it out
                if (m.setZIndexOffset) {
                    m.setZIndexOffset(1000000); // Make normal markers appear on top of EVERYTHING
                }
                if (m.clusterHide) {
                    m.clusterHide();
                }

                // Vectors just get immediately added
                fg.addLayer(m);

                if (m._setPos) {
                    m._setPos(thisLayerPos);
                }
            }

            group._forceLayout();
            group._animationStart();

            // Reveal markers and spider legs.
            for (i = childMarkers.length - 1; i >= 0; i--) {
                newPos = map.layerPointToLatLng(positions[i]);
                m = childMarkers[i];

                //Move marker to new position
                m._preSpiderfyLatlng = m._latlng;
                m.setLatLng(newPos);

                if (m.clusterShow) {
                    m.clusterShow();
                }

                // Animate leg (animation is actually delegated to CSS transition).
                if (svg) {
                    leg = m._spiderLeg;
                    legPath = leg._path;
                    legPath.style.strokeDashoffset = 0;
                    //legPath.style.strokeOpacity = finalLegOpacity;
                    leg.setStyle({ opacity: finalLegOpacity });
                }
            }
            this.setOpacity(0.3);

            setTimeout(function () {
                group._animationEnd();
                group.fire('spiderfied', {
                    cluster: me,
                    markers: childMarkers
                });
            }, 200);
        },

        _animationUnspiderfy: function (zoomDetails) {
            var me = this,
                group = this._group,
                map = group._map,
                fg = group._featureGroup,
                thisLayerPos = zoomDetails ? map._latLngToNewLayerPoint(this._latlng, zoomDetails.zoom, zoomDetails.center) : map.latLngToLayerPoint(this._latlng),
                childMarkers = this.getAllChildMarkers(),
                svg = L.Path.SVG,
                m, i, leg, legPath, legLength, nonAnimatable;

            group._animationStart();

            //Make us visible and bring the child markers back in
            this.setOpacity(1);
            for (i = childMarkers.length - 1; i >= 0; i--) {
                m = childMarkers[i];

                //Marker was added to us after we were spiderfied
                if (!m._preSpiderfyLatlng) {
                    continue;
                }

                //Fix up the location to the real one
                m.setLatLng(m._preSpiderfyLatlng);
                delete m._preSpiderfyLatlng;

                //Hack override the location to be our center
                nonAnimatable = true;
                if (m._setPos) {
                    m._setPos(thisLayerPos);
                    nonAnimatable = false;
                }
                if (m.clusterHide) {
                    m.clusterHide();
                    nonAnimatable = false;
                }
                if (nonAnimatable) {
                    fg.removeLayer(m);
                }

                // Animate the spider leg back in (animation is actually delegated to CSS transition).
                if (svg) {
                    leg = m._spiderLeg;
                    legPath = leg._path;
                    legLength = legPath.getTotalLength() + 0.1;
                    legPath.style.strokeDashoffset = legLength;
                    leg.setStyle({ opacity: 0 });
                }
            }

            setTimeout(function () {
                //If we have only <= one child left then that marker will be shown on the map so don't remove it!
                var stillThereChildCount = 0;
                for (i = childMarkers.length - 1; i >= 0; i--) {
                    m = childMarkers[i];
                    if (m._spiderLeg) {
                        stillThereChildCount++;
                    }
                }


                for (i = childMarkers.length - 1; i >= 0; i--) {
                    m = childMarkers[i];

                    if (!m._spiderLeg) { //Has already been unspiderfied
                        continue;
                    }

                    if (m.clusterShow) {
                        m.clusterShow();
                    }
                    if (m.setZIndexOffset) {
                        m.setZIndexOffset(0);
                    }

                    if (stillThereChildCount > 1) {
                        fg.removeLayer(m);
                    }

                    map.removeLayer(m._spiderLeg);
                    delete m._spiderLeg;
                }
                group._animationEnd();
                group.fire('unspiderfied', {
                    cluster: me,
                    markers: childMarkers
                });
            }, 200);
        }
    });


    L.MarkerClusterGroup.include({
        //The MarkerCluster currently spiderfied (if any)
        _spiderfied: null,

        _spiderfierOnAdd: function () {
            this._map.on('click', this._unspiderfyWrapper, this);

            if (this._map.options.zoomAnimation) {
                this._map.on('zoomstart', this._unspiderfyZoomStart, this);
            }
            //Browsers without zoomAnimation or a big zoom don't fire zoomstart
            this._map.on('zoomend', this._noanimationUnspiderfy, this);
        },

        _spiderfierOnRemove: function () {
            this._map.off('click', this._unspiderfyWrapper, this);
            this._map.off('zoomstart', this._unspiderfyZoomStart, this);
            this._map.off('zoomanim', this._unspiderfyZoomAnim, this);
            this._map.off('zoomend', this._noanimationUnspiderfy, this);

            //Ensure that markers are back where they should be
            // Use no animation to avoid a sticky leaflet-cluster-anim class on mapPane
            this._noanimationUnspiderfy();
        },

        //On zoom start we add a zoomanim handler so that we are guaranteed to be last (after markers are animated)
        //This means we can define the animation they do rather than Markers doing an animation to their actual location
        _unspiderfyZoomStart: function () {
            if (!this._map) { //May have been removed from the map by a zoomEnd handler
                return;
            }

            this._map.on('zoomanim', this._unspiderfyZoomAnim, this);
        },

        _unspiderfyZoomAnim: function (zoomDetails) {
            //Wait until the first zoomanim after the user has finished touch-zooming before running the animation
            if (L.DomUtil.hasClass(this._map._mapPane, 'leaflet-touching')) {
                return;
            }

            this._map.off('zoomanim', this._unspiderfyZoomAnim, this);
            this._unspiderfy(zoomDetails);
        },

        _unspiderfyWrapper: function () {
            /// <summary>_unspiderfy but passes no arguments</summary>
            this._unspiderfy();
        },

        _unspiderfy: function (zoomDetails) {
            if (this._spiderfied) {
                this._spiderfied.unspiderfy(zoomDetails);
            }
        },

        _noanimationUnspiderfy: function () {
            if (this._spiderfied) {
                this._spiderfied._noanimationUnspiderfy();
            }
        },

        //If the given layer is currently being spiderfied then we unspiderfy it so it isn't on the map anymore etc
        _unspiderfyLayer: function (layer) {
            if (layer._spiderLeg) {
                this._featureGroup.removeLayer(layer);

                if (layer.clusterShow) {
                    layer.clusterShow();
                }
                //Position will be fixed up immediately in _animationUnspiderfy
                if (layer.setZIndexOffset) {
                    layer.setZIndexOffset(0);
                }

                this._map.removeLayer(layer._spiderLeg);
                delete layer._spiderLeg;
            }
        }
    });


    /**
     * Adds 1 public method to MCG and 1 to L.Marker to facilitate changing
     * markers' icon options and refreshing their icon and their parent clusters
     * accordingly (case where their iconCreateFunction uses data of childMarkers
     * to make up the cluster icon).
     */


    L.MarkerClusterGroup.include({
        /**
         * Updates the icon of all clusters which are parents of the given marker(s).
         * In singleMarkerMode, also updates the given marker(s) icon.
         * @param layers L.MarkerClusterGroup|L.LayerGroup|Array(L.Marker)|Map(L.Marker)|
         * L.MarkerCluster|L.Marker (optional) list of markers (or single marker) whose parent
         * clusters need to be updated. If not provided, retrieves all child markers of this.
         * @returns {L.MarkerClusterGroup}
         */
        refreshClusters: function (layers) {
            if (!layers) {
                layers = this._topClusterLevel.getAllChildMarkers();
            } else if (layers instanceof L.MarkerClusterGroup) {
                layers = layers._topClusterLevel.getAllChildMarkers();
            } else if (layers instanceof L.LayerGroup) {
                layers = layers._layers;
            } else if (layers instanceof L.MarkerCluster) {
                layers = layers.getAllChildMarkers();
            } else if (layers instanceof L.Marker) {
                layers = [layers];
            } // else: must be an Array(L.Marker)|Map(L.Marker)
            this._flagParentsIconsNeedUpdate(layers);
            this._refreshClustersIcons();

            // In case of singleMarkerMode, also re-draw the markers.
            if (this.options.singleMarkerMode) {
                this._refreshSingleMarkerModeMarkers(layers);
            }

            return this;
        },

        /**
         * Simply flags all parent clusters of the given markers as having a "dirty" icon.
         * @param layers Array(L.Marker)|Map(L.Marker) list of markers.
         * @private
         */
        _flagParentsIconsNeedUpdate: function (layers) {
            var id, parent;

            // Assumes layers is an Array or an Object whose prototype is non-enumerable.
            for (id in layers) {
                // Flag parent clusters' icon as "dirty", all the way up.
                // Dumb process that flags multiple times upper parents, but still
                // much more efficient than trying to be smart and make short lists,
                // at least in the case of a hierarchy following a power law:
                // http://jsperf.com/flag-nodes-in-power-hierarchy/2
                parent = layers[id].__parent;
                while (parent) {
                    parent._iconNeedsUpdate = true;
                    parent = parent.__parent;
                }
            }
        },

        /**
         * Refreshes the icon of all "dirty" visible clusters.
         * Non-visible "dirty" clusters will be updated when they are added to the map.
         * @private
         */
        _refreshClustersIcons: function () {
            this._featureGroup.eachLayer(function (c) {
                if (c instanceof L.MarkerCluster && c._iconNeedsUpdate) {
                    c._updateIcon();
                }
            });
        },

        /**
         * Re-draws the icon of the supplied markers.
         * To be used in singleMarkerMode only.
         * @param layers Array(L.Marker)|Map(L.Marker) list of markers.
         * @private
         */
        _refreshSingleMarkerModeMarkers: function (layers) {
            var id, layer;

            for (id in layers) {
                layer = layers[id];

                // Make sure we do not override markers that do not belong to THIS group.
                if (this.hasLayer(layer)) {
                    // Need to re-create the icon first, then re-draw the marker.
                    layer.setIcon(this._overrideMarkerIcon(layer));
                }
            }
        }
    });

    L.Marker.include({
        /**
         * Updates the given options in the marker's icon and refreshes the marker.
         * @param options map object of icon options.
         * @param directlyRefreshClusters boolean (optional) true to trigger
         * MCG.refreshClustersOf() right away with this single marker.
         * @returns {L.Marker}
         */
        refreshIconOptions: function (options, directlyRefreshClusters) {
            var icon = this.options.icon;

            L.setOptions(icon, options);

            this.setIcon(icon);

            // Shortcut to refresh the associated MCG clusters right away.
            // To be used when refreshing a single marker.
            // Otherwise, better use MCG.refreshClusters() once at the end with
            // the list of modified markers.
            if (directlyRefreshClusters && this.__parent) {
                this.__parent._group.refreshClusters(this);
            }

            return this;
        }
    });


}(window, document));;
/*! minerva-app.js */function doSubStr(str, cutStart, cutEnd) {
    return str.substr(0, cutStart);
}

$(function () {
    $('meta[name=screen-width]').attr('content', $(window).width());
    $('meta[name=screen-height]').attr('content', $(window).height());
});

// TO FIX

/*angular.module('ng').filter('cut', function () {
    return function (value, wordwise, max, tail) {
        return value;
        if (value == undefined || !value) return '';

        max = parseInt(max, 10);
        if (!max) return value;
        if (value.length <= max) return value;

        value = value.substr(0, max);
        if (wordwise) {
            var lastspace = value.lastIndexOf(' ');
            if (lastspace != -1) {
                value = value.substr(0, lastspace);
            }
        }

        return value + (tail || ' …');
    };
});*/


/**
 * demoApp - 1.0.0rc2
 */
/*
angular.module("sa", [])
    .config(["$sceDelegateProvider",
    function ($sceDelegateProvider) {
        $sceDelegateProvider.resourceUrlWhitelist(["self",
            "https://app.powerbi.com/view?r=**"
        ]);
    }
]); */

/* EOF */
angular.module('ajaxloader', [])
    .config(function ($httpProvider) {
        $httpProvider.responseInterceptors.push('myHttpInterceptor');
        var spinnerFunction = function (data, headersGetter) {
            // todo start the spinner here
            try {
                if ($ != undefined || $('#divloader') != undefined) {
                    if ($('#divloader').attr('enabled') != undefined) {
                        if ($('#divloader').attr('enabled') != 'false') {
                            $('#divloader').show();
                        }
                    }
                }
            } catch (e)
            { $('#divloader').show(); }

            return data;
        };
        $httpProvider.defaults.transformRequest.push(spinnerFunction);
    })

// register the interceptor as a service, intercepts ALL angular ajax http calls
    .factory('myHttpInterceptor', function ($q, $window) {
        return function (promise) {
            return promise.then(function (response) {
                // do something on success
                // todo hide the spinner
                try {
                    if ($ != undefined || $('#divloader') != undefined) {
                        if ($('#divloader').attr('enabled') != undefined) {
                            if ($('#divloader').attr('enabled') != 'false') {
                                $('#divloader').hide();
                            }
                        }
                    }
                } catch (e)
                { $('#divloader').hide(); }
                return response;

            }, function (response) {
                // do something on error
                // todo hide the spinner
                try {
                    if ($ != undefined || $('#divloader') != undefined) {
                        if ($('#divloader').attr('enabled') != undefined) {
                            if ($('#divloader').attr('enabled') != 'false') {
                                $('#divloader').hide();
                            }
                        }
                    }
                } catch (e)
                { $('#divloader').hide(); }
                return $q.reject(response);
            });
        };
    });

/**************************************************************************************
 LEAFLET MANAGEMENT
 **************************************************************************************
 This simple module manage the minerva maps with three components.
 Two services:
   - minervaMap: a specialized minerva service
   - leafletMap: a general leaflet map service
 One directive:
   - leaflet: element directive that create a leflet map
 
 notes: 
 The map tile used is defined by default by LEAFLET_DEFAULT_TILE constant.
 Tile is overridable setting the "tile" attribute of <leaflet> directive tag.
 Your view is required to include:
   - ~/Scripts/leaflet/leaflet.js
   - ~/Scripts/leaflet/leaflet.css
 If you need to use cluster features your view needs to include this additional files:
   - ~/Scripts/leaflet/leaflet.markercluster-src.js
   - ~/Scripts/leaflet/markercluster.css
   - ~/Scripts/leaflet/markercluster.default.css

 check the documentation for correct usage.
 **************************************************************************************/
//var LEAFLET_DEFAULT_TILE = '//europa.eu/webtools/maps/tiles/osm-ec/{z}/{x}/{y}.png';
var LEAFLET_DEFAULT_TILE = '//gisco-services.ec.europa.eu/maps/tiles/OSMCartoBackground/EPSG3857/{z}/{x}/{y}.png';
angular.module('leaflet', [])
    /**************************************************************************************
     minervaMap factory service
     **************************************************************************************
     The service manage the minerva maps with shortcuts for minerva purpose.
     All service function need the parameter id that is the HTML leaflet element id.
     The methods are:
       - getMap: return a map promise
       - reset: reset map layers and return a map promise
       - addPins: add an array of marker to the map
       - removePins: remove all markers from the map
       - createEstablismentMarker: create a marker for Establishment selecting the right
         icons with seveso type
    **************************************************************************************/
    .factory('minervaMap', function (leafletMap) {
        return {
            /*************************************************************************
             getMap(id)
             Returns a promise with the map object.
             Parameters:
               - id: the HTML leaflet element id
             Returns:
               - a promise with map
             Usage:
               minervaMap.getMap('myid').then(function(map) {
                  map is the leaflet map returned by L.map(...)
               });
            **************************************************************************/
            getMap: function (id) {
                return leafletMap.getMap(id);
            },
            /*************************************************************************
             reset(id)
             clean map markers and reset map size then returns a promise with the map object.
             Parameters:
               - id: the HTML leaflet element id
             Returns:
               - a promise with map
             Usage:
               minervaMap.reset('myid').then(function(map) {
                  map is the leaflet map returned by L.map(...)
               });
            **************************************************************************/
            reset: function (id) {
                return leafletMap.reset(id);
            },
            /*************************************************************************
             addPins(id, markers, isCluster, fitToBounds, maxZoom)
             add the markers to a map layer. Resize and centers the map 
             Parameters:
               - id: the HTML leaflet element id
               - markers: an array of 1-n markers elements
               - isCluster: Boolean. If true the markers are visualized using 
                 the cluser paradigma
               - fitToBouds: Boolean. If true the map is centered and resized 
                 using the markers bounds
               - maxZoom: maximum zoon used after center and resize of the map
             Returns:
               - void
             Usage:
               va mymarkers = []
               minervaMap.addPins('myid', mymarkers, true, true, 7);
            **************************************************************************/
            addPins: function (id, markers, isCluster, fitToBounds, maxZoom) {
                if (markers.length > 0) {
                    var feat = leafletMap.initFeatureGroup(id, 'default', isCluster);
                    markers.forEach(function (item) {
                        leafletMap.addMarker(id, 'default', item);
                    })
                    leafletMap.saveFeatureGroup(id, 'default');
                    if (fitToBounds) {
                        leafletMap.getMap(id).then(function (map) {
                            var options = maxZoom ? { maxZoom: maxZoom } : {};
                            map.fitBounds(feat.getBounds(), options);
                        });
                    }
                }
            },
            /*************************************************************************
             removePins(id)
             clean map markers 
             Parameters:
               - id: the HTML leaflet element id
             Returns:
               - void
             Usage:
               minervaMap.removePins('myid');
            **************************************************************************/
            removePins: function (id) {
                leafletMap.removeFeatureGroup(id, 'default');
            },
            /*************************************************************************
             getPin(id, name, alt)
             get a marker in a featureGroup finding it by alt property. 
             Paramters:
               - id: the HTML leaflet element id
               - alt: alt value
             Returns:
               - the found marker
             Usage:
               leafletMap.getMarker('myid', 'establisments', '0');
            **************************************************************************/
            getPin: function (id, alt) {
                return leafletMap.getMarker(id, 'default', alt);
            },
            /*************************************************************************
            createEstablismentMarker(id)
            create a specialized marker for establisment
            Parameters:
              - latlng: latitide and longitude as array
              - title: the title of the marker. title is shown on marker mouseover
              - desc: description for the marker. The description is shown on marker
                tooltip popup when th marker is clicked.
              - sevesoId: the seveso id ('UPPER_TIER', 'LOWER_TIER'). Used for icon
                selection
            Returns:
              - the marker created
            Usage:
              var marker = minervaMap.createEstablismentMarker(
                               [lat,lon],
                               'my title',
                               'marker description',
                               'UPPER_TIER');
           **************************************************************************/
            createEstablismentMarker: function (latlng, title, desc, sevesoId) {
                var pinurl;
                if (sevesoId == 'UPPER_TIER')
                    pinurl = '/Content/images/symmetricPin_red.png';
                else if (sevesoId == 'LOWER_TIER')
                    pinurl = '/Content/images/symmetricPin_blue.png';
                else
                    pinurl = '/Content/images/symmetricPin.png';

                if (!latlng[0]) latlng[0] = 0;
                if (!latlng[1]) latlng[1] = 0;

                var marker = L.marker(latlng,
                    {
                        icon: L.icon({ iconUrl: pinurl })
                        , title: title
                    });
                if (desc) {
                    marker.bindPopup(desc);
                }
                return marker;
            }
        }
    })
    /**************************************************************************************
     leafletMap factory service
     **************************************************************************************
     The service manage the a leaflet maps with some shortcut for map created by leaflet
     directive.
     All service function need the parameter id that is the HTML leaflet element id.
     The methods are:
       - getMap: return a map promise
       - reset: reset map layers and return a map promise
       - initFeatureGroup: initialize a new group feature that will collect markers
       - saveFeatureGroup: save the feature group and add it to map layer
       - removeFeatureGroup: remove the feature group from map layers
       - addMarker: add the marker to a feature group
    **************************************************************************************/
    .factory('leafletMap', function ($q) {
        var _maps = [];
        var _inits = [];
        var _features = [];
        var fact = this;
        var _defaultDrags = [];
        this.initFun = null;

        return {
            /*************************************************************************
             getMap(id)
             Returns a promise with the map object.
             Parameters:
               - id: the HTML leaflet element id
             Returns:
               - a promise with map
             Usage:
               minervaMap.getMap('myid').then(function(map) {
                  map is the leaflet map returned by L.map(...)
               });
            **************************************************************************/
            getMap: function (id) {
                if (_maps[id])
                    return _maps[id]();
            },
            /*************************************************************************
             reset(id)
             clean map markers and reset map size then returns a promise with the map object.
             Parameters:
               - id: the HTML leaflet element id
             Returns:
               - a promise with map
             Usage:
               minervaMap.reset('myid').then(function(map) {
                  map is the leaflet map returned by L.map(...)
               });
            **************************************************************************/
            reset: function (id) {
                if (_maps[id] && _inits[id]) {
                    if (_features[id]) {
                        for (var mapfeature in _features) {
                            for (var featurename in _features[mapfeature]) {
                                (_maps[id])().then(function (map) {
                                    map.removeLayer(_features[mapfeature][featurename]);
                                });
                            }
                        };
                    }
                    return _inits[id]();
                }
            },
            /*************************************************************************
             initFeatureGroup(id, name, isCluster, options)
             clean map markers and reset map size then returns a promise with the map object.
             Parameters:
               - id: the HTML leaflet element id
               - name: the name of feature group
               - isCluster: Boolean. If true che feature group ha a cluster visualization
               - options: leaflet native featuregroup options
             Returns:
               - the created feature group
             Usage:
               var group = leafletMap.initFeatureGroup('myid', 'establisments', true, {});
            **************************************************************************/
            initFeatureGroup: function (id, name, isCluster, options) {
                if (_maps[id]) {
                    _features[id] = {};
                    if (isCluster) {
                        _features[id][name] = L.markerClusterGroup(options);
                    } else {
                        _features[id][name] = L.featureGroup(options);
                    }
                    return _features[id][name];
                }
            },
            /*************************************************************************
             saveFeatureGroup(id, name)
             save the feature group adding id to the proper map. 
             Paramters:
               - id: the HTML leaflet element id
               - name: the name of feature group
             Returns:
               - void
             Usage:
               leafletMap.saveFeatureGroup('myid', 'establisments');
            **************************************************************************/
            saveFeatureGroup: function (id, name) {
                if (_maps[id] && _features[id] && _features[id][name]) {
                    (_maps[id])().then(function (map) {
                        map.addLayer(_features[id][name]);
                    });
                }
            },
            /*************************************************************************
             removeFeatureGroup(id, name)
             Remove the feature group from map layers 
             Paramters:
               - id: the HTML leaflet element id
               - name: the name of feature group
             Returns:
               - void
             Usage:
               leafletMap.removeFeatureGroup('myid', 'establisments');
            **************************************************************************/
            removeFeatureGroup: function (id, name) {
                if (_maps[id] && _features[id] && _features[id][name]) {
                    (_maps[id])().then(function (map) {
                        map.removeLayer(_features[id][name]);
                    });
                }
            },
            /*************************************************************************
             addMarker(id, name, marker)
             Add a marker to a particular feature group 
             Paramters:
               - id: the HTML leaflet element id
               - name: the name of feature group
               - marker: the marker to be added
             Returns:
               - void
             Usage:
               leafletMap.addMarker('myid', 'establisments', mymarker);
            **************************************************************************/
            addMarker: function (id, name, marker) {
                if (_maps[id] && _features[id] && _features[id][name]) {
                    if (_defaultDrags[id]) {
                        marker.options.draggable = true;
                        marker.on('dragend', function (event) {
                            (_defaultDrags[id])(event);
                        });
                    }
                    (_features[id][name]).addLayer(marker);
                }
            },
            /*************************************************************************
             getMarker(id, name, alt)
             get a marker in a featureGroup finding it by alt property. 
             Paramters:
               - id: the HTML leaflet element id
               - name: the name of feature group
               - alt: alt value
             Returns:
               - the found marker
             Usage:
               leafletMap.getMarker('myid', 'establisments', '0');
            **************************************************************************/
            getMarker: function (id, name, alt) {
                if (_maps[id] && _features[id] && _features[id][name]) {
                    var layers = (_features[id][name]).getLayers();
                    for (i = 0; i < layers.length; i++) {
                        if (layers[i].options.alt == alt)
                            return layers[i];
                    }
                    return null;
                }
            },
            /*************************************************************************
             This function should be considered private
             *************************************************************************/
            _setMap: function (m, initfun, id) {
                if (m) {
                    _maps[id] = m;
                    _inits[id] = initfun;
                }
            },
            /*************************************************************************
             This function should be considered private
             *************************************************************************/
            _setDefaultDrag: function (id, dragfun) {
                _defaultDrags[id] = dragfun;
            }
        }
    })
    /**************************************************************************************
     leaflet directive
     **************************************************************************************
     This directive manage the leaflet map.
     The HTML tag supports following attributes:
       - id: the map id. This id is used in all services functions
       - style: the stylesheet style that the div will use
       - class: the class of map div
       - tile: a customized tile url template.
       - on-doubleclick-zoom: name of the function in the controller called when the user
         make a double click on the map. The function will receive the 
         leaflet event parameter "event"
       - on-default-drag: name of the function in the controller called when the user
         end the drag and drop on a map marker. If defined this function is used for all 
         markers. The function will receive the leaflet event parameter "event".
         If marker needs different drag and drop event you need to add them directly to the 
         marker object with marker.on(...) function.
    
     Usage example:
     
     <leaflet id='viewMap' 
        style="width: 100%; height: 740px;"
        on-doubleclick-zoom="onMapDoubleClick"
        on-default-drag="onMapDrag"
        class="mymapstyle"
        tile="//europa.eu/webtools/maps/tiles/osm-ec/{z}/{x}/{y}.png">
     </leaflet>

    **************************************************************************************/
    .directive('leaflet', function ($q, leafletMap) {
        return {
            restrict: "EA",
            replace: true,
            scope: {
                tiles: '=',
                onDoubleclickZoom: '=',
                onDefaultDrag: '='
            },
            transclude: true,
            template: '<div class="angular-leaflet-map"></div>',
            controller: function ($scope) {
                var ctrl = this;
                this._leafletMap = $q.defer();
                this.getMap = function () {
                    return ctrl._leafletMap.promise;
                };

                this.getLeafletScope = function () {
                    return $scope;
                };
            },
            link: function (scope, element, attrs, ctrl) {
                scope.mapId = attrs.id;
                scope.element = element;
                scope.attrs = attrs;

                function reset() {
                    return ctrl.getMap().then(function (data) {
                        data.invalidateSize()
                        return ctrl.getMap();
                    });
                }

                function _init() {
                    // Create the Leaflet Map Object with the options
                    var map = new L.Map(scope.element[0]);

                    if (scope.onDoubleclickZoom) {
                        map.doubleClickZoom.disable();
                        map.on('dblclick', function (event) {
                            scope.onDoubleclickZoom(event)
                        });
                    } else {
                        map.doubleClickZoom.enable();
                    }

                    map.setView([57, 8], 5);
                    ctrl._leafletMap.resolve(map);

                    if (attrs.style !== null) {
                        scope.$watch(
                            function () {
                                return scope.element[0].getAttribute('style');
                            },
                            function (style) {
                                if (!style) {
                                    return;
                                }
                                attrs.$set('style', style);
                                map.invalidateSize();
                            });
                    }

                    // If no layers nor tiles defined, set the default tileLayer
                    if (attrs.tiles !== null) {
                        var tileLayerObj = L.tileLayer(LEAFLET_DEFAULT_TILE);
                        tileLayerObj.addTo(map);
                    }

                    map.whenReady(function () {
                        leafletMap._setMap(ctrl.getMap, reset, attrs.id);
                        if (scope.onDefaultDrag) {
                            leafletMap._setDefaultDrag(attrs.id, scope.onDefaultDrag);
                        }
                    });

                    return ctrl.getMap();
                }

                _init();

                scope.$on('$destroy', function () {
                    ctrl._leafletMap.remove();
                });

                scope.$on('invalidateSize', function () {
                    ctrl._leafletMap.invalidateSize();
                });
            }
        }
    });

   
//angular.module('minervaApp', []).config(['$controllerProvider', function ($controllerProvider) {
//    // this option might be handy for migrating old apps, but please don't use it
//    // in new ones!
//    $controllerProvider.allowGlobals();
//}]);

//regular angular initialization continued below....
// TO FIX: var app = angular.module('minervaApp', ['ui', 'ajaxloader', 'ngDragDrop', 'angularTreeview']);
var app = angular.module('minervaApp', ['ui', 'angularTreeview', 'ngSanitize', 'feeds', 'ngDragDrop', 'ngIdle', 'leaflet', 'ngAnimate', 'ngSanitize', 'ui.bootstrap']);

app.config(function ($provide) {
    $provide.decorator("$exceptionHandler", ['$delegate', function ($delegate) {
        return function (exception, cause) {
            console.log(exception);
            console.log("Cause: " + cause);
            $delegate(exception, cause);
        };
    }]);
});

app.config(function (IdleProvider, KeepaliveProvider) {
    // configure Idle settings
    IdleProvider.idle(1170); // in seconds //19.30 min
    IdleProvider.timeout(30); // in seconds
})

app.config(['$httpProvider', function ($httpProvider) {
    csrfToken = $('meta[name=csrf-token]').attr('content');
    $httpProvider.defaults.headers.post['X-CSRF-Token'] = csrfToken;
    $httpProvider.defaults.headers.put['X-CSRF-Token'] = csrfToken;
    $httpProvider.defaults.headers.common['X-CSRF-Token'] = csrfToken;
}]);

app.run(function (Idle) {
    // start watching when the app runs. also starts the Keepalive service by default.
    Idle.watch();
});

app.directive('ngBlur', function () {
    return function (scope, elem, attrs) {
        elem.bind('blur', function () {
            scope.$apply(attrs.ngBlur);
        });
    };
});

var FLOAT_REGEXP = /^\-?\d+((\.|\,)\d+)?$/;
//var FLOAT_REGEXP = /^\-?\d+((\.)\d+)?$/;

app.directive('smartFloat', function () {
    return {
        require: 'ngModel',
        link: function (scope, elm, attrs, ctrl) {

            var htmlInput = elm[0];

            var fun = function (value) {

                //if (FLOAT_REGEXP.test(value)) {
                //    ctrl.$setValidity('float', true);
                //}
                //else { ctrl.$setValidity('float', false); }

                //var transform = value.toString().replace(',', '.');
                var min = attrs.min;
                if (min == undefined || min == null)
                    min = attrs.Min;
                if (FLOAT_REGEXP.test(min))
                    min = parseFloat(min);

                if (value == '-' && min < 0) return 0;


                var max = attrs.max;
                if (max == undefined || max == null)
                    max = attrs.Max;
                if (FLOAT_REGEXP.test(max))
                    max = parseFloat(max);

                if (min != null && min >= 0 && value == '-')
                { elm.val(''); return 0; }

                // split string into a char array 
                var split = value.toString().split('');
                value = "";
                var hascommadot = false;
                var hasless = false;
                var i = 0;
                for (i = 0; i < split.length; i++) {
                    if (split[i] == ' ')
                        continue;

                    // replace comma with dot because js work with this char
                    if (split[i] == ',')
                        split[i] = '.';

                    // is not possible to put dot or 0 at the first position
                    if (i == 0 && split[i] == '.')
                        continue;

                    // if the number is less than 1 and more than -1 and after the 0 there is not a dot, it is an error
                    if ((i == 1 && split[0] == '0' && split[i] != '.' && !(i == 1 && split[0] == '-' && split[i] != '0'))
                        || (i == 2 && split[0] == '-' && split[1] == '0' && split[i] != '.')) {
                        value = '';
                        break;
                    }

                    // check if a less char is in the first position and/or was already looped
                    if (split[i] == '-') {
                        if (i > 0)
                        { continue; }
                        else {
                            if (hasless)
                                continue;
                            else
                                hasless = true;
                        }
                    }

                    if (split[i] == '1' || split[i] == '2' || split[i] == '3' || split[i] == '4'
                        || split[i] == '5' || split[i] == '6' || split[i] == '7' || split[i] == '8'
                        || split[i] == '9' || split[i] == '0' || split[i] == '.' || split[i] == '-') {
                        // check if a comma or dot char was already looped
                        if (split[i] == '.') {
                            if (hascommadot)
                                continue;
                            else
                                hascommadot = true;
                        }

                        // add char to the reusult string
                        value = value + split[i];

                        // check the min and max value. in case is more or less 
                        // it remove the last char pushed into the result string
                        if (split[i] != '.' && split[i] != '-') {
                            var fval = parseFloat(value);
                            if (min != undefined && min != null) {
                                if (fval < min)
                                    if (i > 1)
                                        value = value.substring(0, i);
                                    else
                                    { elm.val(''); return 0; }

                            }
                            if (max != undefined && max != null) {
                                if (fval > max)
                                    if (i > 1)
                                        value = value.substring(0, i);
                                    else
                                    { elm.val(''); return 0; }
                            }
                        }

                    }

                }

                var fval = parseFloat(value);
                if (isNaN(fval))
                { elm.val(''); return 0; }

                if (min != null) {
                    if (fval < min) {
                        fval = min; elm.val(fval); return fval;
                    }

                }
                if (max != null) {
                    if (fval > max)
                    { fval = max; elm.val(fval); return fval; }

                }
                elm.val(value);
                //ctrl.$setViewValue(fval);
                return fval;

                //return parseFloat(transform);
                //} else {
                //    ctrl.$setValidity('float', false);
                //    return undefined;
                //}
            }

            htmlInput.addEventListener("keyup", function (e) {
                if ((e.keyCode || e.which) == 37 ||
                    (e.keyCode || e.which) == 39 ||
                    (e.keyCode || e.which) == 39 ||
                    (e.keyCode || e.which) == 40 ||
                    (e.keyCode || e.which) == 46 ||
                    (e.keyCode || e.which) == 13 ||
                    (e.keyCode || e.which) == 16 ||
                    (e.keyCode || e.which) == 17 ||
                    (e.keyCode || e.which) == 18) {
                    return;
                }
                value = htmlInput.value;
                fun(value);
            });
            //ctrl.$parsers.push(fun);

            //ctrl.$parsers.unshift();
        }
    };
});

app.directive('dirDraggable', function () {
    return {
        restrict: 'A',
        link: function (scope, elm, attrs) {
            var options = scope.$eval(attrs.dirDraggable); //allow options to be passed in
            elm.draggable(options);
        }
    };
});

app.directive('tooltipcaller', function ($timeout) {
    return {
        restrict: 'A',
        link: function (scope, el, attrs, ctrl) {
            el.bind("mouseenter", tooltipMouseEnter);
            el.bind("mouseleave", tooltipMouseLeave);
        }
    }
});

app.directive('carouselDirective', function () {
    return function (scope, element, attrs) {
        if (scope.$last) {

            angular.element($(".imgcarousel")).ready(function () {
                console.log("imgcarousel wait");
                setTimeout(function () {
                    console.log("imgcarousel run");
                    $('.imgcarousel').flickity({ cellAlign: 'left', autoPlay: false });
                }, 1000);
            });
        }
    };
});

app.directive('onFinishRenderScrollLeft', function ($timeout) {
    return {
        restrict: 'A',
        link: function (scope, el, attrs, ctrl) {
            if (scope.$last === true) {
                $timeout(function () {
                    $('#' + attrs.elidtoscroll).scrollLeft($('#' + attrs.elidtoscroll).width());
                });
            }
        }
    }
});

app.directive('ngTabs', function () {
    return function (scope, elm) {
        setTimeout(function () {
            elm.tabs();
        }, 0);
    };
});

app.directive('autocomplete', ['$http', function ($http) {
    return function (scope, element, attrs) {
        element.autocomplete({
            minLength: 0,
            dataType: "json",
            source: function (request, response) {
                var url = attrs.url + "&searchingtext=" + request.term;
                $http.get(url).success(function (data) {
                    response(data.Data);
                });
            }, focus: function (event, ui) {
                eval(attrs.onfocuscallback);
                return false;
            }, select: function (event, ui) {
                eval(attrs.onselectcallback);
                return false;
            }, change: function (event, ui) {
                eval(attrs.onchangecallback);
                return false;
            } /*, create: function () {
                element.data('ui-autocomplete')._renderItem = function (ul, item) {
                    return $('<li>')
                        .append("<a>" + eval(attrs.selitemname) + "</a>")
                        .appendTo(ul);
                };
            }*/
        }).data("ui-autocomplete")._renderItem = function (ul, item) {
            return $("<li>")
                .append("<a>" + eval(attrs.selitemname) + "</a>")
               // .append("<a>Pippo</a>")
                .appendTo(ul);
        };

        //element.bind('blur', function () {
        //    eval(attrs.onblurcallback);
        //});

    }
}]);



app.directive('chartDir', function () {
    return {
        restrict: 'E',
        require: 'ngModel',
        scope: {
            model: '=ngModel',
            search2d: '&',
            search3d: '&',
            iszoom: '=iszoom'
        },
        link: function (scope, el, attrs, ctrl) {

            var appoModel;

            draw = function () {

                var chartType = appoModel.SelectedChart.Id.toLowerCase();
                var stacking = '';

                var iszoom = false;
                var isprint = false;

                var viewlegend = (appoModel.Id == 'EST_COUNTRY_SEVESO_TIER' && (chartType == 'bar' || chartType == 'column')) ? true : false;

                var i = 0;
                if (el.context != undefined) {
                    if (el.context.classList != undefined) {
                        for (i = 0; i < el.context.classList.length; i++) {
                            if (el.context.classList[i] == "zoom") {
                                iszoom = true;
                                break;
                            } if (el.context.classList[i] == "print") {
                                isprint = true;
                                break;
                            }
                        }
                    }
                }
                //var pointerFormat = '<b>{point.y}</b> - {point.percentage:.1f}%';
                switch (chartType) {
                    case "donut":
                        chartType = 'pie';
                        appoModel.series[0].innerSize = '50%';
                        break;
                    case "pie":
                        appoModel.series[0].innerSize = '';
                        break;
                    case "stacked_bar":
                        chartType = 'column';
                        //      pointerFormat = '<span style="color:{series.color}"><b>{point.y}</b> - {point.percentage:.1f} %';
                        break;
                    case "bar":
                        //      pointerFormat = '<span style="color:{series.color}"><b>{point.y}</b> - {point.percentage:.1f}%';
                        break;
                    case "grouped_column":
                        chartType = 'column';
                        if (appoModel.Categories.length >= 10)
                            chartType = 'bar';
                        break;
                    case "stacked_column":
                        chartType = 'column';

                        stacking = 'normal';
                        break;

                }

                el.highcharts({
                    chart: {
                        type: chartType
                    },
                    exporting: {
                        enabled: false
                    },
                    title: {
                        text: appoModel.Title,
                        align: 'center',
                        y: 20
                    },
                    tooltip: {
                        //                            pointFormat: pointerFormat   
                        enabled: (isprint) ? false : true,
                        useHTML: true,
                        backgroundColor: "rgba(255, 255, 255, 1)",
                        formatter: function () {
                            var tooltip = "";
                            if (appoModel.TypeStat == "2D") {

                                var i = 0;
                                for (i = 0; i < appoModel.DataSerialized.Rows.length; i++) {
                                    if (appoModel.DataSerialized.Rows[i].Xin == this.key) {
                                        if (appoModel.DataSerialized.Rows[i].Xitip != undefined && appoModel.DataSerialized.Rows[i].Xitip != '') {
                                            tooltip = "<br><br>" + appoModel.DataSerialized.Rows[i].Xitip;
                                        }
                                        break;
                                    }
                                }
                                if (tooltip != "") {
                                    return "<div style='width: 400px; white-space:normal; '>" + "<b>" + this.key + " - " + this.y + "</b> - " + getGraphicPercent(this.y / appoModel.DataSerialized.Sum.Yit * 100) + "%" + tooltip + "</div>";
                                }
                                else { return "<b>" + this.key + " - " + this.y + "</b> - " + getGraphicPercent(this.y / appoModel.DataSerialized.Sum.Yit * 100) + "%"; }
                            }
                            for (i = 0; i < appoModel.DataSerialized.Rows.length; i++) {
                                var row = appoModel.DataSerialized.Rows[i];
                                if (row.Xin == this.x) {

                                    var j = 0;
                                    for (j = 0; j < appoModel.DataSerialized.Rows[i].Cols.length; j++) {
                                        if (appoModel.DataSerialized.Rows[i].Cols[j].Yin == this.key) {
                                            if (appoModel.DataSerialized.Rows[i].Cols[j].Yitip != undefined && appoModel.DataSerialized.Rows[i].Cols[j].Yitip != '') {
                                                tooltip = "<br><br>" + appoModel.DataSerialized.Rows[i].Cols[j].Yitip;
                                            }
                                            break;
                                        }
                                    }
                                    if (tooltip != "") {
                                        return "<div style='width: 400px; white-space:normal;'>" + "<b>" + this.key + " - " + this.y + "</b> - " + getGraphicPercent(this.y / row.Sum.Zit * 100) + "%" + tooltip + "</div>";
                                    }
                                    else { return "<b>" + this.key + " - " + this.y + "</b> - " + getGraphicPercent(this.y / row.Sum.Zit * 100) + "%"; }

                                    //return "<b>" + this.y + "</b> - " + getGraphicPercent(this.y / row.Sum.Zit * 100) + "%";
                                }

                            }

                            //           return "<b>" + this.y + "</b> - " + getGraphicPercent(this.y / appoModel.Data.Sum.Zit * 100) + "%";

                        },

                    },
                    xAxis: {
                        categories: appoModel.Categories,
                        title: {
                            text: appoModel.DataSerialized.Def.Xil
                        }
                    },
                    yAxis: {
                        min: 0,
                        title: {
                            text: (appoModel.TypeStat == "2D") ? appoModel.DataSerialized.Def.Yil : appoModel.DataSerialized.Def.Zil
                        }
                    },
                    legend: { enabled: viewlegend },
                    plotOptions: {
                        series: {
                            cursor: 'pointer',
                            dataLabels: {
                                enabled: true,
                                //formatter: function () {
                                //    var val = this.y;
                                //    if (val <= 0) {
                                //        return '';
                                //    }
                                //    if (val.length > 20) { val = val.substr(0, 20) + '...'; }
                                //    return val;
                                //}
                            },
                            events: {
                                click: function (event) {
                                    if (appoModel.TypeStat == "2D") {

                                        scope.search2d({
                                            xik: event.point.Id,
                                            xin: event.point.name
                                        });
                                    }
                                    else {

                                        var code = event.point.category;
                                        for (i = 0; i < appoModel.Data.Rows.length; i++) {
                                            if (appoModel.Data.Rows[i].Xin == event.point.category) { code = appoModel.Data.Rows[i].Xik; break; }
                                        }
                                        scope.search3d({
                                            yik: event.point.Id,
                                            xik: code,
                                            yin: event.point.name,
                                            xin: event.point.category
                                        });
                                    }
                                }
                            }
                        },

                        bar: {
                            allowPointSelect: true,
                            dataLabels: {
                                enabled: true,
                                formatter: function () {
                                    var val = this.y;
                                    if (val <= 0) {
                                        return '';
                                    }
                                    return val;
                                },
                                color: (Highcharts.theme && Highcharts.theme.dataLabelsColor) || 'black',
                                style: { "font-size": "9pt" }
                            }
                        },
                        column: {
                            allowPointSelect: true,

                            stacking: stacking,
                            dataLabels: {
                                enabled: true,
                                formatter: function () {
                                    //console.log(this);
                                    var val = this.y;
                                    if (val <= 0) {
                                        return '';
                                    }
                                    return val;
                                },
                                color: (Highcharts.theme && Highcharts.theme.dataLabelsColor) || 'black',
                                style: { "font-size": "9pt" }
                            }
                        },
                        pie: {
                            allowPointSelect: true,
                            cursor: 'pointer',
                            dataLabels: {
                                enabled: true,
                                useHTML: true,
                                formatter: function () {
                                    var val = this.y;
                                    var name = this.point.name;

                                    if (name.length > 20) { name = name.substr(0, 20) + '...'; }


                                    if (val <= 0) {
                                        return '<b>' + name + '</b>';
                                    }
                                    return '<b>' + name + '/</b> ' + val;
                                    //return '<b>{point.name}</b>: '+ val;
                                }

                                //format: '<b>{point.name}</b>: {point.percentage:.1f} %'
                                //enabled: true,
                                //color: '#000000',
                                //connectorColor: '#000000',
                                //format: '<b>{point.name}</b>: {point.percentage:.1f} %'
                                //distance: -50,
                                //style: {
                                //    fontWeight: 'bold',
                                //    color: 'white',
                                //    textShadow: '0px 1px 2px black'
                                //}
                            }
                        }
                    },
                    series: appoModel.series
                });


            }

            scope.$watch("model.Statistic.SelectedChart", function (model) {
                if (appoModel != undefined) { draw(); }
            });

            scope.$watch("model.Statistic", function (model) {
                if (model != undefined) {
                    appoModel = model;
                    draw();
                }
            });



        }
    };
});

app.directive('fancytree', function ($compile) {
    var generatedIds = 0;
    return {
        require: 'ngModel',
        link: function (scope, element, attrs, ctrl) {
            var treeinitialized = [];

            var dndenabled = (attrs.dndenabled != undefined && attrs.dndenabled == 'true');
            if (!attrs.id) { attrs.$set('id', 'fancytree' + (generatedIds++)); }

            scope.$watch(function () { return ctrl.$viewValue }, function (newVal) {
                //console.log("Changed to " + newVal);
                ctrl.$setViewValue(newVal);
                initTree(attrs.id);
                if (newVal != undefined && newVal != null) {

                    //$("#" + attrs.id).fancytree("getTree").reload(newVal.children);
                    element.data("ui-fancytree").getTree().reload(newVal.children);

                    var invoker = scope.$eval(attrs.treeafterreload);
                    if (invoker) invoker(scope, null);
                }
            });

            //if (attrs.selectedid != undefined) {
            // activate node if selected id change
            scope.$watch(function () { return attrs.selectedid }, function (value) {
                if (value == undefined || value == '')
                    return;
                var tree = element.data("ui-fancytree").getTree();

                tree.activateKey(value);

                // if flag drop from outside the tree is set as true 
                if (attrs.isextdroppable != undefined && attrs.isextdroppable == 'true') {

                    // if the drang and drop external array name is set
                    var arrayName;
                    if (attrs.dndextarrname != undefined && attrs.dndextarrname != '')
                        arrayName = attrs.dndextarrname;
                    else
                        arrayName = "null";

                    // loop all internal nodes and add the div for drag and drop from outside
                    tree.visit(function (node) {
                        if ($(node.span) != null && $(node.span)[0] != undefined && $(node.span)[0].children != undefined) {

                            var compiled = $compile("<div ui-on-drop=\"onDrop($event, $data, " + arrayName + ", '" + node.key + "', true)\" class=\"treefolder dropclass\" style=\"position: absolute; width: 80%; height: 22px; margin-top: -22px; margin-left: 15px;\" ></div>")(scope);
                            // add only if div is not already contained within the node
                            var i = 0;
                            var alreadypresent = false;
                            for (i = 0; i < $(node.span)[0].children.length; i++) {
                                if ($(node.span)[0].children[i].tagName == 'DIV') {
                                    alreadypresent = true; i = $(node.span)[0].children.length;
                                }
                            }
                            if (!alreadypresent)
                                $(node.span).append(compiled);
                        }
                    })

                    // add the dragover event for every element with dropclass set
                    $('.dropclass').on('dragover', function () {
                        $(this).addClass('upload-is-dragover');
                    }).on('dragleave dragend drop', function () {
                        $(this).removeClass('upload-is-dragover');
                    });
                }
            });
            //}
            options = {
                createNode: function (event, data) {
                    //var invoker = scope.$eval(attrs.treeclick);
                    //bindContextMenu(data.node.span); //rightClickRenameFolder('" + node.key + "', $event, true)
                    data.node.span.setAttribute('data-ng-right-click', attrs.treerightclick + '($event, "' + data.node.key + '")');
                    $compile(data.node.span)(scope);

                },
                clickFolderMode: 1,
                activeVisible: true,
                extensions: ["dnd"], // "edit"
                source: [],
                click: function (event, data) {
                    //console.log("click");
                    var invoker = scope.$eval(attrs.treeclick);
                    if (invoker) invoker(scope, {
                        arg1: event, arg2: data
                    });
                },
                dnd: {
                    autoExpandMS: 400,
                    focusOnClick: true,
                    preventVoidMoves: true, // Prevent dropping nodes 'before self', etc.
                    preventRecursiveMoves: true, // Prevent dropping nodes on own descendants
                    dragStart: function (node, data) {
                        return dndenabled;
                    },
                    dragEnter: function (node, data) {
                        if (!node.folder) return ["before", "after"];
                        return dndenabled;
                    },
                    dragDrop: function (node, data) {
                        data.otherNode.moveTo(node, data.hitMode);

                        var rootNode = element.fancytree("getRootNode");
                        var d = rootNode.toDict(true);
                        ctrl.$setViewValue(d);

                        // console.log("move");
                        var invoker = scope.$eval(attrs.treemove);
                        if (invoker) invoker(data.otherNode, node, data.hitMode);
                    },
                }
            };

            //setTimeout(function () {
            //    initTree(attrs.id);
            //});

            scope.expandAll = function () {
                element.fancytree("getRootNode").visit(function (node) {
                    node.setExpanded(true);
                })
            };

            scope.collapseAll = function () {
                element.fancytree("getRootNode").visit(function (node) {
                    node.setExpanded(false);
                })
            };

            scope.activateNode = function (nodeKey) {
                var tempTree = element.data("ui-fancytree").getTree();

                var activeNode = tempTree.getActiveNode();
                if (activeNode != null)
                    activeNode.setActive(false);

                tempTree.activateKey(nodeKey);
            };

            initTree = function (id) {
                var alreadyinit = false;

                if (treeinitialized != undefined) {
                    var i = 0;
                    for (i = 0; i < treeinitialized.length; i++) {
                        if (treeinitialized[i] == id)
                            alreadyinit = true;
                    }
                }

                if (!alreadyinit) {
                    element.fancytree(options);
                    treeinitialized.push(id);
                }
            }


        }
    };
});

app.directive('wsTinymce', function ($compile) {
    //uiTinymceConfig = uiTinymceConfig || {};
    var generatedIds = 0;
    return {
        require: 'ngModel',

        link: function (scope, element, attrs, ctrl) {

            var expression, options, tinyInstance,
              updateView = function () {
                  scope.$apply(function () {
                      var value = tinyMCE.get(attrs.id).getContent();
                      ctrl.$setViewValue(value);
                      // scope.$broadcast('resetContentEditableModel');
                  });
              };
            // generate an ID if not present

            tinymce.baseURL = "/minerva/js/tinymce";
            if (!attrs.id) {
                attrs.$set('id', 'uiTinymce' + generatedIds++);
            }

            if (attrs.uiTinymce) {
                expression = scope.$eval(attrs.uiTinymce);
            } else {
                expression = {
                };
            }

            var basePlugins = [
            "advlist autolink lists link image charmap anchor", //print preview 
            "searchreplace visualblocks code fullscreen",
            "insertdatetime table contextmenu paste textcolor lineheight tablestyle" //media
            ];

            var baseToolbar = "insertfile undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify lineheightselect tablestyleselect | forecolor backcolor | bullist numlist outdent indent | link image";
            var baseRemoved_menuitems = 'print';

            if (attrs.limited) {        //if emars

                basePlugins = [
           "advlist autolink lists charmap anchor", //print preview 
           "searchreplace visualblocks fullscreen",
           "insertdatetime table contextmenu paste textcolor lineheight tablestyle " //media
                ];
                baseToolbar = "undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify lineheightselect tablestyleselect | forecolor backcolor | bullist numlist outdent indent";
                baseRemoved_menuitems = 'print, file'
            }

            if (attrs.forum) {        //if forum

                basePlugins = [
           "autolink charmap", //print preview 
           "searchreplace ",
           "insertdatetime paste emoticons" //media
                ];
                baseToolbar = "emoticons | undo redo | bold italic | alignleft aligncenter alignright alignjustify ";
                baseRemoved_menuitems = 'print, file'
            }


            options = {
                // Update model when calling setContent (such as from the source editor popup)
                setup: function (ed) {
                    var args;
                    ed.on('init', function (args) {
                        ctrl.$render();
                        setTimeout(function () {
                            scope.$eval(attrs.onInit);
                        }, 1500);
                        if (ed.getContent() != '') {
                            $('#' + ed.bodyElement.id).removeClass('isEmpty').removeClass('invalid').removeClass('ng-invalid');
                        } else {
                            $('#' + ed.bodyElement.id).addClass('isEmpty');
                            $('#' + ed.bodyElement.id + ' .required').addClass('invalid').addClass('ng-invalid');
                        }
                        updateView();
                    });
                    ed.on("change keyup", function (e) {
                        scope.$eval(attrs.onChange);
                        if (ed.getContent() != '') {
                            $('#' + ed.bodyElement.id).removeClass('isEmpty').removeClass('invalid').removeClass('ng-invalid');
                        } else {
                            $('#' + ed.bodyElement.id).addClass('isEmpty');
                            $('#' + ed.bodyElement.id + ' .required').addClass('invalid').addClass('ng-invalid');
                        }
                        updateView();
                    });
                    ed.on('paste', function (e) {
                        scope.$eval(attrs.onChange);
                        updateView();
                    });
                    ed.on('ExecCommand', function (e) {
                        scope.$eval(attrs.onChange);
                        if (tinyMCE.activeEditor.isDirty()) {
                            updateView();
                        }
                        scope.$apply();
                    });
                },
                mode: 'exact',
              //  extended_valid_elements: 'a[id|rel|target|class|href|data-toggle|aria-expanded|aria-controls]',

                elements: attrs.id,
                inline: true,
                relative_urls: false,
                plugins: basePlugins,
                table_default_styles: { "width": "100%" },
                table_appearance_options: false,
                //table_toolbar: "tableprops tabledelete | tableinsertrowbefore tableinsertrowafter tabledeleterow | tableinsertcolbefore tableinsertcolafter tabledeletecol",
                table_class_list: [
                    { title: 'None', value: 'none' },
                    { title: 'Block', value: 'block' },
                    { title: 'LightLines', value: 'lightlines' },
                    { title: 'TwoTones', value: 'twotones' }
                ],

                /*    style_formats: [
                        { title: 'table block', block: 'table', classes: 'block' },
                        { title: 'table lightlines', block: 'table', classes: 'lightlines' },
                        { title: 'table twotones', block: 'table', classes: 'twotones' }
                        ], */

                //table_appearance_options: false,
                table_advtab: false,
                table_cell_advtab: false,
                table_row_advtab: false,
                toolbar: baseToolbar,
                lineheight_formats: "8pt 9pt 10pt 11pt 12pt 14pt 16pt 18pt 20pt 22pt 24pt 26pt 36pt",
                removed_menuitems: baseRemoved_menuitems,
                file_browser_callback:  /*'myFileBrowser' */
                    function (field_name, url, type, win) {

                        /* Here goes the URL to your server-side script which manages all file browser things. */
                        //var cmsURL = window.location.pathname.toLowerCase().replace("/edit", "/browse");     // your URL could look like "/scripts/my_file_browser.php"
                        var cmsURL = "/en/admin/cms/webcontentresourcesearch";
                        var searchString = window.location.search; // possible parameters
                        if (searchString.length < 1) {
                            // add "?" to the URL to include parameters (in other words: create a search string because there wasn't one before)
                            searchString = "?";
                        }

                        // newer writing style of the TinyMCE developers for tinyMCE.openWindow
                        winbrowser = tinyMCE.activeEditor.windowManager.open({
                            //file: cmsURL + searchString + "&type=" + type, // PHP session ID is now included if there is one at all
                            title: "File Browser",
                            width: 855,  // Your dimensions may differ - toy around with them!
                            height: 650,
                            close_previous: "no",
                            buttons: [{
                                text: 'Cancel',
                                onclick: 'close'
                            }],
                            body: [],
                        }, {
                            window: win,
                            input: field_name,
                            resizable: "yes",
                            inline: "yes",  // This parameter only has an effect if you use the inlinepopups plugin!
                            editor_id: attrs.id,
                            type: type
                        });

                        var loadurl = cmsURL + "/" + searchString + "&type=" + type;

                        $("DIV[aria-label='File Browser'] .mce-container-body:first").html($compile("<ng-include src='\"" + loadurl + "\"'></ng-include>")(scope));

                        scope.$apply();
                        return false;
                    }
            };
            // extend options with initial uiTinymceConfig and options from directive attribute value
            setTimeout(function () {
                tinymce.init(options);
            });


            ctrl.$render = function () {
                if (!tinyInstance) {
                    tinyInstance = tinymce.get(attrs.id);
                }

                if (tinyInstance) {
                    tinyInstance.setContent(ctrl.$viewValue || '');
                    if (tinyInstance.getContent() != '') {
                        $('#' + tinyInstance.bodyElement.id).removeClass('isEmpty').removeClass('invalid').removeClass('ng-invalid');
                    } else {
                        $('#' + tinyInstance.bodyElement.id).addClass('isEmpty');
                        $('#' + tinyInstance.bodyElement.id + '.required').addClass('invalid').addClass('ng-invalid');
                    }
                }
            };
        }
    };
});


app.directive('colorpicker', function () {
    return {
        require: '?^ngModel',
        link: function ($scope, $element, $attrs, ngModel) {

            var pickerName = "#" + $attrs.colorpicker; // Get the name of the variable 
            $(pickerName).hide();
            var htmlInput = $element[0];

            $.farbtastic(pickerName).linkTo(function onColorChange(color) {
                $(htmlInput).val(color);
                ngModel.$setViewValue(color);
                ChangeColor(color);
            });



            // 2 evento quanto c'è una modifica sul modello (metodo dichiarato sopra)
            ngModel.$formatters.push(function (color) {
                ChangeColor(color);
                $(htmlInput).val(color);
            });

            htmlInput.addEventListener("blur", function () {
                $(pickerName).hide();
            });

            htmlInput.addEventListener("keyup", function () {
                $(pickerName).show();
                color = ngModel.$viewValue;
                ChangeColor(color);
            });

            htmlInput.addEventListener("focus", function () {
                value = htmlInput.value;
                if (value != null && value != undefined && value != '') {
                    $.farbtastic(pickerName).setColor(value);

                }
                ChangeColor(value);
                $(pickerName).show();
            });

            function ChangeColor(color) {
                if (color != null && color != undefined && color != '') {
                    $(htmlInput).css({ 'color': invertHex(color) });
                    $(htmlInput).css({ 'background-color': color });
                } else {
                    $(htmlInput).css({ 'color': '' });
                    $(htmlInput).css({ 'background-color': '' });
                }
            };


            function invertHex(hexnum) {
                if (hexnum == null || hexnum == undefined || hexnum.length != 7) {
                    return "#000000";
                }

                hexnum = hexnum.toUpperCase();
                var splitnum = hexnum.split("");
                var resultnum = "";
                var simplenum = "FEDCBA9876".split("");
                var complexnum = new Array();
                complexnum.A = "5";
                complexnum.B = "4";
                complexnum.C = "3";
                complexnum.D = "2";
                complexnum.E = "1";
                complexnum.F = "0";

                for (i = 1; i < 7; i++) {
                    if (!isNaN(splitnum[i])) {
                        resultnum += simplenum[splitnum[i]];
                    } else if (complexnum[splitnum[i]]) {
                        resultnum += complexnum[splitnum[i]];
                    } else {
                        console.error("Hex colors must only include hex numbers 0-9, and A-F");
                        return false;
                    }
                }

                return resultnum;
            }


        }
    };
});



// Latitude and Longitude formats


//SDMS           -41°25'01" symbol (+-) degree°minutes'seconds"
app.filter('SDMSLatitude', function () {
    return function (item) {
        return formatLatitude(item, 'SDMSLatitude');
    };
});
app.filter('SDMSLongitude', function () {
    return function (item) {
        return formatLatitude(item, 'SDMSLongitude');
    };
});

//SDD            -120.9762  symbol (+-) degree.decimals         (DATABASE STANDARD)
app.filter('SDDLatitude', function () {
    return function (item) {
        return formatLatitude(item, 'SDDLatitude');
    };
});
app.filter('SDDLongitude', function () {
    return function (item) {
        return formatLatitude(item, 'SDDLongitude');
    };
});

//CDMS           N 41°25'01" cardinal (NS/EW) degree°minutes'seconds"
app.filter('CDMSLatitude', function () {
    return function (item) {
        return formatLatitude(item, 'CDMSLatitude');
    }
});
app.filter('CDMSLongitude', function () {
    return function (item) {
        return formatLatitude(item, 'CDMSLongitude');
    }
});

//CDD            N 120.9762  cardinal (NS/EW) degree.decimals
app.filter('CDDLatitude', function () {
    return function (item) {
        return formatLatitude(item, 'CDDLongitude');
    }
});
app.filter('CDDLongitude', function () {
    return function (item) {
        return formatLatitude(item, 'CDDLongitude');
    }
});

function formatLatitude(item, format) {
    //item = (item != undefined ? parseFloat(item).toFixed(6) : item);
    switch (format) {
        case "SDMSLatitude":
            {
                signlat = (item < 0) ? -1 : 1;
                latAbs = Math.abs(Math.round(item * 1000000));
                return ((Math.floor(latAbs / 1000000) * signlat) + '°' + Math.floor(((latAbs / 1000000) - Math.floor(latAbs / 1000000)) * 60) + '\'' + (Math.floor(((((latAbs / 1000000) - Math.floor(latAbs / 1000000)) * 60) - Math.floor(((latAbs / 1000000) - Math.floor(latAbs / 1000000)) * 60)) * 100000) * 60 / 100000) + '"');
            }
        case "SDMSLongitude":
            {
                signlat = (item < 0) ? -1 : 1;
                latAbs = Math.abs(Math.round(item * 1000000));
                return ((Math.floor(latAbs / 1000000) * signlat) + '°' + Math.floor(((latAbs / 1000000) - Math.floor(latAbs / 1000000)) * 60) + '\'' + (Math.floor(((((latAbs / 1000000) - Math.floor(latAbs / 1000000)) * 60) - Math.floor(((latAbs / 1000000) - Math.floor(latAbs / 1000000)) * 60)) * 100000) * 60 / 100000) + '"');
            }
        case "SDDLatitude": return item;
        case "SDDLongitude": return item;
        case "CDMSLatitude":
            {
                latAbs = Math.abs(Math.round(item * 1000000));
                return ((item < 0) ? "S" : "N") + " " + ((Math.floor(latAbs / 1000000)) + '°' + Math.floor(((latAbs / 1000000) - Math.floor(latAbs / 1000000)) * 60) + '\'' + (Math.floor(((((latAbs / 1000000) - Math.floor(latAbs / 1000000)) * 60) - Math.floor(((latAbs / 1000000) - Math.floor(latAbs / 1000000)) * 60)) * 100000) * 60 / 100000) + '"');
            }
        case "CDMSLongitude":
            {
                latAbs = Math.abs(Math.round(item * 1000000));
                return ((item < 0) ? "W" : "E") + " " + ((Math.floor(latAbs / 1000000)) + '°' + Math.floor(((latAbs / 1000000) - Math.floor(latAbs / 1000000)) * 60) + '\'' + (Math.floor(((((latAbs / 1000000) - Math.floor(latAbs / 1000000)) * 60) - Math.floor(((latAbs / 1000000) - Math.floor(latAbs / 1000000)) * 60)) * 100000) * 60 / 100000) + '"');
            }
        case "CDDLatitude": return ((item < 0) ? "S" : "N") + " " + Math.abs(item);
        case "CDDLongitude": return ((item < 0) ? "W" : "E") + " " + Math.abs(item);
    }
}

function parseLatitude(item, format) {
    switch (format) {
        case "SDMSLatitude": {
            dlat = item.split('°')[0];
            mlat = item.split('°')[1].split('\'')[0];
            slat = item.split('°')[1].split('\'')[1].split('"')[0];
            signlat = (dlat < 0) ? -1 : 1;
            absdlat = Math.abs(Math.round(dlat * 1000000));
            absmlat = Math.abs(Math.round(mlat * 1000000));
            absslat = Math.abs(Math.round(slat * 1000000));
            return Math.round(absdlat + (absmlat / 60.) + (absslat / 3600.)) * signlat / 1000000;
        }
        case "SDMSLongitude": {
            dlat = item.split('°')[0];
            mlat = item.split('°')[1].split('\'')[0];
            slat = item.split('°')[1].split('\'')[1].split('"')[0];
            signlat = (dlat < 0) ? -1 : 1;
            absdlat = Math.abs(Math.round(dlat * 1000000));
            absmlat = Math.abs(Math.round(mlat * 1000000));
            absslat = Math.abs(Math.round(slat * 1000000));
            return Math.round(absdlat + (absmlat / 60.) + (absslat / 3600.)) * signlat / 1000000;
        }
        case "SDDLatitude": return item;
        case "SDDLongitude": return item;
        case "CDMSLatitude": {
            sign = (item.split(' ')[0] == 'E') ? 1 : -1;
            dlat = item.split(' ')[1].split('°')[0];
            mlat = item.split(' ')[1].split('°')[1].split('\'')[0];
            slat = item.split(' ')[1].split('°')[1].split('\'')[1].split('"')[0];
            signlat = (dlat < 0) ? -1 : 1;
            absdlat = Math.abs(Math.round(dlat * 1000000));
            absmlat = Math.abs(Math.round(mlat * 1000000));
            absslat = Math.abs(Math.round(slat * 1000000));
            return Math.round(absdlat + (absmlat / 60.) + (absslat / 3600.)) * signlat / 1000000;
        }
        case "CDMSLongitude": {
            sign = (item.split(' ')[0] == 'N') ? 1 : -1;
            dlat = item.split(' ')[1].split('°')[0];
            mlat = item.split(' ')[1].split('°')[1].split('\'')[0];
            slat = item.split(' ')[1].split('°')[1].split('\'')[1].split('"')[0];
            signlat = (dlat < 0) ? -1 : 1;
            absdlat = Math.abs(Math.round(dlat * 1000000));
            absmlat = Math.abs(Math.round(mlat * 1000000));
            absslat = Math.abs(Math.round(slat * 1000000));
            return Math.round(absdlat + (absmlat / 60.) + (absslat / 3600.)) * signlat / 1000000;
        }
        case "CDDLatitude": { return item.replace('E ', '').replace('W', '-'); }
        case "CDDLongitude": {
            return item.replace('N ', '').replace('S', '-');
        }
    }
}


app.directive('gisparser', function () {
    return {
        restrict: 'A',
        require: 'ngModel',
        link: function (scope, element, attr, ngModel) {
            var source = attr.gisformat;
            // 1 evento quanto l'utente scrive 
            function fromUser(text) {
                var regex;
                //test in http://regex101.com/#javascript
                switch (source) {
                    case "SDMSLatitude": regex = /^-?(90°(0{1,2}\')?|90°0{1,2}\'0{1,2}(\.0{1,6})?\"|([1-8]?[0-9])°|([1-8]?[0-9])°([0-5]?[0-9])\'|([1-8]?[0-9])°([0-5]?[0-9])\'([0-5]?[0-9])(\.\d{1,6})?\")$/; break;
                    case "SDMSLongitude": regex = /^-?(180°(0{1,2}\')?|180°0{1,2}\'0{1,2}(\.0{1,6})?\"|(1[0-7][0-9]|[1-9][0-9]|[0-9])°|(1[0-7][0-9]|[1-9][0-9]|[0-9])°([0-5]?[0-9])\'|(1[0-7][0-9]|[1-9][0-9]|[0-9])°([0-5]?[0-9])\'([0-5]?[0-9])(\.\d{1,6})?\")$/; break;
                    case "SDDLatitude": regex = /^-?(90(\.0{1,6})?|([1-8]?[0-9])(\.\d{1,6})?)$/; break;
                    case "SDDLongitude": regex = /^-?(180(\.0{1,6})?|(1[0-7][0-9]|[1-9][0-9]|[0-9])(\.\d{1,6})?)$/; break;
                    case "CDMSLatitude": regex = /^[NS]\s(90°(0{1,2}\')?|90°0{1,2}\'0{1,2}(\.0{1,6})?\"|([1-8]?[0-9])°|([1-8]?[0-9])°([0-5]?[0-9])\'|([1-8]?[0-9])°([0-5]?[0-9])\'([0-5]?[0-9])(\.\d{1,6})?\")$/; break;
                    case "CDMSLongitude": regex = /^[EW]\s(180°(0{1,2}\')?|180°0{1,2}\'0{1,2}(\.0{1,6})?\"|(1[0-7][0-9]|[1-9][0-9]|[0-9])°|(1[0-7][0-9]|[1-9][0-9]|[0-9])°([0-5]?[0-9])\'|(1[0-7][0-9]|[1-9][0-9]|[0-9])°([0-5]?[0-9])\'([0-5]?[0-9])(\.\d{1,6})?\")$/; break;
                    case "CDDLatitude": regex = /^[NS]\s(90(\.0{1,6})?|([1-8]?[0-9])(\.\d{1,6})?)$/; break;
                    case "CDDLongitude": regex = /^[EW]\s(180(\.0{1,6})?|(1[0-7][0-9]|[1-9][0-9]|[0-9])(\.\d{1,6})?)$/; break;
                }
                var valid = regex.test(text);
                ngModel.$setValidity('wrongformat_' + source, valid);
                return valid ? parseLatitude(text, source) : 0;
            }
            // 2 evento quanto c'è una modifica sul modello
            function toUser(text) {
                if (text != null && text != undefined && text != '') {
                    ngModel.$setValidity('wrongformat_' + source, true);
                    return formatLatitude(text, source);
                }
            }

            // 1 evento quanto l'utente scrive  (metodo dichiarato sopra)
            ngModel.$parsers.push(fromUser);
            // 2 evento quanto c'è una modifica sul modello (metodo dichiarato sopra)
            ngModel.$formatters.push(toUser);

        }
    };
});


app.directive('checklistModel', ['$parse', '$compile', function ($parse, $compile) {
    // contains
    function contains(arr, item) {
        if (angular.isArray(arr)) {
            for (var i = 0; i < arr.length; i++) {
                /* FIX compare without hashKey */
                delete item.$$hashKey;
                if (angular.equals(arr[i], item)) {
                    return true;
                }
            }
        }
        return false;
    }

    // add
    function add(arr, item) {
        arr = angular.isArray(arr) ? arr : [];
        for (var i = 0; i < arr.length; i++) {
            if (angular.equals(arr[i], item)) {
                return arr;
            }
        }
        arr.push(item);
        return arr;
    }

    // remove
    function remove(arr, item) {
        if (angular.isArray(arr)) {
            for (var i = 0; i < arr.length; i++) {
                if (angular.equals(arr[i], item)) {
                    arr.splice(i, 1);
                    break;
                }
            }
        }
        return arr;
    }

    // http://stackoverflow.com/a/19228302/1458162
    function postLinkFn(scope, elem, attrs) {
        // compile with `ng-model` pointing to `checked`
        $compile(elem)(scope);

        // getter / setter for original model
        var getter = $parse(attrs.checklistModel);
        var setter = getter.assign;

        // value added to list
        var value = $parse(attrs.checklistValue)(scope.$parent);

        // watch UI checked change
        scope.$watch('checked', function (newValue, oldValue) {
            if (newValue === oldValue) {
                return;
            }
            var current = getter(scope.$parent);
            if (newValue === true) {
                setter(scope.$parent, add(current, value));
            } else {
                setter(scope.$parent, remove(current, value));
            }
        });

        // watch original model change
        scope.$parent.$watch(attrs.checklistModel, function (newArr, oldArr) {
            scope.checked = contains(newArr, value);
        }, true);
    }

    return {
        restrict: 'A',
        priority: 1000,
        terminal: true,
        scope: true,
        compile: function (tElement, tAttrs) {
            if (tElement[0].tagName !== 'INPUT' || !tElement.attr('type', 'checkbox')) {
                throw 'checklist-model should be applied to `input[type="checkbox"]`.';
            }

            if (!tAttrs.checklistValue) {
                throw 'You should provide `checklist-value`.';
            }

            // exclude recursion
            tElement.removeAttr('checklist-model');

            // local scope var storing individual checkbox model
            tElement.attr('ng-model', 'checked');

            return postLinkFn;
        }
    };
}]);


app.directive('bindHtmlUnsafe', function ($compile) {
    return function ($scope, $element, $attrs) {

        var compile = function (newHTML) { // Create re-useable compile function
            newHTML = $compile(newHTML.replace(/(\r\n|\n|\r)/gm, "").replace("\t", "").replace("  ", ""))($scope); // Compile html
            $element.html('').append(newHTML); // Clear and append it
        };

        var htmlName = $attrs.bindHtmlUnsafe; // Get the name of the variable 
        // Where the HTML is stored

        $scope.$watch(htmlName, function (newHTML) { // Watch for changes to 
            // the HTML
            if (!newHTML) return;
            //element.html(value || '');
            compile(newHTML);   // Compile it
        });

    };
});

app.directive('ngKeystroke', function () {
    return {
        restrict: 'A',
        link: function (scope, elem, attrs) {
            elem.bind("keyup", function () {
                if (scope != undefined && scope.log != undefined)
                    scope.log.push('called');
                scope.$digest();
            });
        }
    };
});


app.directive('minervaChartLogarithmicDirective', function () {
    var res = {
        restrict: 'E',
        require: 'ngModel',
        scope: {
            model: '=ngModel'
        },
        link: function (scope, el, attrs, ctrl) {

            var appoModel;
            var minervaChartLogarithmic_HighlightedSeries = null;

            draw = function () {

                if (appoModel == null || appoModel.IsValid() == false)
                    return;

                var yLabel = appoModel.Desc.YAxisLabel;
                var xLabel = appoModel.Desc.XAxisLabel;

                var MinX = null;
                var MaxX = null;
                var MinY = null;
                var MaxY = null;
                if (appoModel.Axes != null && appoModel.Axes.length > 1) {
                    // Y Axis
                    if (appoModel.Axes[0].Min != 0.0 || appoModel.Axes[0].Max != 0.0) {
                        MinY = appoModel.Axes[0].Min;
                        MaxY = appoModel.Axes[0].Max;
                    }

                    // X Axis
                    if (appoModel.Axes[1].Min != 0.0 || appoModel.Axes[1].Max != 0.0) {
                        MinX = appoModel.Axes[1].Min;
                        MaxX = appoModel.Axes[1].Max;
                    }
                }


                var ChartSeries = [];
                for (var i = 0; i < appoModel.Series.length; i++) {
                    ChartSeries.push({
                        name: appoModel.Series[i].Name, data: appoModel.Series[i].points, turboThreshold: 0, lineWidth: 2
                    });
                }

                el.highcharts({
                    chart: {
                        type: 'spline',
                        marginRight: 20,
                        marginTop: 20,
                        width: 870,
                        style: {
                            fontSize: '14'
                        }
                    },

                    title: {
                        text: null
                    },
                    xAxis: {
                        type: 'logarithmic',
                        min: MinX,
                        max: MaxX,
                        tickInterval: 1,
                        minorTickInterval: 0.1,
                        labels: {
                            formatter: function () {
                                return Number(this.value).toExponential();
                            },
                            style: {
                                'font-size': '14px'
                            },
                            y: 20
                        },
                        title: {
                            text: xLabel
                        },
                    },
                    yAxis: {
                        min: MinY,
                        max: MaxY,
                        title: {
                            text: yLabel
                        },
                        labels: {
                            style: {
                                'font-size': '14px'
                            },
                            padding: 100,
                            y: 0
                        },
                    },
                    tooltip: {
                        formatter: function () {
                            return Highcharts.numberFormat(this.x, 16, '.').toString() + "<br/>" + Highcharts.numberFormat(this.y, 16, '.').toString()
                        }
                    },
                    plotOptions: {
                        spline: {
                            marker: {
                                enabled: false
                            }
                        }
					},
					exporting: { enabled: false },
                    legend: {
                        itemWidth: 220,
                        enabled: true
                    },
                    series: ChartSeries
                });
            }

            scope.$watch("model", function (model) {
                if (model != undefined) {
                    appoModel = model;
                    draw();
                }
            });

            scope.$watch("model.SelectedSeries", function (changed) {
                if (appoModel != undefined) {

                    if (minervaChartLogarithmic_HighlightedSeries != null) {
                        el.highcharts().series[minervaChartLogarithmic_HighlightedSeries].update({ lineWidth: 1 });
                    }

                    el.highcharts().series[changed].update({ lineWidth: 6 });
                    minervaChartLogarithmic_HighlightedSeries = changed;
                }
            });
        }
    };
    return res;
});


app.directive('minervaChartDirective', function () {
    var res = {
        restrict: 'E',
        require: 'ngModel',
        scope: {
            model: '=ngModel'
        },
        link: function (scope, el, attrs, ctrl) {

            var appoModel;

            draw = function () {

                if (appoModel == null || appoModel.IsValid() == false)
                    return;

                var yLabel = appoModel.Desc.YAxisLabel;
                var xLabel = appoModel.Desc.XAxisLabel;

                var MinX = null;
                var MaxX = null;
                var MinY = null;
                var MaxY = null;
                if (appoModel.Axes != null && appoModel.Axes.length > 1) {
                    // Y Axis
                    if (appoModel.Axes[0].Min != 0.0 || appoModel.Axes[0].Max != 0.0) {
                        MinY = appoModel.Axes[0].Min;
                        MaxY = appoModel.Axes[0].Max;
                    }

                    // X Axis
                    if (appoModel.Axes[1].Min != 0.0 || appoModel.Axes[1].Max != 0.0) {
                        MinX = appoModel.Axes[1].Min;
                        MaxX = appoModel.Axes[1].Max;
                    }
                }


                var ChartSeries = [];
                for (var i = 0; i < appoModel.Series.length; i++) {
                    ChartSeries.push({
                        name: appoModel.Series[i].Name, data: appoModel.Series[i].points, turboThreshold: 0
                    });
                    // Add additional series if present
                    if (appoModel.Series[i].additionalSeriesGroups && appoModel.Series[i].additionalSeriesGroups.length > 0) {
                        for (var gi = 0; gi < appoModel.Series[i].additionalSeriesGroups.length; gi++) {
                            for (var ii = 0; ii < appoModel.Series[i].additionalSeriesGroups[gi].length; ii++) {
                                var addSeries = {
                                    name: appoModel.Series[i].Name,
                                    data: appoModel.Series[i].additionalSeriesGroups[gi][ii],
                                    turboThreshold: 0
                                };
                                if (appoModel.Series[i].additionalSeriesGroups[gi].color) {
                                    addSeries["color"] = appoModel.Series[i].additionalSeriesGroups[gi].color;
                                }
                                ChartSeries.push(addSeries);
                            }
                        }
                    }
                }


                el.highcharts({
                    chart: {
                        type: 'spline'
                    },
                    title: {
                        text: null
                    },
                    xAxis: {
                        min: MinX,
                        max: MaxX,
                        labels: {
                            staggerLines: 1
                        },
                        title: {
                            text: xLabel
                        }
                    },
                    yAxis: {
                        min: MinY,
                        max: MaxY,
                        labels: {
                            staggerLines: 1
                        },
                        title: {
                            text: yLabel
                        }
                    },
                    tooltip: {
                        formatter: function () {
                            return Highcharts.numberFormat(this.x, 16, '.').toString() + "<br/>" + Highcharts.numberFormat(this.y, 16, '.').toString()
                        }
                    },
                    plotOptions: {
                        spline: {
                            marker: {
                                enabled: false
                            }
                        }
                    },
                    legend: {
                        enabled: false
					},
					exporting: { enabled: false },
                    series: ChartSeries
                });
            }

            scope.$watch("model", function (model) {
                if (model != undefined) {
                    appoModel = model;
                    draw();
                }
            });
        }
    };
    return res;
});

/* for validate float with exponents */
var FLOATWEXP_REGEXP = /^[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?$/;
app.directive('floatWExponents', function () {
    return {
        require: 'ngModel',
        link: function (scope, elm, attr, ctrl) {
            ctrl.$parsers.unshift(function (value) {
                if (value != null) {
                    //var bool_value = scope.$eval(attr.ngToValidate) == "false" ? false : true
                    //if (bool_value) {
                    if (FLOATWEXP_REGEXP.test(value)) {
                        ctrl.$setValidity('floatWExp', true);
                        var transform = value.toString().replace(',', '.');
                        return parseFloat(transform);
                    } else {
                        ctrl.$setValidity('floatWExp', false);
                        return value;
                    }
                    //} else return value;
                }
            });
        }
    };
});

function isEmpty(value) {
    return angular.isUndefined(value) || value === '' || value === null || value !== value;
}


//app.directive('ngToValidate', function () {
//    return {
//        restrict: 'A',
//        require: 'ngModel',
//        link: function (scope, elem, attr, ctrl) {
//            scope.$watch(attr.ngToValidate, function () {
//                ctrl.$setViewValue(ctrl.$viewValue);
//            });
//        }
//    };
//});

app.directive('ngMin', function () {
    return {
        restrict: 'A',
        require: 'ngModel',
        link: function (scope, elem, attr, ctrl) {
            scope.$watch(attr.ngMin, function () {
                ctrl.$setViewValue(ctrl.$viewValue);
            });
            var minValidator = function (value) {
                //var bool_value = scope.$eval(attr.ngToValidate) == "false" ? false : true
                //if (bool_value) {
                //var min = scope.$eval(attr.ngMin) || 0;
                var min = parseFloat(attr.ngMin) || 0;
                if (!isEmpty(value) && value < min) {
                    ctrl.$setValidity('ngMin', false);
                    return value;
                } else {
                    ctrl.$setValidity('ngMin', true);
                    return value;
                }
                //} else return value;
            };

            ctrl.$parsers.push(minValidator);
            ctrl.$formatters.push(minValidator);
        }
    };
});

app.directive('ngMax', function () {
    return {
        restrict: 'A',
        require: 'ngModel',
        link: function (scope, elem, attr, ctrl) {
            scope.$watch(attr.ngMax, function () {
                ctrl.$setViewValue(ctrl.$viewValue);
            });
            var maxValidator = function (value) {
                //var bool_value = scope.$eval(attr.ngToValidate) == "false" ? false : true
                //if (bool_value) {
                //var max = scope.$eval(attr.ngMax) || Infinity;
                var max = parseFloat(attr.ngMax) || 0;
                if (!isEmpty(value) && value > max) {
                    ctrl.$setValidity('ngMax', false);
                    return value;
                } else {
                    ctrl.$setValidity('ngMax', true);
                    return value;
                }
                //} else return value;
            };

            ctrl.$parsers.push(maxValidator);
            ctrl.$formatters.push(maxValidator);
        }
    };
});

app.directive('ngDecimals', function () {
    return {
        restrict: 'A',
        require: 'ngModel',
        link: function (scope, elem, attr, ctrl) {
            //scope.$watch(attr.ngDecimals, function () {
            //    ctrl.$setViewValue(ctrl.$viewValue);
            //});
            var setDecimals = function (value) {
                if (value != undefined) {
                    var isDecimal = String(value).indexOf(".");
                    if (isDecimal >= 0) {
                        var dec = scope.$eval(attr.ngDecimals) || 2;
                        var n = parseFloat(value);
                        if (parseInt(n, 10) !== n && String(n).split('.')[1].length > dec) {
                            return n.toFixed(dec);
                        }
                    } return value;
                } return value;
            };

            ctrl.$parsers.push(setDecimals);
            ctrl.$formatters.push(setDecimals);
        }
    };
});

app.filter('numberFixedLen', function () {
    return function (n, len) {
        var num = parseInt(n, 10);
        len = parseInt(len, 10);
        if (isNaN(num) || isNaN(len)) {
            return n;
        }
        num = '' + num;
        while (num.length < len) {
            num = '0' + num;
        }
        return num;
    };
});

app.filter('range', function () {
    return function (input, min, max, step) {
        if (step == '') {
            step = 1;
        }
        step = parseInt(step);
        min = parseInt(min); //Make string input int
        max = parseInt(max);
        for (var i = min; i <= max;) {
            input.push(i);
            i += step;
        }
        return input;
    };
});
app.filter('cut', function () {
    return function (value, wordwise, max, tail) {
        if (value == undefined || value == '') return '';
        var val = value.toString();

        max = parseInt(max, 10);
        if (!max) return val;
        if (val.length <= max) return val;

        val = val.substr(0, max);
        if (wordwise) {
            var lastspace = val.lastIndexOf(' ');
            if (lastspace != -1) {
                val = val.substr(0, lastspace);
            }
        }

        return val + (tail || ' …');
    };
});

/* BOOTSTRAP CONTEXTMENU DIRECTIVE */
angular.module('ui.bootstrap.contextMenu', [])
.directive('contextMenu', ["$parse", function ($parse) {
    var renderContextMenu = function ($scope, event, options, model) {
        if (!$) {
            var $ = angular.element;
        }
        $(event.currentTarget).addClass('context');
        var $contextMenu = $('<div>');
        $contextMenu.addClass('dropdown clearfix');
        var $ul = $('<ul>');
        $ul.addClass('dropdown-menu');
        $ul.attr({
            'role': 'menu'
        });
        $ul.css({
            display: 'block',
            position: 'absolute',
            left: event.pageX + 'px',
            top: event.pageY + 'px'
        });
        angular.forEach(options, function (item, i) {
            var $li = $('<li>');
            if (item === null) {
                $li.addClass('divider');
            } else {
                var $a = $('<a>');
                $a.attr({
                    tabindex: '-1', href: '#'
                });
                var text = typeof item[0] == 'string' ? item[0] : item[0].call($scope, $scope, event);
                $a.text(text);
                $li.append($a);
                var enabled = angular.isDefined(item[2]) ? item[2].call($scope, $scope, event, text, model) : true;
                if (enabled) {
                    $li.on('click', function ($event) {
                        $event.preventDefault();
                        $scope.$apply(function () {
                            $(event.currentTarget).removeClass('context');
                            $contextMenu.remove();
                            item[1].call($scope, $scope, event, model);
                        });
                    });
                } else {
                    $li.on('click', function ($event) {
                        $event.preventDefault();
                    });
                    $li.addClass('disabled');
                }
            }
            $ul.append($li);
        });
        $contextMenu.append($ul);
        var height = Math.max(
            document.body.scrollHeight, document.documentElement.scrollHeight,
            document.body.offsetHeight, document.documentElement.offsetHeight,
            document.body.clientHeight, document.documentElement.clientHeight
        );
        $contextMenu.css({
            width: '100%',
            height: height + 'px',
            position: 'absolute',
            top: 0,
            left: 0,
            zIndex: 9999
        });
        $(document).find('body').append($contextMenu);
        $contextMenu.on("mousedown", function (e) {
            if ($(e.target).hasClass('dropdown')) {
                $(event.currentTarget).removeClass('context');
                $contextMenu.remove();
            }
        }).on('contextmenu', function (event) {
            $(event.currentTarget).removeClass('context');
            event.preventDefault();
            $contextMenu.remove();
        });
    };
    return function ($scope, element, attrs) {
        element.on('contextmenu', function (event) {
            $scope.$apply(function () {
                event.preventDefault();
                var options = $scope.$eval(attrs.contextMenu);
                var model = $scope.$eval(attrs.model);
                if (options instanceof Array) {
                    if (options.length === 0) {
                        return;
                    }
                    renderContextMenu($scope, event, options, model);
                } else {
                    throw '"' + attrs.contextMenu + '" not an array';
                }
            });
        });
    };
}]);

//app.provider('toggleSwitchConfig', function () {
//    this.onLabel = 'On';
//    this.offLabel = 'Off';
//    this.knobLabel = '\u00a0';

//    var self = this;
//    this.$get = function () {
//        return {
//            onLabel: self.onLabel,
//            offLabel: self.offLabel,
//            knobLabel: self.knobLabel
//        };
//    };
//});

//app.directive('toggleSwitch', function (toggleSwitchConfig) {
//    return {
//        restrict: 'EA',
//        replace: true,
//        require: 'ngModel',
//        scope: {
//            disabled: '@',
//            onLabel: '@',
//            offLabel: '@',
//            knobLabel: '@'
//        },
//        template: '<div role="radio" class="toggle-switch" ng-class="{ \'disabled\': disabled }">' +
//            '<div class="toggle-switch-animate" ng-class="{\'switch-off\': !model, \'switch-on\': model}">' +
//            '<span class="switch-left" ng-bind="onLabel"></span>' +
//            '<span class="knob" ng-bind="knobLabel"></span>' +
//            '<span class="switch-right" ng-bind="offLabel"></span>' +
//            '</div>' +
//            '</div>',
//        compile: function (element, attrs) {
//            if (!attrs.onLabel) { attrs.onLabel = toggleSwitchConfig.onLabel; }
//            if (!attrs.offLabel) { attrs.offLabel = toggleSwitchConfig.offLabel; }
//            if (!attrs.knobLabel) { attrs.knobLabel = toggleSwitchConfig.knobLabel; }

//            return this.link;
//        },
//        link: function (scope, element, attrs, ngModelCtrl) {
//            var KEY_SPACE = 32;

//            element.on('click', function () {
//                scope.$apply(scope.toggle);
//            });

//            element.on('keydown', function (e) {
//                var key = e.which ? e.which : e.keyCode;
//                if (key === KEY_SPACE) {
//                    scope.$apply(scope.toggle);
//                }
//            });

//            ngModelCtrl.$formatters.push(function (modelValue) {
//                return modelValue;
//            });

//            ngModelCtrl.$parsers.push(function (viewValue) {
//                return viewValue;
//            });

//            ngModelCtrl.$render = function () {
//                scope.model = ngModelCtrl.$viewValue;
//            };

//            scope.toggle = function toggle() {
//                if (!scope.disabled) {
//                    scope.model = !scope.model;
//                    ngModelCtrl.$setViewValue(scope.model);
//                }
//            };
//        }
//    };
//});

app.filter('trustAsHtml', function ($sce) {
    return $sce.trustAsHtml;
});

app.filter('stripClasses', function () {
    return function (str) {
        if (str != undefined) {
            return str.replace(/class=['"].*["']/, '');
        }
        else {
            return "";
        }
    }
});

app.directive('myDatepicker', function ($filter) {
    return {
        require: 'ngModel',
        link: function (scope, element, attrs, ngModelCtrl) {
            $(function () {
                element.datepicker({
                    showOn: "both",
                    changeYear: true,
                    changeMonth: true,
                    dateFormat: 'dd/mm/yy',
                    axDate: new Date(),
                    yearRange: '1920:2050',

                    onSelect: function (dateText, inst) {
                  
                        //ngModelCtrl.$setViewValue(dateText);
                        var ar = dateText.split("/");
                        date = new Date(ar[2] + "-" + ar[1] + "-" + ar[0]);
                        ngModelCtrl.$setViewValue(date.toISOString().slice(0, 10));
                     //   ngModelCtrl.modelValue = date.toISOString();
                    //    console.log(dateText);
                    //    console.log(date.getDate());
                    //    console.log(date.toISOString());
                      //  ngModelCtrl.$setViewValue(dateText);

                        scope.$apply();
                    }
                    
                });
            });
            ngModelCtrl.$formatters.unshift(function (viewValue) {
                return $filter('date')(viewValue, 'dd/MM/yyyy');
            });

        }

    }
});

app.directive('myDateTimePicker', function () {
    return {
        scope: {
            minDate: '='
        },
        require: 'ngModel',
        link: function (scope, element, attrs, ngModelCtrl) {

            scope.CreateCtrl = function (element, toShow, minDate) {
                element.datetimepicker({
                    dateFormat: 'd MM yy',
                    changeYear: true,
                    changeMonth: true,
                    yearRange: '1920:2030',
                    controlType: 'select',
                    timeFormat: 'HH:mm',
                    stepMinute: 5,
                    minute: 0,
                    minDate: (minDate == null ? moment(Date.now()).format('D MMMM YYYY HH:mm') : minDate),
                    showTimepicker: (toShow == "false"),
                    onSelect: function (dateText, inst) {
                        ngModelCtrl.$setViewValue(dateText);
                        scope.$apply();
                    }
                });
            }

            attrs.$observe('myDateTimePicker', function () {
                element.datetimepicker("destroy");
                scope.CreateCtrl(element, attrs.myDateTimePicker, scope.minDate);
            });

            scope.$watch('minDate', function () {
                element.datetimepicker("destroy");
                scope.CreateCtrl(element, attrs.myDateTimePicker, scope.minDate);
            }, true);
        }
    }
});

app.directive('ngRightClick', function ($parse) {
    return function (scope, element, attrs) {
        var fn = $parse(attrs.ngRightClick);
        element.bind('contextmenu', function (event) {
            scope.$apply(function () {
                event.preventDefault();
                fn(scope, { $event: event });
            });
        });
    };
});

app.filter('htmlToPlaintext', function () {
    return function (text) {
        return text ? String(text).replace(/<[^>]+>/gm, '') : '';
    };
}
);

// Format Can be 'small' or 'big'
app.directive('virtualAvatar', function ($compile) {
    return {
        restrict: 'E',
        replace: true,
        scope: {
            firstname: '=',
            lastname: '=',
            format: '='
        },
        link: function (scope, el, attrs, ctrl) {
            var shadeColor = function (color, percent) {

                var R = parseInt(color.substring(1, 3), 16);
                var G = parseInt(color.substring(3, 5), 16);
                var B = parseInt(color.substring(5, 7), 16);

                R = parseInt(R * (100 + percent) / 100);
                G = parseInt(G * (100 + percent) / 100);
                B = parseInt(B * (100 + percent) / 100);

                R = (R < 255) ? R : 255;
                G = (G < 255) ? G : 255;
                B = (B < 255) ? B : 255;

                var RR = ((R.toString(16).length == 1) ? "0" + R.toString(16) : R.toString(16));
                var GG = ((G.toString(16).length == 1) ? "0" + G.toString(16) : G.toString(16));
                var BB = ((B.toString(16).length == 1) ? "0" + B.toString(16) : B.toString(16));

                return "#" + RR + GG + BB;
            }

            var stringToColour = function (str) {
                var hash = 0;
                for (var i = 0; i < str.length; i++) {
                    hash = str.charCodeAt(i) + ((hash << 5) - hash);
                }
                var colour = '#';
                for (var i = 0; i < 3; i++) {
                    var value = (hash >> (i * 8)) & 0xFF;
                    colour += ('00' + value.toString(16)).substr(-2);
                }
                return colour;
            }

            var color = shadeColor(stringToColour((scope.firstname ? scope.firstname : '') + (scope.lastname ? scope.lastname : '')), -25);

            var html = '<div class="Forum-tlist-icon-' + scope.format + ' Forum-user-initial-' + scope.format + '" style="background:' + color + '">'
                + (scope.firstname ? scope.firstname.substring(0, 1) : '')
                + (scope.lastname ? scope.lastname.substring(0, 1) : '')
                + '</div>';
            $compile(html);
            el.html(html);
        }
    };
});
// Decoupling merge with rev. 4300
app.directive('autosizetextbox', function ($document) {
    return {
        require: 'ngModel',
        link: function (scope, element, attrs, ctrl) {
            var placeholder, span, resize;

            placeholder = element.attr('placeholder') || '';

            span = angular.element('<span></span>');
            span[0].style.cssText = getComputedStyle(element[0]).cssText;
            span.css('display', 'none')
                .css('visibility', 'hidden')
                .css('width', 'auto');

            $document.find('body').append(span);

            resize = function (value) {
                if (value.length < placeholder.length) {
                    value = placeholder;
                }
                span.text(value);
                span.css('display', '');
                try {
                    element.css('width', span.prop('offsetWidth') + 'px');
                }
                finally {
                    span.css('display', 'none');
                }
            };

            ctrl.$parsers.unshift(function (value) {
                resize(value);
                return value;
            });

            ctrl.$formatters.unshift(function (value) {
                resize(value);
                return value;
            })
        }
    };
});
// END Decoupling merge with rev. 4300;
/*! SpatialTypesWithWebAPI.js */function SpatialTypesWithWebAPI() { }

SpatialTypesWithWebAPI.MapDivId = 'bing_map';
SpatialTypesWithWebAPI._map = null;
SpatialTypesWithWebAPI.ipInfoDbKey = '';
SpatialTypesWithWebAPI.LatLong = null;
SpatialTypesWithWebAPI.draggable = false;
SpatialTypesWithWebAPI.doubleclickable = false;
SpatialTypesWithWebAPI._onMapDoubleClick;
SpatialTypesWithWebAPI._onEndDrag;
SpatialTypesWithWebAPI._pinInfoBox;
SpatialTypesWithWebAPI._points = [];
var _customPinUrl = '';

SpatialTypesWithWebAPI.LoadMap = function (mapdivId, latitude, longitude, onMapLoaded, onMapDoubleClick, custompin) {

	Microsoft.Maps.loadModule('Microsoft.Maps.Themes.BingTheme', {
		callback: function () {
			if (custompin != undefined && custompin != '')
			{ _customPinUrl = custompin; }

			// if map div id is set otherwise map div id in the page must be 'bing_map'
			if (mapdivId == undefined || mapdivId == "")
			{ mapdivId = SpatialTypesWithWebAPI.MapDivId; }
			
			// if map is not instanced yet
			if (SpatialTypesWithWebAPI._map == null) {
				SpatialTypesWithWebAPI._map = new Microsoft.Maps.Map(document.getElementById(mapdivId), { credentials: "fJgAZub1GrKgxD4O4SgQ~mykbXrsNf-dofLiDaTysJg~AkxCj5InopY_7JQFLhaoxLA3GWiXMbN1rtGdQ4KaX7LF21pHtAWpzo3TEFk5MCK5", theme: new Microsoft.Maps.Themes.BingTheme() });
			}
			else {
				// if map is instanced but with a different than the mapdivId, reload it
				if (SpatialTypesWithWebAPI._map.ID != mapdivId)
					SpatialTypesWithWebAPI._map = new Microsoft.Maps.Map(document.getElementById(mapdivId), { credentials: "fJgAZub1GrKgxD4O4SgQ~mykbXrsNf-dofLiDaTysJg~AkxCj5InopY_7JQFLhaoxLA3GWiXMbN1rtGdQ4KaX7LF21pHtAWpzo3TEFk5MCK5", theme: new Microsoft.Maps.Themes.BingTheme() });
			}

			var options = SpatialTypesWithWebAPI._map.getOptions();

			// If there are Latitude and Longitude, open a map using this coordinates
			if (latitude != null && longitude != null) {

				var center = new Microsoft.Maps.Location();
				center.latitude = parseFloat(latitude);
				center.longitude = parseFloat(longitude);

				options.center = center;

				SpatialTypesWithWebAPI._map.setView(options);
			}

			// set map double click call back properties if double click callback parameter is set 
			if (onMapDoubleClick != undefined) {
				Microsoft.Maps.Events.addHandler(SpatialTypesWithWebAPI._map, "dblclick", SpatialTypesWithWebAPI.onDoubleClick);
				SpatialTypesWithWebAPI._onMapDoubleClick = onMapDoubleClick;
			}

			SpatialTypesWithWebAPI._pinInfoBox = new Microsoft.Maps.Infobox({ location: new Microsoft.Maps.Location(0,0)});
			SpatialTypesWithWebAPI._map.entities.push(SpatialTypesWithWebAPI._pinInfoBox);

			// call custom map load call back if map load callback parameter is set
			if (onMapLoaded != null) {
				onMapLoaded();
			}

			//Make sure all pushpins are visible
			if (SpatialTypesWithWebAPI._points.length > 1) {
				var bestView = Microsoft.Maps.LocationRect.fromLocations(SpatialTypesWithWebAPI._points);
				SpatialTypesWithWebAPI._map.setView({ bounds: bestView });
			}
		}
	});
}


// Customizing Moveable Push Pins (find location) in v7.0
SpatialTypesWithWebAPI.onDoubleClick = function (e) {
	// if the map is double clickable
	if (SpatialTypesWithWebAPI.doubleclickable) {

		if (e.targetType == "map") {
			var point = new Microsoft.Maps.Point(e.getX(), e.getY());
			var loc = SpatialTypesWithWebAPI._map.tryPixelToLocation(point);
		}
		// clear map
		SpatialTypesWithWebAPI.ClearMap();

		// load pin 
		SpatialTypesWithWebAPI.LoadPin(loc);

		// call double click callback
		if (SpatialTypesWithWebAPI._onMapDoubleClick != undefined) {
			SpatialTypesWithWebAPI._onMapDoubleClick(loc.latitude, loc.longitude);
		}
	}

	e.handled = true;
	return true;
}


// get string result from location
SpatialTypesWithWebAPI.GetResults = function (locations) {
	var s = "Lat: " + SpatialTypesWithWebAPI.LatLong.Latitude + " Long: " + SpatialTypesWithWebAPI.LatLong.Longitude + " ";

	if (locations) {
		for (var i = 0; i < locations.length; i++) {
			s += locations[i].Name;
		}
	}
	else {
		s += 'No Result found.';
	}
	return s;
}


SpatialTypesWithWebAPI.SetZoomLevel = function (value) {
	SpatialTypesWithWebAPI._map.setView({ zoom: value });
}


// get and set auto center of the map
SpatialTypesWithWebAPI.GetCenter = function () {
	return SpatialTypesWithWebAPI._map.getCenter();
}


// cleanup map from pushpins and shapes
SpatialTypesWithWebAPI.ClearMap = function () {
	if (SpatialTypesWithWebAPI._map != null) {
		ClearAllPins();
	}
}


// ********************************************************** Customizing Moveable Push Pins (find location) in v7.0
SpatialTypesWithWebAPI.LoadPin = function (LL, customPinUrl) {
	if (LL.Latitude == 0 || LL.Longitude == 0) {
		return;
	}

	// instance new push pin
	var pin = new Microsoft.Maps.Pushpin(LL);

	// if draggable set pushpin ondragend function
	if (SpatialTypesWithWebAPI.draggable == true) {
		pin.setOptions({ draggable: true });
		Microsoft.Maps.Events.addHandler(pin, 'dragend', SpatialTypesWithWebAPI._onEndDrag);
	}

	// ********************************************************** Customizingicon of pushpin in v7.0
	if (customPinUrl == undefined || customPinUrl == '') {
		if (_customPinUrl != undefined && _customPinUrl != '') {
			pin.setOptions({ icon: _customPinUrl });
		}
		else {
			pin.setOptions({ icon: '/Content/images/customPin.png' });
		} //establishment_orange
	}
	else {
		pin.setOptions({ icon: customPinUrl });
	}

	pin.setOptions({ width: 24, height: 24, anchor: new Microsoft.Maps.Point(12, 12) });


	var latitudeFormat = $("#latformat").val();
	var longitudeFormat = $("#longformat").val();

	if (latitudeFormat == undefined || latitudeFormat == '') var latitudeFormat = "SDDLatitude";
	if (longitudeFormat == undefined || longitudeFormat == '') var longitudeFormat = "SDDLongitude";
	var latitude = formatLatitude(LL.latitude, latitudeFormat);
	var longitude = formatLatitude(LL.longitude, longitudeFormat);


	var desc = "";
	if (latitude != undefined)
	{ desc = desc + "<strong>Latitude:</strong> " + latitude; }

	if (longitude != undefined)
	{ desc = desc + " <br/><strong>Longitude:</strong> " + longitude; }

	//pin.setOptions({ text: desc });

	if (SpatialTypesWithWebAPI.draggable == false) {

		// Add a handler for the pushpin click event.
		Microsoft.Maps.Events.addHandler(pin, 'click', function (e) {
			displayInfobox(e, desc);
		});

		Microsoft.Maps.Events.addHandler(pin, 'mouseover', function (e) {
			displayInfobox(e, desc);
		});

		Microsoft.Maps.Events.addHandler(pin, 'mouseout', hideInfobox);

		// Hide the info box when the map is moved.
		Microsoft.Maps.Events.addHandler(SpatialTypesWithWebAPI._map, 'viewchange', hideInfobox);
	}

	// add pushpin to map
	SpatialTypesWithWebAPI._map.entities.push(pin);
}


function displayInfobox(e, desc) {
	if (e.targetType == "pushpin") {
		var pin = e.target;

		SpatialTypesWithWebAPI._pinInfoBox.setOptions({ visible: false });

		SpatialTypesWithWebAPI._pinInfoBox.setOptions({ description: desc });

		SpatialTypesWithWebAPI._pinInfoBox.setLocation(pin.getLocation());

		SpatialTypesWithWebAPI._pinInfoBox.setOptions({ visible: true });
	}
}


function hideInfobox(e) {
	SpatialTypesWithWebAPI._pinInfoBox.setOptions({ visible: false });
}






/*************************************************************************************************************/
/************************* Wrapper bing.d.ts methods. This methods have same name as  ************************/
/************************************** method declarations within bing.d.ts.  *******************************/
/*************************************************************************************************************/


// Call load map method
function LoadMap(mapdivId, latitude, longitude, onloadMap, onEndDrag, onMapDoubleClick, custompin) {
	SpatialTypesWithWebAPI._onEndDrag = null;
	SpatialTypesWithWebAPI._onEndDrag = onEndDrag;
	SpatialTypesWithWebAPI.LoadMap(mapdivId, latitude, longitude, onloadMap, onMapDoubleClick, custompin);
}


// load pin passing coordinates or automatically getting center
function LoadPin(ll) {
	if (ll != null)
		SpatialTypesWithWebAPI.LoadPin(ll);
	else
		SpatialTypesWithWebAPI.LoadPin(SpatialTypesWithWebAPI.GetCenter());
}


// load pin with all the information passing coordinates or automatically getting center
function ReLoadPin(ll, customPinUrl) {
	SpatialTypesWithWebAPI.ClearMap();

	if (ll != null)
		SpatialTypesWithWebAPI.LoadPin(ll, customPinUrl);
	else
		SpatialTypesWithWebAPI.LoadPin(SpatialTypesWithWebAPI.GetCenter(), customPinUrl);
}


// add new pin with all the information passing coordinates
function AddPin(ll, customPinUrl) {
	if (ll != null) {
		SpatialTypesWithWebAPI.LoadPin(ll, customPinUrl);
		SpatialTypesWithWebAPI._map.getCenter();
		SpatialTypesWithWebAPI._points.push(ll);
	}
}


// Set draggable callback function
function SetDraggable(value) {
	SpatialTypesWithWebAPI.draggable = value;
}


// Set double click callback function
function SetDoubleClickable(value) {
	SpatialTypesWithWebAPI.doubleclickable = value;
}


// Set a zoom level by value
function SetZoomLevel(value) {
	SpatialTypesWithWebAPI.SetZoomLevel(value);
}


// get bing lat long object by coordinates
function GetLatLong(lat, long) {

	retValue = new Microsoft.Maps.Location();
	retValue.latitude = parseFloat(lat);
	retValue.longitude = parseFloat(long);

	return retValue;
}


// remove all pins inside the map
function ClearAllPins() {

	SpatialTypesWithWebAPI._map.entities.clear();
	SpatialTypesWithWebAPI._points = [];

	SpatialTypesWithWebAPI._map.entities.push(SpatialTypesWithWebAPI._pinInfoBox);
}


// set center of the map by coordinates
function SetCenterlatLong() {
	SpatialTypesWithWebAPI._map.setView({ center: latLong });
}


// set center of the map by coordinates and zoom it by the passed value
function SetCenterAndZoom(latLong, zoomLevel) {
	SpatialTypesWithWebAPI._map.setView({ center: latLong, zoom: zoomLevel });
}
;
/*! minerva.controllers.js */var __extends = (this && this.__extends) || function (d, b) {
    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
    function __() { this.constructor = d; }
    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};

// http interface module. This module provide all the http async calls
var HttpModule;
(function (HttpModule) {
    var httpService;
    var HttpController = (function () {
        function HttpController($http, $scope) {
            if ($scope === void 0) { $scope = null; }
            this.httpService = $http;
            var controller = this;
            // cache the messages information
            if (controller.Messages == null) {
                controller.GetCache("messages", function (data) {
                    controller.Messages = data.Data;
                });
            }
            if ($scope != null) {
                //   var scope: angular.IScope;
                $scope.$on('IdleStart', function () {
                    // console.log('reset');
                });
                $scope.$on('IdleWarn', function (e, countdown) {
                    // follows after the IdleStart event, but includes a countdown until the user is considered timed out
                    // the countdown arg is the number of seconds remaining until then.
                    // you can change the title or display a warning dialog from here.
                    // you can let them resume their session by calling Idle.watch()
                    MessagesModule.MessagesController.prototype.ShowMessageBox("Your session will expire in " + countdown + " seconds", MessagesModule.MessageBoxType.WARNING, "Session Expiring");
                });
                $scope.$on('IdleTimeout', function () {
                    // the user has timed out (meaning idleDuration + timeout has passed without any activity)
                    // this is where you'd log them
                    var UrlVars = window.location.toString().replace("http://", '').replace("https://", '').split("?")[0].split("/");
                    var language = UrlVars[1].toString();
                    HttpModule.HttpController.prototype.RedirectToAction(language, "common", "security", "Abandon");
                });
                $scope.$on('IdleEnd', function () {
                    // the user has come back from AFK and is doing stuff. if you are warning them, you can use this to hide the dialog
                    // console.log('idleend');
                });
                $scope.$on('Keepalive', function () {
                    // do something to keep the user's session alive
                    MessagesModule.MessagesController.prototype.HideMessageBox();
                    controller.CallGetAction("Minerva", "KeepAlive", null, null);
                });
            }
        }
        //* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 
        // * * * * * * * * * * * * * * * * * * * * *  Rest base async Callbacks * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 
        //* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 
        // launch the set callback in case of error
        HttpController.prototype.OnJsonError = function (data, status, headers, config, callback) {
            //   this.DisableLoaderSpinner(); is always null and it is not found by this function... the code was copied to hide the load spinner
            try {
                if ($ != undefined || $('#divloader') != undefined) {
                    if ($('#divloader').attr('enabled') != undefined) {
                        $('#divloader').attr('enabled', "false");
                        $('#divloader').hide();
                    }
                }
            }
            catch (e) { }
            if (status == 403) {
                if (data.Type == 2) {
                    if (callback != null)
                        callback(data);
                    else {
                        MessagesModule.MessagesController.prototype.ShowMessageBox("Not Authorised.", MessagesModule.MessageBoxType.ERROR, data.Message, null, null, function () {
                            //HttpModule.HttpController.prototype.RedirectToAction(language, "minerva");
                            window.history.back();
                        });
                    }
                }
                if (data.Type == 3) {
                    var UrlVars = window.location.toString().replace("http://", '').replace("https://", '').split("?")[0].split("/");
                    var language = UrlVars[1].toString();
                    var area = UrlVars[2].toString();
                    MessagesModule.MessagesController.prototype.ShowMessageBox("Session is Expired", MessagesModule.MessageBoxType.XCSRF_ERROR, "The page will be automatically refreshed to fix the problem.", null, null,
                    //http://localhost:16911/en/common/Security/PrivateArea?source=Minerva
                    function () {
                        //HttpModule.HttpController.prototype.RedirectToAction(language, "minerva");
                        location.href = location.href;
                    });
                }
            }
            else {
                if (callback != null) {
                    callback(data);
                }
                else {
                    if (data.Message != null && data.Message != "" && data.Message != undefined)
                        MessagesModule.MessagesController.prototype.ShowMessageBox(data.Message, MessagesModule.MessageBoxType.ERROR, status, null, null, function () {
                            //HttpModule.HttpController.prototype.RedirectToAction(language, "minerva");
                            window.history.back();
                        });
                }
            }
        };
        // launch the set callback in case of success
        HttpController.prototype.OnJsonSuccess = function (data, successCallback, onerrorCallback) {
            if (onerrorCallback === void 0) { onerrorCallback = null; }
            this.DisableLoaderSpinner();
            if (data.Type == 0) {
                try {
                    if (data.Message != null && data.Message != "" && data.Message != undefined)
                        MessagesModule.MessagesController.prototype.ShowMessageBox(data.Message, MessagesModule.MessageBoxType.SUCCESS, null, null, null, function () {
                            if (successCallback != null) {
                                successCallback(data);
                            }
                        }, function () {
                            if (successCallback != null) {
                                successCallback(data);
                            }
                        });
                    else if (successCallback != null)
                        successCallback(data);
                }
                catch (e) {
                    if (data.Message != null)
                        alert(data.Message);
                    if (successCallback != null)
                        successCallback(data);
                }
            }
            if (data.Type == 1) {
                try {
                    MessagesModule.MessagesController.prototype.ShowMessageBox(data.Message, MessagesModule.MessageBoxType.ERROR, null, null, null, function () {
                        if (onerrorCallback != null)
                            onerrorCallback(data);
                    }, function () {
                        if (onerrorCallback != null)
                            onerrorCallback(data);
                    });
                }
                catch (e) {
                    if (data.Message != null)
                        alert(data.Message);
                    if (onerrorCallback != null)
                        onerrorCallback(data);
                }
            }
            if (data.Type == 2) {
                if (data == null || data.Message == null)
                    data.Message = "You are not Authorised to perform the action.";
                try {
                    MessagesModule.MessagesController.prototype.ShowMessageBox("Not Authorised", MessagesModule.MessageBoxType.ERROR, data.Message, null, null, function () {
                        HttpModule.HttpController.prototype.RedirectToAction("en", "minerva", "content", "index");
                    });
                }
                catch (e) {
                    alert(data.Message);
                    HttpModule.HttpController.prototype.RedirectToAction("en", "minerva", "content", "index");
                }
            }
            if (data.Type == 3) {
                if (data == null || data.Message == null)
                    data.Message = "The page will be automatically refreshed to fix the problem.";
                try {
                    MessagesModule.MessagesController.prototype.ShowMessageBox("Session Expired", MessagesModule.MessageBoxType.XCSRF_ERROR, data.Message, null, null, function () {
                        location.href = location.href;
                    });
                }
                catch (e) {
                    alert(data.Message);
                    location.href = location.href;
                }
            }
        };
        //* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 
        // * * * * * * * * * * * * * * * * * * * * *  Rest base async calls * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 
        //* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 
        HttpController.prototype.Get = function (servicename, parId, secondParId, successCallback, onerrorCallback, loaderEnabled, queryString) {
            if (onerrorCallback === void 0) { onerrorCallback = null; }
            if (loaderEnabled === void 0) { loaderEnabled = true; }
            if (queryString === void 0) { queryString = null; }
            if (!loaderEnabled) {
                this.DisableLoaderSpinner();
            }
            else {
                this.EnableLoaderSpinner();
            }
            var url = '/api/Json' + servicename + "/" + parId;
            if (secondParId != null && secondParId != "")
                url = url + "/" + secondParId;
            if (queryString != null && queryString != '') {
                if (queryString.indexOf('?') == -1)
                    queryString = "?" + queryString;
                url = url + queryString;
            }
            this.httpService.get(url)
                .success(function (data, status, headers, config) { HttpController.prototype.OnJsonSuccess(data, successCallback, onerrorCallback); })
                .error(function (data, status, headers, config) { HttpController.prototype.OnJsonError(data, status, headers, config, onerrorCallback); });
        };
        HttpController.prototype.Lock = function (servicename, parId, lang, successCallback, onerrorCallback, loaderEnabled, queryString) {
            if (onerrorCallback === void 0) { onerrorCallback = null; }
            if (loaderEnabled === void 0) { loaderEnabled = true; }
            if (queryString === void 0) { queryString = null; }
            if (!loaderEnabled) {
                this.DisableLoaderSpinner();
            }
            else {
                this.EnableLoaderSpinner();
            }
            var url = '/api/Json' + servicename + "/actions/lock/" + parId;
            if (lang != null && lang != "")
                url = url + "/null/" + lang;
            if (queryString != null && queryString != '') {
                if (queryString.indexOf('?') == -1)
                    queryString = "?" + queryString;
                url = url + queryString;
            }
            this.httpService.put(url, null)
                .success(function (data, status, headers, config) { HttpController.prototype.OnJsonSuccess(data, successCallback, onerrorCallback); })
                .error(function (data, status, headers, config) { HttpController.prototype.OnJsonError(data, status, headers, config, onerrorCallback); });
        };
        HttpController.prototype.Unlock = function (servicename, parId, successCallback, onerrorCallback, loaderEnabled, queryString) {
            if (onerrorCallback === void 0) { onerrorCallback = null; }
            if (loaderEnabled === void 0) { loaderEnabled = true; }
            if (queryString === void 0) { queryString = null; }
            if (!loaderEnabled) {
                this.DisableLoaderSpinner();
            }
            else {
                this.EnableLoaderSpinner();
            }
            var url = '/api/Json' + servicename + "/actions/unlock/" + parId;
            if (queryString != null && queryString != '') {
                if (queryString.indexOf('?') == -1)
                    queryString = "?" + queryString;
                url = url + queryString;
            }
            this.httpService.put(url, null)
                .success(function (data, status, headers, config) { HttpController.prototype.OnJsonSuccess(data, successCallback, onerrorCallback); })
                .error(function (data, status, headers, config) { HttpController.prototype.OnJsonError(data, status, headers, config, onerrorCallback); });
        };
        HttpController.prototype.List = function (servicename, successCallback, onerrorCallback, loaderEnabled, queryString) {
            if (onerrorCallback === void 0) { onerrorCallback = null; }
            if (loaderEnabled === void 0) { loaderEnabled = true; }
            if (queryString === void 0) { queryString = null; }
            if (!loaderEnabled) {
                this.DisableLoaderSpinner();
            }
            else {
                this.EnableLoaderSpinner();
            }
            var url = '/api/Json' + servicename + "/list";
            if (queryString != null && queryString != '') {
                if (queryString.indexOf('?') == -1)
                    queryString = "?" + queryString;
                url = url + queryString;
            }
            this.httpService.get(url)
                .success(function (data, status, headers, config) { HttpController.prototype.OnJsonSuccess(data, successCallback, onerrorCallback); })
                .error(function (data, status, headers, config) { HttpController.prototype.OnJsonError(data, status, headers, config, onerrorCallback); });
        };
        HttpController.prototype.Search = function (servicename, query, successCallback, onerrorCallback, loaderEnabled) {
            if (onerrorCallback === void 0) { onerrorCallback = null; }
            if (loaderEnabled === void 0) { loaderEnabled = true; }
            if (!loaderEnabled) {
                this.DisableLoaderSpinner();
            }
            else {
                this.EnableLoaderSpinner();
            }
            this.httpService.get('/api/Json' + servicename + "/search?" + query)
                .success(function (data, status, headers, config) { HttpController.prototype.OnJsonSuccess(data, successCallback, onerrorCallback); })
                .error(function (data, status, headers, config) { HttpController.prototype.OnJsonError(data, status, headers, config, onerrorCallback); });
        };
        HttpController.prototype.Post = function (servicename, moduleToAdd, successCallback, onerrorCallback, queryString, loaderEnabled) {
            if (onerrorCallback === void 0) { onerrorCallback = null; }
            if (queryString === void 0) { queryString = null; }
            if (loaderEnabled === void 0) { loaderEnabled = true; }
            if (!loaderEnabled) {
                this.DisableLoaderSpinner();
            }
            else {
                this.EnableLoaderSpinner();
            }
            var url = '/api/Json' + servicename;
            if (queryString != null && queryString != '') {
                if (queryString.indexOf('?') == -1)
                    queryString = "?" + queryString;
                url = url + queryString;
            }
            this.httpService.post(url, moduleToAdd)
                .success(function (data, status, headers, config) { HttpController.prototype.OnJsonSuccess(data, successCallback, onerrorCallback); })
                .error(function (data, status, headers, config) { HttpController.prototype.OnJsonError(data, status, headers, config, onerrorCallback); });
        };
        HttpController.prototype.Put = function (servicename, parId, moduleToUpdate, successCallback, onerrorCallback, queryString, loaderEnabled) {
            if (onerrorCallback === void 0) { onerrorCallback = null; }
            if (queryString === void 0) { queryString = null; }
            if (loaderEnabled === void 0) { loaderEnabled = true; }
            if (!loaderEnabled) {
                this.DisableLoaderSpinner();
            }
            else {
                this.EnableLoaderSpinner();
            }
            var url = '/api/Json' + servicename + "/" + parId;
            if (queryString != null && queryString != '') {
                if (queryString.indexOf('?') == -1)
                    queryString = "?" + queryString;
                url = url + queryString;
            }
            this.httpService.put(url, moduleToUpdate)
                .success(function (data, status, headers, config) { HttpController.prototype.OnJsonSuccess(data, successCallback, onerrorCallback); })
                .error(function (data, status, headers, config) { HttpController.prototype.OnJsonError(data, status, headers, config, onerrorCallback); });
        };
        HttpController.prototype.Delete = function (servicename, parId, successCallback, onerrorCallback, queryString, loaderEnabled) {
            if (onerrorCallback === void 0) { onerrorCallback = null; }
            if (queryString === void 0) { queryString = null; }
            if (loaderEnabled === void 0) { loaderEnabled = true; }
            if (!loaderEnabled) {
                this.DisableLoaderSpinner();
            }
            else {
                this.EnableLoaderSpinner();
            }
            var url = '/api/Json' + servicename;
            if (parId != null && parId != "")
                url = url + "/" + parId;
            else
                alert("Id parameter is mandatory.");
            if (queryString != null && queryString != '') {
                if (queryString.indexOf('?') == -1)
                    queryString = "?" + queryString;
                url = url + queryString;
            }
            this.httpService.delete(url)
                .success(function (data, status, headers, config) { HttpController.prototype.OnJsonSuccess(data, successCallback, onerrorCallback); })
                .error(function (data, status, headers, config) { HttpController.prototype.OnJsonError(data, status, headers, config, onerrorCallback); });
        };
        HttpController.prototype.UserIsAdmin = function (callback) {
            if (callback == null)
                return;
            this.httpService.get('/api/JsonMinerva/actions/IsUserAdmin')
                .success(function (data, status, headers, config) {
                    if (data.Type == Module.VMResultType.NOT_AUTHORIZED)
                        callback(false);
                    else
                        callback(true);
                })
                .error(function (data, status, headers, config) { callback(false); });
        };
        HttpController.prototype.CallGetAction = function (servicename, actionname, parId, successCallback, onerrorCallback, queryString, loaderEnabled) {
            if (onerrorCallback === void 0) { onerrorCallback = null; }
            if (queryString === void 0) { queryString = null; }
            if (loaderEnabled === void 0) { loaderEnabled = true; }
            if (!loaderEnabled) {
                this.DisableLoaderSpinner();
            }
            else {
                this.EnableLoaderSpinner();
            }
            var url = '/api/Json' + servicename + "/actions/" + actionname;
            if (parId != null && parId != "")
                url = url + "/" + parId;
            if (queryString != null && queryString != '') {
                if (queryString.indexOf('?') == -1)
                    queryString = "?" + queryString;
                url = url + queryString;
            }
            this.httpService.get(url)
                .success(function (data, status, headers, config) { HttpController.prototype.OnJsonSuccess(data, successCallback, onerrorCallback); })
                .error(function (data, status, headers, config) { HttpController.prototype.OnJsonError(data, status, headers, config, onerrorCallback); });
        };
        HttpController.prototype.CallGetActionWithVersion = function (servicename, actionname, parId, secondParId, successCallback, onerrorCallback, queryString, loaderEnabled) {
            if (onerrorCallback === void 0) { onerrorCallback = null; }
            if (queryString === void 0) { queryString = null; }
            if (loaderEnabled === void 0) { loaderEnabled = true; }
            if (!loaderEnabled) {
                this.DisableLoaderSpinner();
            }
            else {
                this.EnableLoaderSpinner();
            }
            var url = '/api/Json' + servicename + "/actions/" + actionname;
            if (parId != null && parId != "")
                url = url + "/" + parId;
            if (secondParId != null && secondParId != "")
                url = url + "/" + secondParId;
            if (queryString != null && queryString != '') {
                if (queryString.indexOf('?') == -1)
                    queryString = "?" + queryString;
                url = url + queryString;
            }
            this.httpService.get(url)
                .success(function (data, status, headers, config) { HttpController.prototype.OnJsonSuccess(data, successCallback, onerrorCallback); })
                .error(function (data, status, headers, config) { HttpController.prototype.OnJsonError(data, status, headers, config, onerrorCallback); });
        };
        HttpController.prototype.CallWAGetAction = function (servicename, actionname, waUrlId, pars, successCallback, onerrorCallback, loaderEnabled) {
            if (onerrorCallback === void 0) { onerrorCallback = null; }
            if (loaderEnabled === void 0) { loaderEnabled = true; }
            if (!loaderEnabled) {
                this.DisableLoaderSpinner();
            }
            else {
                this.EnableLoaderSpinner();
            }
            var url = '/waapi/' + servicename + '/' + actionname + '/' + waUrlId;
            if (pars != null) {
                for (var i = 0; i < pars.length; i++) {
                    url += '/' + pars[i];
                }
            }
            this.httpService.get(url)
                .success(function (data, status, headers, config) { HttpController.prototype.OnJsonSuccess(data, successCallback, onerrorCallback); })
                .error(function (data, status, headers, config) { HttpController.prototype.OnJsonError(data, status, headers, config, onerrorCallback); });
        };
        HttpController.prototype.CallGetActionByLang = function (servicename, actionname, parId, secondParId, lang, successCallback, onerrorCallback, queryString, loaderEnabled) {
            if (onerrorCallback === void 0) { onerrorCallback = null; }
            if (queryString === void 0) { queryString = null; }
            if (loaderEnabled === void 0) { loaderEnabled = true; }
            if (!loaderEnabled) {
                this.DisableLoaderSpinner();
            }
            else {
                this.EnableLoaderSpinner();
            }
            var url = '/api/Json' + servicename + "/actions/" + actionname;
            if (parId != null && parId != "")
                url = url + "/" + parId;
            if (secondParId != null && secondParId != "")
                url = url + "/" + secondParId;
            if (lang != null && lang != "") {
                if (secondParId == null || secondParId == "")
                    url = url + "/null";
                url = url + "/" + lang;
            }
            if (queryString != null && queryString != '') {
                if (queryString.indexOf('?') == -1)
                    queryString = "?" + queryString;
                url = url + queryString;
            }
            this.httpService.get(url)
                .success(function (data, status, headers, config) { HttpController.prototype.OnJsonSuccess(data, successCallback, onerrorCallback); })
                .error(function (data, status, headers, config) { HttpController.prototype.OnJsonError(data, status, headers, config, onerrorCallback); });
        };
        HttpController.prototype.CallHttpUrl = function (url, successCallback, onerrorCallback, loaderEnabled) {
            if (onerrorCallback === void 0) { onerrorCallback = null; }
            if (loaderEnabled === void 0) { loaderEnabled = true; }
            if (!loaderEnabled) {
                this.DisableLoaderSpinner();
            }
            else {
                this.EnableLoaderSpinner();
            }
            this.httpService.get(url)
                .success(function (data, status, headers, config) { HttpController.prototype.OnJsonSuccess(data, successCallback, onerrorCallback); })
                .error(function (data, status, headers, config) { HttpController.prototype.OnJsonError(data, status, headers, config, onerrorCallback); });
        };
        HttpController.prototype.CallPostAction = function (servicename, actionname, parId, moduleToAdd, successCallback, onerrorCallback, queryString, loaderEnabled) {
            if (onerrorCallback === void 0) { onerrorCallback = null; }
            if (queryString === void 0) { queryString = null; }
            if (loaderEnabled === void 0) { loaderEnabled = true; }
            if (!loaderEnabled) {
                this.DisableLoaderSpinner();
            }
            else {
                this.EnableLoaderSpinner();
            }
            var url = '/api/Json' + servicename + "/actions/" + actionname;
            if (parId != null && parId != "")
                url = url + "/" + parId;
            if (queryString != null && queryString != '') {
                if (queryString.indexOf('?') == -1)
                    queryString = "?" + queryString;
                url = url + queryString;
            }
            this.httpService.post(url, moduleToAdd)
                .success(function (data, status, headers, config) { HttpController.prototype.OnJsonSuccess(data, successCallback, onerrorCallback); })
                .error(function (data, status, headers, config) { HttpController.prototype.OnJsonError(data, status, headers, config, onerrorCallback); });
        };
        HttpController.prototype.CallPostActionByLang = function (servicename, actionname, parId, lang, moduleToAdd, successCallback, onerrorCallback, queryString, loaderEnabled) {
            if (onerrorCallback === void 0) { onerrorCallback = null; }
            if (queryString === void 0) { queryString = null; }
            if (loaderEnabled === void 0) { loaderEnabled = true; }
            if (!loaderEnabled) {
                this.DisableLoaderSpinner();
            }
            else {
                this.EnableLoaderSpinner();
            }
            var url = '/api/Json' + servicename + "/actions/" + actionname;
            if (parId != null && parId != "")
                url = url + "/" + parId;
            if (lang != null && lang != "")
                url = url + "/null/" + lang;
            if (queryString != null && queryString != '') {
                if (queryString.indexOf('?') == -1)
                    queryString = "?" + queryString;
                url = url + queryString;
            }
            this.httpService.post(url, moduleToAdd)
                .success(function (data, status, headers, config) { HttpController.prototype.OnJsonSuccess(data, successCallback, onerrorCallback); })
                .error(function (data, status, headers, config) { HttpController.prototype.OnJsonError(data, status, headers, config, onerrorCallback); });
        };
        HttpController.prototype.CallWAPostAction = function (servicename, actionname, waUrlId, pars, moduleToAdd, successCallback, onerrorCallback, loaderEnabled) {
            if (loaderEnabled === void 0) { loaderEnabled = true; }
            if (!loaderEnabled)
                this.DisableLoaderSpinner();
            else
                this.EnableLoaderSpinner();
            var url = '/waapi/' + servicename + '/' + actionname + '/' + waUrlId;
            if (pars != null) {
                for (var i = 0; i < pars.length; i++) {
                    url += '/' + pars[i];
                }
            }
            this.httpService.post(url, moduleToAdd)
                .success(function (data, status, headers, config) { HttpController.prototype.OnJsonSuccess(data, successCallback, onerrorCallback); })
                .error(function (data, status, headers, config) { HttpController.prototype.OnJsonError(data, status, headers, config, onerrorCallback); });
        };
        HttpController.prototype.CallPutAction = function (servicename, actionname, parId, moduleToUpdate, successCallback, onerrorCallback, queryString, loaderEnabled) {
            if (onerrorCallback === void 0) { onerrorCallback = null; }
            if (queryString === void 0) { queryString = null; }
            if (loaderEnabled === void 0) { loaderEnabled = true; }
            if (!loaderEnabled) {
                this.DisableLoaderSpinner();
            }
            else {
                this.EnableLoaderSpinner();
            }
            var url = '/api/Json' + servicename + "/actions/" + actionname;
            if (parId != null && parId != "")
                url = url + "/" + parId;
            if (queryString != null && queryString != '') {
                if (queryString.indexOf('?') == -1)
                    queryString = "?" + queryString;
                url = url + queryString;
            }
            this.httpService.put(url, moduleToUpdate)
                .success(function (data, status, headers, config) { HttpController.prototype.OnJsonSuccess(data, successCallback, onerrorCallback); })
                .error(function (data, status, headers, config) { HttpController.prototype.OnJsonError(data, status, headers, config, onerrorCallback); });
        };
        HttpController.prototype.CallPutActionByLang = function (servicename, actionname, parId, lang, moduleToUpdate, successCallback, onerrorCallback, queryString, loaderEnabled) {
            if (onerrorCallback === void 0) { onerrorCallback = null; }
            if (queryString === void 0) { queryString = null; }
            if (loaderEnabled === void 0) { loaderEnabled = true; }
            if (!loaderEnabled) {
                this.DisableLoaderSpinner();
            }
            else {
                this.EnableLoaderSpinner();
            }
            var url = '/api/Json' + servicename + "/actions/" + actionname;
            if (parId != null && parId != "")
                url = url + "/" + parId;
            if (lang != null && lang != "")
                url = url + "/null/" + lang;
            if (queryString != null && queryString != '') {
                if (queryString.indexOf('?') == -1)
                    queryString = "?" + queryString;
                url = url + queryString;
            }
            this.httpService.put(url, moduleToUpdate)
                .success(function (data, status, headers, config) { HttpController.prototype.OnJsonSuccess(data, successCallback, onerrorCallback); })
                .error(function (data, status, headers, config) { HttpController.prototype.OnJsonError(data, status, headers, config, onerrorCallback); });
        };
        HttpController.prototype.EnableLoaderSpinner = function () {
            try {
                if ($ != undefined || $('#divloader') != undefined) {
                    if ($('#divloader').attr('enabled') != undefined) {
                        $('#divloader').attr('enabled', "true");
                        $('#divloader').show();
                    }
                }
            }
            catch (e) { }
        };
        HttpController.prototype.DisableLoaderSpinner = function () {
            try {
                if ($ != undefined || $('#divloader') != undefined) {
                    if ($('#divloader').attr('enabled') != undefined) {
                        $('#divloader').attr('enabled', "false");
                        $('#divloader').hide();
                    }
                }
            }
            catch (e) { }
        };
        //* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 
        // * * * * * * * * * * * * * * * * * * * * *  Messagges and Cache * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 
        //* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 
        // Method to get a cached value calling the async service
        HttpController.prototype.GetCache = function (cacheKey, successCallback) {
            var servicename = "cache";
            this.Get(servicename, cacheKey, null, successCallback, null);
        };
        // Method used to get a message from the globalisation cached information. Message type are filtered by ANGULAR
        HttpController.prototype.GetMessage = function (key) {
            var controller = this;
            if (controller.Messages != null) {
                var i = 0;
                for (i = 0; i < this.Messages.length; i++) {
                    if (controller.Messages[i].GlobalisationKeyId.toLocaleUpperCase() == key.toLocaleUpperCase()) {
                        return controller.Messages[i].Value;
                    }
                }
            }
            return "";
        };
        //* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 
        // * * * * * * * * * * * * * * * * * * * * *  Utilities * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 
        //* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 
        // redirect to a specific action of the mvc application
        HttpController.prototype.RedirectToAction = function (language, area, controllerName, action, idModel, querystring, idVersion) {
            if (controllerName === void 0) { controllerName = null; }
            if (action === void 0) { action = null; }
            if (idModel === void 0) { idModel = null; }
            if (querystring === void 0) { querystring = null; }
            if (idVersion === void 0) { idVersion = null; }
            var url = this.CreateUrl(language, area, controllerName, action, idModel, querystring, idVersion);
            this.RedirectToUrl(url);
        };
        // redirect to a specific action of the mvc application
        HttpController.prototype.RedirectToActionWithLocationPars = function (language, area, controllerName, action, idModel, querystring, idVersion, locationPars) {
            if (controllerName === void 0) { controllerName = null; }
            if (action === void 0) { action = null; }
            if (idModel === void 0) { idModel = null; }
            if (querystring === void 0) { querystring = null; }
            if (idVersion === void 0) { idVersion = null; }
            var url = this.CreateUrl(language, area, controllerName, action, idModel, querystring, idVersion);
            url = url + "/#?" + locationPars;
            this.RedirectToUrl(url);
        };
        // redirect to a specific url
        HttpController.prototype.RedirectToUrl = function (url) {
            location.href = url;
        };
        // create a valid url starting from the passed routing parameters
        HttpController.prototype.CreateUrl = function (language, area, controllerName, action, idModel, queryString, idVersion) {
            if (action === void 0) { action = null; }
            if (idModel === void 0) { idModel = null; }
            if (queryString === void 0) { queryString = null; }
            if (idVersion === void 0) { idVersion = null; }
            var url = "";
            if (language != null && language != undefined && language != "")
                url = url + "/" + language;
            else {
                //alert("There is a language problem in url redirection. Contact system administrator.");
                url = url + "/en";
            }
            if (area != null && area != undefined && area != "")
                url = url + "/" + area;
            if (controllerName != null && controllerName != undefined && action != '') {
                url = url + "/" + controllerName;
            }
            if (action != null && action != undefined && action != '') {
                url = url + "/" + action;
            }
            if (idModel != null && idModel != undefined && idModel != '') {
                url = url + "/" + idModel;
                if (idVersion != null && idVersion != undefined && idVersion != '') {
                    url = url + "/" + idVersion;
                }
            }
            if (queryString != null && queryString != '') {
                if (queryString.indexOf('?') == -1)
                    queryString = "?" + queryString;
                url = url + queryString;
            }
            return url;
        };
        // get the object type
        HttpController.prototype.GetObjectClass = function (obj) {
            if (obj && obj.constructor && obj.constructor.toString) {
                var arr = obj.constructor.toString().match(/function\s*(\w+)/);
                if (arr && arr.length == 2) {
                    return arr[1];
                }
            }
            return undefined;
        };
        HttpController.prototype.GetParameterByName = function (name) {
            name = name.replace(/[\[]/, "\\[").replace(/[\]]/, "\\]");
            var regex = new RegExp("[\\?&]" + name + "=([^&#]*)"), results = regex.exec(location.search);
            return results == null ? "" : decodeURIComponent(results[1].replace(/\+/g, " "));
        };
        return HttpController;
    }());
    HttpModule.HttpController = HttpController;
})(HttpModule || (HttpModule = {}));

var BaseModule;
(function (BaseModule) {
    var BaseController = (function (_super) {
        __extends(BaseController, _super);
        function BaseController($scope, $http) {
            _super.call(this, $http, $scope);
            var controller = this;
            // Get language fron the url
            $scope.urlVars = window.location.toString().replace("http://", '').replace("https://", '').split("?")[0].split("#")[0].split("/");
            $scope.Language = $scope.urlVars[1].toString();
            if (window.location.toString().indexOf("https") > -1)
                $scope.baseUrl = "https://" + $scope.urlVars[0];
            else
                $scope.baseUrl = "http://" + $scope.urlVars[0];
            if ($scope.Language == null || $scope.Language == '')
                $scope.Language = $("#langCode").val();
            // redirect to a specific action of the mvc application
            $scope.redirectToAction = function (language, area, controllerName, action, idModel, querystring) {
                if (action === void 0) { action = null; }
                if (idModel === void 0) { idModel = null; }
                if (querystring === void 0) { querystring = null; }
                if (language == null || language == '') {
                    language = $("#langCode").val();
                }
                controller.RedirectToAction(language, area, controllerName, action, idModel, querystring);
            };
            // angular js safe apply to do scope.apply only if there is not a started scope apply phase
            $scope.safeApply = function (fn) {
                var phase = this.$root.$$phase;
                if (phase == '$apply' || phase == '$digest') {
                    if (fn && (typeof (fn) === 'function')) {
                        fn();
                    }
                }
                else {
                    this.$apply(fn);
                }
            };
            //
            $scope.applyChange = function () {
                $scope.safeApply();
            };
            //
            $scope.addRemoveElement = function (list, item) {
                var i = 0;
                var alreadycontains = false;
                for (i = 0; i < list.length; i++) {
                    if (list[i].Id == item.Id) {
                        alreadycontains = true;
                        list.splice(i, 1);
                        break;
                    }
                }
                if (!alreadycontains) {
                    list.push(item);
                }
            };
            // replace spaces from a string content for a seo scope
            $scope.getSeoContent = function (value) {
                if (value == null)
                    return "";
                return value.replace(" ", "_");
            };
            //
            $scope.clone = function (obj) {
                // Handle the 3 simple types, and null or undefined
                if (null == obj || "object" != typeof obj)
                    return obj;
                // Handle Date
                if (obj instanceof Date) {
                    var copy = new Date();
                    copy.setTime(obj.getTime());
                    return copy;
                }
                // Handle Array
                if (obj instanceof Array) {
                    var copy = [];
                    for (var i = 0, len = obj.length; i < len; i++) {
                        copy[i] = $scope.clone(obj[i]);
                    }
                    return copy;
                }
                // Handle Object
                if (obj instanceof Object) {
                    var copy = {};
                    for (var attr in obj) {
                        if (obj.hasOwnProperty(attr))
                            copy[attr] = $scope.clone(obj[attr]);
                    }
                    return copy;
                }
                throw new Error("Unable to copy obj! Its type isn't supported.");
            };
            //
            $scope.formatString = function (str, param1, param2, param3) {
                if (param2 === void 0) { param2 = null; }
                if (param3 === void 0) { param3 = null; }
                if (param1 != null) {
                    str = str.replace('{0}', param1);
                }
                if (param2 != null) {
                    str = str.replace('{1}', param2);
                }
                if (param3 != null) {
                    str = str.replace('{2}', param3);
                }
                return str;
            };
            //
            $scope.scrollLeft = function (elId) { $('#' + elId).delay(5000).scrollLeft($('#' + elId).width()); };
            //
            $scope.CheckIfUserIsAdmin = function (callback) {
                controller.UserIsAdmin(function (data) {
                    $scope.CurrentUserIsAdmin = data;
                    callback();
                });
            };
            //
            $scope.getAngularMessage = function (key) {
                var message = controller.GetMessage(key);
                return message;
            };
            // Show Tooltip funciton for html control buttons
            $scope.showTooltip = function (id, tooltipId) {
                var pos = $("#" + id).position();
                var height = $("#" + id).outerHeight();
                var width = $("#" + id).outerWidth();
                var tooltipWidth = $("#" + tooltipId).outerWidth();
                if (pos != null && height != null && width != null && tooltipWidth != null) {
                    $("#" + tooltipId).css({
                        top: (pos.top + height) + "px",
                        //top: (pos.top + height + 3) + "px",
                        left: ((pos.left + (width / 2) + 5) - (tooltipWidth / 2)) + "px"
                    }).show();
                }
            };
            // Show Tooltip funciton for html control buttons
            $scope.showTooltip_Top = function (id, tooltipId) {
                var pos = $("#" + id).position();
                var height = $("#" + id).outerHeight();
                var width = $("#" + id).outerWidth();
                var tooltipWidth = $("#" + tooltipId).outerWidth();
                var tooltipHeight = $("#" + tooltipId).outerHeight();
                if (pos != null && height != null && width != null && tooltipWidth != null) {
                    $("#" + tooltipId).css({
                        top: (pos.top - tooltipHeight) + "px",
                        left: ((pos.left + (width / 2) + 5) - (tooltipWidth / 2)) + "px"
                    }).show();
                }
            };
            // Hide Tooltip funciton
            $scope.hideTooltip = function (tooltipId) {
                $("#" + tooltipId).hide();
            };
            // show if hidden or hide if showed, a html control and put the class selected to it
            $scope.showHideDiv = function (id, elCallerId, showcallback, hidecallback) {
                if (elCallerId === void 0) { elCallerId = null; }
                if (showcallback === void 0) { showcallback = null; }
                if (hidecallback === void 0) { hidecallback = null; }
                if ($("#" + id).is(":hidden")) {
                    $("#" + id).show();
                    if (elCallerId != null) {
                        if (!$("#" + elCallerId).hasClass("selected")) {
                            $("#" + elCallerId).addClass("selected");
                        }
                    }
                    if (showcallback != null)
                        showcallback();
                }
                else {
                    $("#" + id).hide();
                    if (elCallerId != null) {
                        if ($("#" + elCallerId).hasClass("selected")) {
                            $("#" + elCallerId).removeClass("selected");
                        }
                    }
                    if (hidecallback != null)
                        hidecallback();
                }
            };
            $scope.s4 = function () {
                return Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);
            };
            $scope.guid = function () {
                return $scope.s4() + $scope.s4() + '-' + $scope.s4() + '-' + $scope.s4() + '-' +
                    $scope.s4() + '-' + $scope.s4() + $scope.s4() + $scope.s4();
            };
            /*************************** NUMERIC FNCTION ***********************************************/
            $scope.checkIfIsPositiveNumeric = function (value) {
                if (value == null || value == undefined) {
                    return null;
                }
                else {
                    if (value < 0) {
                        return null;
                    }
                }
                return value;
            };
            // this method check and validate a float control. if an alphanumeric char is passed it return only the float value.
            // is possible to pass to the method a min and/or a max allowed value
            $scope.numericalControl = function (str, min, max) {
                if (min === void 0) { min = null; }
                if (max === void 0) { max = null; }
                //$scope.wasChanged = true;
                if (str == null)
                    return null;
                if (min >= 0 && str == '-')
                    return null;
                // split string into a char array 
                var split = str.toString().split('');
                str = "";
                var hascommadot = false;
                var hasless = false;
                var i = 0;
                for (i = 0; i < split.length; i++) {
                    if (split[i] == ' ')
                        continue;
                    // replace comma with dot because js work with this char
                    if (split[i] == ',')
                        split[i] = '.';
                    // is not possible to put dot or 0 at the first position
                    if (i == 0 && split[i] == '.')
                        continue;
                    // if the number is less than 1 and more than -1 and after the 0 there is not a dot, it is an error
                    if ((i == 1 && split[0] == '0' && split[i] != '.' && !(i == 1 && split[0] == '-' && split[i] != '0'))
                        || (i == 2 && split[0] == '-' && split[1] == '0' && split[i] != '.')) {
                        str = '';
                        break;
                    }
                    // check if a less char is in the first position and/or was already looped
                    if (split[i] == '-') {
                        if (i > 0) {
                            continue;
                        }
                        else {
                            if (hasless)
                                continue;
                            else
                                hasless = true;
                        }
                    }
                    if (split[i] == '1' || split[i] == '2' || split[i] == '3' || split[i] == '4'
                        || split[i] == '5' || split[i] == '6' || split[i] == '7' || split[i] == '8'
                        || split[i] == '9' || split[i] == '0' || split[i] == '.' || split[i] == '-') {
                        // check if a comma or dot char was already looped
                        if (split[i] == '.') {
                            if (hascommadot)
                                continue;
                            else
                                hascommadot = true;
                        }
                        // add char to the reusult string
                        str = str + split[i];
                        // check the min and max value. in case is more or less 
                        // it remove the last char pushed into the result string
                        if (split[i] != '.' && split[i] != '-') {
                            var fval = parseFloat(str);
                            if (min != null) {
                                if (str < min)
                                    if (i > 1)
                                        str = str.substring(0, i);
                                    else
                                        return null;
                            }
                            if (max != null) {
                                if (str > max)
                                    if (i > 1)
                                        str = str.substring(0, i);
                                    else
                                        return null;
                            }
                        }
                    }
                }
                var fval = parseFloat(str);
                if (min != null) {
                    if (str < min)
                        str = min;
                }
                if (max != null) {
                    if (str > max)
                        str = max;
                }
                return str;
            };
        }
        return BaseController;
    }(HttpModule.HttpController));
    BaseModule.BaseController = BaseController;
    var CrudBaseController = (function (_super) {
        __extends(CrudBaseController, _super);
        function CrudBaseController($scope, $http, areaName, serviceName, callLoadPage) {
            if (callLoadPage === void 0) { callLoadPage = true; }
            // this.httpService = $http;
            $scope.serviceName = serviceName;
            $scope.areaName = areaName;
            _super.call(this, $scope, $http);
            var controller = this;
            //* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 
            // * * * * * * * * * * * * * * * * * * * * *  Initialisation  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 
            //* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 
            // inizialize the changing form control properties
            $scope.wasChanged = false;
            $scope.shouldBeSaved = false;
            $scope.forceExit = false;
            $scope.IsLockedByMe = false;
            $scope.doNotUnlockOnExit = false;
            // Get language fron the url
            $scope.urlVars = window.location.toString().replace("http://", '').replace("https://", '').split("?")[0].split("#")[0].split("/");
            $scope.Language = $scope.urlVars[1].toString();
            if (window.location.toString().indexOf("https") > -1)
                $scope.baseUrl = "https://" + $scope.urlVars[0];
            else
                $scope.baseUrl = "http://" + $scope.urlVars[0];
            //* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 
            // * * * * * * * * * * * * * * * * * * * * *  form events management and scope utils * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 
            //* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 
            // angular js safe apply to do scope.apply only if there is not a started scope apply phase
            $scope.safeApply = function (fn) {
                var phase = this.$root.$$phase;
                if (phase == '$apply' || phase == '$digest') {
                    if (fn && (typeof (fn) === 'function')) {
                        fn();
                    }
                }
                else {
                    this.$apply(fn);
                }
            };
            $scope.applyChange = function () {
                $scope.wasChanged = true;
                $scope.safeApply();
            };
            // Register all the form events that are outside the ng controller
            $scope.registerOutFormEvents = function () {
                if (!$scope.hasRegisterOutFormEvents) {
                    $scope.hasRegisterOutFormEvents = true;
                    // manage all change event controls to intercept if there are some changes in the form 
                    // TOFIX: event: Event doesnt work. With any works, to fix
                    $scope.$on("changeEvent", function (event) {
                        $scope.safeApply(function () { $scope.wasChanged = true; });
                        $(window).on('beforeunload', function () {
                            if (!$scope.forceExit && $scope.wasChanged) {
                                return controller.GetMessage("MINERVA_LEAVE_CURRENT_PAGE_QUESTION");
                            }
                        });
                    });
                }
                $("#" + $scope.className + " :input").unbind('change._regout').bind('change._regout', function () {
                    $scope.$broadcast('changeEvent', true);
                    return true;
                });
                $("#" + $scope.className + " :input").unbind('change._regout').bind('change._regout', function () {
                    $scope.$broadcast('changeEvent', true);
                    return true;
                });
                $("#" + $scope.className + " input, #" + $scope.className + " textarea").unbind('keypress._regout').bind('keypress._regout', function (e) {
                    var key = (e.which) ? e.which : e.keyCode;
                    if (key != 9 && key != 37 && key != 38 && key != 39 && key != 40) {
                        $scope.$broadcast('changeEvent', true);
                    }
                    return true;
                });
                // inizialize events for all page anchor controls that are out of the angular js management. 
                if ($scope.IsEdit || $scope.IsNew || $scope.IsView) {
                    //ignore messagebox and ckedit
                    $("a:not(#" + $scope.className + " a, #messagebox a, #toppage, .freelinks), .search").each(function (index, item) {
                        $(item).unbind('click._regout').bind('click._regout', function (event) {
                            event.preventDefault();
                            var url = $(item).attr("href");
                            if (url != null && url != '') {
                                if ($scope.wasChanged) {
                                    MessagesModule.MessagesController.prototype.ShowMessageBox(controller.GetMessage("MINERVA_SAVE_QUESTION"), MessagesModule.MessageBoxType.YESNOCANCEL, "", function () {
                                        $scope.forceExit = true;
                                        if ($scope.IsNew)
                                            $scope.addModel(url);
                                        else
                                            $scope.updateModel($scope.IdModel, url);
                                    }, function () {
                                        $scope.forceExit = true;
                                        if (!$scope.IsLockedByMe || $scope.doNotUnlockOnExit)
                                            controller.RedirectToUrl(url);
                                        else {
                                            $scope.unlockModel($scope.IdModel, function () {
                                                controller.RedirectToUrl(url);
                                            });
                                        }
                                    });
                                }
                                else {
                                    if (!$scope.IsLockedByMe || $scope.doNotUnlockOnExit)
                                        controller.RedirectToUrl(url);
                                    else {
                                        $scope.unlockModel($scope.IdModel, function () {
                                            controller.RedirectToUrl(url);
                                        });
                                    }
                                }
                            }
                            return false;
                        });
                    });
                }
            };
            //* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * * * * * * * * * * 
            // * * * * * * * * * * * * * * * * * * * * *  CRUD and Model management functions * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 
            //* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * * * * * * * * * * 
            $scope.getModel = function (parId, secondParId) {
                if (secondParId === void 0) { secondParId = null; }
                controller.Get($scope.serviceName, parId, secondParId, function (data) {
                    $scope.Model = data.Data;
                    $scope.registerOutFormEvents();
                    if ($scope.getModelCallBack != null)
                        $scope.getModelCallBack($scope.Model);
                }, null, true, $scope.GetModelQueryString);
            };
            // lock method call
            $scope.lockModel = function (parId, secondParId, refreshTab) {
                if (secondParId === void 0) { secondParId = null; }
                if (refreshTab === void 0) { refreshTab = true; }
                controller.Lock($scope.serviceName, parId, secondParId, function (data) {
                    $scope.$watch($scope.serviceName, function (newValue, oldValue) {
                        if ($scope.$eval("mainForm.$invalid")) {
                            $scope.shouldBeSaved = true;
                        }
                    });
                    $scope.IsLockedByMe = true;
                    $scope.Model = data.Data;
                    if ($scope.lockModelCallBack != null)
                        $scope.lockModelCallBack(refreshTab);
                    $scope.registerOutFormEvents();
                }, $scope.lockModelErrorCallBack, true, $scope.LockModelQueryString);
            };
            // unlock method call
            $scope.unlockModel = function (id, callback) {
                if (callback === void 0) { callback = null; }
                controller.Unlock($scope.serviceName, id, function (data) {
                    $scope.IsLockedByMe = false;
                    if (callback != null)
                        callback();
                }, null, true, $scope.UnlockModelQueryString);
            };
            // Call a web api method to get a new instance of the current model
            $scope.getNewModelInstance = function (queryString) {
                if (queryString === void 0) { queryString = null; }
                controller.CallGetAction($scope.serviceName, "NewObjectInstance", null, function (data) {
                    $scope.Model = data.Data;
                    if ($scope.getNewModelInstanceCallBack != null)
                        $scope.getNewModelInstanceCallBack();
                    $scope.registerOutFormEvents();
                }, null, queryString);
            };
            // Call a web api method to get a new instance of the current model start to an exists model
            $scope.getNewModelInstanceFromExistsModel = function (id) {
                controller.CallGetAction($scope.serviceName, "NewObjectInstanceFromExistsModel", id, function (data) {
                    $scope.Model = data.Data;
                    if ($scope.getNewModelInstanceFromExistsModelCallBack != null)
                        $scope.getNewModelInstanceFromExistsModelCallBack();
                    $scope.registerOutFormEvents();
                });
            };
            // method to associate to a generic save button. With one call the system understand if the action is to edit or to add an item.
            $scope.saveModel = function (id, urlRedirect) {
                if (id === void 0) { id = null; }
                if (urlRedirect === void 0) { urlRedirect = null; }
                if ($scope.IsNew) {
                    if (id != null && id != "") {
                        alert("The model has an identifier, thus it is not a new item.");
                        return;
                    }
                    $scope.addModel(urlRedirect);
                }
                else {
                    if (id == null || id == "") {
                        alert("The model does not have a identifier, thus it is not possible to edit.");
                        return;
                    }
                    $scope.updateModel(id, urlRedirect);
                }
            };
            // method to create a new model in a generic CRUD context  (without customizations)
            $scope.addModel = function (urlRedirect) {
                if (urlRedirect === void 0) { urlRedirect = null; }
                if ($scope.checkMandatoryFields == null) {
                    alert("checkMandatoryFields is not implemented and is mandatory");
                }
                if ($scope.beforeSaveCallBack != null)
                    $scope.beforeSaveCallBack();
                $scope.checkMandatoryFields(function () {
                    controller.Post($scope.serviceName, $scope.Model, function (data) {
                        $scope.forceExit = true;
                        if (urlRedirect != null && urlRedirect != undefined && urlRedirect != '') {
                            controller.RedirectToUrl(urlRedirect);
                        }
                        else {
                            if ($scope.addModelCallBack != null)
                                $scope.addModelCallBack(data.Data);
                        }
                    }, null, $scope.AddModelQueryString);
                });
            };
            // method to update the model in a generic CRUD context  (without customizations)
            $scope.updateModel = function (id, urlRedirect) {
                if (urlRedirect === void 0) { urlRedirect = null; }
                if (id == null) {
                    id = $scope.IdModel;
                }
                if ($scope.checkMandatoryFields == null) {
                    alert("$scope.checkMandatoryFields is not implemented and is mandatory");
                }
                // if beforeSaveCallBack is set call it
                if ($scope.beforeSaveCallBack != null)
                    $scope.beforeSaveCallBack();
                $scope.checkMandatoryFields(function () {
                    controller.Put($scope.serviceName, id, $scope.Model, function (data) {
                        $scope.safeApply(function () {
                            $scope.wasChanged = false;
                            $scope.shouldBeSaved = false;
                            $scope.forceExit = true;
                        });
                        if (urlRedirect != null && urlRedirect != undefined && urlRedirect != '') {
                            controller.RedirectToUrl(urlRedirect);
                        }
                        else {
                            if (data != null && data.Data != null)
                                $scope.Model = data.Data;
                            if ($scope.afterSaveCallBack != null) {
                                $scope.afterSaveCallBack(data.Data);
                            }
                        }
                    }, null, $scope.UpdateModelQueryString);
                });
            };
            // Get the name of the current class (if the costructor is called by a child get the child ts class name)
            $scope.className = this.GetObjectClass(this);
            if (callLoadPage) {
                // method to inizialize a CRUD page properties
                $scope.loadPage = function (refreshTab) {
                    if (refreshTab === void 0) { refreshTab = true; }
                    $scope.wasChanged = false;
                    $scope.shouldBeSaved = false;
                    $scope.forceExit = false;
                    // set IsNew, IsViewPage, IsEdit properties and get model and in case version ids
                    if ($scope.urlVars.length > 4) {
                        $scope.IsNew = ($scope.urlVars[5] == null);
                        $scope.IsViewPage = ($scope.urlVars[4] != null && $scope.urlVars[5] != null && $scope.urlVars[5] != "" && ($scope.urlVars[4].toString().toLowerCase() == "view" || $scope.urlVars[4].toString().toLowerCase() == "confidentialitymanagement"));
                        $scope.IsEdit = ($scope.urlVars[4] != null && $scope.urlVars[4].toString().toLowerCase() == "edit" && $scope.urlVars[5] != null && $scope.urlVars[5] != "");
                        if ($scope.urlVars[5] != null && $scope.urlVars[5] != "")
                            $scope.IdModel = $scope.urlVars[5].toString();
                        if ($scope.urlVars.length > 8) {
                            if ($scope.urlVars[6] != null && $scope.urlVars[6] != "")
                                $scope.IdModelVersion = $scope.urlVars[6].toString();
                            if ($scope.urlVars[7] != null && $scope.urlVars[7] != "")
                                $scope.SelectedLanguage = $scope.urlVars[7].toString();
                        }
                        else {
                            if ($scope.urlVars[6] != null && $scope.urlVars[6] != "") {
                                if ($scope.urlVars[6].length == 36) {
                                    $scope.IdModelVersion = $scope.urlVars[6].toString();
                                }
                                else {
                                    $scope.SelectedLanguage = $scope.urlVars[6].toString();
                                }
                            }
                        }
                    }
                    $scope.IsLockedBySomeone = false;
                    // if is view page
                    if ($scope.IsViewPage) {
                        if ($scope.IdModel != null && $scope.IdModel != "" && $scope.IdModel.length == 36) {
                            $scope.getModel($scope.IdModel, $scope.IdModelVersion, refreshTab);
                            // Get if the model is locked and by who
                            controller.CallGetAction($scope.serviceName, "IsLocked", $scope.IdModel, function (data) {
                                $scope.LockUser = data.Data;
                                if ($scope.LockUser != null) {
                                    $scope.IsLockedBySomeone = true;
                                }
                            });
                        }
                    }
                    else {
                        $scope.IsView = false;
                        // if is a split model// if is a new model
                        if ($scope.IsNew) {
                            var splitModelId = controller.GetParameterByName("splitId");
                            if (splitModelId.length > 0) {
                                $scope.SplitId = splitModelId;
                                $scope.getNewModelInstanceFromExistsModel(splitModelId);
                            }
                            else {
                                $scope.getNewModelInstance();
                            }
                        }
                        else {
                            // get model id from query string
                            $scope.IsView = false;
                            $scope.IsEdit = true;
                            $scope.lockModel($scope.IdModel, $scope.IdModelVersion);
                        }
                    }
                };
                // Launch the loadpage function to inizialize the page
                $scope.loadPage();
            }
            // function to manage discard changes button click event
            $scope.discardChanges = function (area, controllerName, action, id, queryString) {
                if (action === void 0) { action = null; }
                if (id === void 0) { id = null; }
                if (queryString === void 0) { queryString = null; }
                var url = controller.CreateUrl($scope.Language, area, controllerName, action, id, queryString);
                if ($scope.wasChanged) {
                    MessagesModule.MessagesController.prototype.ShowMessageBox(controller.GetMessage("MINERVA_DISCARD_QUESTION"), MessagesModule.MessageBoxType.YESNO, "", function () {
                        if (id == null)
                            id = $scope.IdModel;
                        $scope.forceExit = true;
                        // if is locked by the current user
                        if ((!$scope.IsNew || $scope.IsLockedByMe) && id != null)
                            $scope.unlockModel(id, function () {
                                controller.RedirectToUrl(url);
                            });
                        else
                            controller.RedirectToUrl(url);
                    });
                }
                else {
                    $scope.forceExit = true;
                    if (id == null)
                        id = $scope.IdModel;
                    // if is locked by the current user
                    if ((!$scope.IsNew || $scope.IsLockedByMe) && id != null)
                        $scope.unlockModel(id, function () {
                            controller.RedirectToUrl(url);
                        });
                    else
                        controller.RedirectToUrl(url);
                }
            };
            //* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 
            // * * * * * * * * * * * * * * * * * * * * *  Utilities  * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 
            //* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 
            // function to simulate string format method with a limit of 3 passed params 
            $scope.formatString = function (str, param1, param2, param3) {
                if (param2 === void 0) { param2 = null; }
                if (param3 === void 0) { param3 = null; }
                if (param1 != null) {
                    str = str.replace('{0}', param1);
                }
                if (param2 != null) {
                    str = str.replace('{1}', param2);
                }
                if (param3 != null) {
                    str = str.replace('{2}', param3);
                }
                return str;
            };
            // replace spaces from a string content for a seo scope
            $scope.getSeoContent = function (value) {
                if (value == null)
                    return "";
                return value.replace(" ", "_");
            };
            $scope.redirectToAction = function (language, area, controllerName, action, idModel, querystring, unlockModel) {
                if (action === void 0) { action = null; }
                if (idModel === void 0) { idModel = null; }
                if (querystring === void 0) { querystring = null; }
                if (unlockModel === void 0) { unlockModel = true; }
                if (idModel != null && idModel != "" && unlockModel) {
                    $scope.unlockModel(idModel, function () {
                        controller.RedirectToAction(language, area, controllerName, action, idModel, querystring);
                    });
                }
                else
                    controller.RedirectToAction(language, area, controllerName, action, idModel, querystring);
            };
        }
        return CrudBaseController;
    }(BaseModule.BaseController));
    BaseModule.CrudBaseController = CrudBaseController;
    var CrudExtendedController = (function (_super) {
        __extends(CrudExtendedController, _super);
        function CrudExtendedController($scope, $http, areaName, serviceName, refreshTab, loadPage) {
            if (loadPage === void 0) { loadPage = true; }
            _super.call(this, $scope, $http, areaName, serviceName, false);
            var controller = this;
            // Get and set if the seveso directive is expired
            $scope.IsSevesoDirectiveIIExpired = false;
            if ($scope.CheckIsSevesoDirectiveIIExpired != null && $scope.CheckIsSevesoDirectiveIIExpired) {
                controller.CallGetAction("Establishment", "IsSevesoDirectiveIIExpired", null, function (data) {
                    if (data.Data != null)
                        $scope.IsSevesoDirectiveIIExpired = data.Data;
                });
            }
            //* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * * * * * * * * * * 
            // * * * * * * * * * * * * * * * * * * * * *  CRUD and Model management functions * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 
            //* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * * * * * * * * * * 
            // method to inizialize a CRUD page properties
            $scope.loadPage = function (refreshTab) {
                if (refreshTab === void 0) { refreshTab = true; }
                /************** GIS RESOLUTION ********************/
                var getTransactionId = null;
                $scope.wasChanged = false;
                $scope.shouldBeSaved = false;
                $scope.forceExit = false;
                // set IsNew, IsViewPage, IsEdit properties and get model and in case version ids
                if ($scope.urlVars.length > 4) {
                    $scope.IsNew = ($scope.urlVars[5] == null);
                    $scope.IsViewPage = ($scope.urlVars[4] != null && $scope.urlVars[5] != null && $scope.urlVars[5] != "" && ($scope.urlVars[4].toString().toLowerCase().indexOf("view") > -1 || $scope.urlVars[4].toString().toLowerCase().indexOf("confidentiality") > -1));
                    $scope.IsEdit = ($scope.urlVars[4] != null && $scope.urlVars[4].toString().toLowerCase().indexOf("edit") > -1 && $scope.urlVars[5] != null && $scope.urlVars[5] != "");
                    if ($scope.urlVars[5] != null && $scope.urlVars[5] != "")
                        $scope.IdModel = $scope.urlVars[5].toString();
                    if ($scope.urlVars.length > 8) {
                        if ($scope.urlVars[6] != null && $scope.urlVars[6] != "")
                            $scope.IdModelVersion = $scope.urlVars[6].toString();
                        if ($scope.urlVars[7] != null && $scope.urlVars[7] != "")
                            $scope.SelectedLanguage = $scope.urlVars[7].toString();
                    }
                    else {
                        if ($scope.urlVars[6] != null && $scope.urlVars[6] != "") {
                            if ($scope.urlVars[6].length == 36) {
                                $scope.IdModelVersion = $scope.urlVars[6].toString();
                            }
                            else {
                                $scope.SelectedLanguage = $scope.urlVars[6].toString();
                            }
                        }
                    }
                }
                if (getTransactionId == null) {
                    getTransactionId = $scope.IdModel;
                }
                $scope.IsLockedBySomeone = false;
                // if is view page
                if ($scope.IsViewPage) {
                    if ($scope.IdModel != null && $scope.IdModel != "" && $scope.IdModel.length == 36) {
                        if ($scope.HasTransactions) {
                            // new instance of transaction manager class for view page
                            $scope.SMTransaction = new SMTransactionModule.SMTransactionController($http, $scope.serviceName, null, function () {
                                if ($scope.afterTransactionCallBack != null)
                                    $scope.afterTransactionCallBack();
                            });
                            $scope.SMTransaction.getTransactions(getTransactionId, $scope.SelectedLanguage, function (buttons, hasSaveButton) {
                                $scope.SMTransaction.SMButtons = buttons;
                                $scope.SMTransaction.HasSaveButton = hasSaveButton;
                            });
                        }
                        $scope.getModel($scope.IdModel, $scope.IdModelVersion, refreshTab);
                        // Get if the model is locked and by who
                        controller.CallGetAction($scope.serviceName, "IsLocked", $scope.IdModel, function (data) {
                            $scope.LockUser = data.Data;
                            if ($scope.LockUser != null) {
                                $scope.IsLockedBySomeone = true;
                            }
                        });
                    }
                }
                else {
                    $scope.IsView = false;
                    // if is a split model// if is a new model
                    if ($scope.IsNew) {
                        var splitModelId = controller.GetParameterByName("splitId");
                        if (splitModelId.length > 0) {
                            $scope.SplitId = splitModelId;
                            $scope.getNewModelInstanceFromExistsModel(splitModelId);
                        }
                        else {
                            if ($scope.HasTransactions) {
                                // new instance of transaction manager class for edit page and new model actions
                                $scope.SMTransaction = new SMTransactionModule.SMTransactionController($http, $scope.serviceName, $scope.addModel, function () {
                                    //$scope.loadPage();
                                    if ($scope.afterTransactionCallBack != null)
                                        $scope.afterTransactionCallBack();
                                }, function () { alert("Transaction Error"); });
                                $scope.SMTransaction.getTransactions(null, null, function (buttons, hasSaveButton) {
                                    $scope.SMTransaction.SMButtons = buttons;
                                    $scope.SMTransaction.HasSaveButton = hasSaveButton;
                                    // if there aren't state machine buttons or the save button return an error
                                    if ($scope.SMTransaction.SMButtons == null || $scope.SMTransaction.SMButtons.length < 1 || !hasSaveButton) {
                                        MessagesModule.MessagesController.prototype.ShowMessageBox(controller.GetMessage("CMS_CREATE_TRANSLATION_GET_BUTTONS_ERROR"), MessagesModule.MessageBoxType.ERROR, "ERROR");
                                        return;
                                    }
                                    else {
                                        // generate the empty scope model 
                                        $scope.getNewModelInstance();
                                    }
                                    $scope.SMTransaction.HasSaveButton = true;
                                });
                            }
                            else
                                $scope.getNewModelInstance();
                        }
                    }
                    else {
                        // get model id from query string
                        if ($scope.HasTransactions) {
                            // new instance of transaction manager class for edit page and edit model actions
                            $scope.SMTransaction = new SMTransactionModule.SMTransactionController($http, $scope.serviceName, $scope.updateModel, function () {
                                // $scope.loadPage();
                                if ($scope.afterTransactionCallBack != null)
                                    $scope.afterTransactionCallBack();
                            }, function () { alert("Transaction Error"); });
                            $scope.SMTransaction.getTransactions(getTransactionId, $scope.SelectedLanguage, function (buttons, hasSaveButton) {
                                $scope.SMTransaction.SMButtons = buttons;
                                $scope.SMTransaction.HasSaveButton = hasSaveButton;
                                // if there are state machine buttons
                                if ($scope.SMTransaction.SMButtons != null && $scope.SMTransaction.SMButtons.length > 0) {
                                    // lock the model
                                    $scope.lockModel($scope.IdModel, $scope.SelectedLanguage, refreshTab);
                                    // if there isn't a save button the form is in a view mode
                                    if (!hasSaveButton) {
                                        $scope.IsView = true;
                                        $scope.IsEdit = false;
                                    }
                                }
                                else {
                                    // if there isn't a save button the form is in a view mode
                                    $scope.getModel($scope.IdModel, $scope.IdModelVersion);
                                    // if the model is locked but there aren't action buttons, unlock it
                                    if ($scope.IsLockedByMe)
                                        $scope.unlockModel($scope.IdModel);
                                    // without buttons, view mode
                                    $scope.redirectToAction($scope.Language, $scope.areaName, $scope.serviceName, "view", $scope.IdModel);
                                }
                            });
                        }
                        else {
                            $scope.IsView = false;
                            $scope.IsEdit = true;
                            $scope.getModel($scope.IdModel, $scope.IdModelVersion);
                        }
                    }
                }
            };
            // Launch the loadpage function to inizialize the page
            if (loadPage)
                $scope.loadPage(refreshTab);
            // Check if a form tab has at least one control with invalid class
            $scope.tabIsValid = function (tabId) {
                //eval('("#" + tabId).validate();');
                var controlToValidate = "#" + tabId + " :input"; //, #" + tabId +" div[ws-tinymce]
                if ($(controlToValidate).hasClass("invalid") || $(controlToValidate).hasClass("ng-invalid"))
                    return false;
                return true;
            };
        }
        return CrudExtendedController;
    }(BaseModule.CrudBaseController));
    BaseModule.CrudExtendedController = CrudExtendedController;
    var CrudConfidentialController = (function (_super) {
        __extends(CrudConfidentialController, _super);
        function CrudConfidentialController($scope, $http, areaName, serviceName, refreshTab, loadPage) {
            if (loadPage === void 0) { loadPage = true; }
            // Initialize base class
            _super.call(this, $scope, $http, areaName, serviceName, refreshTab, loadPage);
            var controller = this;
            $scope.IsAlreadyConfidential = false;
            $scope.IsConfidentialityAddressSet = false;
            $scope.IsConfidentialityIdentificationSet = false;
            //* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * * * * * * * * * * 
            // * * * * * * * * * * * * * * * * * * * * *  Confidentiality management functions * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 
            //* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *  * * * * * * * * * * * * * * * * * * * 
            //
            function loadConfidentialityData(data) {
                $scope.ConfidentialityKeysArr = [];
                $scope.ConfidentialityModel = [];
                var i = 0;
                for (i = 0; i < data.Data.length; i++) {
                    if (data.Data[i].RelationId == null || data.Data[i].RelationId == "") {
                        $scope.ConfidentialityKeysArr.push(data.Data[i].FieldName);
                        $scope.ConfidentialityModel[data.Data[i].FieldName] = data.Data[i];
                        $scope.ConfidentialityModel[data.Data[i].FieldName].FinalFieldName = data.Data[i].FieldName;
                        if ($scope.ConfidentialityModel[data.Data[i].FieldName].JustificationKey == null
                            || $scope.ConfidentialityModel[data.Data[i].FieldName].JustificationKey == '')
                            $scope.ConfidentialityModel[data.Data[i].FieldName].JustificationKey = "NOT_CONFIDENTIAL";
                        if ($scope.ConfidentialityModel[data.Data[i].FieldName].IsConfidential)
                            $scope.IsAlreadyConfidential = true;
                    }
                    else {
                        $scope.ConfidentialityKeysArr.push(data.Data[i].FieldName + "__" + data.Data[i].RelationId);
                        $scope.ConfidentialityModel[data.Data[i].FieldName + "__" + data.Data[i].RelationId] = data.Data[i];
                        $scope.ConfidentialityModel[data.Data[i].FieldName + "__" + data.Data[i].RelationId].FinalFieldName = data.Data[i].FieldName + "__" + data.Data[i].RelationId;
                        if ($scope.ConfidentialityModel[data.Data[i].FieldName + "__" + data.Data[i].RelationId].JustificationKey == null
                            || $scope.ConfidentialityModel[data.Data[i].FieldName + "__" + data.Data[i].RelationId].JustificationKey == '')
                            $scope.ConfidentialityModel[data.Data[i].FieldName + "__" + data.Data[i].RelationId].JustificationKey = "NOT_CONFIDENTIAL";
                        if ($scope.ConfidentialityModel[data.Data[i].FieldName + "__" + data.Data[i].RelationId].IsConfidential)
                            $scope.IsAlreadyConfidential = true;
                    }
                }
                if ($scope.afterLoadConfCallBack != null) {
                    $scope.afterLoadConfCallBack();
                }
            }
            //
            $scope.getConfidentialityTable = function (modelId) {
                if (modelId == null || modelId == '')
                    modelId = $scope.IdModel;
                controller.CallGetAction($scope.serviceName, "GetConfidentiality", modelId, function (data) {
                    loadConfidentialityData(data);
                });
            };
            //
            $scope.saveConfidentiality = function (modelId) {
                if (modelId == null || modelId == '')
                    modelId = $scope.IdModel;
                var i = 0;
                var resultArray = [];
                for (i = 0; i < $scope.ConfidentialityKeysArr.length; i++) {
                    resultArray.push($scope.ConfidentialityModel[$scope.ConfidentialityKeysArr[i]]);
                }
                //var message = "Do you want to save confidentiality?";
                var message = controller.GetMessage("ESPIRS_CONFIRM_SAVE_CONFIDENTIALITY");
                //var title = "Save confidentiality";
                var title = controller.GetMessage("ESPIRS_TITLE_SAVE_CONFIDENTIALITY");
                MessagesModule.MessagesController.prototype.ShowMessageBox(message, MessagesModule.MessageBoxType.YESNO, title, function () {
                    controller.CallPutAction($scope.serviceName, "SaveConfidentiality", modelId, resultArray, function (data) {
                        loadConfidentialityData(data);
                        $scope.wasChanged = false;
                        $scope.shouldBeSaved = false;
                        $scope.safeApply();
                    });
                });
            };
            //
            $scope.discardConfidentiality = function (modelId) {
                if ($scope.wasChanged) {
                    //var message = "Do you want to discard changes?";
                    var message = controller.GetMessage("MINERVA_CONFIRM_EXIT_LOOSE_CHANGE");
                    //var title = "Discard confidentiality";
                    var title = controller.GetMessage("ESPIRS_TITLE_EXIT_CONFIDENTIALITY_LOOSE_CHANGE");
                    MessagesModule.MessagesController.prototype.ShowMessageBox(message, MessagesModule.MessageBoxType.YESNO, title, function () {
                        $scope.redirectToAction($scope.Language, $scope.areaName, $scope.serviceName, "view", modelId);
                    });
                }
                else {
                    $scope.redirectToAction($scope.Language, $scope.areaName, $scope.serviceName, "view", modelId);
                }
            };
            //
            $scope.exitConfidentialityMode = function (modelId) {
                //var title = "Exit from confidentiality mode";
                var title = controller.GetMessage("ESPIRS_TITLE_EXIT_FROM_CONFIDENTIALITY_LOOSE_CHANGE");
                if ($scope.wasChanged) {
                    // var message = "Do you want to exit CONFIDENTIALITY MODE and return to VIEW MODE? Do you want to discard changes?";
                    var message = controller.GetMessage("ESPIRS_CONFIRM_EXIT_FROM_EDIT_CONFIDENTIALITY_LOOSE_CHANGE");
                    MessagesModule.MessagesController.prototype.ShowMessageBox(message, MessagesModule.MessageBoxType.YESNO, title, function () {
                        $scope.redirectToAction($scope.Language, $scope.areaName, $scope.serviceName, "view", modelId);
                    });
                }
                else {
                    // var message = "Do you want to exit CONFIDENTIALITY MODE and return to VIEW MODE?";
                    //var message = controller.GetMessage("ESPIRS_CONFIRM_EXIT_FROM_EDIT_CONFIDENTIALITY");
                    // MessagesModule.MessagesController.prototype.ShowMessageBox(message, MessagesModule.MessageBoxType.YESNO, title, function () {
                    $scope.redirectToAction($scope.Language, $scope.areaName, $scope.serviceName, "view", modelId, null, false);
                }
            };
            //
            $scope.openConfidentialityComment = function (finalFieldName) {
                if (finalFieldName == null || finalFieldName == '')
                    return;
                var i = 0;
                var resultArray = [];
                for (i = 0; i < $scope.ConfidentialityKeysArr.length; i++) {
                    if (finalFieldName == $scope.ConfidentialityModel[$scope.ConfidentialityKeysArr[i]].FinalFieldName)
                        $scope.ConfidentialityModel[$scope.ConfidentialityKeysArr[i]].IsCommentOpened = !$scope.ConfidentialityModel[$scope.ConfidentialityKeysArr[i]].IsCommentOpened;
                    else
                        $scope.ConfidentialityModel[$scope.ConfidentialityKeysArr[i]].IsCommentOpened = false;
                }
            };
            //
            $scope.setRemoveConfidentiality = function (field) {
                if (field == null || field == '')
                    return;
                var message = "";
                var title = "";
                if (field.IsConfidential && field.JustificationKey != null && field.JustificationKey != '' && field.JustificationKey != 'NOT_CONFIDENTIAL')
                    return;
                field.IsConfidential = !field.IsConfidential;
                if (!field.IsConfidential) {
                    field.Comment = null;
                }
                $scope.wasChanged = true;
                $scope.safeApply();
            };
            //
            $scope.setConfidentialityGroup = function (fieldArray, fieldToCopy) {
                if (fieldArray == null || fieldArray.length <= 0)
                    return;
                var i = 0;
                //var message = "Do you want to propagate confidentiality?";
                //var title = "SET GROUP CONFIDENTIALITY";
                // MessagesModule.MessagesController.prototype.ShowMessageBox(message, MessagesModule.MessageBoxType.YESNO, title, function () {
                for (i = 0; i < fieldArray.length; i++) {
                    fieldArray[i].IsConfidential = fieldToCopy.IsConfidential;
                    fieldArray[i].JustificationKey = fieldToCopy.JustificationKey;
                    fieldArray[i].JustificationValue = fieldToCopy.JustificationValue;
                    fieldArray[i].Comment = fieldToCopy.Comment;
                }
                $scope.wasChanged = true;
                $scope.safeApply();
            };
            //
            $scope.isConfidentialitySubEmpty = function (relationArr, key) {
                if ($scope.ConfidentialityModel == null || relationArr == null || relationArr.length < 1)
                    return false;
                var i = 0;
                for (i = 0; i < relationArr.length; i++) {
                    if ($scope.ConfidentialityModel[key + relationArr[i].SubstanceId] != null
                        && $scope.ConfidentialityModel[key + relationArr[i].SubstanceId].IsConfidential)
                        return true;
                }
                return false;
            };
            //
            $scope.isConfidentialityNaceIndTypeEmpty = function (itRelationArr, itkey, naceRelationArr, nacekey) {
                if ($scope.ConfidentialityModel == null)
                    return false;
                var i = 0;
                if (itRelationArr != null && itRelationArr.length > 0) {
                    for (i = 0; i < itRelationArr.length; i++) {
                        if (itRelationArr[i] != null) {
                            if ($scope.ConfidentialityModel[itkey + itRelationArr[i].IndustryTypeId] != null
                                && $scope.ConfidentialityModel[itkey + itRelationArr[i].IndustryTypeId].IsConfidential)
                                return true;
                        }
                    }
                }
                if (naceRelationArr != null && naceRelationArr.length > 0) {
                    for (i = 0; i < naceRelationArr.length; i++) {
                        if (naceRelationArr[i] != null) {
                            if ($scope.ConfidentialityModel[nacekey + naceRelationArr[i].NaceId] != null
                                && $scope.ConfidentialityModel[nacekey + naceRelationArr[i].NaceId].IsConfidential)
                                return true;
                        }
                    }
                }
                return false;
            };
        }
        return CrudConfidentialController;
    }(BaseModule.CrudExtendedController));
    BaseModule.CrudConfidentialController = CrudConfidentialController;
})(BaseModule || (BaseModule = {}));

var SearchModule;
(function (SearchModule) {
    var GenericSearchController = (function (_super) {
        __extends(GenericSearchController, _super);
        // private httpService: any;
        function GenericSearchController($scope, $http, getSortFilters) {
            if (getSortFilters === void 0) { getSortFilters = false; }
            _super.call(this, $http, $scope);
            var controller = this;
            // Get language fron the url
            $scope.urlVars = window.location.toString().replace("http://", '').replace("https://", '').split("?")[0].split("/");
            $scope.Language = $scope.urlVars[1].toString();
            $scope.Language = $("#langCode").val();
            if ($scope.sortDirection == undefined || $scope.sortDirection == "")
                $scope.sortDirection = "ASC";
            ///////// Initialize record per page number and set the default value from web config ///////////////////
            $scope.RecordPerPage = 0;
            $scope.RecordPerPageList = [{
                PageNum: 5,
                sPageNum: "5"
            },
                {
                    PageNum: 10,
                    sPageNum: "10"
                }, {
                    PageNum: 20,
                    sPageNum: "20"
                },
                {
                    PageNum: 50,
                    sPageNum: "50"
                }
            ];
            $scope.RecordPerPage = $scope.RecordPerPageList[0].PageNum;
            try {
                $scope.DefaultRecordPerPage = parseInt($("#rpp_def").val());
                var i = 0;
                for (i = 0; i < $scope.RecordPerPageList.length; i++) {
                    if ($scope.DefaultRecordPerPage == $scope.RecordPerPageList[i].PageNum) {
                        $scope.RecordPerPage = $scope.RecordPerPageList[i].PageNum;
                        break;
                    }
                }
            }
            catch (e) { }
            $scope.paging = new PagingModule.PagingController();
            ///////// END Initialize record per page number and set the default value from web config ///////////////////
            // sort establishments
            $scope.sortSearch = function (sortKey, sortDir) {
                if (sortKey != null && sortKey != '')
                    $scope.sortSelectedItem = sortKey;
                if (sortDir != null && sortDir != '')
                    $scope.sortDirection = sortDir;
                $scope.safeApply();
                $scope.search($scope.paging.currentPage);
            };
            // show if hidden or hide if showed, a html control and put the class selected to it
            $scope.showHideDiv = function (id, elCallerId, showcallback, hidecallback) {
                if (elCallerId === void 0) { elCallerId = null; }
                if (showcallback === void 0) { showcallback = null; }
                if (hidecallback === void 0) { hidecallback = null; }
                if ($("#" + id).is(":hidden")) {
                    $("#" + id).show();
                    if (elCallerId != null) {
                        if (!$("#" + elCallerId).hasClass("selected")) {
                            $("#" + elCallerId).addClass("selected");
                        }
                    }
                    if (showcallback != null)
                        showcallback();
                }
                else {
                    $("#" + id).hide();
                    if (elCallerId != null) {
                        if ($("#" + elCallerId).hasClass("selected")) {
                            $("#" + elCallerId).removeClass("selected");
                        }
                    }
                    if (hidecallback != null)
                        hidecallback();
                }
            };
            // format a string, with max 3 parameters, as c# do
            $scope.formatString = function (str, param1, param2, param3) {
                if (param2 === void 0) { param2 = null; }
                if (param3 === void 0) { param3 = null; }
                if (param1 != null) {
                    str = str.replace('{0}', param1);
                }
                if (param2 != null) {
                    str = str.replace('{1}', param2);
                }
                if (param3 != null) {
                    str = str.replace('{2}', param3);
                }
                return str;
            };
            // angular scope safe apply 
            $scope.safeApply = function (fn) {
                var phase = this.$root.$$phase;
                if (phase == '$apply' || phase == '$digest') {
                    if (fn && (typeof (fn) === 'function')) {
                        fn();
                    }
                }
                else {
                    this.$apply(fn);
                }
            };
            $scope.redirectToAction = function (language, area, controllerName, action, idModel, querystring) {
                if (action === void 0) { action = null; }
                if (idModel === void 0) { idModel = null; }
                if (querystring === void 0) { querystring = null; }
                if (language == null || language == '') {
                    language = $("#langCode").val();
                }
                controller.RedirectToAction(language, area, controllerName, action, idModel, querystring);
            };
            $scope.isSortFiltersVisible = function () {
                return $scope.results != null && $scope.results.length > 0 && $scope.sortFields != null && $scope.sortFields.length > 0;
            };
            if ($scope.searchServiceName != null && $scope.searchServiceName != "undefined" && $scope.searchServiceName != "" && getSortFilters) {
                controller.CallGetAction($scope.searchServiceName, "GetSortFilters", null, function (data) {
                    $scope.sortFields = data.Data;
                });
            }
        }
        return GenericSearchController;
    }(HttpModule.HttpController));
    SearchModule.GenericSearchController = GenericSearchController;
})(SearchModule || (SearchModule = {}));
angular.module('minervaApp').controller("SearchModule.GenericSearchController", ["$scope", "$http", SearchModule.GenericSearchController]);

/// <reference path="../../typings/angularjs/angular.d.ts" />

var MapModule;
(function (MapModule) {
    // Controller to manage map multiresults
    var BingMapController = (function () {
        function BingMapController(bing_map_id, addresses, selAddrCallBack, $scope) {
            if ($scope === void 0) { $scope = null; }
            this.bingmapid = "";
            var controller = this;
            controller.bingmapid = bing_map_id;
            controller.Addresses = addresses;
            controller.SelectAddressCallBack = selAddrCallBack;
            controller.LoadMultiResultMap(controller.bingmapid, $scope);
        }
        // 
        BingMapController.prototype.SelectAddress = function (selected) {
            var controller = this;
            controller.CloseMapModal();
            controller.ResetSelection();
            if (controller.SelectAddressCallBack != null)
                controller.SelectAddressCallBack(selected);
        };
        //
        BingMapController.prototype.SelectMarkerOnMap = function (selected) {
            var controller = this;
            controller.ClearSelectedResults(controller);
            var controlId = "#result_" + selected.AddressId;
            $(controlId).css("background-color", "#fef3f3");
            SetCenterAndZoom(GetLatLong(selected.Latitude, selected.Longitude), 17);
        };
        //
        BingMapController.prototype.CloseMapModal = function () {
            $("#gismodal").hide();
        };
        //
        BingMapController.prototype.OpenMapModal = function () {
            $("#gismodal").show();
        };
        //
        BingMapController.prototype.ClearSelectedResults = function (controller) {
            var controlId = "";
            var i = 0;
            for (i = 0; i < controller.Addresses.length; i++) {
                controlId = "#result_" + i.toString();
                $(controlId).css("background-color", "#fff");
            }
        };
        //
        BingMapController.prototype.ResetSelection = function () {
            var controller = this;
            controller.LoadMapOptions(controller);
            controller.ClearSelectedResults(controller);
        };
        //
        BingMapController.prototype.LoadMapOptions = function (controller, reloadPins) {
            if (reloadPins === void 0) { reloadPins = false; }
            var maxLat = -85;
            var minLat = 85;
            var maxLon = -180;
            var minLon = 180;
            var i = 0;
            for (i = 0; i < controller.Addresses.length; i++) {
                maxLat = Math.max(controller.Addresses[i].Latitude, maxLat);
                minLat = Math.min(controller.Addresses[i].Latitude, minLat);
                maxLon = Math.max(controller.Addresses[i].Longitude, maxLon);
                minLon = Math.min(controller.Addresses[i].Longitude, minLon);
                if (reloadPins) {
                    controller.Addresses[i].AddressId = i.toString();
                    LoadPin(GetLatLong(controller.Addresses[i].Latitude, controller.Addresses[i].Longitude));
                }
            }
            this.center = GetLatLong((maxLat + minLat) / 2, (maxLon + minLon) / 2);
            var zoom1 = 0;
            var zoom2 = 0;
            if ((maxLon != minLon) && (maxLat != minLat)) {
                zoom1 = Math.log(360 / 256 * $("#" + controller.bingmapid).width() / (maxLon - minLon)) / Math.log(2);
                zoom2 = Math.log(180 / 256 * $("#" + controller.bingmapid).height() / (maxLat - minLat)) / Math.log(2);
            }
            this.zoom = Math.round(Math.min(zoom1, zoom2)) - 1;
            SetCenterAndZoom(this.center, this.zoom);
        };
        //
        BingMapController.prototype.LoadMultiResultMap = function (mapId, $scope) {
            if ($scope === void 0) { $scope = null; }
            var controller = this;
            var interval = setInterval(function () {
                if ((eval("typeof Microsoft.Maps.Map") != "undefined") && document.getElementById(mapId) != null) {
                    clearInterval(interval);
                    LoadMap(mapId, null, null, function () {
                        SetDraggable(false);
                        SetDoubleClickable(false);
                        controller.LoadMapOptions(controller, true);
                        if ($scope != null)
                            $scope.safeApply();
                    }, null, null, null);
                }
            }, 1000);
        };
        return BingMapController;
    }());
    MapModule.BingMapController = BingMapController;
    /*
    Manage Leaf Let map
    */
    var LeafLetMapController = (function () {
        function LeafLetMapController(map_id, addresses, selAddrCallBack, mMap, $scope) {
            if ($scope === void 0) { $scope = null; }
            this.mapid = "";
            this.selectedAddress = null;
            var controller = this;
            controller.mapid = map_id;
            controller.Addresses = addresses;
            controller.SelectAddressCallBack = selAddrCallBack;
            controller.minervaMap = mMap;
            controller.LoadMultiResultMap(controller.mapid, mMap, $scope);
        }
        LeafLetMapController.prototype.LoadMultiResultMap = function (mapId, minervaMap, $scope) {
            if ($scope === void 0) { $scope = null; }
            var controller = this;
            controller.minervaMap = minervaMap;
            var interval = setInterval(function () {
                if (document.getElementById(mapId) != null) {
                    clearInterval(interval);
                    minervaMap.reset(controller.mapid).then(function (map) {
                        controller.map = map;
                        controller.LoadMapOptions(controller, true, minervaMap);
                    });
                    if ($scope != null)
                        $scope.safeApply();
                }
            }, 1000);
        };
        LeafLetMapController.prototype.LoadMapOptions = function (controller, reloadPins, minervaMap) {
            if (reloadPins === void 0) { reloadPins = false; }
            var maxLat = -85;
            var minLat = 85;
            var maxLon = -180;
            var minLon = 180;
            var i = 0;
            var pins = [];
            for (i = 0; i < controller.Addresses.length; i++) {
                maxLat = Math.max(controller.Addresses[i].Latitude, maxLat);
                minLat = Math.min(controller.Addresses[i].Latitude, minLat);
                maxLon = Math.max(controller.Addresses[i].Longitude, maxLon);
                minLon = Math.min(controller.Addresses[i].Longitude, minLon);
                if (reloadPins) {
                    controller.Addresses[i].AddressId = i.toString();
                    var marker = minervaMap.createEstablismentMarker([controller.Addresses[i].Latitude, controller.Addresses[i].Longitude], controller.Addresses[i].FormattedAddress, controller.Addresses[i].FormattedAddress + '</br>'
                        + 'Lat: ' + controller.Addresses[i].Latitude
                        + ' - Lon: ' + controller.Addresses[i].Longitude, null);
                    marker.options.alt = i;
                    pins.push(marker);
                }
            }
            if (pins.length > 0)
                minervaMap.addPins(controller.mapid, pins, false, true, 12);
        };
        LeafLetMapController.prototype.SelectAddress = function (selected) {
            var controller = this;
            controller.CloseMapModal();
            controller.ResetSelection();
            if (controller.SelectAddressCallBack != null)
                controller.SelectAddressCallBack(selected);
        };
        LeafLetMapController.prototype.SelectMarkerOnMap = function (selected) {
            var controller = this;
            if (controller.selectedAddress) {
                controller.minervaMap.getPin(controller.mapid, controller.selectedAddress)
                    .setIcon(L.icon({ iconUrl: '/minerva/images/symmetricPin.png' }));
            }
            var selpin = controller.minervaMap.getPin(controller.mapid, selected.AddressId);
            if (selpin) {
                controller.selectedAddress = selected.AddressId;
                selpin.setIcon(L.icon({ iconUrl: '/minerva/images/symmetricPin_red.png' }));
            }
            controller.selectedAddress = selected.AddressId;
            controller.minervaMap.getMap(controller.mapid).then(function (map) {
                map.setView([selected.Latitude, selected.Longitude]);
            });
        };
        LeafLetMapController.prototype.CloseMapModal = function () {
            $("#gismodal").hide();
        };
        //
        LeafLetMapController.prototype.OpenMapModal = function () {
            $("#gismodal").show();
        };
        //
        /*ClearSelectedResults(controller) {
            var controlId = "";
            var i = 0;
            for (i = 0; i < controller.Addresses.length; i++) {
                controlId = "#result_" + i.toString();
                $(controlId).css("background-color", "#fff");
            }
        }*/
        //
        LeafLetMapController.prototype.ResetSelection = function () {
            var controller = this;
            controller.LoadMapOptions(controller, false, controller.minervaMap);
            /*controller.ClearSelectedResults(controller);*/
        };
        return LeafLetMapController;
    }());
    MapModule.LeafLetMapController = LeafLetMapController;
})(MapModule || (MapModule = {}));


//(function (n) { var t = function () { function n(n, t, i, r) { typeof r == "undefined" && (r = null), this.bingmapid = ""; var u = this; u.bingmapid = n, u.Addresses = t, u.SelectAddressCallBack = i, u.LoadMultiResultMap(u.bingmapid, r) } return n.prototype.SelectAddress = function (n) { var t = this; t.CloseMapModal(), t.ResetSelection(), t.SelectAddressCallBack != null && t.SelectAddressCallBack(n) }, n.prototype.SelectMarkerOnMap = function (n) { var t = this, i; t.ClearSelectedResults(t), i = "#result_" + n.AddressId, $(i).css("background-color", "#fef3f3"), SetCenterAndZoom(GetLatLong(n.Latitude, n.Longitude), 17) }, n.prototype.CloseMapModal = function () { $("#gismodal").hide() }, n.prototype.OpenMapModal = function () { $("#gismodal").show() }, n.prototype.ClearSelectedResults = function (n) { for (var i = "", t = 0, t = 0; t < n.Addresses.length; t++) i = "#result_" + t.toString(), $(i).css("background-color", "#fff") }, n.prototype.ResetSelection = function () { var n = this; n.LoadMapOptions(n), n.ClearSelectedResults(n) }, n.prototype.LoadMapOptions = function (n, t) { var o, s; typeof t == "undefined" && (t = !1); for (var r = -85, u = 85, f = -180, e = 180, i = 0, i = 0; i < n.Addresses.length; i++) r = Math.max(n.Addresses[i].Latitude, r), u = Math.min(n.Addresses[i].Latitude, u), f = Math.max(n.Addresses[i].Longitude, f), e = Math.min(n.Addresses[i].Longitude, e), t && (n.Addresses[i].AddressId = i.toString(), LoadPin(GetLatLong(n.Addresses[i].Latitude, n.Addresses[i].Longitude), n.Addresses[i].FormattedAddress)); this.center = GetLatLong((r + u) / 2, (f + e) / 2), o = 0, s = 0, f != e && r != u && (o = Math.log(360 / 256 * $("#" + n.bingmapid).width() / (f - e)) / Math.log(2), s = Math.log(180 / 256 * $("#" + n.bingmapid).height() / (r - u)) / Math.log(2)), this.zoom = Math.round(Math.min(o, s)) - 1, SetCenterAndZoom(this.center, this.zoom) }, n.prototype.LoadMultiResultMap = function (n, t) { typeof t == "undefined" && (t = null); var i = this, r = setInterval(function () { eval("typeof VEMap") != "undefined" && document.getElementById(n) != null && document.getElementById(n).attachEvent != undefined && (clearInterval(r), LoadMap(n, null, null, function () { SetDraggable(!1), SetDoubleClickable(!1), i.LoadMapOptions(i, !0), t != null && t.safeApply() }, null, null, null)) }, 10) }, n }(); n.MapController = t })(MapModule || (MapModule = {}));

var confirmationYesResponseFunction;
var confirmationNoResponseFunction;
var confirmationOkResponseFunction;
var confirmationCancelResponseFunction;
var confirmationOkwithCommentResponseFunction;
// expose message modal box button callbacks
function ConfirmYes() {
    $("#messagebox").hide();
    if (confirmationYesResponseFunction != null) {
        confirmationYesResponseFunction();
    }
}
function ConfirmNo() {
    $("#messagebox").hide();
    if (confirmationNoResponseFunction != null) {
        confirmationNoResponseFunction();
    }
}
function Ok() {
    $("#messagebox").hide();
    if (confirmationOkResponseFunction != null) {
        confirmationOkResponseFunction();
    }
}
function Cancel() {
    var comment = $("#messagebox #messagebox_comment").val();
    if (comment.replace(' ', '') != "") {
        $("#messagebox #messagebox_comment").val("");
    }
    $("#messagebox").hide();
    if (confirmationCancelResponseFunction != null) {
        confirmationCancelResponseFunction();
    }
}
function OkWithComment() {
    var comment = $("#messagebox #messagebox_comment").val();
    if (comment.replace(' ', '') == "") {
        $("#messagebox #comment_error").show();
        return;
    }
    else {
        $("#messagebox #messagebox_comment").val("");
        $("#messagebox").hide();
        if (confirmationOkwithCommentResponseFunction != null) {
            confirmationOkwithCommentResponseFunction(comment);
        }
        else {
            alert("ERROR: The confirmationOkwithCommentResponseFunction is not set!");
        }
    }
}
// This module manage the message modal box shared in all the application. 
// The behavior of the modal box (colors, icons, etc) and the buttons are completly managed using jquery functionalities and js callbacks  
var MessagesModule;
(function (MessagesModule) {
    (function (MessageBoxType) {
        MessageBoxType[MessageBoxType["MESSAGE"] = 0] = "MESSAGE";
        MessageBoxType[MessageBoxType["YESNOCANCEL"] = 1] = "YESNOCANCEL";
        MessageBoxType[MessageBoxType["YESNO"] = 2] = "YESNO";
        MessageBoxType[MessageBoxType["SUCCESS"] = 3] = "SUCCESS";
        MessageBoxType[MessageBoxType["ERROR"] = 4] = "ERROR";
        MessageBoxType[MessageBoxType["WARNING"] = 5] = "WARNING";
        MessageBoxType[MessageBoxType["SEND_COMMENT"] = 6] = "SEND_COMMENT";
        MessageBoxType[MessageBoxType["NOT_AUTHORIZED"] = 7] = "NOT_AUTHORIZED";
        MessageBoxType[MessageBoxType["XCSRF_ERROR"] = 8] = "XCSRF_ERROR";
    })(MessagesModule.MessageBoxType || (MessagesModule.MessageBoxType = {}));
    var MessageBoxType = MessagesModule.MessageBoxType;
    var MessagesController = (function () {
        function MessagesController() {
        }
        MessagesController.prototype.HideMessageBox = function () {
            this.ResetMessageBox('');
            $("#messagebox").hide();
        };
        MessagesController.prototype.ShowMessageBox = function (message, type, messageTitle, yesresponsecallback, noresponsecallback, okresponsecallback, cancelresponsecallback) {
            console.log("ShowMessageBox");
            if (messageTitle === void 0) { messageTitle = ""; }
            if (yesresponsecallback === void 0) { yesresponsecallback = null; }
            if (noresponsecallback === void 0) { noresponsecallback = null; }
            if (okresponsecallback === void 0) { okresponsecallback = null; }
            if (cancelresponsecallback === void 0) { cancelresponsecallback = null; }
            // register callbacks functions
            confirmationYesResponseFunction = yesresponsecallback;
            confirmationNoResponseFunction = noresponsecallback;
            confirmationCancelResponseFunction = cancelresponsecallback;
            confirmationOkResponseFunction = okresponsecallback;
            // Reset Message box fields and layout
            this.ResetMessageBox(message);
            // set the modal color and icon based on the message type
            switch (type) {
                case MessageBoxType.YESNOCANCEL:
                    $("#messagebox #yesnocancelButtons").show();
                    $("#messagebox #white_content").addClass("confirm");
                    $("#messagebox #white_content #header_icon img").attr('src', '/minerva/themes/minerva/images/icons/question_icon.png');
                    $("#messagebox #white_content #header_icon img").show();
                    break;
                case MessageBoxType.YESNO:
                    $("#messagebox #yesnoButtons").show();
                    $("#messagebox #white_content").addClass("confirm");
                    $("#messagebox #white_content #header_icon img").attr('src', '/minerva/themes/minerva/images/icons/question_icon.png');
                    $("#messagebox #white_content #header_icon img").show();
                    break;
                case MessageBoxType.ERROR:
                    $("#messagebox #okButtons").show();
                    $("#messagebox #white_content").addClass("error");
                    $("#messagebox #white_content #header_icon img").attr('src', '/minerva/themes/minerva/images/icons/error_icon.png');
                    $("#messagebox #white_content #header_icon img").show();
                    break;
                case MessageBoxType.MESSAGE:
                    $("#messagebox #okButtons").show();
                    break;
                case MessageBoxType.SUCCESS:
                    $("#messagebox #okButtons").show();
                    $("#messagebox #white_content").addClass("success");
                    $("#messagebox #white_content #header_icon img").attr('src', '/minerva/themes/minerva/images/icons/success_icon.png');
                    $("#messagebox #white_content #header_icon img").show();
                    break;
                case MessageBoxType.WARNING:
                    $("#messagebox #okButtons").show();
                    $("#messagebox #white_content").addClass("warning");
                    $("#messagebox #white_content #header_icon img").attr('src', '/minerva/themes/minerva/images/icons/warning_icon.png');
                    $("#messagebox #white_content #header_icon img").show();
                    break;
                case MessageBoxType.XCSRF_ERROR:
                    $("#messagebox #okButtons").show();
                    $("#messagebox #white_content").addClass("warning");
                    $("#messagebox #white_content #header_icon img").attr('src', '/minerva/themes/minerva/images/icons/warning_icon.png');
                    $("#messagebox #white_content #header_icon img").show();
                    break;
                case MessageBoxType.NOT_AUTHORIZED:
                    $("#messagebox #okButtons").show();
                    $("#messagebox #white_content").addClass("error");
                    $("#messagebox #white_content #header_icon img").attr('src', '/minerva/themes/minerva/images/icons/error_icon.png');
                    $("#messagebox #white_content #header_icon img").show();
                    break;
            }
            if (messageTitle != null && messageTitle != "") {
                if (!$("#messagebox #white_content #messagebox_title").hasClass("msg_title")) {
                    $("#messagebox #white_content #messagebox_title").addClass("msg_title");
                }
                $("#messagebox #white_content #message_title").html(messageTitle);
                $("#messagebox #white_content #message_title").show();
            }
            else {
                $("#messagebox #white_content #message_title").hide();
            }
            $("#messagebox").show();
        };
        MessagesController.prototype.ShowCommentMessageBox = function (message, messageTitle, okcommentresponsecallback, cancelresponsecallback) {
            if (cancelresponsecallback === void 0) { cancelresponsecallback = null; }
            // register callbacks functions
            confirmationOkwithCommentResponseFunction = okcommentresponsecallback;
            confirmationCancelResponseFunction = cancelresponsecallback;
            // Reset Message box fields and layout
            this.ResetMessageBox(message);
            $("#messagebox #okWithCommentButtons").show();
            $("#messagebox #comment").show();
            if (messageTitle != null && messageTitle != "") {
                //if (!$("#messagebox #white_content #messagebox_title").hasClass("msg_title")) {
                //    $("#messagebox #white_content #messagebox_title").addClass("msg_title");
                //}
                $("#messagebox #white_content #message_title").html(messageTitle);
                $("#messagebox #white_content #message_title").show();
            }
            else {
                $("#messagebox #white_content #message_title").hide();
            }
            $("#messagebox").show();
        };
        MessagesController.prototype.ResetMessageBox = function (message) {
            // set the message 
            $("#messagebox #messagebox_content").html(message);
            if (message != null && message != "")
                $("#messagebox #msg_container").show();
            else
                $("#messagebox #msg_container").hide();
            // remove all messagebox classes
            $("#messagebox #white_content").removeClass("confirm");
            $("#messagebox #white_content").removeClass("error");
            $("#messagebox #white_content").removeClass("success");
            $("#messagebox #white_content").removeClass("warning");
            // hide the messagebox top image
            $("#messagebox #white_content #header_icon img").hide();
            // hide the two button group types
            $("#messagebox #yesnocancelButtons").hide();
            $("#messagebox #yesnoButtons").hide();
            $("#messagebox #okButtons").hide();
            $("#messagebox #okWithCommentButtons").hide();
            // hide comment
            $("#messagebox #comment").hide();
            $("#messagebox #comment_error").hide();
            $("#messagebox #white_content #message_title").hide();
        };
        return MessagesController;
    }());
    MessagesModule.MessagesController = MessagesController;
})(MessagesModule || (MessagesModule = {}));

// Module used to manage the minerva pagination
// The page numbers are based on the following examples 
// Istance is based on a displayablePages setted to 5 and the returned total pages are 10 (or less than 10)
// - if the selected page is the 1
//   1 2 3 4 5 6 7 8 9 10
// Istance is based on a displayablePages setted to 5 and the returned total pages are 20
// - if the selected page is the 1
//   1 2 3 4 ... 20
// - if the selected page is the 4
//   1 2 3 4 5 6 ... 20
// - if the selected page is the 6
//   1 ... 4 5 6 7 8 ... 20
// - if the selected page is the 18
//   1 ... 15 16 17 18 19 20
// - if the selected page is the 20
//   1 ... 15 16 17 18 19 20 
var PagingModule;
(function (PagingModule) {
    var PagingController = (function () {
        function PagingController(displayPageNum) {
            if (displayPageNum === void 0) { displayPageNum = null; }
            this.currentPage = 1;
            // set the number of "page numbers" to show in addition to the first and the last page. 
            // Otherwise set default value(5).
            //if (displayPageNum == null)
            this.displayablePages = 5;
            //else
            //this.displayablePages = displayPageNum;
        }
        // calculate and set selectablePages object array 
        PagingController.prototype.GetSelectablePages = function () {
            this.selectablePages = [];
            this.showPageLess = false;
            this.showPageMore = false;
            if (this.maxPages <= 2)
                return null;
            var tempDispPages = this.maxPages - 2;
            if (tempDispPages < this.displayablePages) {
                if (tempDispPages == 1) {
                    this.selectablePages[0] = tempDispPages + 1;
                    return;
                }
                else {
                    var i = 0;
                    var startNumber = 2;
                    for (i = 0; i < tempDispPages; i++) {
                        this.selectablePages[i] = startNumber + i;
                    }
                    return;
                }
            }
            tempDispPages = this.displayablePages;
            var shiftLeft = Math.ceil(this.displayablePages / 2 * (-1));
            var shiftRight = (shiftLeft * (-1));
            if (this.maxPages >= this.displayablePages + 3) {
                if (this.currentPage >= this.displayablePages) {
                    this.showPageLess = true;
                }
            }
            if (this.maxPages >= this.displayablePages + 1) {
                if (this.currentPage <= this.maxPages - this.displayablePages + 1) {
                    this.showPageMore = true;
                }
            }
            if ((this.currentPage - this.displayablePages) < shiftLeft) {
                tempDispPages = tempDispPages + shiftLeft;
                shiftLeft = 0;
            }
            else {
                if ((this.currentPage + shiftLeft) <= 1)
                    shiftLeft = shiftLeft + 1;
            }
            if ((this.maxPages - this.currentPage) < shiftRight) {
                shiftLeft = (this.displayablePages * (-1)) + 1;
            }
            else {
                if ((this.currentPage + shiftRight) == this.maxPages)
                    shiftLeft = shiftLeft - 1;
            }
            var pager = this.currentPage;
            if (this.currentPage == 1)
                pager = pager + 1;
            if (this.currentPage == this.maxPages)
                pager = pager - 1;
            var startNumber = pager + shiftLeft;
            var i = 0;
            for (i = 0; i < tempDispPages; i++) {
                this.selectablePages[i] = startNumber + i;
            }
        };
        return PagingController;
    }());
    PagingModule.PagingController = PagingController;
})(PagingModule || (PagingModule = {}));

var SMTransactionModule;
(function (SMTransactionModule) {
    var SMTransactionController = (function (_super) {
        __extends(SMTransactionController, _super);
        function SMTransactionController($http, controllerName, saveCallBack, endTransactionCallBack, publishCallBack, onErrorCallBack, endGroupTransactionCallBack) {
            if (saveCallBack === void 0) { saveCallBack = null; }
            if (endTransactionCallBack === void 0) { endTransactionCallBack = null; }
            if (publishCallBack === void 0) { publishCallBack = null; }
            if (onErrorCallBack === void 0) { onErrorCallBack = null; }
            if (endGroupTransactionCallBack === void 0) { endGroupTransactionCallBack = null; }
            // this.httpService = $http;
            _super.call(this, $http);
            this.saveCall = saveCallBack;
            if (publishCallBack != null) {
                this.publishCall = publishCallBack;
            }
            else {
                this.publishCall = endTransactionCallBack;
            }
            this.endTransactionCall = endTransactionCallBack;
            this.onErrorCall = onErrorCallBack;
            this.endGroupTransactionCall = endGroupTransactionCallBack;
            this.controllerName = controllerName;
        }
        // call the async get transactions method
        SMTransactionController.prototype.getTransactions = function (idModule, lang, callBack) {
            if (idModule === void 0) { idModule = null; }
            if (lang === void 0) { lang = null; }
            if (callBack === void 0) { callBack = null; }
            this.CallGetActionByLang(this.controllerName, "GetTransactions", idModule, null, lang, function (data) {
                this.SMButtons = data.Data;
                this.HasSaveButton = false;
                if (this.SMButtons != null) {
                    var i = 0;
                    for (i = 0; i < this.SMButtons.length; i++) {
                        if (this.SMButtons[i].Type == Module.VMSMButtonType.SAVE) {
                            this.HasSaveButton = true;
                            break;
                        }
                    }
                }
                callBack(this.SMButtons, this.HasSaveButton);
            });
        };
        // call the async do transactions method
        SMTransactionController.prototype.doTransaction = function (idModule, button, lang, comment) {
            if (lang === void 0) { lang = null; }
            if (comment === void 0) { comment = null; }
            if (comment != null)
                button.comment = comment;  // comment = "comment=" + comment;
            if (lang != null) {
                this.CallPostActionByLang(this.controllerName, "DoTransaction", idModule, lang, button, this.endTransactionCall, null, comment);
                return;
            }
            this.CallPostAction(this.controllerName, "DoTransaction", idModule, button, this.endTransactionCall, null, comment);
        };
        // method called on state machine button click
        SMTransactionController.prototype.SMButtonClick = function (idModule, button, lang, showCommentForm) {
            if (lang === void 0) { lang = null; }
            if (showCommentForm === void 0) { showCommentForm = true; }
            switch (button.Type) {
                case Module.VMSMButtonType.SAVE:
                    this.saveCall();
                    break;
                case Module.VMSMButtonType.PUBLISHED:
                    this.publishCall();
                    break;
                case Module.VMSMButtonType.CHANGE_STATE:
                    var controller = this;
                    if (showCommentForm) {
                        MessagesModule.MessagesController.prototype.ShowCommentMessageBox(HttpModule.HttpController.prototype.GetMessage("MINERVA_LIFECYCLE_INSERT_COMMENT"), button.ActionText, function (comment) {
                            controller.doTransaction(idModule, button, lang, comment);
                        });
                    }
                    else {
                        controller.doTransaction(idModule, button, lang);
                    }
                    break;
                default:
                    break;
            }
        };
        SMTransactionController.prototype.SMGroupButtonClick = function (ModelIds, button, lang, showCommentForm) {
            if (lang === void 0) { lang = null; }
            if (showCommentForm === void 0) { showCommentForm = true; }
            switch (button.Type) {
                case Module.VMSMButtonType.CHANGE_STATE:
                    var controller = this;
                    if (showCommentForm) {
                        MessagesModule.MessagesController.prototype.ShowCommentMessageBox(HttpModule.HttpController.prototype.GetMessage("MINERVA_LIFECYCLE_INSERT_COMMENT"), button.ActionText, function (comment) {
                            controller.doGroupTransaction(ModelIds, button, lang, comment);
                        });
                    }
                    else {
                        controller.doGroupTransaction(ModelIds, button, lang);
                    }
                    break;
                default:
                    break;
            }
        };
        SMTransactionController.prototype.doGroupTransaction = function (ModelIds, button, lang, comment) {
            if (lang === void 0) { lang = null; }
            if (comment === void 0) { comment = null; }
            var qs = "actionText=" + button.ActionText;
            qs = qs + "&idtransaction=" + button.Idtransaction;
            qs = qs + "&incrementReleaseId=" + button.IncrementReleaseId;
            if (comment != null)
                qs = qs + "&comment=" + comment;
            if (lang != null) {
                this.CallPostActionByLang(this.controllerName, "DoGroupTransaction", null, lang, ModelIds, this.endGroupTransactionCall, null, qs);
                return;
            }
            this.CallPostAction(this.controllerName, "DoGroupTransaction", null, ModelIds, this.endGroupTransactionCall, null, qs);
        };
        // check if button is a save button type
        SMTransactionController.prototype.isSaveButton = function (button) {
            switch (button.Type) {
                case Module.VMSMButtonType.SAVE:
                    return true;
                default:
                    return false;
            }
            //return true;
        };
        // check if button is a delete button
        SMTransactionController.prototype.isDeleteButton = function (button) {
            return (button.Type == Module.VMSMButtonType.CHANGE_STATE && button.ActionId.indexOf('DELETE') > -1);
        };
        // check if button require a validation before the click event is enabled 
        SMTransactionController.prototype.isValidationRequested = function () {
            if (this.SMButtons != null) {
                var i = 0;
                for (i = 0; i < this.SMButtons.length; i++) {
                    if (this.SMButtons[i].IsValidationRequested) {
                        return true;
                    }
                }
            }
            return false;
        };
        // check if there is at least one transaction to do
        SMTransactionController.prototype.hasButtons = function () {
            if (this.SMButtons == null) {
                return false;
            }
            if (this.SMButtons.length < 1) {
                return false;
            }
            return true;
        };
        // check if there is at least one transactionale button
        SMTransactionController.prototype.hasTransactionButtons = function () {
            if (this.SMButtons != null) {
                var i = 0;
                for (i = 0; i < this.SMButtons.length; i++) {
                    if (this.SMButtons[i].Type != Module.VMSMButtonType.SAVE) {
                        return true;
                    }
                }
            }
            return false;
        };
        // get button color class 
        SMTransactionController.prototype.getSMButtonClass = function (button, formisinvalid) {
            if (this.isSaveButton(button)) {
                return 'btn-' + button.ColorClass;
            }
            /*if (button.IsValidationRequested && formisinvalid) {
                console.log(button.ActionText + " " + button.ColorClass + " ** DISA ** formisinvalid " + formisinvalid);
                return "disabled";
            } */
            return 'btn-' + button.ColorClass;
        };
        // check if the button is currently enabled
        SMTransactionController.prototype.isSMButtonEnabled = function (button, formisinvalid, wasChanged, customIsDisabledCondition, qmcheck) {
            if (customIsDisabledCondition === void 0) { customIsDisabledCondition = true; }
            if (qmcheck === void 0) { qmcheck = true; }
            if (this.isDeleteButton(button))
                return true;
            if (this.isSaveButton(button)) {
                if (wasChanged && !customIsDisabledCondition)
                    return !formisinvalid;
                else
                    return false;
            }
            else {
                if (!qmcheck)
                    return false;
                if (customIsDisabledCondition)
                    return false;
                if (button.IsValidationRequested) {
                    if (formisinvalid || wasChanged) {
                        return false;
                    }
                    else {
                        return true;
                    }
                }
                else if (wasChanged) {
                    return false;
                }
                else {
                    return true;
                }
            }
        };
        return SMTransactionController;
    }(HttpModule.HttpController));
    SMTransactionModule.SMTransactionController = SMTransactionController;
})(SMTransactionModule || (SMTransactionModule = {}));

var MinervaChartModule;
(function (MinervaChartModule) {
    // Parameters for axis description
    var AxisDesc = (function () {
        function AxisDesc(min, max) {
            this.Min = min;
            this.Max = max;
        }
        return AxisDesc;
    }());
    MinervaChartModule.AxisDesc = AxisDesc;
    var MinervaAxesGenerator = (function () {
        function MinervaAxesGenerator(Series) {
            if (Series != null && Series.length > 0) {
                this.Axes = [];
                // Y axis
                if (Series[0].MinY != 0.0 || Series[0].MaxY != 0.0)
                    this.Axes.push(new MinervaChartModule.AxisDesc(Series[0].MinY, Series[0].MaxY));
                // X axis
                if (Series[0].MinX != 0.0 || Series[0].MaxX != 0.0)
                    this.Axes.push(new MinervaChartModule.AxisDesc(Series[0].MinX, Series[0].MaxX));
            }
        }
        MinervaAxesGenerator.prototype.GetAxes = function () {
            return this.Axes;
        };
        return MinervaAxesGenerator;
    }());
    MinervaChartModule.MinervaAxesGenerator = MinervaAxesGenerator;
    // Wrap chart's configuration and data in one single object for simpler change notifications
    var MinervaChart = (function () {
        function MinervaChart(ChartDesc, ChartSeries, AxesGen) {
            if (AxesGen === void 0) { AxesGen = null; }
            // Both input parameters must be valid
            if (ChartDesc != null && ChartSeries != null) {
                this.Desc = ChartDesc;
                this.Series = ChartSeries;
                if (AxesGen != null)
                    this.AxesGenerator = AxesGen;
                else
                    this.AxesGenerator = new MinervaAxesGenerator(this.Series);
                this.Axes = this.AxesGenerator.GetAxes();
            }
            else {
                this.Desc = null;
                this.Series = null;
                this.AxesGenerator = null;
            }
            this.SelectedSeries = 0;
        }
        MinervaChart.prototype.IsValid = function () {
            if (this.Desc != null && this.Series != null && this.Series.length > 0)
                return true;
            return false;
        };
        return MinervaChart;
    }());
    MinervaChartModule.MinervaChart = MinervaChart;
})(MinervaChartModule || (MinervaChartModule = {}));;
/*! upload.js */app.directive('uploaddirective', function () {
    return {
        templateUrl: '/minerva/js/upload/uploadfile-directive.html',
        replace: true,
        restrict: 'EA',
        scope: {
            model: '=ngModel',
            uploadurl: '=',
            folder: '=',
            fileid: '=',
            parentid: '=',
            formid: '=',
            isdocumentlibrary: '=',
            workingareaid: '=',
            storeinsession: '=',
            showdnd: '=',
            formwidth: '=',
            isconf: '=',
            langid:'=',
            url: '=',
            zoom: '=',
            thumb: '=',
            icon: '=',
            naming: '=',
            text: '=',
            tooltiptext: '=',
            genericprofile: '=',
            onFileUploaded: '&'
        },
        controller: ['$scope', function ($scope) {
            $scope.returnOnUploadCallBack = function (data) {
                $scope.$apply(function () {
                    $scope.onFileUploaded({ newfile: data.result });
                });
            }
        }],
        link: function (scope, elm, attrs) {
            //scope.UploadFileId = eval(attrs.formid);
            /*returnOnUploadCallBack = function (data) {
                scope.onFileUploaded({ newfile: data.result });
            } */

            $(elm).find("#uploadButton").click(function (event) {
                var upld = $(elm).find("input.uploadInputField");
                upld.click();
                return false;
            });

            $(elm).fileupload({
                dataType: 'json',
                paramName: 'files[]',
                previewMaxWidth: 100,
                previewMaxHeight: 100,
                previewCrop: true,
                dropZone: $(elm),
                url: scope.uploadurl,
                progressall: function (e, data) {
                    scope.uploadInProgress = true;

                    var progress = parseInt(data.loaded / data.total * 100, 10);

                    $(elm).find('#progress').show();
                    var bar = $(elm).find('.bar');
                    $(bar).show();
                    $(bar).css(
                        'width',
                        progress + '%'
                    );

                    /*
                    var perc = $(this).find('.showProgressPercentage')[0];
                    if (perc != null) {
                        perc.innerHTML = progress + ' %';
                    }
                    
                    $scope.$apply(function () {
                        $scope.progress = progress;
                    }); */
                },

                done: function (e, data) {
                    $(elm).find('#progress').hide();
                    $(elm).find("#uploadButton").show();
                    scope.uploadInProgress = false;
                    console.log('done: ' + data);

                    data.filepath = data.fileInput[0].value;
                    scope.returnOnUploadCallBack(data);
                    //scope.onFileUploaded({ newfile: data.result });
                    /* setTimeout(function () {
                        if ($('#progress').length > 0) $('#progress')[0].id = 'progress_' + scope.formid;
                    }, 100);*/

                  
                }

                }).on('dragover dragenter', function () {
                    $(this).addClass('upload-is-dragover');
                })

                .on('dragleave dragend drop', function () {
                    $(this).removeClass('upload-is-dragover');
                })
                .on('fileuploaddrop', function (data) {
                    if ($(this).is(':visible')) {
                        return true;
                    } else {
                        return false;
                    }
                })
                .on('fileuploadadd', function (e, data) {
                    if (!scope.uploadInProgress) {

                     /*   if ($('#progress_' + scope.formid).length > 0)
                            $('#progress_' + scope.formid)[0].id = 'progress';
                            */
                        scope.uploadInProgress = true;
                        $(elm).find('#progress').show();
                        var bar = $(elm).find('.bar');
                        $(bar).css(
                            'width', '0%'
                        );

                        var fel = $(elm).children('#files');
                        $(fel).html('');
                        data.context = $('<div/>').appendTo(fel);
                        $.each(data.files, function (index, file) {
                            var node = $('<p/>')
                                    .append($('<span/>').text(file.name));
                            if (!index) {
                                node
                                    .append('<br>')
                            }
                            node.appendTo(data.context);
                        });
                    }
                })
                .on('fileuploadprocessalways', function (e, data) {
                    $(elm).find("#uploadButton").hide();
                   /*  var index = data.index,
                        file = data.files[index];
                       node = $(data.context.children()[index]);
                    if (file.preview) {
                        node
                            .prepend('<br>')
                            .prepend(file.preview);
                    }
                    if (file.error) {
                        node
                            .append('<br>')
                            .append(file.error);
                    }
                    if (index + 1 === data.files.length) {
                        data.context.find('button')
                            .text('Upload')
                            .prop('disabled', !!data.files.error);
                    }*/
                });
        }
    }
});
;
