File: D:/HostingSpaces/SBogers10/douven.komma.pro/wwwroot/js/kms/ui-bound.js
/*
* angular-ui-bootstrap
* http://angular-ui.github.io/bootstrap/
* Version: 0.12.1 - 2015-02-20
* License: MIT
*/
angular.module("ui.bootstrap",["ui.bootstrap.tpls","ui.bootstrap.transition","ui.bootstrap.collapse","ui.bootstrap.accordion","ui.bootstrap.alert","ui.bootstrap.bindHtml","ui.bootstrap.buttons","ui.bootstrap.carousel","ui.bootstrap.dateparser","ui.bootstrap.position","ui.bootstrap.datepicker","ui.bootstrap.dropdown","ui.bootstrap.modal","ui.bootstrap.pagination","ui.bootstrap.tooltip","ui.bootstrap.popover","ui.bootstrap.progressbar","ui.bootstrap.rating","ui.bootstrap.tabs","ui.bootstrap.timepicker","ui.bootstrap.typeahead"]),angular.module("ui.bootstrap.tpls",["template/accordion/accordion-group.html","template/accordion/accordion.html","template/alert/alert.html","template/carousel/carousel.html","template/carousel/slide.html","template/datepicker/datepicker.html","template/datepicker/day.html","template/datepicker/month.html","template/datepicker/popup.html","template/datepicker/year.html","template/modal/backdrop.html","template/modal/window.html","template/pagination/pager.html","template/pagination/pagination.html","template/tooltip/tooltip-html-unsafe-popup.html","template/tooltip/tooltip-popup.html","template/popover/popover.html","template/progressbar/bar.html","template/progressbar/progress.html","template/progressbar/progressbar.html","template/rating/rating.html","template/tabs/tab.html","template/tabs/tabset.html","template/timepicker/timepicker.html","template/typeahead/typeahead-match.html","template/typeahead/typeahead-popup.html"]),angular.module("ui.bootstrap.transition",[]).factory("$transition",["$q","$timeout","$rootScope",function(a,b,c){function d(a){for(var b in a)if(void 0!==f.style[b])return a[b]}var e=function(d,f,g){g=g||{};var h=a.defer(),i=e[g.animation?"animationEndEventName":"transitionEndEventName"],j=function(){c.$apply(function(){d.unbind(i,j),h.resolve(d)})};return i&&d.bind(i,j),b(function(){angular.isString(f)?d.addClass(f):angular.isFunction(f)?f(d):angular.isObject(f)&&d.css(f),i||h.resolve(d)}),h.promise.cancel=function(){i&&d.unbind(i,j),h.reject("Transition cancelled")},h.promise},f=document.createElement("trans"),g={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd",transition:"transitionend"},h={WebkitTransition:"webkitAnimationEnd",MozTransition:"animationend",OTransition:"oAnimationEnd",transition:"animationend"};return e.transitionEndEventName=d(g),e.animationEndEventName=d(h),e}]),angular.module("ui.bootstrap.collapse",["ui.bootstrap.transition"]).directive("collapse",["$transition",function(a){return{link:function(b,c,d){function e(b){function d(){j===e&&(j=void 0)}var e=a(c,b);return j&&j.cancel(),j=e,e.then(d,d),e}function f(){k?(k=!1,g()):(c.removeClass("collapse").addClass("collapsing"),e({height:c[0].scrollHeight+"px"}).then(g))}function g(){c.removeClass("collapsing"),c.addClass("collapse in"),c.css({height:"auto"})}function h(){if(k)k=!1,i(),c.css({height:0});else{c.css({height:c[0].scrollHeight+"px"});{c[0].offsetWidth}c.removeClass("collapse in").addClass("collapsing"),e({height:0}).then(i)}}function i(){c.removeClass("collapsing"),c.addClass("collapse")}var j,k=!0;b.$watch(d.collapse,function(a){a?h():f()})}}}]),angular.module("ui.bootstrap.accordion",["ui.bootstrap.collapse"]).constant("accordionConfig",{closeOthers:!0}).controller("AccordionController",["$scope","$attrs","accordionConfig",function(a,b,c){this.groups=[],this.closeOthers=function(d){var e=angular.isDefined(b.closeOthers)?a.$eval(b.closeOthers):c.closeOthers;e&&angular.forEach(this.groups,function(a){a!==d&&(a.isOpen=!1)})},this.addGroup=function(a){var b=this;this.groups.push(a),a.$on("$destroy",function(){b.removeGroup(a)})},this.removeGroup=function(a){var b=this.groups.indexOf(a);-1!==b&&this.groups.splice(b,1)}}]).directive("accordion",function(){return{restrict:"EA",controller:"AccordionController",transclude:!0,replace:!1,templateUrl:"template/accordion/accordion.html"}}).directive("accordionGroup",function(){return{require:"^accordion",restrict:"EA",transclude:!0,replace:!0,templateUrl:"template/accordion/accordion-group.html",scope:{heading:"@",isOpen:"=?",isDisabled:"=?"},controller:function(){this.setHeading=function(a){this.heading=a}},link:function(a,b,c,d){d.addGroup(a),a.$watch("isOpen",function(b){b&&d.closeOthers(a)}),a.toggleOpen=function(){a.isDisabled||(a.isOpen=!a.isOpen)}}}}).directive("accordionHeading",function(){return{restrict:"EA",transclude:!0,template:"",replace:!0,require:"^accordionGroup",link:function(a,b,c,d,e){d.setHeading(e(a,function(){}))}}}).directive("accordionTransclude",function(){return{require:"^accordionGroup",link:function(a,b,c,d){a.$watch(function(){return d[c.accordionTransclude]},function(a){a&&(b.html(""),b.append(a))})}}}),angular.module("ui.bootstrap.alert",[]).controller("AlertController",["$scope","$attrs",function(a,b){a.closeable="close"in b,this.close=a.close}]).directive("alert",function(){return{restrict:"EA",controller:"AlertController",templateUrl:"template/alert/alert.html",transclude:!0,replace:!0,scope:{type:"@",close:"&"}}}).directive("dismissOnTimeout",["$timeout",function(a){return{require:"alert",link:function(b,c,d,e){a(function(){e.close()},parseInt(d.dismissOnTimeout,10))}}}]),angular.module("ui.bootstrap.bindHtml",[]).directive("bindHtmlUnsafe",function(){return function(a,b,c){b.addClass("ng-binding").data("$binding",c.bindHtmlUnsafe),a.$watch(c.bindHtmlUnsafe,function(a){b.html(a||"")})}}),angular.module("ui.bootstrap.buttons",[]).constant("buttonConfig",{activeClass:"active",toggleEvent:"click"}).controller("ButtonsController",["buttonConfig",function(a){this.activeClass=a.activeClass||"active",this.toggleEvent=a.toggleEvent||"click"}]).directive("btnRadio",function(){return{require:["btnRadio","ngModel"],controller:"ButtonsController",link:function(a,b,c,d){var e=d[0],f=d[1];f.$render=function(){b.toggleClass(e.activeClass,angular.equals(f.$modelValue,a.$eval(c.btnRadio)))},b.bind(e.toggleEvent,function(){var d=b.hasClass(e.activeClass);(!d||angular.isDefined(c.uncheckable))&&a.$apply(function(){f.$setViewValue(d?null:a.$eval(c.btnRadio)),f.$render()})})}}}).directive("btnCheckbox",function(){return{require:["btnCheckbox","ngModel"],controller:"ButtonsController",link:function(a,b,c,d){function e(){return g(c.btnCheckboxTrue,!0)}function f(){return g(c.btnCheckboxFalse,!1)}function g(b,c){var d=a.$eval(b);return angular.isDefined(d)?d:c}var h=d[0],i=d[1];i.$render=function(){b.toggleClass(h.activeClass,angular.equals(i.$modelValue,e()))},b.bind(h.toggleEvent,function(){a.$apply(function(){i.$setViewValue(b.hasClass(h.activeClass)?f():e()),i.$render()})})}}}),angular.module("ui.bootstrap.carousel",["ui.bootstrap.transition"]).controller("CarouselController",["$scope","$timeout","$interval","$transition",function(a,b,c,d){function e(){f();var b=+a.interval;!isNaN(b)&&b>0&&(h=c(g,b))}function f(){h&&(c.cancel(h),h=null)}function g(){var b=+a.interval;i&&!isNaN(b)&&b>0?a.next():a.pause()}var h,i,j=this,k=j.slides=a.slides=[],l=-1;j.currentSlide=null;var m=!1;j.select=a.select=function(c,f){function g(){if(!m){if(j.currentSlide&&angular.isString(f)&&!a.noTransition&&c.$element){c.$element.addClass(f);{c.$element[0].offsetWidth}angular.forEach(k,function(a){angular.extend(a,{direction:"",entering:!1,leaving:!1,active:!1})}),angular.extend(c,{direction:f,active:!0,entering:!0}),angular.extend(j.currentSlide||{},{direction:f,leaving:!0}),a.$currentTransition=d(c.$element,{}),function(b,c){a.$currentTransition.then(function(){h(b,c)},function(){h(b,c)})}(c,j.currentSlide)}else h(c,j.currentSlide);j.currentSlide=c,l=i,e()}}function h(b,c){angular.extend(b,{direction:"",active:!0,leaving:!1,entering:!1}),angular.extend(c||{},{direction:"",active:!1,leaving:!1,entering:!1}),a.$currentTransition=null}var i=k.indexOf(c);void 0===f&&(f=i>l?"next":"prev"),c&&c!==j.currentSlide&&(a.$currentTransition?(a.$currentTransition.cancel(),b(g)):g())},a.$on("$destroy",function(){m=!0}),j.indexOfSlide=function(a){return k.indexOf(a)},a.next=function(){var b=(l+1)%k.length;return a.$currentTransition?void 0:j.select(k[b],"next")},a.prev=function(){var b=0>l-1?k.length-1:l-1;return a.$currentTransition?void 0:j.select(k[b],"prev")},a.isActive=function(a){return j.currentSlide===a},a.$watch("interval",e),a.$on("$destroy",f),a.play=function(){i||(i=!0,e())},a.pause=function(){a.noPause||(i=!1,f())},j.addSlide=function(b,c){b.$element=c,k.push(b),1===k.length||b.active?(j.select(k[k.length-1]),1==k.length&&a.play()):b.active=!1},j.removeSlide=function(a){var b=k.indexOf(a);k.splice(b,1),k.length>0&&a.active?j.select(b>=k.length?k[b-1]:k[b]):l>b&&l--}}]).directive("carousel",[function(){return{restrict:"EA",transclude:!0,replace:!0,controller:"CarouselController",require:"carousel",templateUrl:"template/carousel/carousel.html",scope:{interval:"=",noTransition:"=",noPause:"="}}}]).directive("slide",function(){return{require:"^carousel",restrict:"EA",transclude:!0,replace:!0,templateUrl:"template/carousel/slide.html",scope:{active:"=?"},link:function(a,b,c,d){d.addSlide(a,b),a.$on("$destroy",function(){d.removeSlide(a)}),a.$watch("active",function(b){b&&d.select(a)})}}}),angular.module("ui.bootstrap.dateparser",[]).service("dateParser",["$locale","orderByFilter",function(a,b){function c(a){var c=[],d=a.split("");return angular.forEach(e,function(b,e){var f=a.indexOf(e);if(f>-1){a=a.split(""),d[f]="("+b.regex+")",a[f]="$";for(var g=f+1,h=f+e.length;h>g;g++)d[g]="",a[g]="$";a=a.join(""),c.push({index:f,apply:b.apply})}}),{regex:new RegExp("^"+d.join("")+"$"),map:b(c,"index")}}function d(a,b,c){return 1===b&&c>28?29===c&&(a%4===0&&a%100!==0||a%400===0):3===b||5===b||8===b||10===b?31>c:!0}this.parsers={};var e={yyyy:{regex:"\\d{4}",apply:function(a){this.year=+a}},yy:{regex:"\\d{2}",apply:function(a){this.year=+a+2e3}},y:{regex:"\\d{1,4}",apply:function(a){this.year=+a}},MMMM:{regex:a.DATETIME_FORMATS.MONTH.join("|"),apply:function(b){this.month=a.DATETIME_FORMATS.MONTH.indexOf(b)}},MMM:{regex:a.DATETIME_FORMATS.SHORTMONTH.join("|"),apply:function(b){this.month=a.DATETIME_FORMATS.SHORTMONTH.indexOf(b)}},MM:{regex:"0[1-9]|1[0-2]",apply:function(a){this.month=a-1}},M:{regex:"[1-9]|1[0-2]",apply:function(a){this.month=a-1}},dd:{regex:"[0-2][0-9]{1}|3[0-1]{1}",apply:function(a){this.date=+a}},d:{regex:"[1-2]?[0-9]{1}|3[0-1]{1}",apply:function(a){this.date=+a}},EEEE:{regex:a.DATETIME_FORMATS.DAY.join("|")},EEE:{regex:a.DATETIME_FORMATS.SHORTDAY.join("|")}};this.parse=function(b,e){if(!angular.isString(b)||!e)return b;e=a.DATETIME_FORMATS[e]||e,this.parsers[e]||(this.parsers[e]=c(e));var f=this.parsers[e],g=f.regex,h=f.map,i=b.match(g);if(i&&i.length){for(var j,k={year:1900,month:0,date:1,hours:0},l=1,m=i.length;m>l;l++){var n=h[l-1];n.apply&&n.apply.call(k,i[l])}return d(k.year,k.month,k.date)&&(j=new Date(k.year,k.month,k.date,k.hours)),j}}}]),angular.module("ui.bootstrap.position",[]).factory("$position",["$document","$window",function(a,b){function c(a,c){return a.currentStyle?a.currentStyle[c]:b.getComputedStyle?b.getComputedStyle(a)[c]:a.style[c]}function d(a){return"static"===(c(a,"position")||"static")}var e=function(b){for(var c=a[0],e=b.offsetParent||c;e&&e!==c&&d(e);)e=e.offsetParent;return e||c};return{position:function(b){var c=this.offset(b),d={top:0,left:0},f=e(b[0]);f!=a[0]&&(d=this.offset(angular.element(f)),d.top+=f.clientTop-f.scrollTop,d.left+=f.clientLeft-f.scrollLeft);var g=b[0].getBoundingClientRect();return{width:g.width||b.prop("offsetWidth"),height:g.height||b.prop("offsetHeight"),top:c.top-d.top,left:c.left-d.left}},offset:function(c){var d=c[0].getBoundingClientRect();return{width:d.width||c.prop("offsetWidth"),height:d.height||c.prop("offsetHeight"),top:d.top+(b.pageYOffset||a[0].documentElement.scrollTop),left:d.left+(b.pageXOffset||a[0].documentElement.scrollLeft)}},positionElements:function(a,b,c,d){var e,f,g,h,i=c.split("-"),j=i[0],k=i[1]||"center";e=d?this.offset(a):this.position(a),f=b.prop("offsetWidth"),g=b.prop("offsetHeight");var l={center:function(){return e.left+e.width/2-f/2},left:function(){return e.left},right:function(){return e.left+e.width}},m={center:function(){return e.top+e.height/2-g/2},top:function(){return e.top},bottom:function(){return e.top+e.height}};switch(j){case"right":h={top:m[k](),left:l[j]()};break;case"left":h={top:m[k](),left:e.left-f};break;case"bottom":h={top:m[j](),left:l[k]()};break;default:h={top:e.top-g,left:l[k]()}}return h}}}]),angular.module("ui.bootstrap.datepicker",["ui.bootstrap.dateparser","ui.bootstrap.position"]).constant("datepickerConfig",{formatDay:"dd",formatMonth:"MMMM",formatYear:"yyyy",formatDayHeader:"EEE",formatDayTitle:"MMMM yyyy",formatMonthTitle:"yyyy",datepickerMode:"day",minMode:"day",maxMode:"year",showWeeks:!0,startingDay:0,yearRange:20,minDate:null,maxDate:null}).controller("DatepickerController",["$scope","$attrs","$parse","$interpolate","$timeout","$log","dateFilter","datepickerConfig",function(a,b,c,d,e,f,g,h){var i=this,j={$setViewValue:angular.noop};this.modes=["day","month","year"],angular.forEach(["formatDay","formatMonth","formatYear","formatDayHeader","formatDayTitle","formatMonthTitle","minMode","maxMode","showWeeks","startingDay","yearRange"],function(c,e){i[c]=angular.isDefined(b[c])?8>e?d(b[c])(a.$parent):a.$parent.$eval(b[c]):h[c]}),angular.forEach(["minDate","maxDate"],function(d){b[d]?a.$parent.$watch(c(b[d]),function(a){i[d]=a?new Date(a):null,i.refreshView()}):i[d]=h[d]?new Date(h[d]):null}),a.datepickerMode=a.datepickerMode||h.datepickerMode,a.uniqueId="datepicker-"+a.$id+"-"+Math.floor(1e4*Math.random()),this.activeDate=angular.isDefined(b.initDate)?a.$parent.$eval(b.initDate):new Date,a.isActive=function(b){return 0===i.compare(b.date,i.activeDate)?(a.activeDateId=b.uid,!0):!1},this.init=function(a){j=a,j.$render=function(){i.render()}},this.render=function(){if(j.$modelValue){var a=new Date(j.$modelValue),b=!isNaN(a);b?this.activeDate=a:f.error('Datepicker directive: "ng-model" value must be a Date object, a number of milliseconds since 01.01.1970 or a string representing an RFC2822 or ISO 8601 date.'),j.$setValidity("date",b)}this.refreshView()},this.refreshView=function(){if(this.element){this._refreshView();var a=j.$modelValue?new Date(j.$modelValue):null;j.$setValidity("date-disabled",!a||this.element&&!this.isDisabled(a))}},this.createDateObject=function(a,b){var c=j.$modelValue?new Date(j.$modelValue):null;return{date:a,label:g(a,b),selected:c&&0===this.compare(a,c),disabled:this.isDisabled(a),current:0===this.compare(a,new Date)}},this.isDisabled=function(c){return this.minDate&&this.compare(c,this.minDate)<0||this.maxDate&&this.compare(c,this.maxDate)>0||b.dateDisabled&&a.dateDisabled({date:c,mode:a.datepickerMode})},this.split=function(a,b){for(var c=[];a.length>0;)c.push(a.splice(0,b));return c},a.select=function(b){if(a.datepickerMode===i.minMode){var c=j.$modelValue?new Date(j.$modelValue):new Date(0,0,0,0,0,0,0);c.setFullYear(b.getFullYear(),b.getMonth(),b.getDate()),j.$setViewValue(c),j.$render()}else i.activeDate=b,a.datepickerMode=i.modes[i.modes.indexOf(a.datepickerMode)-1]},a.move=function(a){var b=i.activeDate.getFullYear()+a*(i.step.years||0),c=i.activeDate.getMonth()+a*(i.step.months||0);i.activeDate.setFullYear(b,c,1),i.refreshView()},a.toggleMode=function(b){b=b||1,a.datepickerMode===i.maxMode&&1===b||a.datepickerMode===i.minMode&&-1===b||(a.datepickerMode=i.modes[i.modes.indexOf(a.datepickerMode)+b])},a.keys={13:"enter",32:"space",33:"pageup",34:"pagedown",35:"end",36:"home",37:"left",38:"up",39:"right",40:"down"};var k=function(){e(function(){i.element[0].focus()},0,!1)};a.$on("datepicker.focus",k),a.keydown=function(b){var c=a.keys[b.which];if(c&&!b.shiftKey&&!b.altKey)if(b.preventDefault(),b.stopPropagation(),"enter"===c||"space"===c){if(i.isDisabled(i.activeDate))return;a.select(i.activeDate),k()}else!b.ctrlKey||"up"!==c&&"down"!==c?(i.handleKeyDown(c,b),i.refreshView()):(a.toggleMode("up"===c?1:-1),k())}}]).directive("datepicker",function(){return{restrict:"EA",replace:!0,templateUrl:"template/datepicker/datepicker.html",scope:{datepickerMode:"=?",dateDisabled:"&"},require:["datepicker","?^ngModel"],controller:"DatepickerController",link:function(a,b,c,d){var e=d[0],f=d[1];f&&e.init(f)}}}).directive("daypicker",["dateFilter",function(a){return{restrict:"EA",replace:!0,templateUrl:"template/datepicker/day.html",require:"^datepicker",link:function(b,c,d,e){function f(a,b){return 1!==b||a%4!==0||a%100===0&&a%400!==0?i[b]:29}function g(a,b){var c=new Array(b),d=new Date(a),e=0;for(d.setHours(12);b>e;)c[e++]=new Date(d),d.setDate(d.getDate()+1);return c}function h(a){var b=new Date(a);b.setDate(b.getDate()+4-(b.getDay()||7));var c=b.getTime();return b.setMonth(0),b.setDate(1),Math.floor(Math.round((c-b)/864e5)/7)+1}b.showWeeks=e.showWeeks,e.step={months:1},e.element=c;var i=[31,28,31,30,31,30,31,31,30,31,30,31];e._refreshView=function(){var c=e.activeDate.getFullYear(),d=e.activeDate.getMonth(),f=new Date(c,d,1),i=e.startingDay-f.getDay(),j=i>0?7-i:-i,k=new Date(f);j>0&&k.setDate(-j+1);for(var l=g(k,42),m=0;42>m;m++)l[m]=angular.extend(e.createDateObject(l[m],e.formatDay),{secondary:l[m].getMonth()!==d,uid:b.uniqueId+"-"+m});b.labels=new Array(7);for(var n=0;7>n;n++)b.labels[n]={abbr:a(l[n].date,e.formatDayHeader),full:a(l[n].date,"EEEE")};if(b.title=a(e.activeDate,e.formatDayTitle),b.rows=e.split(l,7),b.showWeeks){b.weekNumbers=[];for(var o=h(b.rows[0][0].date),p=b.rows.length;b.weekNumbers.push(o++)<p;);}},e.compare=function(a,b){return new Date(a.getFullYear(),a.getMonth(),a.getDate())-new Date(b.getFullYear(),b.getMonth(),b.getDate())},e.handleKeyDown=function(a){var b=e.activeDate.getDate();if("left"===a)b-=1;else if("up"===a)b-=7;else if("right"===a)b+=1;else if("down"===a)b+=7;else if("pageup"===a||"pagedown"===a){var c=e.activeDate.getMonth()+("pageup"===a?-1:1);e.activeDate.setMonth(c,1),b=Math.min(f(e.activeDate.getFullYear(),e.activeDate.getMonth()),b)}else"home"===a?b=1:"end"===a&&(b=f(e.activeDate.getFullYear(),e.activeDate.getMonth()));e.activeDate.setDate(b)},e.refreshView()}}}]).directive("monthpicker",["dateFilter",function(a){return{restrict:"EA",replace:!0,templateUrl:"template/datepicker/month.html",require:"^datepicker",link:function(b,c,d,e){e.step={years:1},e.element=c,e._refreshView=function(){for(var c=new Array(12),d=e.activeDate.getFullYear(),f=0;12>f;f++)c[f]=angular.extend(e.createDateObject(new Date(d,f,1),e.formatMonth),{uid:b.uniqueId+"-"+f});b.title=a(e.activeDate,e.formatMonthTitle),b.rows=e.split(c,3)},e.compare=function(a,b){return new Date(a.getFullYear(),a.getMonth())-new Date(b.getFullYear(),b.getMonth())},e.handleKeyDown=function(a){var b=e.activeDate.getMonth();if("left"===a)b-=1;else if("up"===a)b-=3;else if("right"===a)b+=1;else if("down"===a)b+=3;else if("pageup"===a||"pagedown"===a){var c=e.activeDate.getFullYear()+("pageup"===a?-1:1);e.activeDate.setFullYear(c)}else"home"===a?b=0:"end"===a&&(b=11);e.activeDate.setMonth(b)},e.refreshView()}}}]).directive("yearpicker",["dateFilter",function(){return{restrict:"EA",replace:!0,templateUrl:"template/datepicker/year.html",require:"^datepicker",link:function(a,b,c,d){function e(a){return parseInt((a-1)/f,10)*f+1}var f=d.yearRange;d.step={years:f},d.element=b,d._refreshView=function(){for(var b=new Array(f),c=0,g=e(d.activeDate.getFullYear());f>c;c++)b[c]=angular.extend(d.createDateObject(new Date(g+c,0,1),d.formatYear),{uid:a.uniqueId+"-"+c});a.title=[b[0].label,b[f-1].label].join(" - "),a.rows=d.split(b,5)},d.compare=function(a,b){return a.getFullYear()-b.getFullYear()},d.handleKeyDown=function(a){var b=d.activeDate.getFullYear();"left"===a?b-=1:"up"===a?b-=5:"right"===a?b+=1:"down"===a?b+=5:"pageup"===a||"pagedown"===a?b+=("pageup"===a?-1:1)*d.step.years:"home"===a?b=e(d.activeDate.getFullYear()):"end"===a&&(b=e(d.activeDate.getFullYear())+f-1),d.activeDate.setFullYear(b)},d.refreshView()}}}]).constant("datepickerPopupConfig",{datepickerPopup:"yyyy-MM-dd",currentText:"Today",clearText:"Clear",closeText:"Done",closeOnDateSelection:!0,appendToBody:!1,showButtonBar:!0}).directive("datepickerPopup",["$compile","$parse","$document","$position","dateFilter","dateParser","datepickerPopupConfig",function(a,b,c,d,e,f,g){return{restrict:"EA",require:"ngModel",scope:{isOpen:"=?",currentText:"@",clearText:"@",closeText:"@",dateDisabled:"&"},link:function(h,i,j,k){function l(a){return a.replace(/([A-Z])/g,function(a){return"-"+a.toLowerCase()})}function m(a){if(a){if(angular.isDate(a)&&!isNaN(a))return k.$setValidity("date",!0),a;if(angular.isString(a)){var b=f.parse(a,n)||new Date(a);return isNaN(b)?void k.$setValidity("date",!1):(k.$setValidity("date",!0),b)}return void k.$setValidity("date",!1)}return k.$setValidity("date",!0),null}var n,o=angular.isDefined(j.closeOnDateSelection)?h.$parent.$eval(j.closeOnDateSelection):g.closeOnDateSelection,p=angular.isDefined(j.datepickerAppendToBody)?h.$parent.$eval(j.datepickerAppendToBody):g.appendToBody;h.showButtonBar=angular.isDefined(j.showButtonBar)?h.$parent.$eval(j.showButtonBar):g.showButtonBar,h.getText=function(a){return h[a+"Text"]||g[a+"Text"]},j.$observe("datepickerPopup",function(a){n=a||g.datepickerPopup,k.$render()});var q=angular.element("<div datepicker-popup-wrap><div datepicker></div></div>");q.attr({"ng-model":"date","ng-change":"dateSelection()"});var r=angular.element(q.children()[0]);j.datepickerOptions&&angular.forEach(h.$parent.$eval(j.datepickerOptions),function(a,b){r.attr(l(b),a)}),h.watchData={},angular.forEach(["minDate","maxDate","datepickerMode"],function(a){if(j[a]){var c=b(j[a]);if(h.$parent.$watch(c,function(b){h.watchData[a]=b}),r.attr(l(a),"watchData."+a),"datepickerMode"===a){var d=c.assign;h.$watch("watchData."+a,function(a,b){a!==b&&d(h.$parent,a)})}}}),j.dateDisabled&&r.attr("date-disabled","dateDisabled({ date: date, mode: mode })"),k.$parsers.unshift(m),h.dateSelection=function(a){angular.isDefined(a)&&(h.date=a),k.$setViewValue(h.date),k.$render(),o&&(h.isOpen=!1,i[0].focus())},i.bind("input change keyup",function(){h.$apply(function(){h.date=k.$modelValue})}),k.$render=function(){var a=k.$viewValue?e(k.$viewValue,n):"";i.val(a),h.date=m(k.$modelValue)};var s=function(a){h.isOpen&&a.target!==i[0]&&h.$apply(function(){h.isOpen=!1})},t=function(a){h.keydown(a)};i.bind("keydown",t),h.keydown=function(a){27===a.which?(a.preventDefault(),a.stopPropagation(),h.close()):40!==a.which||h.isOpen||(h.isOpen=!0)},h.$watch("isOpen",function(a){a?(h.$broadcast("datepicker.focus"),h.position=p?d.offset(i):d.position(i),h.position.top=h.position.top+i.prop("offsetHeight"),c.bind("click",s)):c.unbind("click",s)}),h.select=function(a){if("today"===a){var b=new Date;angular.isDate(k.$modelValue)?(a=new Date(k.$modelValue),a.setFullYear(b.getFullYear(),b.getMonth(),b.getDate())):a=new Date(b.setHours(0,0,0,0))}h.dateSelection(a)},h.close=function(){h.isOpen=!1,i[0].focus()};var u=a(q)(h);q.remove(),p?c.find("body").append(u):i.after(u),h.$on("$destroy",function(){u.remove(),i.unbind("keydown",t),c.unbind("click",s)})}}}]).directive("datepickerPopupWrap",function(){return{restrict:"EA",replace:!0,transclude:!0,templateUrl:"template/datepicker/popup.html",link:function(a,b){b.bind("click",function(a){a.preventDefault(),a.stopPropagation()})}}}),angular.module("ui.bootstrap.dropdown",[]).constant("dropdownConfig",{openClass:"open"}).service("dropdownService",["$document",function(a){var b=null;this.open=function(e){b||(a.bind("click",c),a.bind("keydown",d)),b&&b!==e&&(b.isOpen=!1),b=e},this.close=function(e){b===e&&(b=null,a.unbind("click",c),a.unbind("keydown",d))};var c=function(a){if(b){var c=b.getToggleElement();a&&c&&c[0].contains(a.target)||b.$apply(function(){b.isOpen=!1})}},d=function(a){27===a.which&&(b.focusToggleElement(),c())}}]).controller("DropdownController",["$scope","$attrs","$parse","dropdownConfig","dropdownService","$animate",function(a,b,c,d,e,f){var g,h=this,i=a.$new(),j=d.openClass,k=angular.noop,l=b.onToggle?c(b.onToggle):angular.noop;this.init=function(d){h.$element=d,b.isOpen&&(g=c(b.isOpen),k=g.assign,a.$watch(g,function(a){i.isOpen=!!a}))},this.toggle=function(a){return i.isOpen=arguments.length?!!a:!i.isOpen},this.isOpen=function(){return i.isOpen},i.getToggleElement=function(){return h.toggleElement},i.focusToggleElement=function(){h.toggleElement&&h.toggleElement[0].focus()},i.$watch("isOpen",function(b,c){f[b?"addClass":"removeClass"](h.$element,j),b?(i.focusToggleElement(),e.open(i)):e.close(i),k(a,b),angular.isDefined(b)&&b!==c&&l(a,{open:!!b})}),a.$on("$locationChangeSuccess",function(){i.isOpen=!1}),a.$on("$destroy",function(){i.$destroy()})}]).directive("dropdown",function(){return{controller:"DropdownController",link:function(a,b,c,d){d.init(b)}}}).directive("dropdownToggle",function(){return{require:"?^dropdown",link:function(a,b,c,d){if(d){d.toggleElement=b;var e=function(e){e.preventDefault(),b.hasClass("disabled")||c.disabled||a.$apply(function(){d.toggle()})};b.bind("click",e),b.attr({"aria-haspopup":!0,"aria-expanded":!1}),a.$watch(d.isOpen,function(a){b.attr("aria-expanded",!!a)}),a.$on("$destroy",function(){b.unbind("click",e)})}}}}),angular.module("ui.bootstrap.modal",["ui.bootstrap.transition"]).factory("$$stackedMap",function(){return{createNew:function(){var a=[];return{add:function(b,c){a.push({key:b,value:c})},get:function(b){for(var c=0;c<a.length;c++)if(b==a[c].key)return a[c]},keys:function(){for(var b=[],c=0;c<a.length;c++)b.push(a[c].key);return b},top:function(){return a[a.length-1]},remove:function(b){for(var c=-1,d=0;d<a.length;d++)if(b==a[d].key){c=d;break}return a.splice(c,1)[0]},removeTop:function(){return a.splice(a.length-1,1)[0]},length:function(){return a.length}}}}}).directive("modalBackdrop",["$timeout",function(a){return{restrict:"EA",replace:!0,templateUrl:"template/modal/backdrop.html",link:function(b,c,d){b.backdropClass=d.backdropClass||"",b.animate=!1,a(function(){b.animate=!0})}}}]).directive("modalWindow",["$modalStack","$timeout",function(a,b){return{restrict:"EA",scope:{index:"@",animate:"="},replace:!0,transclude:!0,templateUrl:function(a,b){return b.templateUrl||"template/modal/window.html"},link:function(c,d,e){d.addClass(e.windowClass||""),c.size=e.size,b(function(){c.animate=!0,d[0].querySelectorAll("[autofocus]").length||d[0].focus()}),c.close=function(b){var c=a.getTop();c&&c.value.backdrop&&"static"!=c.value.backdrop&&b.target===b.currentTarget&&(b.preventDefault(),b.stopPropagation(),a.dismiss(c.key,"backdrop click"))}}}}]).directive("modalTransclude",function(){return{link:function(a,b,c,d,e){e(a.$parent,function(a){b.empty(),b.append(a)})}}}).factory("$modalStack",["$transition","$timeout","$document","$compile","$rootScope","$$stackedMap",function(a,b,c,d,e,f){function g(){for(var a=-1,b=n.keys(),c=0;c<b.length;c++)n.get(b[c]).value.backdrop&&(a=c);return a}function h(a){var b=c.find("body").eq(0),d=n.get(a).value;n.remove(a),j(d.modalDomEl,d.modalScope,300,function(){d.modalScope.$destroy(),b.toggleClass(m,n.length()>0),i()})}function i(){if(k&&-1==g()){var a=l;j(k,l,150,function(){a.$destroy(),a=null}),k=void 0,l=void 0}}function j(c,d,e,f){function g(){g.done||(g.done=!0,c.remove(),f&&f())}d.animate=!1;var h=a.transitionEndEventName;if(h){var i=b(g,e);c.bind(h,function(){b.cancel(i),g(),d.$apply()})}else b(g)}var k,l,m="modal-open",n=f.createNew(),o={};return e.$watch(g,function(a){l&&(l.index=a)}),c.bind("keydown",function(a){var b;27===a.which&&(b=n.top(),b&&b.value.keyboard&&(a.preventDefault(),e.$apply(function(){o.dismiss(b.key,"escape key press")})))}),o.open=function(a,b){n.add(a,{deferred:b.deferred,modalScope:b.scope,backdrop:b.backdrop,keyboard:b.keyboard});var f=c.find("body").eq(0),h=g();if(h>=0&&!k){l=e.$new(!0),l.index=h;var i=angular.element("<div modal-backdrop></div>");i.attr("backdrop-class",b.backdropClass),k=d(i)(l),f.append(k)}var j=angular.element("<div modal-window></div>");j.attr({"template-url":b.windowTemplateUrl,"window-class":b.windowClass,size:b.size,index:n.length()-1,animate:"animate"}).html(b.content);var o=d(j)(b.scope);n.top().value.modalDomEl=o,f.append(o),f.addClass(m)},o.close=function(a,b){var c=n.get(a);c&&(c.value.deferred.resolve(b),h(a))},o.dismiss=function(a,b){var c=n.get(a);c&&(c.value.deferred.reject(b),h(a))},o.dismissAll=function(a){for(var b=this.getTop();b;)this.dismiss(b.key,a),b=this.getTop()},o.getTop=function(){return n.top()},o}]).provider("$modal",function(){var a={options:{backdrop:!0,keyboard:!0},$get:["$injector","$rootScope","$q","$http","$templateCache","$controller","$modalStack",function(b,c,d,e,f,g,h){function i(a){return a.template?d.when(a.template):e.get(angular.isFunction(a.templateUrl)?a.templateUrl():a.templateUrl,{cache:f}).then(function(a){return a.data})}function j(a){var c=[];return angular.forEach(a,function(a){(angular.isFunction(a)||angular.isArray(a))&&c.push(d.when(b.invoke(a)))}),c}var k={};return k.open=function(b){var e=d.defer(),f=d.defer(),k={result:e.promise,opened:f.promise,close:function(a){h.close(k,a)},dismiss:function(a){h.dismiss(k,a)}};if(b=angular.extend({},a.options,b),b.resolve=b.resolve||{},!b.template&&!b.templateUrl)throw new Error("One of template or templateUrl options is required.");var l=d.all([i(b)].concat(j(b.resolve)));return l.then(function(a){var d=(b.scope||c).$new();d.$close=k.close,d.$dismiss=k.dismiss;var f,i={},j=1;b.controller&&(i.$scope=d,i.$modalInstance=k,angular.forEach(b.resolve,function(b,c){i[c]=a[j++]}),f=g(b.controller,i),b.controllerAs&&(d[b.controllerAs]=f)),h.open(k,{scope:d,deferred:e,content:a[0],backdrop:b.backdrop,keyboard:b.keyboard,backdropClass:b.backdropClass,windowClass:b.windowClass,windowTemplateUrl:b.windowTemplateUrl,size:b.size})},function(a){e.reject(a)}),l.then(function(){f.resolve(!0)},function(){f.reject(!1)}),k},k}]};return a}),angular.module("ui.bootstrap.pagination",[]).controller("PaginationController",["$scope","$attrs","$parse",function(a,b,c){var d=this,e={$setViewValue:angular.noop},f=b.numPages?c(b.numPages).assign:angular.noop;this.init=function(f,g){e=f,this.config=g,e.$render=function(){d.render()},b.itemsPerPage?a.$parent.$watch(c(b.itemsPerPage),function(b){d.itemsPerPage=parseInt(b,10),a.totalPages=d.calculateTotalPages()}):this.itemsPerPage=g.itemsPerPage},this.calculateTotalPages=function(){var b=this.itemsPerPage<1?1:Math.ceil(a.totalItems/this.itemsPerPage);return Math.max(b||0,1)},this.render=function(){a.page=parseInt(e.$viewValue,10)||1},a.selectPage=function(b){a.page!==b&&b>0&&b<=a.totalPages&&(e.$setViewValue(b),e.$render())},a.getText=function(b){return a[b+"Text"]||d.config[b+"Text"]},a.noPrevious=function(){return 1===a.page},a.noNext=function(){return a.page===a.totalPages},a.$watch("totalItems",function(){a.totalPages=d.calculateTotalPages()}),a.$watch("totalPages",function(b){f(a.$parent,b),a.page>b?a.selectPage(b):e.$render()})}]).constant("paginationConfig",{itemsPerPage:10,boundaryLinks:!1,directionLinks:!0,firstText:"First",previousText:"Previous",nextText:"Next",lastText:"Last",rotate:!0}).directive("pagination",["$parse","paginationConfig",function(a,b){return{restrict:"EA",scope:{totalItems:"=",firstText:"@",previousText:"@",nextText:"@",lastText:"@"},require:["pagination","?ngModel"],controller:"PaginationController",templateUrl:"template/pagination/pagination.html",replace:!0,link:function(c,d,e,f){function g(a,b,c){return{number:a,text:b,active:c}}function h(a,b){var c=[],d=1,e=b,f=angular.isDefined(k)&&b>k;f&&(l?(d=Math.max(a-Math.floor(k/2),1),e=d+k-1,e>b&&(e=b,d=e-k+1)):(d=(Math.ceil(a/k)-1)*k+1,e=Math.min(d+k-1,b)));for(var h=d;e>=h;h++){var i=g(h,h,h===a);c.push(i)}if(f&&!l){if(d>1){var j=g(d-1,"...",!1);c.unshift(j)}if(b>e){var m=g(e+1,"...",!1);c.push(m)}}return c}var i=f[0],j=f[1];if(j){var k=angular.isDefined(e.maxSize)?c.$parent.$eval(e.maxSize):b.maxSize,l=angular.isDefined(e.rotate)?c.$parent.$eval(e.rotate):b.rotate;c.boundaryLinks=angular.isDefined(e.boundaryLinks)?c.$parent.$eval(e.boundaryLinks):b.boundaryLinks,c.directionLinks=angular.isDefined(e.directionLinks)?c.$parent.$eval(e.directionLinks):b.directionLinks,i.init(j,b),e.maxSize&&c.$parent.$watch(a(e.maxSize),function(a){k=parseInt(a,10),i.render()
});var m=i.render;i.render=function(){m(),c.page>0&&c.page<=c.totalPages&&(c.pages=h(c.page,c.totalPages))}}}}}]).constant("pagerConfig",{itemsPerPage:10,previousText:"« Previous",nextText:"Next »",align:!0}).directive("pager",["pagerConfig",function(a){return{restrict:"EA",scope:{totalItems:"=",previousText:"@",nextText:"@"},require:["pager","?ngModel"],controller:"PaginationController",templateUrl:"template/pagination/pager.html",replace:!0,link:function(b,c,d,e){var f=e[0],g=e[1];g&&(b.align=angular.isDefined(d.align)?b.$parent.$eval(d.align):a.align,f.init(g,a))}}}]),angular.module("ui.bootstrap.tooltip",["ui.bootstrap.position","ui.bootstrap.bindHtml"]).provider("$tooltip",function(){function a(a){var b=/[A-Z]/g,c="-";return a.replace(b,function(a,b){return(b?c:"")+a.toLowerCase()})}var b={placement:"top",animation:!0,popupDelay:0},c={mouseenter:"mouseleave",click:"click",focus:"blur"},d={};this.options=function(a){angular.extend(d,a)},this.setTriggers=function(a){angular.extend(c,a)},this.$get=["$window","$compile","$timeout","$document","$position","$interpolate",function(e,f,g,h,i,j){return function(e,k,l){function m(a){var b=a||n.trigger||l,d=c[b]||b;return{show:b,hide:d}}var n=angular.extend({},b,d),o=a(e),p=j.startSymbol(),q=j.endSymbol(),r="<div "+o+'-popup title="'+p+"title"+q+'" content="'+p+"content"+q+'" placement="'+p+"placement"+q+'" animation="animation" is-open="isOpen"></div>';return{restrict:"EA",compile:function(){var a=f(r);return function(b,c,d){function f(){D.isOpen?l():j()}function j(){(!C||b.$eval(d[k+"Enable"]))&&(s(),D.popupDelay?z||(z=g(o,D.popupDelay,!1),z.then(function(a){a()})):o()())}function l(){b.$apply(function(){p()})}function o(){return z=null,y&&(g.cancel(y),y=null),D.content?(q(),w.css({top:0,left:0,display:"block"}),D.$digest(),E(),D.isOpen=!0,D.$digest(),E):angular.noop}function p(){D.isOpen=!1,g.cancel(z),z=null,D.animation?y||(y=g(r,500)):r()}function q(){w&&r(),x=D.$new(),w=a(x,function(a){A?h.find("body").append(a):c.after(a)})}function r(){y=null,w&&(w.remove(),w=null),x&&(x.$destroy(),x=null)}function s(){t(),u()}function t(){var a=d[k+"Placement"];D.placement=angular.isDefined(a)?a:n.placement}function u(){var a=d[k+"PopupDelay"],b=parseInt(a,10);D.popupDelay=isNaN(b)?n.popupDelay:b}function v(){var a=d[k+"Trigger"];F(),B=m(a),B.show===B.hide?c.bind(B.show,f):(c.bind(B.show,j),c.bind(B.hide,l))}var w,x,y,z,A=angular.isDefined(n.appendToBody)?n.appendToBody:!1,B=m(void 0),C=angular.isDefined(d[k+"Enable"]),D=b.$new(!0),E=function(){var a=i.positionElements(c,w,D.placement,A);a.top+="px",a.left+="px",w.css(a)};D.isOpen=!1,d.$observe(e,function(a){D.content=a,!a&&D.isOpen&&p()}),d.$observe(k+"Title",function(a){D.title=a});var F=function(){c.unbind(B.show,j),c.unbind(B.hide,l)};v();var G=b.$eval(d[k+"Animation"]);D.animation=angular.isDefined(G)?!!G:n.animation;var H=b.$eval(d[k+"AppendToBody"]);A=angular.isDefined(H)?H:A,A&&b.$on("$locationChangeSuccess",function(){D.isOpen&&p()}),b.$on("$destroy",function(){g.cancel(y),g.cancel(z),F(),r(),D=null})}}}}}]}).directive("tooltipPopup",function(){return{restrict:"EA",replace:!0,scope:{content:"@",placement:"@",animation:"&",isOpen:"&"},templateUrl:"template/tooltip/tooltip-popup.html"}}).directive("tooltip",["$tooltip",function(a){return a("tooltip","tooltip","mouseenter")}]).directive("tooltipHtmlUnsafePopup",function(){return{restrict:"EA",replace:!0,scope:{content:"@",placement:"@",animation:"&",isOpen:"&"},templateUrl:"template/tooltip/tooltip-html-unsafe-popup.html"}}).directive("tooltipHtmlUnsafe",["$tooltip",function(a){return a("tooltipHtmlUnsafe","tooltip","mouseenter")}]),angular.module("ui.bootstrap.popover",["ui.bootstrap.tooltip"]).directive("popoverPopup",function(){return{restrict:"EA",replace:!0,scope:{title:"@",content:"@",placement:"@",animation:"&",isOpen:"&"},templateUrl:"template/popover/popover.html"}}).directive("popover",["$tooltip",function(a){return a("popover","popover","click")}]),angular.module("ui.bootstrap.progressbar",[]).constant("progressConfig",{animate:!0,max:100}).controller("ProgressController",["$scope","$attrs","progressConfig",function(a,b,c){var d=this,e=angular.isDefined(b.animate)?a.$parent.$eval(b.animate):c.animate;this.bars=[],a.max=angular.isDefined(b.max)?a.$parent.$eval(b.max):c.max,this.addBar=function(b,c){e||c.css({transition:"none"}),this.bars.push(b),b.$watch("value",function(c){b.percent=+(100*c/a.max).toFixed(2)}),b.$on("$destroy",function(){c=null,d.removeBar(b)})},this.removeBar=function(a){this.bars.splice(this.bars.indexOf(a),1)}}]).directive("progress",function(){return{restrict:"EA",replace:!0,transclude:!0,controller:"ProgressController",require:"progress",scope:{},templateUrl:"template/progressbar/progress.html"}}).directive("bar",function(){return{restrict:"EA",replace:!0,transclude:!0,require:"^progress",scope:{value:"=",type:"@"},templateUrl:"template/progressbar/bar.html",link:function(a,b,c,d){d.addBar(a,b)}}}).directive("progressbar",function(){return{restrict:"EA",replace:!0,transclude:!0,controller:"ProgressController",scope:{value:"=",type:"@"},templateUrl:"template/progressbar/progressbar.html",link:function(a,b,c,d){d.addBar(a,angular.element(b.children()[0]))}}}),angular.module("ui.bootstrap.rating",[]).constant("ratingConfig",{max:5,stateOn:null,stateOff:null}).controller("RatingController",["$scope","$attrs","ratingConfig",function(a,b,c){var d={$setViewValue:angular.noop};this.init=function(e){d=e,d.$render=this.render,this.stateOn=angular.isDefined(b.stateOn)?a.$parent.$eval(b.stateOn):c.stateOn,this.stateOff=angular.isDefined(b.stateOff)?a.$parent.$eval(b.stateOff):c.stateOff;var f=angular.isDefined(b.ratingStates)?a.$parent.$eval(b.ratingStates):new Array(angular.isDefined(b.max)?a.$parent.$eval(b.max):c.max);a.range=this.buildTemplateObjects(f)},this.buildTemplateObjects=function(a){for(var b=0,c=a.length;c>b;b++)a[b]=angular.extend({index:b},{stateOn:this.stateOn,stateOff:this.stateOff},a[b]);return a},a.rate=function(b){!a.readonly&&b>=0&&b<=a.range.length&&(d.$setViewValue(b),d.$render())},a.enter=function(b){a.readonly||(a.value=b),a.onHover({value:b})},a.reset=function(){a.value=d.$viewValue,a.onLeave()},a.onKeydown=function(b){/(37|38|39|40)/.test(b.which)&&(b.preventDefault(),b.stopPropagation(),a.rate(a.value+(38===b.which||39===b.which?1:-1)))},this.render=function(){a.value=d.$viewValue}}]).directive("rating",function(){return{restrict:"EA",require:["rating","ngModel"],scope:{readonly:"=?",onHover:"&",onLeave:"&"},controller:"RatingController",templateUrl:"template/rating/rating.html",replace:!0,link:function(a,b,c,d){var e=d[0],f=d[1];f&&e.init(f)}}}),angular.module("ui.bootstrap.tabs",[]).controller("TabsetController",["$scope",function(a){var b=this,c=b.tabs=a.tabs=[];b.select=function(a){angular.forEach(c,function(b){b.active&&b!==a&&(b.active=!1,b.onDeselect())}),a.active=!0,a.onSelect()},b.addTab=function(a){c.push(a),1===c.length?a.active=!0:a.active&&b.select(a)},b.removeTab=function(a){var e=c.indexOf(a);if(a.active&&c.length>1&&!d){var f=e==c.length-1?e-1:e+1;b.select(c[f])}c.splice(e,1)};var d;a.$on("$destroy",function(){d=!0})}]).directive("tabset",function(){return{restrict:"EA",transclude:!0,replace:!0,scope:{type:"@"},controller:"TabsetController",templateUrl:"template/tabs/tabset.html",link:function(a,b,c){a.vertical=angular.isDefined(c.vertical)?a.$parent.$eval(c.vertical):!1,a.justified=angular.isDefined(c.justified)?a.$parent.$eval(c.justified):!1}}}).directive("tab",["$parse",function(a){return{require:"^tabset",restrict:"EA",replace:!0,templateUrl:"template/tabs/tab.html",transclude:!0,scope:{active:"=?",heading:"@",onSelect:"&select",onDeselect:"&deselect"},controller:function(){},compile:function(b,c,d){return function(b,c,e,f){b.$watch("active",function(a){a&&f.select(b)}),b.disabled=!1,e.disabled&&b.$parent.$watch(a(e.disabled),function(a){b.disabled=!!a}),b.select=function(){b.disabled||(b.active=!0)},f.addTab(b),b.$on("$destroy",function(){f.removeTab(b)}),b.$transcludeFn=d}}}}]).directive("tabHeadingTransclude",[function(){return{restrict:"A",require:"^tab",link:function(a,b){a.$watch("headingElement",function(a){a&&(b.html(""),b.append(a))})}}}]).directive("tabContentTransclude",function(){function a(a){return a.tagName&&(a.hasAttribute("tab-heading")||a.hasAttribute("data-tab-heading")||"tab-heading"===a.tagName.toLowerCase()||"data-tab-heading"===a.tagName.toLowerCase())}return{restrict:"A",require:"^tabset",link:function(b,c,d){var e=b.$eval(d.tabContentTransclude);e.$transcludeFn(e.$parent,function(b){angular.forEach(b,function(b){a(b)?e.headingElement=b:c.append(b)})})}}}),angular.module("ui.bootstrap.timepicker",[]).constant("timepickerConfig",{hourStep:1,minuteStep:1,showMeridian:!0,meridians:null,readonlyInput:!1,mousewheel:!0}).controller("TimepickerController",["$scope","$attrs","$parse","$log","$locale","timepickerConfig",function(a,b,c,d,e,f){function g(){var b=parseInt(a.hours,10),c=a.showMeridian?b>0&&13>b:b>=0&&24>b;return c?(a.showMeridian&&(12===b&&(b=0),a.meridian===p[1]&&(b+=12)),b):void 0}function h(){var b=parseInt(a.minutes,10);return b>=0&&60>b?b:void 0}function i(a){return angular.isDefined(a)&&a.toString().length<2?"0"+a:a}function j(a){k(),o.$setViewValue(new Date(n)),l(a)}function k(){o.$setValidity("time",!0),a.invalidHours=!1,a.invalidMinutes=!1}function l(b){var c=n.getHours(),d=n.getMinutes();a.showMeridian&&(c=0===c||12===c?12:c%12),a.hours="h"===b?c:i(c),a.minutes="m"===b?d:i(d),a.meridian=n.getHours()<12?p[0]:p[1]}function m(a){var b=new Date(n.getTime()+6e4*a);n.setHours(b.getHours(),b.getMinutes()),j()}var n=new Date,o={$setViewValue:angular.noop},p=angular.isDefined(b.meridians)?a.$parent.$eval(b.meridians):f.meridians||e.DATETIME_FORMATS.AMPMS;this.init=function(c,d){o=c,o.$render=this.render;var e=d.eq(0),g=d.eq(1),h=angular.isDefined(b.mousewheel)?a.$parent.$eval(b.mousewheel):f.mousewheel;h&&this.setupMousewheelEvents(e,g),a.readonlyInput=angular.isDefined(b.readonlyInput)?a.$parent.$eval(b.readonlyInput):f.readonlyInput,this.setupInputEvents(e,g)};var q=f.hourStep;b.hourStep&&a.$parent.$watch(c(b.hourStep),function(a){q=parseInt(a,10)});var r=f.minuteStep;b.minuteStep&&a.$parent.$watch(c(b.minuteStep),function(a){r=parseInt(a,10)}),a.showMeridian=f.showMeridian,b.showMeridian&&a.$parent.$watch(c(b.showMeridian),function(b){if(a.showMeridian=!!b,o.$error.time){var c=g(),d=h();angular.isDefined(c)&&angular.isDefined(d)&&(n.setHours(c),j())}else l()}),this.setupMousewheelEvents=function(b,c){var d=function(a){a.originalEvent&&(a=a.originalEvent);var b=a.wheelDelta?a.wheelDelta:-a.deltaY;return a.detail||b>0};b.bind("mousewheel wheel",function(b){a.$apply(d(b)?a.incrementHours():a.decrementHours()),b.preventDefault()}),c.bind("mousewheel wheel",function(b){a.$apply(d(b)?a.incrementMinutes():a.decrementMinutes()),b.preventDefault()})},this.setupInputEvents=function(b,c){if(a.readonlyInput)return a.updateHours=angular.noop,void(a.updateMinutes=angular.noop);var d=function(b,c){o.$setViewValue(null),o.$setValidity("time",!1),angular.isDefined(b)&&(a.invalidHours=b),angular.isDefined(c)&&(a.invalidMinutes=c)};a.updateHours=function(){var a=g();angular.isDefined(a)?(n.setHours(a),j("h")):d(!0)},b.bind("blur",function(){!a.invalidHours&&a.hours<10&&a.$apply(function(){a.hours=i(a.hours)})}),a.updateMinutes=function(){var a=h();angular.isDefined(a)?(n.setMinutes(a),j("m")):d(void 0,!0)},c.bind("blur",function(){!a.invalidMinutes&&a.minutes<10&&a.$apply(function(){a.minutes=i(a.minutes)})})},this.render=function(){var a=o.$modelValue?new Date(o.$modelValue):null;isNaN(a)?(o.$setValidity("time",!1),d.error('Timepicker directive: "ng-model" value must be a Date object, a number of milliseconds since 01.01.1970 or a string representing an RFC2822 or ISO 8601 date.')):(a&&(n=a),k(),l())},a.incrementHours=function(){m(60*q)},a.decrementHours=function(){m(60*-q)},a.incrementMinutes=function(){m(r)},a.decrementMinutes=function(){m(-r)},a.toggleMeridian=function(){m(720*(n.getHours()<12?1:-1))}}]).directive("timepicker",function(){return{restrict:"EA",require:["timepicker","?^ngModel"],controller:"TimepickerController",replace:!0,scope:{},templateUrl:"template/timepicker/timepicker.html",link:function(a,b,c,d){var e=d[0],f=d[1];f&&e.init(f,b.find("input"))}}}),angular.module("ui.bootstrap.typeahead",["ui.bootstrap.position","ui.bootstrap.bindHtml"]).factory("typeaheadParser",["$parse",function(a){var b=/^\s*([\s\S]+?)(?:\s+as\s+([\s\S]+?))?\s+for\s+(?:([\$\w][\$\w\d]*))\s+in\s+([\s\S]+?)$/;return{parse:function(c){var d=c.match(b);if(!d)throw new Error('Expected typeahead specification in form of "_modelValue_ (as _label_)? for _item_ in _collection_" but got "'+c+'".');return{itemName:d[3],source:a(d[4]),viewMapper:a(d[2]||d[1]),modelMapper:a(d[1])}}}}]).directive("typeahead",["$compile","$parse","$q","$timeout","$document","$position","typeaheadParser",function(a,b,c,d,e,f,g){var h=[9,13,27,38,40];return{require:"ngModel",link:function(i,j,k,l){var m,n=i.$eval(k.typeaheadMinLength)||1,o=i.$eval(k.typeaheadWaitMs)||0,p=i.$eval(k.typeaheadEditable)!==!1,q=b(k.typeaheadLoading).assign||angular.noop,r=b(k.typeaheadOnSelect),s=k.typeaheadInputFormatter?b(k.typeaheadInputFormatter):void 0,t=k.typeaheadAppendToBody?i.$eval(k.typeaheadAppendToBody):!1,u=i.$eval(k.typeaheadFocusFirst)!==!1,v=b(k.ngModel).assign,w=g.parse(k.typeahead),x=i.$new();i.$on("$destroy",function(){x.$destroy()});var y="typeahead-"+x.$id+"-"+Math.floor(1e4*Math.random());j.attr({"aria-autocomplete":"list","aria-expanded":!1,"aria-owns":y});var z=angular.element("<div typeahead-popup></div>");z.attr({id:y,matches:"matches",active:"activeIdx",select:"select(activeIdx)",query:"query",position:"position"}),angular.isDefined(k.typeaheadTemplateUrl)&&z.attr("template-url",k.typeaheadTemplateUrl);var A=function(){x.matches=[],x.activeIdx=-1,j.attr("aria-expanded",!1)},B=function(a){return y+"-option-"+a};x.$watch("activeIdx",function(a){0>a?j.removeAttr("aria-activedescendant"):j.attr("aria-activedescendant",B(a))});var C=function(a){var b={$viewValue:a};q(i,!0),c.when(w.source(i,b)).then(function(c){var d=a===l.$viewValue;if(d&&m)if(c.length>0){x.activeIdx=u?0:-1,x.matches.length=0;for(var e=0;e<c.length;e++)b[w.itemName]=c[e],x.matches.push({id:B(e),label:w.viewMapper(x,b),model:c[e]});x.query=a,x.position=t?f.offset(j):f.position(j),x.position.top=x.position.top+j.prop("offsetHeight"),j.attr("aria-expanded",!0)}else A();d&&q(i,!1)},function(){A(),q(i,!1)})};A(),x.query=void 0;var D,E=function(a){D=d(function(){C(a)},o)},F=function(){D&&d.cancel(D)};l.$parsers.unshift(function(a){return m=!0,a&&a.length>=n?o>0?(F(),E(a)):C(a):(q(i,!1),F(),A()),p?a:a?void l.$setValidity("editable",!1):(l.$setValidity("editable",!0),a)}),l.$formatters.push(function(a){var b,c,d={};return s?(d.$model=a,s(i,d)):(d[w.itemName]=a,b=w.viewMapper(i,d),d[w.itemName]=void 0,c=w.viewMapper(i,d),b!==c?b:a)}),x.select=function(a){var b,c,e={};e[w.itemName]=c=x.matches[a].model,b=w.modelMapper(i,e),v(i,b),l.$setValidity("editable",!0),r(i,{$item:c,$model:b,$label:w.viewMapper(i,e)}),A(),d(function(){j[0].focus()},0,!1)},j.bind("keydown",function(a){0!==x.matches.length&&-1!==h.indexOf(a.which)&&(-1!=x.activeIdx||13!==a.which&&9!==a.which)&&(a.preventDefault(),40===a.which?(x.activeIdx=(x.activeIdx+1)%x.matches.length,x.$digest()):38===a.which?(x.activeIdx=(x.activeIdx>0?x.activeIdx:x.matches.length)-1,x.$digest()):13===a.which||9===a.which?x.$apply(function(){x.select(x.activeIdx)}):27===a.which&&(a.stopPropagation(),A(),x.$digest()))}),j.bind("blur",function(){m=!1});var G=function(a){j[0]!==a.target&&(A(),x.$digest())};e.bind("click",G),i.$on("$destroy",function(){e.unbind("click",G),t&&H.remove()});var H=a(z)(x);t?e.find("body").append(H):j.after(H)}}}]).directive("typeaheadPopup",function(){return{restrict:"EA",scope:{matches:"=",query:"=",active:"=",position:"=",select:"&"},replace:!0,templateUrl:"template/typeahead/typeahead-popup.html",link:function(a,b,c){a.templateUrl=c.templateUrl,a.isOpen=function(){return a.matches.length>0},a.isActive=function(b){return a.active==b},a.selectActive=function(b){a.active=b},a.selectMatch=function(b){a.select({activeIdx:b})}}}}).directive("typeaheadMatch",["$http","$templateCache","$compile","$parse",function(a,b,c,d){return{restrict:"EA",scope:{index:"=",match:"=",query:"="},link:function(e,f,g){var h=d(g.templateUrl)(e.$parent)||"template/typeahead/typeahead-match.html";a.get(h,{cache:b}).success(function(a){f.replaceWith(c(a.trim())(e))})}}}]).filter("typeaheadHighlight",function(){function a(a){return a.replace(/([.?*+^$[\]\\(){}|-])/g,"\\$1")}return function(b,c){return c?(""+b).replace(new RegExp(a(c),"gi"),"<strong>$&</strong>"):b}}),angular.module("template/accordion/accordion-group.html",[]).run(["$templateCache",function(a){a.put("template/accordion/accordion-group.html",'<div class="panel panel-default">\n <div class="panel-heading">\n <h4 class="panel-title">\n <a href class="accordion-toggle" ng-click="toggleOpen()" accordion-transclude="heading"><span ng-class="{\'text-muted\': isDisabled}">{{heading}}</span></a>\n </h4>\n </div>\n <div class="panel-collapse" collapse="!isOpen">\n <div class="panel-body" ng-transclude></div>\n </div>\n</div>\n')}]),angular.module("template/accordion/accordion.html",[]).run(["$templateCache",function(a){a.put("template/accordion/accordion.html",'<div class="panel-group" ng-transclude></div>')}]),angular.module("template/alert/alert.html",[]).run(["$templateCache",function(a){a.put("template/alert/alert.html",'<div class="alert" ng-class="[\'alert-\' + (type || \'warning\'), closeable ? \'alert-dismissable\' : null]" role="alert">\n <button ng-show="closeable" type="button" class="close" ng-click="close()">\n <span aria-hidden="true">×</span>\n <span class="sr-only">Close</span>\n </button>\n <div ng-transclude></div>\n</div>\n')}]),angular.module("template/carousel/carousel.html",[]).run(["$templateCache",function(a){a.put("template/carousel/carousel.html",'<div ng-mouseenter="pause()" ng-mouseleave="play()" class="carousel" ng-swipe-right="prev()" ng-swipe-left="next()">\n <ol class="carousel-indicators" ng-show="slides.length > 1">\n <li ng-repeat="slide in slides track by $index" ng-class="{active: isActive(slide)}" ng-click="select(slide)"></li>\n </ol>\n <div class="carousel-inner" ng-transclude></div>\n <a class="left carousel-control" ng-click="prev()" ng-show="slides.length > 1"><span class="glyphicon glyphicon-chevron-left"></span></a>\n <a class="right carousel-control" ng-click="next()" ng-show="slides.length > 1"><span class="glyphicon glyphicon-chevron-right"></span></a>\n</div>\n')}]),angular.module("template/carousel/slide.html",[]).run(["$templateCache",function(a){a.put("template/carousel/slide.html","<div ng-class=\"{\n 'active': leaving || (active && !entering),\n 'prev': (next || active) && direction=='prev',\n 'next': (next || active) && direction=='next',\n 'right': direction=='prev',\n 'left': direction=='next'\n }\" class=\"item text-center\" ng-transclude></div>\n")}]),angular.module("template/datepicker/datepicker.html",[]).run(["$templateCache",function(a){a.put("template/datepicker/datepicker.html",'<div ng-switch="datepickerMode" role="application" ng-keydown="keydown($event)">\n <daypicker ng-switch-when="day" tabindex="0"></daypicker>\n <monthpicker ng-switch-when="month" tabindex="0"></monthpicker>\n <yearpicker ng-switch-when="year" tabindex="0"></yearpicker>\n</div>')}]),angular.module("template/datepicker/day.html",[]).run(["$templateCache",function(a){a.put("template/datepicker/day.html",'<table role="grid" aria-labelledby="{{uniqueId}}-title" aria-activedescendant="{{activeDateId}}">\n <thead>\n <tr>\n <th><button type="button" class="btn btn-default btn-sm pull-left" ng-click="move(-1)" tabindex="-1"><i class="glyphicon glyphicon-chevron-left"></i></button></th>\n <th colspan="{{5 + showWeeks}}"><button id="{{uniqueId}}-title" role="heading" aria-live="assertive" aria-atomic="true" type="button" class="btn btn-default btn-sm" ng-click="toggleMode()" tabindex="-1" style="width:100%;"><strong>{{title}}</strong></button></th>\n <th><button type="button" class="btn btn-default btn-sm pull-right" ng-click="move(1)" tabindex="-1"><i class="glyphicon glyphicon-chevron-right"></i></button></th>\n </tr>\n <tr>\n <th ng-show="showWeeks" class="text-center"></th>\n <th ng-repeat="label in labels track by $index" class="text-center"><small aria-label="{{label.full}}">{{label.abbr}}</small></th>\n </tr>\n </thead>\n <tbody>\n <tr ng-repeat="row in rows track by $index">\n <td ng-show="showWeeks" class="text-center h6"><em>{{ weekNumbers[$index] }}</em></td>\n <td ng-repeat="dt in row track by dt.date" class="text-center" role="gridcell" id="{{dt.uid}}" aria-disabled="{{!!dt.disabled}}">\n <button type="button" style="width:100%;" class="btn btn-default btn-sm" ng-class="{\'btn-info\': dt.selected, active: isActive(dt)}" ng-click="select(dt.date)" ng-disabled="dt.disabled" tabindex="-1"><span ng-class="{\'text-muted\': dt.secondary, \'text-info\': dt.current}">{{dt.label}}</span></button>\n </td>\n </tr>\n </tbody>\n</table>\n')}]),angular.module("template/datepicker/month.html",[]).run(["$templateCache",function(a){a.put("template/datepicker/month.html",'<table role="grid" aria-labelledby="{{uniqueId}}-title" aria-activedescendant="{{activeDateId}}">\n <thead>\n <tr>\n <th><button type="button" class="btn btn-default btn-sm pull-left" ng-click="move(-1)" tabindex="-1"><i class="glyphicon glyphicon-chevron-left"></i></button></th>\n <th><button id="{{uniqueId}}-title" role="heading" aria-live="assertive" aria-atomic="true" type="button" class="btn btn-default btn-sm" ng-click="toggleMode()" tabindex="-1" style="width:100%;"><strong>{{title}}</strong></button></th>\n <th><button type="button" class="btn btn-default btn-sm pull-right" ng-click="move(1)" tabindex="-1"><i class="glyphicon glyphicon-chevron-right"></i></button></th>\n </tr>\n </thead>\n <tbody>\n <tr ng-repeat="row in rows track by $index">\n <td ng-repeat="dt in row track by dt.date" class="text-center" role="gridcell" id="{{dt.uid}}" aria-disabled="{{!!dt.disabled}}">\n <button type="button" style="width:100%;" class="btn btn-default" ng-class="{\'btn-info\': dt.selected, active: isActive(dt)}" ng-click="select(dt.date)" ng-disabled="dt.disabled" tabindex="-1"><span ng-class="{\'text-info\': dt.current}">{{dt.label}}</span></button>\n </td>\n </tr>\n </tbody>\n</table>\n')}]),angular.module("template/datepicker/popup.html",[]).run(["$templateCache",function(a){a.put("template/datepicker/popup.html",'<ul class="dropdown-menu" ng-style="{display: (isOpen && \'block\') || \'none\', top: position.top+\'px\', left: position.left+\'px\'}" ng-keydown="keydown($event)">\n <li ng-transclude></li>\n <li ng-if="showButtonBar" style="padding:10px 9px 2px">\n <span class="btn-group pull-left">\n <button type="button" class="btn btn-sm btn-info" ng-click="select(\'today\')">{{ getText(\'current\') }}</button>\n <button type="button" class="btn btn-sm btn-danger" ng-click="select(null)">{{ getText(\'clear\') }}</button>\n </span>\n <button type="button" class="btn btn-sm btn-success pull-right" ng-click="close()">{{ getText(\'close\') }}</button>\n </li>\n</ul>\n')}]),angular.module("template/datepicker/year.html",[]).run(["$templateCache",function(a){a.put("template/datepicker/year.html",'<table role="grid" aria-labelledby="{{uniqueId}}-title" aria-activedescendant="{{activeDateId}}">\n <thead>\n <tr>\n <th><button type="button" class="btn btn-default btn-sm pull-left" ng-click="move(-1)" tabindex="-1"><i class="glyphicon glyphicon-chevron-left"></i></button></th>\n <th colspan="3"><button id="{{uniqueId}}-title" role="heading" aria-live="assertive" aria-atomic="true" type="button" class="btn btn-default btn-sm" ng-click="toggleMode()" tabindex="-1" style="width:100%;"><strong>{{title}}</strong></button></th>\n <th><button type="button" class="btn btn-default btn-sm pull-right" ng-click="move(1)" tabindex="-1"><i class="glyphicon glyphicon-chevron-right"></i></button></th>\n </tr>\n </thead>\n <tbody>\n <tr ng-repeat="row in rows track by $index">\n <td ng-repeat="dt in row track by dt.date" class="text-center" role="gridcell" id="{{dt.uid}}" aria-disabled="{{!!dt.disabled}}">\n <button type="button" style="width:100%;" class="btn btn-default" ng-class="{\'btn-info\': dt.selected, active: isActive(dt)}" ng-click="select(dt.date)" ng-disabled="dt.disabled" tabindex="-1"><span ng-class="{\'text-info\': dt.current}">{{dt.label}}</span></button>\n </td>\n </tr>\n </tbody>\n</table>\n')}]),angular.module("template/modal/backdrop.html",[]).run(["$templateCache",function(a){a.put("template/modal/backdrop.html",'<div class="modal-backdrop fade {{ backdropClass }}"\n ng-class="{in: animate}"\n ng-style="{\'z-index\': 1040 + (index && 1 || 0) + index*10}"\n></div>\n')}]),angular.module("template/modal/window.html",[]).run(["$templateCache",function(a){a.put("template/modal/window.html",'<div tabindex="-1" role="dialog" class="modal fade" ng-class="{in: animate}" ng-style="{\'z-index\': 1050 + index*10, display: \'block\'}" ng-click="close($event)">\n <div class="modal-dialog" ng-class="{\'modal-sm\': size == \'sm\', \'modal-lg\': size == \'lg\'}"><div class="modal-content" modal-transclude></div></div>\n</div>')}]),angular.module("template/pagination/pager.html",[]).run(["$templateCache",function(a){a.put("template/pagination/pager.html",'<ul class="pager">\n <li ng-class="{disabled: noPrevious(), previous: align}"><a href ng-click="selectPage(page - 1)">{{getText(\'previous\')}}</a></li>\n <li ng-class="{disabled: noNext(), next: align}"><a href ng-click="selectPage(page + 1)">{{getText(\'next\')}}</a></li>\n</ul>')}]),angular.module("template/pagination/pagination.html",[]).run(["$templateCache",function(a){a.put("template/pagination/pagination.html",'<ul class="pagination">\n <li ng-if="boundaryLinks" ng-class="{disabled: noPrevious()}"><a href ng-click="selectPage(1)">{{getText(\'first\')}}</a></li>\n <li ng-if="directionLinks" ng-class="{disabled: noPrevious()}"><a href ng-click="selectPage(page - 1)">{{getText(\'previous\')}}</a></li>\n <li ng-repeat="page in pages track by $index" ng-class="{active: page.active}"><a href ng-click="selectPage(page.number)">{{page.text}}</a></li>\n <li ng-if="directionLinks" ng-class="{disabled: noNext()}"><a href ng-click="selectPage(page + 1)">{{getText(\'next\')}}</a></li>\n <li ng-if="boundaryLinks" ng-class="{disabled: noNext()}"><a href ng-click="selectPage(totalPages)">{{getText(\'last\')}}</a></li>\n</ul>')}]),angular.module("template/tooltip/tooltip-html-unsafe-popup.html",[]).run(["$templateCache",function(a){a.put("template/tooltip/tooltip-html-unsafe-popup.html",'<div class="tooltip {{placement}}" ng-class="{ in: isOpen(), fade: animation() }">\n <div class="tooltip-arrow"></div>\n <div class="tooltip-inner" bind-html-unsafe="content"></div>\n</div>\n')}]),angular.module("template/tooltip/tooltip-popup.html",[]).run(["$templateCache",function(a){a.put("template/tooltip/tooltip-popup.html",'<div class="tooltip {{placement}}" ng-class="{ in: isOpen(), fade: animation() }">\n <div class="tooltip-arrow"></div>\n <div class="tooltip-inner" ng-bind="content"></div>\n</div>\n')}]),angular.module("template/popover/popover.html",[]).run(["$templateCache",function(a){a.put("template/popover/popover.html",'<div class="popover {{placement}}" ng-class="{ in: isOpen(), fade: animation() }">\n <div class="arrow"></div>\n\n <div class="popover-inner">\n <h3 class="popover-title" ng-bind="title" ng-show="title"></h3>\n <div class="popover-content" ng-bind="content"></div>\n </div>\n</div>\n')}]),angular.module("template/progressbar/bar.html",[]).run(["$templateCache",function(a){a.put("template/progressbar/bar.html",'<div class="progress-bar" ng-class="type && \'progress-bar-\' + type" role="progressbar" aria-valuenow="{{value}}" aria-valuemin="0" aria-valuemax="{{max}}" ng-style="{width: percent + \'%\'}" aria-valuetext="{{percent | number:0}}%" ng-transclude></div>')}]),angular.module("template/progressbar/progress.html",[]).run(["$templateCache",function(a){a.put("template/progressbar/progress.html",'<div class="progress" ng-transclude></div>')}]),angular.module("template/progressbar/progressbar.html",[]).run(["$templateCache",function(a){a.put("template/progressbar/progressbar.html",'<div class="progress">\n <div class="progress-bar" ng-class="type && \'progress-bar-\' + type" role="progressbar" aria-valuenow="{{value}}" aria-valuemin="0" aria-valuemax="{{max}}" ng-style="{width: percent + \'%\'}" aria-valuetext="{{percent | number:0}}%" ng-transclude></div>\n</div>')}]),angular.module("template/rating/rating.html",[]).run(["$templateCache",function(a){a.put("template/rating/rating.html",'<span ng-mouseleave="reset()" ng-keydown="onKeydown($event)" tabindex="0" role="slider" aria-valuemin="0" aria-valuemax="{{range.length}}" aria-valuenow="{{value}}">\n <i ng-repeat="r in range track by $index" ng-mouseenter="enter($index + 1)" ng-click="rate($index + 1)" class="glyphicon" ng-class="$index < value && (r.stateOn || \'glyphicon-star\') || (r.stateOff || \'glyphicon-star-empty\')">\n <span class="sr-only">({{ $index < value ? \'*\' : \' \' }})</span>\n </i>\n</span>')}]),angular.module("template/tabs/tab.html",[]).run(["$templateCache",function(a){a.put("template/tabs/tab.html",'<li ng-class="{active: active, disabled: disabled}">\n <a href ng-click="select()" tab-heading-transclude>{{heading}}</a>\n</li>\n')}]),angular.module("template/tabs/tabset.html",[]).run(["$templateCache",function(a){a.put("template/tabs/tabset.html",'<div>\n <ul class="nav nav-{{type || \'tabs\'}}" ng-class="{\'nav-stacked\': vertical, \'nav-justified\': justified}" ng-transclude></ul>\n <div class="tab-content">\n <div class="tab-pane" \n ng-repeat="tab in tabs" \n ng-class="{active: tab.active}"\n tab-content-transclude="tab">\n </div>\n </div>\n</div>\n')}]),angular.module("template/timepicker/timepicker.html",[]).run(["$templateCache",function(a){a.put("template/timepicker/timepicker.html",'<table>\n <tbody>\n <tr class="text-center">\n <td><a ng-click="incrementHours()" class="btn btn-link"><span class="glyphicon glyphicon-chevron-up"></span></a></td>\n <td> </td>\n <td><a ng-click="incrementMinutes()" class="btn btn-link"><span class="glyphicon glyphicon-chevron-up"></span></a></td>\n <td ng-show="showMeridian"></td>\n </tr>\n <tr>\n <td style="width:50px;" class="form-group" ng-class="{\'has-error\': invalidHours}">\n <input type="text" ng-model="hours" ng-change="updateHours()" class="form-control text-center" ng-mousewheel="incrementHours()" ng-readonly="readonlyInput" maxlength="2">\n </td>\n <td>:</td>\n <td style="width:50px;" class="form-group" ng-class="{\'has-error\': invalidMinutes}">\n <input type="text" ng-model="minutes" ng-change="updateMinutes()" class="form-control text-center" ng-readonly="readonlyInput" maxlength="2">\n </td>\n <td ng-show="showMeridian"><button type="button" class="btn btn-default text-center" ng-click="toggleMeridian()">{{meridian}}</button></td>\n </tr>\n <tr class="text-center">\n <td><a ng-click="decrementHours()" class="btn btn-link"><span class="glyphicon glyphicon-chevron-down"></span></a></td>\n <td> </td>\n <td><a ng-click="decrementMinutes()" class="btn btn-link"><span class="glyphicon glyphicon-chevron-down"></span></a></td>\n <td ng-show="showMeridian"></td>\n </tr>\n </tbody>\n</table>\n')}]),angular.module("template/typeahead/typeahead-match.html",[]).run(["$templateCache",function(a){a.put("template/typeahead/typeahead-match.html",'<a tabindex="-1" bind-html-unsafe="match.label | typeaheadHighlight:query"></a>')
}]),angular.module("template/typeahead/typeahead-popup.html",[]).run(["$templateCache",function(a){a.put("template/typeahead/typeahead-popup.html",'<ul class="dropdown-menu" ng-show="isOpen()" ng-style="{top: position.top+\'px\', left: position.left+\'px\'}" style="display: block;" role="listbox" aria-hidden="{{!isOpen()}}">\n <li ng-repeat="match in matches track by $index" ng-class="{active: isActive($index) }" ng-mouseenter="selectActive($index)" ng-click="selectMatch($index)" role="option" id="{{match.id}}">\n <div typeahead-match index="$index" match="match" query="query" template-url="templateUrl"></div>\n </li>\n</ul>\n')}]);
'use strict';
//inject directives and services.
var app = angular.module('fileUpload', ['ngFileUpload']);
app.controller('fileUploadController', ['$scope', 'Upload', '$timeout', function ($scope, Upload, $timeout) {
console.log('fileuploadcontroller loading');
//The files list
//Todo, extend with the files form the database
$scope.files = [];
//These are the fileIds, used for saving
$scope.dynamic = false;
$scope.uploadSizes = '';
$scope.maxImages;
$scope.errorMsg = {};
$scope.subFolder = false;
$scope.attributeKey = '';
$scope.forModel;
$scope.forModelId;
$scope.canUploadNewImage;
/**
* This function uploads the new files
* @param files
* @param errFiles
* @param maximages
* @param blockId
* @param tabId optional
*/
$scope.uploadFiles = function (files, errFiles, maximages, blockId, tabId) {
console.log("upload for block with blockId: "+blockId+" and tabKey: "+tabId);
console.log("uploadedFiles files:");
console.log(files);
//Get the model and the id where we are uploading images for. Should flow via a service but due to bad design and an upcoming newer angular version does not. todo: improve me
var htmlElement = document.querySelector("[ng-controller=KmsAttributeDynamicController]");
$scope.forModel = htmlElement.dataset.forModel;
$scope.forModelId = htmlElement.dataset.forModelId;
console.log("fileUploadController uploading files For model: "+$scope.forModel+" with id: "+$scope.forModelId+" and blockId: "+blockId);
if (typeof $scope.files == 'undefined') $scope.files = [];
if ($scope.files.length >= maximages) {
console.log("File upload not allowed. "+$scope.files.length+"/"+maximages+" uploaded");
return false;
}
//Set the files
$scope.errFiles = errFiles;
//Foreach each file
angular.forEach(files, function (file) {
//Push to true adds image at the back, false at the front
var push = true;
//Add the placeholder
var loadingImage = {loading: true, thumb_image_url: '/images/kms/loading.gif'};
if (push == true) {
$scope.files.push(loadingImage);
var imageKey = $scope.files.length - 1
} else {
//Add at the front
$scope.files.unshift(loadingImage);
var imageKey = 0
}
//Upload to the server
file.upload = Upload.upload({
url: '/kms/upload',
data: {
//Sent the data and the options
file: file,
uploadSizes: $scope.uploadSizes,
dynamic: $scope.dynamic,
subFolder: $scope.subFolder,
attribute_key: $scope.attributeKey,
forModelName: $scope.forModel,
forModelId: $scope.forModelId,
forBlockId: blockId,
forTabId: tabId
}
});
//After uploading
file.upload.then(function (response) {
$timeout(function () {
// delete $scope.files.loading
console.log(response);
//Set the response data
// file.result = response.data;
// file.result = response.data.uploaded[0];
// console.log("Thumb: "+response.data.uploaded.[0].thumb);
console.log("Image Id: "+response.data.uploaded[0].id);
//Add the file to the current files
$scope.files[imageKey] = response.data.uploaded[0];
console.log("$scope.files["+ imageKey +"] =");
console.log($scope.files[imageKey]);
//Reverse push
//Add the file to the current files
// $scope.files.unshift(file.result);
// $scope.setFileIds()
//Update fileids
var forBlockId = response.data.uploaded[0].forBlockId;
var blocksDataObject = $scope.getBlocksData(); //Defined in the KmsAttributeDynamicController
for(var key in blocksDataObject)
{
console.log("Looping over blocks to find a block with id: "+forBlockId+". Current block id: "+blocksDataObject[key].blockId);
// console.log(blocksDataObject[key]);
if(blocksDataObject.hasOwnProperty(key) === false || blocksDataObject[key].hasOwnProperty("blockId") === false) {
console.warn("Detected block data without a block id. Because of this, it is not possible to upload a file for that block. This does not mean that the uploaded image can be linked to the block your uploading currently for");
continue;
}
if(blocksDataObject[key].blockId == forBlockId) {
console.log("found block with id: "+forBlockId);
var data = [];
if(response.data.uploaded[0].forTabId) {
console.log("Uploaded an image for tab: "+response.data.uploaded[0].forTabId);
data = response.data.uploaded[0].id+"#"+response.data.uploaded[0].forTabId; //imageid#multipleimagesslot
} else {
console.log("Uploaded an image without tab: "+response.data.uploaded[0].id);
data = response.data.uploaded[0].id;
}
var fileIds = [];
if(blocksDataObject[key].hasOwnProperty('fileIds')) {
var length = blocksDataObject[key]['fileIds'].length;
//Determine if it is a single image or multiple images.
blocksDataObject[key]['fileIds'][length] = data;
console.log("Updating fileIds with image id: "+data);
} else {
console.log("Setting fileIds to a new array with image id: "+data);
blocksDataObject[key].fileIds = [data];
}
console.log("files.length = "+blocksDataObject[key].fileIds.length+" maxImages = "+$scope.maxImages);
$scope.canUploadNewImage = blocksDataObject[key].fileIds.length < $scope.maxImages;
}
$scope.setBlocksData(blocksDataObject);
}
});
}, function (response) {
if (response.status > 0)
$scope.errorMsg = response.status + ': ' + response.data;
console.error("Error: "+$scope.errorMsg);
}, function (evt) {
file.progress = Math.min(100, parseInt(100.0 *
evt.loaded / evt.total));
});
});
};
$scope.deleteImage = function (index, blockId, tab_key) {
var idToDelete = $scope.files[index].id;
console.log("idToDelete: "+idToDelete+" from blockId: "+blockId);
//Delete the image
$scope.files.splice(index, 1);
console.log("Current image amount: "+$scope.files.length);
//Delete the image id
// $scope.setFileIds()
var blocksDataObject = $scope.getBlocksData(); //Defined in the KmsAttributeDynamicController
for(var blockIndex in blocksDataObject)
{
if(blocksDataObject.hasOwnProperty(blockIndex) === false || blocksDataObject[blockIndex].hasOwnProperty("blockId") === false) {
console.warn("Detected block data without a block id. Because of this, it is not possible to upload a file for that block. This does not mean that the uploaded image can be linked to the block your uploading currently for");
continue;
}
if(blocksDataObject[blockIndex].blockId == blockId) {
console.log("found block with id: " + blockId);
if(blocksDataObject[blockIndex].hasOwnProperty('fileIdsToDelete') === false) blocksDataObject[blockIndex].fileIdsToDelete = [];
console.log("Current files to delete: "+blocksDataObject[blockIndex].fileIdsToDelete);
idToDelete = (typeof tab_key !== 'undefined') ? idToDelete+"#"+tab_key : idToDelete ;
for(var index in blocksDataObject[blockIndex].fileIds)
{
var currentId = blocksDataObject[blockIndex].fileIds[index];
if(typeof tab_key !== 'undefined') {
console.log("Looping over the file ids to find an image with id: "+idToDelete+" for tab: "+tab_key+" ("+currentId+")");
} else {
console.log("Looping over the file ids to find an image with id: "+idToDelete);
}
if(currentId == idToDelete) {
console.log("Image marked to delete on save: "+idToDelete);
blocksDataObject[blockIndex].fileIdsToDelete[blocksDataObject[blockIndex].fileIdsToDelete.length] = idToDelete;
blocksDataObject[blockIndex].fileIds.splice(index, 1);
if(typeof tab_key === 'undefined') {
$scope.canUploadNewImage = blocksDataObject[blockIndex].fileIds.length < $scope.maxImages;
} else {
var count = 0;
for(var index in blocksDataObject[blockIndex].fileIds) {
if(blocksDataObject[blockIndex].fileIds[index].split("#")[1] == tab_key) {
count++;
}
}
$scope.canUploadNewImage = count < $scope.maxImages;
}
break;
}
}
}
}
$scope.setBlocksData(blocksDataObject);
delete $scope.errorMsg.toMany;
};
// $scope.moveImage = function (index, direction) {
// var currentFile = $scope.files[index]
// var currentFileId = $scope.fileIds[index]
//
// if (direction == 'left') {
// $scope.files[index] = $scope.files[index - 1];
// $scope.files[index - 1] = currentFile;
// } else {
// $scope.files[index] = $scope.files[index + 1];
// $scope.files[index + 1] = currentFile;
// }
// $scope.setFileIds()
// };
$scope.initFiles = function (files, maxImages, tab_key) {
console.log("fileupload:initFiles: "+files+" maxImages: "+maxImages);
if(files) {
// //Strip out the image ids that do not belong to the tab we are processing for
var filesToUse = [];
if(typeof tab_key !== 'undefined') {
for(var index in files) {
var split = files[index].split("#");
if(split.length !== 2) continue; //The imageId did not contain a #
if(split[1] != tab_key) {
console.log("fileupload:initFiles: Ignoring image with id: "+split[0]+" because it belongs to tab "+split[1]+" and we are processing images for tab "+tab_key);
} else {
filesToUse[filesToUse.length] = files[index];
}
}
} else {
filesToUse = files;
}
if(filesToUse.length > 0) {
$.ajax({
type: "POST",
url: "/kms/getimagedata",
data: {
fileIds: filesToUse
},
success: function (response) {
console.log("Retrieved files: ");
console.log(response);
// files must look like this files[index].id files[index].sizes
$scope.files = response;
// $scope.setFileIds();
if (typeof maxImages != 'undefined') {
$scope.maxImages = maxImages
}
console.log("files.length < $scope.maxImages: " + filesToUse.length < $scope.maxImages);
$scope.canUploadNewImage = filesToUse.length < $scope.maxImages;
$scope.$apply();
},
error: function () {
console.log("An error occured while retrieving image(s)")
},
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
},
dataType: 'json'
});
} else {
$scope.maxImages = maxImages;
$scope.canUploadNewImage = true;
$scope.files = [];
}
} else {
$scope.maxImages = maxImages;
$scope.canUploadNewImage = true;
$scope.files = [];
}
};
$scope.updateFiles = function(files) {
console.log("Updated files");
$scope.files = files;
};
// $scope.setFileIds = function () {
// var local_fileIds = [];
//
// angular.forEach($scope.files, function (file) {
// local_fileIds.push(file.id);
// });
//
// console.log("set file ids ($scope.fileIds):");
// console.log(local_fileIds);
// $scope.fileIds = local_fileIds
// };
// $scope.getFileCount = function () {
// return $scope.fileIds.length;
// };
$scope.sortableOptions = {
update: function (e, ui) {
ui.item.sortable.cancel();
var originalIndex = ui.item.sortable.index;
var newIndex = ui.item.sortable.dropindex
var oldImage = $scope.files[originalIndex]
$scope.files[originalIndex] = $scope.files[newIndex]
$scope.files[newIndex] = oldImage
$scope.setFileIds()
}
};
console.log('fileuploadcontroller initialized');
}]);
'use strict';
angular.element(document).ready(function () {
// angular.element(document.querySelector('[ng-controller]')).scope().debug();
// //console.log('page loading completed');
});
angular.module('kms.dynamic-attribute', [])
.controller('KmsAttributeDynamicController', function ($scope, $element, $compile, $timeout) {
//console.log('KmsAttributeDynamicController initializing');
// var htmlElement = document.querySelector("[ng-controller=KmsAttributeDynamicController]");
//console.log("Controller element");
//console.log($($element)[0]);
var htmlElement = $($element)[0];
//For which model and id of the model the dynamic attribute works
$scope.forModel = htmlElement.dataset.forModel;
$scope.forModelId = htmlElement.dataset.forModelId;
//console.log("KmsAttributeDynamicController further initializing For model: "+$scope.forModel+" with id: "+$scope.forModelId);
$scope.key = htmlElement.dataset.key;
//console.log("Controller key: "+$scope.key);
var removeBlocks = function () {
// Remove the block list for re-render
angular.element('#' + $scope.key + '_blocks .inner').remove();
};
// Check if any blocks exists to prevent JSON.parse error
// Default is an empty array
$scope.blocks = [];
var html = $('#' + $scope.key + '_data', $element).html();
//console.log("JSON to parse (in element: #"+$scope.key+"_data): "+html);
if (html != '' && html != '""' && html != undefined) {
$scope.blocks = JSON.parse(html);
} else {
//console.log("Element called: " + "#" + $scope.key + "_data does not have json data set");
}
//console.log('scope blocks');
//console.log($scope.blocks);
$scope.blockSettings = {};
var blockSettingsElement = document.getElementById('blockSettings');
if (blockSettingsElement) {
var block_settings = blockSettingsElement.value;
if (block_settings != '' && block_settings != '""') {
$scope.blockSettings = JSON.parse(block_settings);
}
} else {
//console.log("No blocksettings element");
}
//console.log("blocksettings");
//console.log($scope.blockSettings);
$scope.blockTypes = getBlockTypes($scope.blockSettings);
$scope.addBlock = function (blockType) {
if (!angular.isUndefined(blockType)) {
$scope.blocks.push(angular.copy(blockType));
}
};
$scope.removeBlock = function (block) {
startToMove();
var index = $scope.blocks.indexOf(block);
$scope.blocks.splice(index, 1);
stopToMove()
};
$scope.upBlock = function (block) {
//load index from the block
var index = $scope.blocks.indexOf(block);
//if the index is 0 return
if (index < 1) return;
//Remove the editor
startToMove()
//Set the block index-1 to current index
$scope.blocks[index] = $scope.blocks[index - 1];
//set the triggerd block to index-1
$scope.blocks[index - 1] = block;
//enable the editor
stopToMove()
};
$scope.downBlock = function (block) {
//load the index from the block
var index = $scope.blocks.indexOf(block);
//If the block is the last return false
if (index >= $scope.blocks.length - 1) return;
//Remove the editor
startToMove()
//set the block +1 to the current index
$scope.blocks[index] = $scope.blocks[index + 1];
//set the triggerd block to index +1
$scope.blocks[index + 1] = block;
//enable the editor
stopToMove()
};
$scope.deleteImage = function (direction, block_key, image_key, tab_key) {
if (tab_key == null) {
$scope.blocks[block_key].images.splice(image_key, 1)
} else {
$scope.blocks[block_key].tab[tab_key].images.splice(image_key, 1)
}
}
$scope.deleteFile = function (block_key) {
delete $scope.blocks[block_key].file
}
$scope.changeOrder = function (direction, block_key, image_key, tab_key) {
//load the index from the block
var index = image_key
if (tab_key == null) {
var temp_image = $scope.blocks[block_key].images[image_key];
switch (direction) {
case 'left':
if (index == 0) return;
$scope.blocks[block_key].images[image_key] = $scope.blocks[block_key].images[image_key - 1];
$scope.blocks[block_key].images[image_key - 1] = temp_image;
break;
case 'right' :
if (index + 1 == $scope.blocks[block_key].images.length) return;
$scope.blocks[block_key].images[image_key] = $scope.blocks[block_key].images[image_key + 1];
$scope.blocks[block_key].images[image_key + 1] = temp_image;
break;
}
} else {
var temp_image = $scope.blocks[block_key].tab[tab_key].images[image_key];
switch (direction) {
case 'left':
if (index == 0) return;
$scope.blocks[block_key].tab[tab_key].images[image_key] = $scope.blocks[block_key].tab[tab_key].images[image_key - 1];
$scope.blocks[block_key].tab[tab_key].images[image_key - 1] = temp_image
;
break;
case 'right' :
if (index + 1 == $scope.blocks[block_key].tab[tab_key].images.length) return;
$scope.blocks[block_key].tab[tab_key].images[image_key] = $scope.blocks[block_key].tab[tab_key].images[image_key + 1];
$scope.blocks[block_key].tab[tab_key].images[image_key + 1] = temp_image;
break;
}
}
}
/**
* Add new image tab
* @param block_index
*/
$scope.addImageTab = function (block_index) {
$scope.blocks[block_index].tab.push({title: ''})
var new_index = $scope.blocks[block_index].tab.length
//set the new tab as active (-1) arrays start at 0
$scope.setActiveTab(block_index, new_index - 1)
};
/**
* Remove image tab
* @param block_index
*/
$scope.removeImageTab = function (block_index, tab_index) {
$scope.blocks[block_index].tab.splice(tab_index, 1);
//set the active tab to 0
$scope.setActiveTab(block_index, 0)
};
$scope.setActiveTab = function (index, tab) {
$scope.blocks[index].active_tab = tab;
};
$scope.moveTab = function (direction, index, tab_index) {
var temptab = $scope.blocks[index].tab[tab_index]
switch (direction) {
case 'left':
if (tab_index == 0) return;
$scope.blocks[index].tab[tab_index] = $scope.blocks[index].tab[tab_index - 1]
$scope.blocks[index].tab[tab_index - 1] = temptab
break;
case 'right':
if (tab_index + 1 == $scope.blocks[index].tab.length) return;
$scope.blocks[index].tab[tab_index] = $scope.blocks[index].tab[tab_index + 1]
$scope.blocks[index].tab[tab_index + 1] = temptab
break;
}
};
$scope.setUniqueId = function (block_index) {
if (typeof $scope.blocks[block_index].blockId == 'undefined') {
var stamp = new Date().valueOf();
$scope.blocks[block_index].blockId = Math.round(stamp + Math.random() * 1000);
}
};
$scope.debug = function () {
//console.log('key: ' + $scope.key);
};
//console.log('KmsAttributeDynamicController initialized');
})
.directive('block', function ($compile) {
return {
restrict: 'E',
template: '<div class="header">' +
'<div class="start">' +
'<div class="block-title">{{block.typeName}}</div>' +
'<div class="buttons">' +
'<div class="position-button up" ng-click="upBlock(block)"></div>' +
'<div class="position-button down" ng-click="downBlock(block)"></div>' +
'</div></div>' +
'<div class="end">' +
'<div class="btn btn-danger btn-trash" ng-click="removeBlock(block)"> </div>' +
'</div></div>',
link: function (scope, element, attr) {
switch (scope.block.typeSlug) {
case 'content-block':
// //console.log('rendering a content block with this data: '+scope.block);
scope.imageBoxId = scope.key + '-image-box-' + scope.$index;
scope.textBoxId = scope.key + '-text-box-' + scope.$index;
//console.log("Rendering content-block (afbeelding en tekst)");
//console.log(scope.block);
element.append($compile("<content-block></content-block>")(scope));
break;
case 'view-block':
element.append($compile("<view-block></view-block>")(scope));
break;
case 'page-link-block':
element.append($compile("<page-link-block></page-link-block>")(scope));
break;
case 'image-slider-block':
element.append($compile("<image-slider-block></image-slider-block>")(scope));
break;
case 'video-block':
element.append($compile("<video-block></video-block>")(scope));
break;
case 'file-block':
element.append($compile("<file-block></file-block>")(scope));
break;
case 'two-column-block':
scope.textBoxIds = [];
scope.textBoxIds.left = scope.key + '-text-box-' + scope.$index + '-left';
scope.textBoxIds.right = scope.key + '-text-box-' + scope.$index + '-right';
element.append($compile("<two-column-block></two-column-block>")(scope));
break;
case 'full-text-block':
scope.textBoxId = scope.key + '-text-box-' + scope.$index;
element.append($compile("<full-text-block></full-text-block>")(scope));
break;
case 'full-image-block':
element.append($compile("<full-image-block></full-image-block>")(scope));
break;
case 'multiple-images-block':
element.append($compile("<multiple-images-block></multiple-images-block>")(scope));
break;
}
// //console.log("block data element: "+attr.dataElement);
scope.dataElement = document.querySelector(attr.dataElement);
scope.getBlocksData = function() {
return JSON.parse(scope.dataElement.getAttribute("value"));
};
scope.setBlocksData = function(data) {
scope.dataElement.setAttribute("value", JSON.stringify(data));
}
}
}
})
.directive('contentBlock', function ($timeout) {
var count = 0;
return {
restrict: 'E',
templateUrl: '/kms/api/template/dynamic-page-content-block',
link: function (scope, element, attrs) {
scope.image = 'imageHolder';
scope.loopCount = count;
count++;
scope.loopCount = scope.key;
// //console.log("getting content block data");
// //console.log(scope.getBlocksData();
//initiate the tinyMce editor
initiateTinyMceWithDelay(scope, $timeout, 100);
scope.openBox = function (imageBoxId) {
$.colorbox({
href: '/kms/elfinder/standalonepopup/' + imageBoxId,
fastIframe: true,
iframe: true,
width: '70%',
height: '520px'
});
};
}
};
})
.directive('viewBlock', function ($timeout) {
var count = 0;
return {
restrict: 'E',
templateUrl: '/kms/api/template/dynamic-view',
link: function (scope) {
}
};
})
.directive('twoColumnBlock', function ($timeout) {
var count = 0;
return {
restrict: 'E',
templateUrl: '/kms/api/template/two-column-block',
link: function (scope, element, attrs) {
scope.image = 'imageHolder',
scope.loopCount = count
count++;
scope.loopCount = scope.key
//initiate the tinyMce editor
initiateTinyMceWithDelay(scope, $timeout, 100)
scope.openBox = function (imageBoxId) {
$.colorbox({
href: '/kms/elfinder/standalonepopup/' + imageBoxId,
fastIframe: true,
iframe: true,
width: '70%',
height: '520px'
});
};
}
};
})
.directive('imageSliderBlock', function () {
return {
restrict: 'E',
templateUrl: '/kms/api/template/dynamic-image-slider-block',
link: function (node) {
////console.log(node);
}
}
})
.directive('fullImageBlock', function () {
return {
restrict: 'E',
templateUrl: '/kms/api/template/dynamic-full-image-block',
link: function (node) {
////console.log(node);
}
}
})
.directive('fullTextBlock', function ($timeout) {
return {
restrict: 'E',
templateUrl: '/kms/api/template/dynamic-full-text-block',
link: function (scope) {
//initiate the tinyMce editor
initiateTinyMceWithDelay(scope, $timeout, 100)
}
}
})
.directive('multipleImagesBlock', function () {
return {
restrict: 'E',
templateUrl: '/kms/api/template/dynamic-multiple-images-block',
link: function (node) {
////console.log(node);
}
}
})
.directive('pageLinkBlock', function ($http) {
return {
restrict: 'E',
templateUrl: '/kms/api/template/dynamic-page-link-block',
link: function (scope, element, attr) {
scope.pages = [];
$http.get('/kms/api/pages').then(function (response) {
// Adopt data
var pages = [];
var getChildren = function (node) {
for (var child in node) {
if (node[child].title) {
pages.push({
value: node[child].id,
label: node[child].title
});
}
getChildren(node[child].children);
}
};
getChildren(response.data);
// Set the data
scope.pages = pages;
});
}
};
})
.directive('videoBlock', function () {
return {
restrict: 'E',
templateUrl: '/kms/api/template/dynamic-video-block',
link: function (node) {
////console.log(node);
}
}
})
.directive('fileBlock', function () {
return {
restrict: 'E',
templateUrl: '/kms/api/template/dynamic-file-block',
link: function (node) {
////console.log(node);
}
}
});
/**
* Callback function for ElFinder (hard to implement in angular?)
*
* @param filePath
* @param requestingElementId
*/
function processSelectedFile(filePath, requestingElementId) {
// Get the scope of the the element that is passed into ElFinder
var elementScope = $('#' + requestingElementId).parent().parent().scope();
elementScope.block.image = filePath;
elementScope.$apply();
}
/**
* This function will initiate the tinyMce editor on an block
* It the initiation will be delayed because of the angular
*
* @param $scope
* @param $timeout
*/
function initiateTinyMceWithDelay($scope, $timeout, delay) {
//if (tinymce) tinymce.remove();
$timeout(function () {
initiateTinyMce($scope, $scope.textBoxId)
if (typeof $scope.textBoxIds !== 'undefined') {
if ($scope.textBoxIds.left != null) initiateTinyMce($scope, $scope.textBoxIds.left, 'description', 'left')
if ($scope.textBoxIds.right != null) initiateTinyMce($scope, $scope.textBoxIds.right, 'description', 'right')
}
}, delay);
}
function initiateTinyMce($scope, field, blockElement, blockSubElement) {
if (typeof field === 'undefined') {
field = $scope.textboxId;
}
if (typeof blockElement === 'undefined') {
blockElement = 'description';
}
//textboxIds[] =
tinymce.init({
elements: field,
mode: 'exact',
skin: 'kms',
menubar: false,
statusbar: false,
plugins: ['code', 'paste', 'link', 'table'],
toolbar: 'styleselect | bold italic underline | bullist numlist | link | code',
height: '200',
default_link_target: "_blank",
paste_as_text: true,
style_formats: [{title: 'Titel 1', block: 'h1'}, {title: 'Titel 2', block: 'h2'}, {title: 'Titel 3', block: 'h3'}, {title: 'Titel 4', block: 'h4'}],
convert_urls: false,
// link_list: "/kms/file-list?key=komma_kms",
setup: function (editor) {
editor.on('change', function (e) {
editor.save();
if (typeof blockSubElement === 'undefined') {
$scope.block[blockElement] = editor.getContent();
} else {
$scope.block[blockElement][blockSubElement] = editor.getContent();
}
if (!$scope.$$phase) {
$scope.$apply();
}
});
// Update model on keypress
editor.on('KeyUp', function (e) {
editor.save();
if (typeof blockSubElement === 'undefined') {
$scope.block[blockElement] = editor.getContent();
} else {
$scope.block[blockElement][blockSubElement] = editor.getContent();
}
if (!$scope.$$phase) {
$scope.$apply();
}
});
}
});
}
function startToMove() {
$(document).find('textarea').each(function () {
tinyMCE.execCommand('mceFocus', false, $(this).attr('id'));
tinyMCE.execCommand('mceRemoveEditor', false, $(this).attr('id'));
});
}
function stopToMove() {
//todo: Reanbling ONLY works after a timeout.
setTimeout(function () {
$(document).find('textarea').each(function () {
tinymce.execCommand('mceAddEditor', true, $(this).attr('id'));
});
}, 100);
}
function getBlockTypes(blockSettings) {
var blocks = {
two_column_block: {
typeSlug: 'two-column-block',
code_name: '',
view: '',
typeName: 'Twee tekstkolommen',
status: true,
description: {left: '', right: ''},
textWidth: '50',
},
content_block: {
typeSlug: 'content-block',
code_name: '',
view: '',
typeName: 'Afbeelding en tekst',
status: true,
location: 'left',
textWidth: '66',
description: '',
subFolder: 'dynamic',
image: null,
link: '',
link_text: 'Lees meer',
},
view_block: {
typeSlug: 'view-block',
code_name: '',
view: '',
typeName: 'View',
status: true,
},
full_image_block: {
typeSlug: 'full-image-block',
code_name: '',
view: '',
typeName: '100% Afbeelding',
subFolder: 'dynamic',
max_images: 1,
status: true,
image: null
},
full_text_block: {
typeSlug: 'full-text-block',
code_name: '',
view: '',
typeName: 'Een tekstkolom',
description: '',
status: true,
link: '',
link_text: 'Lees meer'
},
multiple_images_block: {
typeSlug: 'multiple-images-block',
code_name: '',
view: '',
typeName: 'Meerdere afbeeldingen',
tab: [
{
title: 'Afbeelding 1', max_images: 1}, {
title: 'Afbeelding 2a',
max_images: 2
},
{
title: 'Afbeelding 2b (optioneel)',
max_images: 1}
],
status: true,
location: 'left',
subFolder: 'dynamic',
textWidth: '66',
},
page_link_block: {
typeSlug: 'page-link-block',
code_name: '',
view: '',
typeName: 'Page-link block',
link_text: 'Lees meer',
special: 0,
status: true,
pageId: null
},
image_slider_block: {
typeSlug: 'image-slider-block',
code_name: '',
view: '',
typeName: 'Image gallery',
tab: [{title: ''}],
subFolder: 'dynamic',
status: true,
pageId: null
},
video_block: {
typeSlug: 'video-block',
code_name: '',
view: '',
typeName: 'Video',
youtube: '',
status: true,
pageId: null
},
file_block: {
typeSlug: 'file-block',
code_name: '',
view: '',
typeName: 'Bestand block',
title: '',
special: 0,
download_text: 'Download',
file: '',
status: true,
pageId: null
}
};
var blockTypes = {};
angular.forEach(blockSettings, function (block, blockName) {
blockTypes[blockName] = angular.merge(blocks[blockName], block);
});
return blockTypes;
}
'use strict';
var attributes = {};
var app = angular.module('kms', [
'ui.bootstrap',
'ngSanitize',
'kms.dynamic-attribute',
'fileUpload'
// 'ui.select',
// 'ui.date',
// 'ui.tree',
// 'ui.sortable',
// 'ui.utils.masks',
]);
app.controller('KmsEntities',[ '$scope', '$element',function ($scope, $element) {
$scope.entities = [];
$scope.paginatedEntities = [];
$scope.isSorting = false;
$scope.isSearching = function () {
return $scope.searchEntitiesText.text.length > 1;
};
$scope.currentPage = 1;
$scope.numPerPage = 5;
$scope.maxSize = 5;
var entityList = $('.entities-list-items .entities-list-item', $element);
for (var i = 0; i < entityList.length; i++) {
var dataElement = entityList[i];
var dataObject = {
thumbHtml: $('.entities-item-pre', dataElement).html(),
text: $('.entities-item-text', dataElement).html(),
status: $('.entities-item-status', dataElement).html(),
link: $('a', dataElement).first().attr('href'),
active: $(dataElement).hasClass('active')
};
$scope.entities.push(dataObject);
}
$scope.activateSorting = function () {
$scope.isSorting = true;
};
$scope.deactivateSorting = function () {
$scope.isSorting = false;
};
$scope.$watch("currentPage + numPerPage", function () {
var begin = (($scope.currentPage - 1) * $scope.numPerPage)
, end = begin + $scope.numPerPage;
$scope.paginatedEntities = $scope.entities.slice(begin, end);
});
}]);
app.controller('KmsEntity',[ '$scope', '$element',function ($scope, $element) {
// console.log("attaching save to scope");
$scope.save = function () {
var button = document.querySelector('input[value="submit-cloaked"]');
// console.log(button);
button.click();
};
}]);
app.controller('SortableTree',['$scope', '$http', '$attrs' ,function ($scope, $http, $attrs) {
$http.defaults.headers.post["Content-Type"] = "application/x-www-form-urlencoded";
$scope.data = [];
$scope.treeOptions = {
dropped: function (event) {
$http({
method: 'POST',
url: '/kms/api/' + $attrs.siteslug + '/' + $attrs.slug,
data: {"tree": angular.toJson($scope.data)}
}).then(function (response) {
});
}
};
$scope.openLink = function (id) {
window.location = $scope.url + '/' + id;
};
$scope.toggle = function (scope) {
scope.toggle();
};
var getRootNodesScope = function () {
return angular.element(document.getElementById('tree-root').scope());
};
$scope.collapseAll = function () {
var scope = getRootNodesScope();
scope.collapseAll();
};
$scope.expandAll = function () {
var scope = getRootNodesScope();
scope.expandAll();
};
// console.log('/kms/api/' + $attrs.siteslug + '/' + $attrs.slug);
$http.get('/kms/api/' + $attrs.siteslug + '/' + $attrs.slug)
.then(function (response) {
// console.log("Kms.js SortableTree controller got data from '"+'/kms/api/' + $attrs.siteslug + '/' + $attrs.slug+"':");
// console.log(angular.toJson(response.data));
$scope.data = response.data[0]['children'];
});
}]);
app.controller('KmsAttributes',['$scope', function ($scope) {
$scope.attributes = attributes;
// console.log("$scope.attributes (via KmsAttributes)");
// console.log($scope.attributes);
// $scope.createModelString = function (modelKey, $itemKey) {
//
//
// //Check if the modelKey is an array structure (field_name[x])
// var match = modelKey.match(/(.*)\[(.*)]/)
//
// //There is no match, so a basic key
// if (match == null) {
// //SEt the modelKey as modelString
// $scope.modelString = modelKey
// //ItemKy is null
// $scope.itemKey = null;
// //return and exit
// return $scope.modelString
// }
//
// //Set the match[2] as the itemKey
// var itemCounter = match[2];
//
// //If the itemKey is not undefined and itemkey is the string itemKey
// if (typeof($itemKey) != "undefined" && match[2] == 'itemKey') {
// //Set the $itemKey to the scope itemKey
// itemCounter = $itemKey
// }
// //Glue the parts together
// var modelString = match[1] + '_' + itemCounter
//
// //Set to the global modelString
// $scope.modelString = modelString;
// $scope.itemCounter = itemCounter;
//
// //Return the string
// return modelString
// }
/**
* Wrapper for Json.parse()
* So i can parse Json in blade
* @param value
*/
$scope.parse = function (value) {
//If there is no value return;
if (!value) {
console.log("Parse without a value");
return;
}
//If it is already an object, return the value
if (typeof(value) == 'object') {
console.log("Parse value: "+value);
return value;
}
//Parse the string to a Json object
console.log("Parse string to Json: "+JSON.parse(value));
return JSON.parse(value);
};
$scope.convertToSlug = function (str) {
str = str.replace(/^\s+|\s+$/g, ''); // trim
str = str.toLowerCase();
// remove accents, swap ñ for n, etc
var from = "ãàáäâẽèéëêìíïîõòóöôùúüûñç·/_,:;";
var to = "aaaaaeeeeeiiiiooooouuuunc------";
for (var i = 0, l = from.length; i < l; i++) {
str = str.replace(new RegExp(from.charAt(i), 'g'), to.charAt(i));
}
str = str.replace(/[^a-z0-9 -]/g, '') // remove invalid chars
.replace(/\s+/g, '-') // collapse whitespace and replace by -
.replace(/-+/g, '-'); // collapse dashes
return str;
};
}]);
app.controller('KmsAttributeTextFieldController',['$scope', '$element' ,function ($scope, $element) {
$scope.attributes = attributes;
}]);
app.controller('KmsAttributePasswordController',['$scope', '$element' ,function ($scope, $element) {
$scope.attributes = attributes;
//Set enabler true (show enable bottom)
$scope.enabler = true
//Change the password field to a hidden field
$('.passwordField', $element).attr('type', 'hidden')
//Method called when clikced on the enable button
$scope.enablePasswordField = function () {
//Change the type of the password field to password
$('.passwordField', $element).attr('type', 'password')
//Disable the enable buttom
$scope.enabler = false
}
}]);
app.controller('KmsAttributeDateController',['$scope', '$element', function ($scope, $element) {
$scope.attributes = attributes;
$('input[ui-date]', $element).datepicker({
changeYear: true,
changeMonth: true,
firstDay: 1,
dateFormat: 'dd-mm-yy',
});
}]);
app.controller('KmsAttributeCurrencyFieldController',['$scope', '$element','$locale', function ($scope, $element, $locale) {
$scope.attributes = attributes;
$scope.initCurrencyField = function (dec_value, currency) {
if (typeof(currency) !== 'undefined') {
//Set currencySymbol
$locale.NUMBER_FORMATS.CURRENCY_SYM = currency;
}
//Set the value an de dec_value
attributes[$scope.modelString] = {"value": dec_value / 100, "dec_value": dec_value}
$scope.$watch('attributes.' + $scope.modelString + '.value', function (newValue, oldValue) {
if (newValue === oldValue) return;
attributes[$scope.modelString].dec_value = newValue * 100
});
}
}]);
app.controller('KmsAttributePercentageFieldController',['$scope', '$element','$timeout', function ($scope, $element, $timeout) {
$scope.attributes = attributes;
}]);
app.controller('KmsAttributeTextFieldCurrencyController',['$scope', '$element','$timeout', function ($scope, $element, $timeout) {
$scope.attributes = attributes;
var id = $('input', $element).attr('id'); // get the id of the hidden field
var taxSelectFieldId = $('[data-kms-tax-field]', $element).attr('data-kms-tax-field');
var taxFieldId = id + '_tax';
var noTaxFieldId = id + '_no_tax';
var parseCurrencyStringToCents = function (currencyString) {
var val = currencyString.toString();
val = val.replace(/[.]/g, '');
val = val.replace(/[,]/g, '.');
val = val.replace(/[^0-9\.]+/g, '');
val = val * 100;
return val;
};
var addTax = function (value) {
if (attributes[taxSelectFieldId].selected) {
var factor = 1 + (attributes[taxSelectFieldId].selected.fullValue.rate / 100);
return value * factor;
}
};
var subtractTax = function (value) {
if (attributes[taxSelectFieldId].selected) {
var factor = 1 + (attributes[taxSelectFieldId].selected.fullValue.rate / 100);
return value / factor;
}
};
$scope.$watch('attributes.' + taxFieldId, function (newValue, oldValue) {
if (newValue !== oldValue) {
$scope.attributes[id] = parseCurrencyStringToCents(newValue);
}
});
$scope.$watch('attributes.' + noTaxFieldId, function (newValue, oldValue) {
if (newValue !== oldValue) {
$scope.attributes[id] = Math.round(addTax(parseCurrencyStringToCents(newValue)));
}
});
var watcherMain = function (newValue, oldVal) {
if ($.isNumeric($scope.attributes[id])) {
var $taxField = $('#' + taxFieldId);
var $noTaxField = $('#' + noTaxFieldId);
if (!$taxField.is(':focus')) {
$taxField.val($scope.attributes[id] / 100)
}
if (!$noTaxField.is(':focus')) {
$noTaxField.val(Math.round(subtractTax($scope.attributes[id])) / 100)
}
}
};
// Clear fields on blur if no value
$('#' + taxFieldId + ', #' + noTaxFieldId).blur(function (e) {
if ($scope.attributes[id] == 0) {
$('#' + taxFieldId + ', #' + noTaxFieldId).val(0);
}
});
$scope.$watch('attributes.' + id, watcherMain);
$scope.$watch('attributes.' + taxSelectFieldId + '.selected', watcherMain);
$scope.attributes[id] = $('input', $element).attr('value'); // get the value of the hidden field
$timeout(watcherMain, 500);
}]);
app.controller('KmsAttributeMultiSelectController',['$scope', '$element', function ($scope, $element) {
$scope.attributes = attributes;
$scope.selectData = [];
/**
* Initialize the choices for ui-select
*
* @param choices | string
* @returns {*}
*/
$scope.initChoices = function (choices) {
//If there are no choices;
if (!choices) return;
//If it is not already an object, make one from json
if (typeof(choices) != 'object') choices = JSON.parse(choices)
//Set the choices to the selectData
$scope.selectData = choices
return choices
};
$scope.setValue = function (values) {
if (!values) return;
// compare "values" array and "$scope.selectData" array
var result = [];
for (var i = 0; i < $scope.selectData.length; i++) {
var valueInElement = false;
for (var j = 0; j < values.length; j++) {
if ($scope.selectData[i].value == values[j]) {
valueInElement = true;
break;
}
}
if (valueInElement) result.push($scope.selectData[i]);
}
// $scope.attributes[id] = angular.toJson(values);
// $scope.attributes[id + '_select'] = result;
return result;
};
}]);
app.controller('KmsAttributeSlugController',['$scope', '$element', function ($scope, $element) {
$scope.attributes = attributes
//Get the slugField key
var slugField = $('[data-kms-slug-field]', $element).attr('data-kms-slug-field');
//Change [[itemCounter]] with the $scope.itemCounter (fieldGroup)
slugField = slugField.replace('[[itemCounter]]', $scope.itemCounter);
//Watch the slugField
$scope.$watch('attributes.' + slugField, function (newValue, oldValue) {
//When nothing changes, do nothing
if (newValue === oldValue) return;
//Set the string converted to Slug to the field
attributes[$scope.modelString] = $scope.convertToSlug(attributes[slugField]);
});
}]);
app.controller('KmsAttributeFieldGroupController', ['$scope', '$element',function ($scope, $element) {
$scope.number = 0;
$scope.itemKey = 1;
$scope.maxFieldGroups = null;
$scope.getNumber = function () {
return new Array($scope.number);
}
$scope.addRow = function () {
if ($scope.itemKey >= $scope.maxFieldGroups) return
//Add one to the itemKey
$scope.itemKey++;
//Addd one to the numbers
$scope.number++;
}
/**
* Initialize the given values
* @param itemKey | Amount (-1) of items fieldGroups
* @param maxFieldGroups | max fieldgroups
*/
$scope.init = function (itemKey, maxFieldGroups) {
//Items in the fieldGroup
$scope.itemKey = itemKey
//Max items in fieldGroup
$scope.maxFieldGroups = maxFieldGroups
//If it is a new, add a row
if (itemKey == 0) $scope.addRow();
}
}]);
app.controller('KmsAttributeRouteController',['$scope', '$element', function ($scope, $element) {
$scope.attributes = attributes;
var id = $('input', $element).attr('id');
attributes[id] = $('input', $element).attr('value');
//Get the name of the slugField
var slugField = $('[data-kms-slug-field]', $element).attr('data-kms-slug-field');
//Change [[itemCounter]] with the $scope.itemCounter (fieldGroup)
slugField = slugField.replace('[[itemCounter]]', $scope.itemCounter)
//Get the name of the parentField
var parentField = $('[data-kms-parent-field]', $element).attr('data-kms-parent-field');
//Set the languageId
var languageId = $('[data-kms-language-id]', $element).attr('data-kms-language-id');
//Set the structure
var structure = $('[data-kms-structure]', $element).attr('data-kms-structure');
/**
* This function generates the correct Route
* when a watched field is changed
*
* @param newValue
* @param oldValue
*/
var parseStructure = function (newValue, oldValue) {
//If the value is the same, stop and return
if (newValue === oldValue) return;
//Create an empty parentSlug string
var parentSlug = '';
// todo: make this work
//Check if the parentField exist in then attributes
if (attributes[parentField]) {
//Check if the parentField has an selected.routes object
if (!attributes[parentField].selected.routes) {
//If not trow an error
console.error('Routes are not set in the ' + parentField + 'field')
}
//Set the routes for the current language to parentSlug
parentSlug = attributes[parentField].selected.routes[languageId];
}
//Create newRoute
var newRoute = '';
//Replace {{parentSlugs}} with the parentSlug
newRoute = structure.replace('{{parentSlugs}}', parentSlug);
//Replace {{slug}} with the Slug
newRoute = newRoute.replace('{{slug}}', $scope.convertToSlug(newValue));
//Set the new route to the attribute of the current $scope.modelString
attributes[$scope.modelString] = newRoute
};
//Set a watch on the slugField, and execute the parseStructure method on a change
$scope.$watch('attributes.' + slugField, parseStructure);
//If there is an parentField
if (attributes[parentField]) {
//Set a watch on the parentField, and execute the parseStructure method on a change
$scope.$watch('attributes.' + parentField + '.selected', parseStructure);
}
}]);
app.controller('KmsAttributeSorterController',['$scope', '$element', function ($scope, $element) {
$scope.attributes = attributes;
}]);
app.filter("multiWordFilter", ['$filter', function ($filter) {
console.log('hoi');
return function (inputArray, searchText) {
var wordArray = searchText ? searchText.toLowerCase().split(/\s+/) : [];
var wordCount = wordArray.length;
for (var i = 0; i < wordCount; i++) {
// Hack for filtering on text parameter: {text:wordArray[i]}
inputArray = $filter('filter')(inputArray, {text: wordArray[i]});
}
return inputArray;
}
}]);
app.filter('valuesToArrayFilter', function () {
return function (input) {
if (!input) return;
var output = input.map(function (a) {
return a.value;
});
return output
}
});
/**
* AngularJS default filter with the following expression:
* "person in people | filter: {name: $select.search, age: $select.search}"
* performs a AND between 'name: $select.search' and 'age: $select.search'.
* We want to perform a OR.
*/
app.filter('propsFilter', function () {
return function (items, props) {
var out = [];
if (angular.isArray(items)) {
items.forEach(function (item) {
var itemMatches = false;
var keys = Object.keys(props);
for (var i = 0; i < keys.length; i++) {
var prop = keys[i];
var text = props[prop].toLowerCase();
if (item[prop].toString().toLowerCase().indexOf(text) !== -1) {
itemMatches = true;
break;
}
}
if (itemMatches) {
out.push(item);
}
});
} else {
// Let the output be the input untouched
out = items;
}
return out;
};
});
var IsJsonString = function (str) {
try {
JSON.parse(str);
} catch (e) {
return false;
}
return true;
};
$(function(){
var activeListItem = $('#sidebar .navigation li.active');
var activeListItemParents = activeListItem.parents();
var activeListItemParentsLength = activeListItemParents.length;
for(var i = 0; i <= activeListItemParentsLength; i++){
var node = activeListItemParents[i];
if(node && node.tagName == 'LI' && node.classList.contains('has-sub-items')){
node.classList.add('active');
}
}
var siteListItems = document.querySelectorAll('#sidebar .navigation .has-sub-items');
var siteListItemsLength = siteListItems.length;
for(var i = 0; i < siteListItemsLength; i++){
var siteListItem = siteListItems[i];
siteListItem.addEventListener('click', function () {
this.classList.toggle('active');
});
}
});
(function ($) {
// TinyMCE
tinymce.init({
selector: 'textarea.tiny-mce',
skin: 'kms',
menubar: false,
statusbar: false,
plugins: ['code', 'paste', 'link'],
toolbar: 'styleselect | bold italic underline | bullist numlist | link image | code',
height: '200',
paste_as_text: true,
style_formats: [{title: 'Titel 1', block: 'h1'}, {title: 'Titel 2', block: 'h2'}, {title: 'Titel 3', block: 'h3'}, {title: 'Titel 4', block: 'h4'}],
convert_urls: false,
// link_list: "/kms/file-list?key=komma_kms",
});
// Hierarchical list
$('.entities-list-item .dropdown-icon').click(function (e) {
e.preventDefault();
if ($(this).parent().parent().hasClass('open')) {
//$('.animate-to-triangle', this)[0].beginElement();
$(this).parent().parent().removeClass('open');
} else {
//$('.animate-to-minus', this)[0].beginElement();
$(this).parent().parent().addClass('open');
}
});
var toggled = true;
// Scroll to active item in list
$(window).load(function () {
if ($('#entity-form .lock').hasClass('open')) {
toggled = false;
$('#entity-form').find('input, textarea').attr('disabled', toggled);
}
var $container = $('#entities .entities-list');
var $activeListItem = $('.entities-list-item.active', $container).first();
if ($activeListItem.length == 0) return;
var top = $activeListItem.position().top;
$container.scrollTop(top);
$('.site-brand-name input').attr('placeholder', $('#global_name').val())
});
$('#global_name').change(function () {
$('.site-brand-name input').attr('placeholder', $(this).val())
});
//error accordian
$('.error-accordion .collapsible-ul').hide();
$('.error-accordion h3').click(function () {
$(this).parent().find('.collapsible-ul').toggle();
});
//$('.order-status.selectize').selectize();
// Product category selector
//$('#productCategorySelector').selectize();
//$('#selectYearAndMonthForm select').selectize();
$('#selectYearAndMonthForm select').change(function () {
var location = '/kms/orders/voltooid?month=' + $('#selectYearAndMonthForm select#orderMonthSelector').val() + '&year=' + $('#selectYearAndMonthForm select#orderYearSelector').val();
window.location = location;
})
$('#productCategorySelector').change(function () {
window.location = '/kms/products?category=' + encodeURIComponent(this.value);
});
// Flash messages
var hideFlashMessage = function () {
$('#flash-message').fadeOut();
};
$('#flash-message').click(function () {
hideFlashMessage();
});
setTimeout(function () {
hideFlashMessage();
}, 5000);
//var toggled = true;
$('#entity-form .lock').click(function () {
$(this).toggleClass('open')
toggled = !toggled;
$(this).parents('#entity-form').find('input, textarea').attr('disabled', toggled);
})
/* grab important elements */
var sortInput = jQuery('#sort_order');
var submit = jQuery('#autoSubmit');
var messageBox = jQuery('#message-box');
var list = jQuery('.fieldGroupItems');
/* create requesting function to avoid duplicate code */
/* worker function */
var fnSubmit = function(save) {
var sortOrder = [];
list.children('li').each(function(){
sortOrder.push(jQuery(this).data('id'));
});
sortInput.val(sortOrder.join(','));
console.log(sortInput.val());
if(save) {
request();
}
};
/* store values */
list.children('li').each(function() {
var li = jQuery(this);
li.data('id',li.attr('title')).attr('title','');
});
/* sortables */
list.sortable({
opacity: 0.7,
update: function() {
}
});
/* ajax form submission */
jQuery('#dd-form').bind('submit',function(e) {
if(e) e.preventDefault();
fnSubmit(true);
});
})(jQuery);
function toggleOnOff($id) {
let onOffBox = document.getElementById($id);
let onOffSwitch = document.getElementById($id + '-switch');
if(onOffBox.checked){
onOffBox.value = '1';
onOffSwitch.classList.add('on');
}
else{
onOffBox.value = '0';
onOffSwitch.classList.remove('on');
}
}
function toggleOnOffSwitch($id) {
let onOffBox = document.getElementById($id);
onOffBox.checked = (onOffBox.checked) ? onOffBox.checked = false : onOffBox.checked = true;
toggleOnOff($id);
}
class PasswordController {
/**
* Validates password fields.
* The password fields values wil be imploded with a pipe symbol
*
* @param firstPasswordInputSelector The css selector for selecting the first password input field
* @param secondPasswordInputSelector The css selector for selecting the second password input field
* @param wrapperSelector The css selector for the wrapper div / element that wraps the first and second input fields along the validation helper
* @param passwordDontMatchErrorText string
* @param saveButtonId The id of a save button that needs to be disabled when the passwords are not valid
* @param minPasswordLength int
* @param wrapperHasTitleAttributeAndErrorClass Must be set to true if the wrapper (selected with the wrapper selector) has the ability to have an error class and title attribute for displaying an error
*/
constructor(firstPasswordInputSelector, secondPasswordInputSelector, wrapperSelector, passwordDontMatchErrorText, saveButtonId, minPasswordLength = 6, wrapperHasTitleAttributeAndErrorClass = false) {
this.wrapperHasTitleAttributeAndErrorClass = wrapperHasTitleAttributeAndErrorClass;
this.wrapper = document.querySelector(wrapperSelector);
this.saveButton = document.querySelector("#"+saveButtonId);
console.log(this.saveButton);
this.firstPasswordInput = this.wrapper.querySelector(firstPasswordInputSelector);
this.secondPasswordInput = this.wrapper.querySelector(secondPasswordInputSelector);
this.validationMessageWrapperSelector = '.validationHelper';
this.validationMessageWrapper = this.wrapper.querySelector(this.validationMessageWrapperSelector);
this.minPasswordLength = minPasswordLength;
//Used for validation
this.letter = this.validationMessageWrapper.querySelector(".letter");
this.capital = this.validationMessageWrapper.querySelector(".capital");
this.number = this.validationMessageWrapper.querySelector(".number");
this.length = this.validationMessageWrapper.querySelector(".length");
this.match = this.validationMessageWrapper.querySelector(".match");
// this.noPipe = document.querySelector(this.validationMessageWrapperSelector + " .noPipe");
this.activateListeners(true);
}
/**
* Activates listening for keyup events on the password fields to that the passwordChanged method is triggered
*
* @param state
*/
activateListeners(state) {
let self = this;
let validationHelper = this.validationMessageWrapper;
if (state) {
this.firstPasswordInput.addEventListener('keyup',
self.debounce(function () {
self.passwordChanged()
}, 100)
);
this.secondPasswordInput.addEventListener('keyup',
self.debounce(function () {
self.passwordChanged()
}, 100)
);
this.firstPasswordInput.addEventListener('focus', function () {
if (!validationHelper.classList.contains('active')) validationHelper.classList.add('active')
});
this.secondPasswordInput.addEventListener('focus', function () {
if (!validationHelper.classList.contains('active')) validationHelper.classList.add('active')
});
this.firstPasswordInput.addEventListener('blur', function () {
if (validationHelper.classList.contains('active')) validationHelper.classList.remove('active')
});
this.secondPasswordInput.addEventListener('blur', function () {
if (validationHelper.classList.contains('active')) validationHelper.classList.remove('active')
});
} else {
this.firstPasswordInput.removeEventListener('keyup', self.debounce);
this.secondPasswordInput.removeEventListener('keyup', self.debounce);
this.firstPasswordInput.removeEventListener('focus');
this.secondPasswordInput.removeEventListener('focus');
this.firstPasswordInput.removeEventListener('blur');
this.secondPasswordInput.removeEventListener('blur');
}
}
/**
* Triggered when one of the password fields are changed and if the listeners for those fields are active
*/
passwordChanged() {
console.log('password changed');
let value1 = this.firstPasswordInput.value;
let value2 = this.secondPasswordInput.value;
let valid = (this.validate(value1, value2));
console.log(valid);
this.enableValidMessage(valid);
this.enableSaveButton(valid);
this.removeWrapperError();
}
enableValidMessage(enable)
{
let validationHelper = this.validationMessageWrapper;
if(enable) {
if(!validationHelper.classList.contains('valid')) validationHelper.classList.add('valid');
} else {
if(validationHelper.classList.contains('valid')) validationHelper.classList.remove('valid');
}
}
enableSaveButton(enable)
{
console.log(enable);
if(enable) {
if(this.saveButton.classList.contains('disabled')) this.saveButton.classList.remove('disabled');
} else {
if(!this.saveButton.classList.contains('disabled')) this.saveButton.classList.add('disabled');
}
}
/**
* Removes the error that may be set on the wrapper
*/
removeWrapperError()
{
if(!this.wrapperHasTitleAttributeAndErrorClass) return;
if(this.wrapper.hasAttribute('title')) this.wrapper.setAttribute('title', '');
if(this.wrapper.classList.contains('error')) this.wrapper.classList.remove('error');
}
/**
* Validate two values and return true or false if respectively valid or not
*
* @param value1
* @param value2
* @returns {boolean}
*/
validate(value1, value2)
{
let valid = true;
// Validate lowercase letters
let lowerCaseLetters = /[a-z]/g;
if(value1.match(lowerCaseLetters)) {
this.letter.classList.remove("invalid");
this.letter.classList.add("valid");
} else {
this.letter.classList.remove("valid");
this.letter.classList.add("invalid");
valid = false;
}
// Validate capital letters
let upperCaseLetters = /[A-Z]/g;
if(value1.match(upperCaseLetters)) {
this.capital.classList.remove("invalid");
this.capital.classList.add("valid");
} else {
this.capital.classList.remove("valid");
this.capital.classList.add("invalid");
valid = false;
}
// Validate numbers
let numbers = /[0-9]/g;
if(value1.match(numbers)) {
this.number.classList.remove("invalid");
this.number.classList.add("valid");
} else {
this.number.classList.remove("valid");
this.number.classList.add("invalid");
valid = false;
}
// Validate numbers
// let pipe = /\|/g;
// if(value1.match(pipe)) {
// this.noPipe.classList.remove("valid");
// this.noPipe.classList.add("invalid");
// valid = false;
// } else {
// this.noPipe.classList.remove("invalid");
// this.noPipe.classList.add("valid");
// }
// Validate length
if(value1.length >= this.minPasswordLength) {
this.length.classList.remove("invalid");
this.length.classList.add("valid");
} else {
this.length.classList.remove("valid");
this.length.classList.add("invalid");
valid = false;
}
// Validate password match
if(value1 === value2 && (value1 !== '' || value2 !== '')) {
this.match.classList.remove("invalid");
this.match.classList.add("valid");
} else {
this.match.classList.remove("valid");
this.match.classList.add("invalid");
valid = false;
}
return valid
}
/**
* Triggers the function after the wait time (milliseconds) has expired and only
* if the function is not triggered again before the wait time expired.
*
* @param func
* @param wait
* @param immediate
* @returns {Function}
*/
debounce(func, wait, immediate) {
let timeout;
return function() {
let context = this, args = arguments;
let later = function() {
timeout = null;
if (!immediate) func.apply(context, args);
};
let callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
};
}
/**
* Fills an ul element with data retrieved from an api and makes the items searchable.
/* The data to and from the api has this structure for example.
*
* [
* {
* id:1
* title:false
* thumbnail:false
* status: "active"
* routes: []
* children: [
* {
* id: 12
* title: "Thuis"
* thumbnail: false
* routes: [{
* 40: "en/Homenew",
* 104: "nl/Thuisnew"
* }]
* }
* ]
* }
* ]
*/
class SearchController {
constructor(sectionId, selector, inputSelector, mainUlId, resultCounterId) {
this.apiUrl = '';
this.data = '';
this.dataToLoad = '';
this.editEntitiesUrl = '';
this.initialized = false;
this.sectionId = sectionId;
this.selector = selector;
this.mainUlId = mainUlId;
this.resultCounterId = resultCounterId;
this.disabled = true;
this.inputSelector = inputSelector;
this.listItemClass = 'entities-list-item';
//The class that is added to li items that must be visible because they match (a part) of the search value).
//This is also the class applied to the rootUl if any results are found.
this.visibleClass = 'active';
this.invisibleClass = 'hide';
this.section = document.getElementById(this.sectionId);
let rootUl = document.querySelector(this.selector);
this.siteSlug = rootUl.dataset.siteSlug;
this.slug = rootUl.dataset.slug;
this.activeId = rootUl.dataset.activeId;
}
/**
* Initializes the controller so that it knows where it can get its data from,
* where it needs to direct users when they click on an item and from which ul it
* should create a sortable ul
*
* @param dataSource A string|Object
* @param editEntitiesUrl A string
*
* In case of the data source being an object it must look like this:
* {
* data: [{
* id: null,
* routes: []
* status: "",
* title: "",
* children: [
* {
* id: "2",
* routes: []
* status: "",
* title: "My username"
* children: []
* }
* ]
* }]
* }
*/
init(dataSource, editEntitiesUrl) {
if(typeof dataSource === "string") {
this.apiUrl = dataSource;
} else {
this.dataToLoad = dataSource;
}
this.editEntitiesUrl = editEntitiesUrl;
this.initialized = true;
}
/**
* Returns a promise that resolves with the root ul that then will contain the items retrieved from the api
*/
load() {
if (this.initialized === false) { console.error('Please initialize the controller with the init method first.'); return; }
let self = this;
return new Promise(function (resolve, reject) {
if (self.initialized === false) { reject('Please initialize the controller with the init method first.') }
if(self.apiUrl !== '' && self.dataToLoad === '') {
axios.get(self.apiUrl).then((response) => {
if (!response.data) {
reject('The sortable did not get any data from the api');
return;
}
// console.log(response);
/** @var Array[] menuItemArrays*/
response.data[0].children.forEach(function (itemObject) {
/** @var array htmlElements **/
let htmlElements = self.createHtmlElement(itemObject);
htmlElements.forEach((element) => {
document.querySelector(self.selector).append(element);
})
});
// console.log(document.querySelector(self.selector));
self.initializeSearch();
resolve(document.querySelector(self.selector));
}).catch((error) => {
reject(error);
});
} else if(self.apiUrl === '' && self.dataToLoad !== '') {
// console.log('data children');
// console.log(self.dataToLoad.data[0].children);
self.dataToLoad.data[0].children.forEach(function(itemObject) {
let htmlElements = self.createHtmlElement(itemObject);
htmlElements.forEach((element) => {
document.querySelector(self.selector).append(element);
});
});
self.initializeSearch();
resolve(document.querySelector(self.selector));
}
});
}
/**
* Initialize search functionality on the ul this searchable does its job for
*/
initializeSearch() {
let section = this.section;
let input = document.querySelector(this.inputSelector);
let searchUl = document.querySelector(this.selector);
let resultCounter = document.getElementById(this.resultCounterId);
//handles searching
input.addEventListener('keyup', (event) => {
let mainUl = document.getElementById(this.mainUlId);
let resultsCount = 0;
let filterValue = input.value.toLowerCase();
let noSearchValue = (filterValue == '') ? true : false;
let listItems = searchUl.querySelectorAll('li');
let listItemsCount = listItems.length;
// console.log(listItemsCount);
for (let i = 0; i < listItemsCount; i++) {
let itemValue = listItems[i].dataset.title.toLowerCase();
if(itemValue.indexOf(filterValue) > -1 && noSearchValue === false) {
//item found
resultsCount++;
listItems[i].classList.add(this.visibleClass)
} else {
//item not found
listItems[i].classList.remove(this.visibleClass)
}
}
if(resultsCount > 0) {
// searchUl.parentNode.classList.add(this.visibleClass);
// mainUl.classList.add(this.invisibleClass);
} else {
// searchUl.parentNode.classList.remove(this.visibleClass);
// mainUl.classList.remove(this.invisibleClass);
}
resultCounter.innerHTML = resultsCount+"";
if(!noSearchValue){
section.classList.add(this.visibleClass);
mainUl.classList.add(this.invisibleClass);
}
else{
section.classList.remove(this.visibleClass);
mainUl.classList.remove(this.invisibleClass);
}
});
searchUl.classList.remove(this.visibleClass);
}
/**
* Creates a menu item (HTMLElement) and sub menu items if necessary
*
* @param data
* @param items array Used internally. Humans must not touch this
* @param currentTreeLevel string Used internally. Humans must not touch this
* @returns {Array}
*/
createHtmlElement(data, items = [], currentTreeLevel = '')
{
if (this.initialized === false) { console.error('Please initialize the controller with the init method first.'); return; }
let self = this;
let activeId = self.activeId;
let id = data.id;
let title = data.title;
let children = data.children;
let status = data.status;
let thumbnail = data.thumbnail;
let statusType = 0;
let breadcrumb = currentTreeLevel ;
let treeBreadcrumb = (currentTreeLevel !== "") ? currentTreeLevel + " | "+title : title ;
// console.log(breadcrumb);
// console.log(data);
//Render all child html items first
let childItems = [];
for(let object of children)
{
childItems.push(this.createHtmlElement(object, items, treeBreadcrumb));
}
switch(status){
//Green
case 'active':
statusType = 1;
break;
//Transparent
default:
break;
}
//Displays a red or green line in front of the item depending on if the status (class) is active or not
let colorStatusHtml = `
<span class="color-status" data-status-type="${statusType}">
</span>
`;
//Displays the items icon thumbnail OR the first character of its title
let iconHtml = `
<div class="icon" ${thumbnail ? `style="background-image: url('${thumbnail}');"` : ''} >
${thumbnail ? '' : `<span>${title ? title.substring(0,1): ``}</span>`}
</div>
`;
//The main item html that has all other items
let listIem = `
<li data-title="${title}" class="${this.listItemClass} ${ id == activeId ? 'active': '' }">
<a href="${this.editEntitiesUrl}/${id}">
${colorStatusHtml}
${iconHtml}
<p data-breadcrumb="${breadcrumb}">${title}</p>
</a>
</li>
`;
//Render it to a real html element
let domParser = new DOMParser();
let document = domParser.parseFromString(listIem, "text/html");
items.push(document.body.firstChild);
// console.log('rendered item: ');
// console.log(items);
//And then add the children inside
if(childItems.length > 0)
{
// console.log('adding rendered childitems');
childItems.forEach(function(element) {
items.push(element[0]);
});
}
// console.log('items result:');
// console.log(items);
//and return it
return items;
}
}
$( function() {
$(".select-menu").selectmenu({
width: '100%',
create: function ( event, ui ) {
buttonId = this.id + '-button';
menuId = this.id + '-menu';
},
open: function ( event, ui ) {
let button = document.getElementById(this.id + '-button');
let menu = document.getElementById(this.id + '-menu');
menu.style.width = button.offsetWidth + 'px';
menu.parentNode.style.width = button.offsetWidth + 'px';
button.classList.add('dropdown-open');
},
change: function( event, ui ) {
let button = document.getElementById(this.id + '-button');
let buttonText = button.querySelector('.ui-selectmenu-text').innerHTML;
button.querySelector('.ui-selectmenu-text').innerHTML = buttonText.replace(/ /gi, '');
},
close: function ( event, ui) {
let button = document.getElementById(this.id + '-button');
button.classList.remove('dropdown-open');
}
});
});
/**
* Fills an ul element with data retrieved from an api and makes the items draggable so that you can sort them.
* Also updates the api with the new positions of the items. The data to and from the api has this structure for example.
*
* [
* {
* id:1
* title:false
* thumbnail:false
* status: "active"
* routes: []
* children: [
* {
* id: 12
* title: "Thuis"
* thumbnail: false
* routes: [{
* 40: "en/Homenew",
* 104: "nl/Thuisnew"
* }]
* }
* ]
* }
* ]
*/
class SortableController {
constructor(selector) {
this.apiUrl = '';
this.editEntitiesUrl = '';
this.initialized = false;
this.selector = selector;
this.disabled = true;
this.listItemClass = 'entities-list-item';
let rootUl = document.querySelector(this.selector);
this.siteSlug = rootUl.dataset.siteSlug;
this.slug = rootUl.dataset.slug;
this.activeId = rootUl.dataset.activeId;
}
/**
* Initializes the controller so that it knows where it can get its data from,
* where it needs to direct users when they click on an item and from which ul it
* should create a sortable ul
*
* @param apiUrl A string
* @param editEntitiesUrl A string
*/
init(apiUrl, editEntitiesUrl) {
this.apiUrl = apiUrl;
this.editEntitiesUrl = editEntitiesUrl;
let itemsThatHaveSubUl = document.querySelectorAll(this.selector+" li ul");
itemsThatHaveSubUl.forEach((node, index) => {
// console.log('clicked ul');
node.style.display = node.style.display !== 'none' ? 'none' : '';
});
this.initialized = true;
}
updateSortableJavascript()
{
// console.log("Updating all sortable elements with these selectors: '" + this.selector+" .sortable' AND '"+this.selector+"'");
$('.sortable').sortable({
connectWith: ".sortable",
placeholder: "sortable-placeholder",
disabled: this.disabled
});
let itemsThatHaveSubUl = document.querySelectorAll(this.selector+" li ul");
itemsThatHaveSubUl.forEach((node, index) => {
// console.log('clicked ul');
// node.style.display = node.style.display !== 'none' ? 'none' : '';
});
}
/**
* Makes the rootUl sortable
*/
enableSortable() {
if (this.initialized === false) { console.error('Please initialize the controller with the init method first.'); return; }
this.disabled = false;
$(this.selector).sortable({
disabled: this.disabled
}).addClass('sorting');
this.updateSortableJavascript();
}
/**
* Diables the rootUl so that it cannot be sorted
*/
disableSortable() {
// if (this.initialized === false) { console.error('Please initialize the controller with the init method first.'); return; }
this.disabled = true;
$(this.selector).sortable({
disabled: this.disabled
}).removeClass('sorting');
this.save();
}
/**
* Saves the item data to the api
*/
save() {
if (this.initialized === false) { console.error('Please initialize the controller with the init method first.'); return; }
let itemsJson = this.itemsToJson(document.querySelector(this.selector));
itemsJson = JSON.stringify(itemsJson);
let apiJson = {
"tree": itemsJson
};
axios.post(this.apiUrl, apiJson).then((response) => {
// console.log('Successfully stored the sort order to the api: ');
// console.log(response);
}).catch((error) => {
// console.error('Could not save sortable sort order to api because an error occured: ');
console.error(error);
});
}
/**
* Converts the rootUl back to json for saving it
*
* @param htmlElement HTMLElement Root ul
* @return null|[] if something went wrong null, or an array containing json items if successfull.
*/
itemsToJson(htmlElement) {
if (this.initialized === false) { console.error('Please initialize the controller with the init method first.'); return; }
let jsonArray = [];
let error = false;
//find all child li items
let listItems = htmlElement.querySelectorAll(':scope > li.'+this.listItemClass);
for(let listItem of listItems)
{
let elementJson = listItem.dataset.json;
if(!elementJson)
{
console.error('One or more li HTMLElements with class "'+this.listItemClass+'" did not have data-json attribute set while it should.');
error = true;
}
let currentItemJson = JSON.parse(listItem.dataset.json);
let childUl = listItem.querySelector('ul');
if(childUl)
{
currentItemJson.children = this.itemsToJson(childUl);
if(!currentItemJson.children) error = true;
} else {
currentItemJson.children = [];
}
jsonArray.push(currentItemJson);
}
if(error) return null;
return jsonArray;
}
/**
* Returns a promise that resolves with the root ul that then will contain the items retrieved from the api
*/
load() {
if (this.initialized === false) { console.error('Please initialize the controller with the init method first.'); return; }
self = this;
return new Promise(function (resolve, reject) {
if (self.initialized === false) { reject('Please initialize the controller with the init method first.') }
axios.get(self.apiUrl).then((response) => {
if (!response.data) {
reject('The sortable did not get any data from the api');
return;
}
/** @var HTMLElement[] menuItems*/
let menuItems = [];
response.data[0].children.forEach(function (itemObject) {
menuItems.push(self.createHtmlElement(itemObject));
});
menuItems.forEach(function (htmlElementItem) {
document.querySelector(self.selector).appendChild(htmlElementItem);
});
self.updateSortableJavascript();
resolve(document.querySelector(self.selector));
}).catch((error) => {
reject(error);
});
});
}
/**
* Removes all children from the ul
*/
clearRootUl()
{
if (this.initialized === false) { console.error('Please initialize the controller with the init method first.'); return; }
let rootUl = document.querySelector(this.selector);
while(rootUl.firstChild) rootUl.removeChild(rootUl.firstChild);
}
/**
* Creates a menu item (HTMLElement) and sub menu items if necessary
*
* @param data
* @returns {HTMLElement}
*/
createHtmlElement(data)
{
if (this.initialized === false) { console.error('Please initialize the controller with the init method first.'); return; }
self = this;
let activeId = self.activeId;
let id = data.id;
let title = data.title;
let thumbnail = data.thumbnail;
let children = data.children;
let status = data.status;
let routes = data.routes;
let statusType = 0;
//Generate json data representing that element
let routesForElement = {};
for (let routeProperty in routes) {
if(!routes.hasOwnProperty(routeProperty)) continue;
routesForElement[routeProperty] = routes[routeProperty];
}
if(routesForElement == {}) routesForElement = [];
let elementJson = {
id: data.id,
title: data.title,
thumbnail: data.thumbnail,
routes: routesForElement,
status: data.status
};
elementJson = JSON.stringify(elementJson);
//Render all child html items first
let childItems = [];
for(let object of children)
{
childItems.push(this.createHtmlElement(object));
}
switch(status){
//Green
case 'active':
statusType = 1;
break;
//Transparent
default:
break;
}
//Displays a red or green line in front of the item depending on if the status (class) is active or not
let colorStatusHtml = `
<span class="color-status" data-status-type="${statusType}">
</span>
`;
//Displays the items icon thumbnail OR the first character of its title
let iconHtml = `
<div class="icon" ${thumbnail ? `style="background-image: url('${thumbnail}');"` : ''} >
${thumbnail ? '' : `<span>${title ? title.substring(0,1): ``}</span>`}
</div>
`;
//The main item html that has all other items
let listIem = `
<li data-json='${elementJson}' class="${this.listItemClass} ${ id == activeId ? 'active': '' }">
<a href="${this.editEntitiesUrl}/${id}">
${colorStatusHtml}
${iconHtml}
<p>${title}</p>
</a>
</li>
`;
//Render it to a real html element
let domParser = new DOMParser();
let document = domParser.parseFromString(listIem, "text/html");
let node = document.body.firstChild;
//And add the children inside
if(childItems.length > 0)
{
let subList = document.createElement('ul');
subList.className = 'sortable';
// console.log('rendered childItems');
// console.log(childItems);
childItems.forEach(function(element) {
subList.appendChild(element);
});
node.appendChild(subList);
}
//and return it
return node;
}
}
class TabsController {
/**
* The tab controller can obviously control tabs.
* You just have to have a couple of divs that hold the content. These divs must all have a class name that
* corresponds to the value of constructor parameter tabContentIdAndClassAndPrefix. And they all must have an id
* that also starts with the value of tabContentIdAndClassAndPrefix followed by the tab slug.
*
* You can specify if you have an hidden input field that needs to be updated with the curent tab slug. You do that
* with the tabSlugInputSelector.
*
* Content tab divs and buttons will also receive an active state class to make them visible / stand out when present
* or hidden / to background. You can specify this class name with the activeClass parameter.
*
* The tabButtonGroupSelector selects an element that holds the tab buttons to switch tabs. This element must have an
* ul element that holds li elements containing a elements.
*
* The reactToUrlHashChange parameter controls if the controller should react if the hash part of the url did change.
*
* @param tabContentIdAndClassAndPrefix stringseelc
* @param tabButtonGroupSelector string
* @param activeClass string
* @param reactToUrlHashChange
* @param tabSlugInputSelector string
*/
constructor(tabContentIdAndClassAndPrefix = 'tab', tabButtonGroupSelector = '.tab-buttons', activeClass = 'active', reactToUrlHashChange = false, tabSlugInputSelector = undefined) {
this.tabContentDivsClassAndIdPrefix = tabContentIdAndClassAndPrefix;
this.tabSlugInputId = tabSlugInputSelector;
this.activeClass = activeClass;
this.reactToUrlHashChange = reactToUrlHashChange;
if(reactToUrlHashChange) this.addListenerForHashChange(reactToUrlHashChange);
}
/**
* Opens the tab by using the tab slug
*
* @param tabSlug
*/
openTab(tabSlug) {
//Remove trailing / on the left hand side
tabSlug = this.removeLeftHandSlashInSlug(tabSlug);
if (!tabSlug) { console.error("TabsController: Could not open tab with an empty slug"); return; }
this.updateTabSlugInput(tabSlug);
this.showTabContentForTabWithSlug(tabSlug);
this.makeTabButtonActiveForSlug(tabSlug);
}
/**
* Removes a / at the beginning of a tab slug
*
* @param tabSlug
*/
removeLeftHandSlashInSlug(tabSlug) {
return tabSlug.replace(/^\/(.*)/, '$1');
}
/**
* Updates a usually hidden input field holding the tab slug with a new slug
*
* @param newSlug string
*/
updateTabSlugInput(newSlug)
{
if(this.tabSlugInputId === undefined) return;
let element = document.querySelector(this.tabSlugInputId);
if(element) element.value = newSlug;
}
makeTabButtonActiveForSlug(slug)
{
$('.entity-tabs >ul >li').removeClass('active');
$('.entity-tabs >ul >li a[href="#' + slug + '"]').parent().addClass('active');
}
/**
* Shows the tab content div with the specified slug and hides the other content div tabs.
* It does this by adding and removing classes (the active class you specified in the constructor)
*
* @param slug string
*/
showTabContentForTabWithSlug(slug)
{
//Remove the active class from all tab content classes
document.querySelectorAll('.'+this.tabContentDivsClassAndIdPrefix).forEach((element) => {
element.classList.remove(this.activeClass)
});
//Add the active class to the tab that has the correct slug appended to the IdPrefix
let activeTab = document.querySelector("#"+this.tabContentDivsClassAndIdPrefix+"-"+slug);
if(!activeTab) { console.error("TabsController: Could not make content tab active. It should have an ID with: #"+this.tabContentDivsClassAndIdPrefix+"-"+slug); return; }
activeTab.classList.add(this.activeClass);
}
/**
* Adds or disables listening for hashchange events to update tabs
*
* @param boolean
*/
addListenerForHashChange(boolean)
{
if(boolean) {
window.addEventListener('hashchange', this.hashChanged.bind(this));
window.addEventListener('load', this.hashChanged.bind(this));
} else {
window.removeEventListener('hashchange', this.hashChanged);
window.removeEventListener('load', this.hashChanged);
}
}
/**
* Called automatically by an internal listener (controlled by the addListenerForHashChange method)
*/
hashChanged(event)
{
let tabSlug = window.location.hash.substring(1);
if (tabSlug) {
this.openTab(tabSlug);
return;
}
let tabSlugElement = document.querySelector(this.tabSlugInputId);
if(!tabSlugElement) return;
this.openTab(tabSlugElement.value);
}
}