Team:UESTC/ui-bootstrap-tpls-0.6.0.js

From 2013.igem.org

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.position","ui.bootstrap.datepicker","ui.bootstrap.dropdownToggle","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/popup.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/rating/rating.html","template/tabs/tab.html","template/tabs/tabset-titles.html","template/tabs/tabset.html","template/timepicker/timepicker.html","template/typeahead/typeahead-match.html","template/typeahead/typeahead-popup.html"]); angular.module('ui.bootstrap.transition', [])

/**

* $transition service provides a consistent interface to trigger CSS 3 transitions and to be informed when they complete.
* @param  {DOMElement} element  The DOMElement that will be animated.
* @param  {string|object|function} trigger  The thing that will cause the transition to start:
*   - As a string, it represents the css class to be added to the element.
*   - As an object, it represents a hash of style attributes to be applied to the element.
*   - As a function, it represents a function to be called that will cause the transition to occur.
* @return {Promise}  A promise that is resolved when the transition finishes.
*/

.factory('$transition', ['$q', '$timeout', '$rootScope', function($q, $timeout, $rootScope) {

 var $transition = function(element, trigger, options) {
   options = options || {};
   var deferred = $q.defer();
   var endEventName = $transition[options.animation ? "animationEndEventName" : "transitionEndEventName"];
   var transitionEndHandler = function(event) {
     $rootScope.$apply(function() {
       element.unbind(endEventName, transitionEndHandler);
       deferred.resolve(element);
     });
   };
   if (endEventName) {
     element.bind(endEventName, transitionEndHandler);
   }
   // Wrap in a timeout to allow the browser time to update the DOM before the transition is to occur
   $timeout(function() {
     if ( angular.isString(trigger) ) {
       element.addClass(trigger);
     } else if ( angular.isFunction(trigger) ) {
       trigger(element);
     } else if ( angular.isObject(trigger) ) {
       element.css(trigger);
     }
     //If browser does not support transitions, instantly resolve
     if ( !endEventName ) {
       deferred.resolve(element);
     }
   });
   // Add our custom cancel function to the promise that is returned
   // We can call this if we are about to run a new transition, which we know will prevent this transition from ending,
   // i.e. it will therefore never raise a transitionEnd event for that transition
   deferred.promise.cancel = function() {
     if ( endEventName ) {
       element.unbind(endEventName, transitionEndHandler);
     }
     deferred.reject('Transition cancelled');
   };
   return deferred.promise;
 };
 // Work out the name of the transitionEnd event
 var transElement = document.createElement('trans');
 var transitionEndEventNames = {
   'WebkitTransition': 'webkitTransitionEnd',
   'MozTransition': 'transitionend',
   'OTransition': 'oTransitionEnd',
   'transition': 'transitionend'
 };
 var animationEndEventNames = {
   'WebkitTransition': 'webkitAnimationEnd',
   'MozTransition': 'animationend',
   'OTransition': 'oAnimationEnd',
   'transition': 'animationend'
 };
 function findEndEventName(endEventNames) {
   for (var name in endEventNames){
     if (transElement.style[name] !== undefined) {
       return endEventNames[name];
     }
   }
 }
 $transition.transitionEndEventName = findEndEventName(transitionEndEventNames);
 $transition.animationEndEventName = findEndEventName(animationEndEventNames);
 return $transition;

}]);

angular.module('ui.bootstrap.collapse',['ui.bootstrap.transition'])

// The collapsible directive indicates a block of html that will expand and collapse .directive('collapse', ['$transition', function($transition) {

 // CSS transitions don't work with height: auto, so we have to manually change the height to a
 // specific value and then once the animation completes, we can reset the height to auto.
 // Unfortunately if you do this while the CSS transitions are specified (i.e. in the CSS class
 // "collapse") then you trigger a change to height 0 in between.
 // The fix is to remove the "collapse" CSS class while changing the height back to auto - phew!
 var fixUpHeight = function(scope, element, height) {
   // We remove the collapse CSS class to prevent a transition when we change to height: auto
   element.removeClass('collapse');
   element.css({ height: height });
   // It appears that  reading offsetWidth makes the browser realise that we have changed the
   // height already :-/
   var x = element[0].offsetWidth;
   element.addClass('collapse');
 };
 return {
   link: function(scope, element, attrs) {
     var isCollapsed;
     var initialAnimSkip = true;
     scope.$watch(function (){ return element[0].scrollHeight; }, function (value) {
       //The listener is called when scollHeight changes
       //It actually does on 2 scenarios: 
       // 1. Parent is set to display none
       // 2. angular bindings inside are resolved
       //When we have a change of scrollHeight we are setting again the correct height if the group is opened
       if (element[0].scrollHeight !== 0) {
         if (!isCollapsed) {
           if (initialAnimSkip) {
             fixUpHeight(scope, element, element[0].scrollHeight + 'px');
           } else {
             fixUpHeight(scope, element, 'auto');
           }
         }
       }
     });
     
     scope.$watch(attrs.collapse, function(value) {
       if (value) {
         collapse();
       } else {
         expand();
       }
     });
     
     var currentTransition;
     var doTransition = function(change) {
       if ( currentTransition ) {
         currentTransition.cancel();
       }
       currentTransition = $transition(element,change);
       currentTransition.then(
         function() { currentTransition = undefined; },
         function() { currentTransition = undefined; }
       );
       return currentTransition;
     };
     var expand = function() {
       if (initialAnimSkip) {
         initialAnimSkip = false;
         if ( !isCollapsed ) {
           fixUpHeight(scope, element, 'auto');
         }
       } else {
         doTransition({ height : element[0].scrollHeight + 'px' })
         .then(function() {
           // This check ensures that we don't accidentally update the height if the user has closed
           // the group while the animation was still running
           if ( !isCollapsed ) {
             fixUpHeight(scope, element, 'auto');
           }
         });
       }
       isCollapsed = false;
     };
     
     var collapse = function() {
       isCollapsed = true;
       if (initialAnimSkip) {
         initialAnimSkip = false;
         fixUpHeight(scope, element, 0);
       } else {
         fixUpHeight(scope, element, element[0].scrollHeight + 'px');
         doTransition({'height':'0'});
       }
     };
   }
 };

}]);

angular.module('ui.bootstrap.accordion', ['ui.bootstrap.collapse'])

.constant('accordionConfig', {

 closeOthers: true

})

.controller('AccordionController', ['$scope', '$attrs', 'accordionConfig', function ($scope, $attrs, accordionConfig) {

 // This array keeps track of the accordion groups
 this.groups = [];
 // Ensure that all the groups in this accordion are closed, unless close-others explicitly says not to
 this.closeOthers = function(openGroup) {
   var closeOthers = angular.isDefined($attrs.closeOthers) ? $scope.$eval($attrs.closeOthers) : accordionConfig.closeOthers;
   if ( closeOthers ) {
     angular.forEach(this.groups, function (group) {
       if ( group !== openGroup ) {
         group.isOpen = false;
       }
     });
   }
 };
 
 // This is called from the accordion-group directive to add itself to the accordion
 this.addGroup = function(groupScope) {
   var that = this;
   this.groups.push(groupScope);
   groupScope.$on('$destroy', function (event) {
     that.removeGroup(groupScope);
   });
 };
 // This is called from the accordion-group directive when to remove itself
 this.removeGroup = function(group) {
   var index = this.groups.indexOf(group);
   if ( index !== -1 ) {
     this.groups.splice(this.groups.indexOf(group), 1);
   }
 };

}])

// The accordion directive simply sets up the directive controller // and adds an accordion CSS class to itself element. .directive('accordion', function () {

 return {
   restrict:'EA',
   controller:'AccordionController',
   transclude: true,
   replace: false,
   templateUrl: 'template/accordion/accordion.html'
 };

})

// The accordion-group directive indicates a block of html that will expand and collapse in an accordion .directive('accordionGroup', ['$parse', '$transition', '$timeout', function($parse, $transition, $timeout) {

 return {
   require:'^accordion',         // We need this directive to be inside an accordion
   restrict:'EA',
   transclude:true,              // It transcludes the contents of the directive into the template
   replace: true,                // The element containing the directive will be replaced with the template
   templateUrl:'template/accordion/accordion-group.html',
   scope:{ heading:'@' },        // Create an isolated scope and interpolate the heading attribute onto this scope
   controller: ['$scope', function($scope) {
     this.setHeading = function(element) {
       this.heading = element;
     };
   }],
   link: function(scope, element, attrs, accordionCtrl) {
     var getIsOpen, setIsOpen;
     accordionCtrl.addGroup(scope);
     scope.isOpen = false;
     
     if ( attrs.isOpen ) {
       getIsOpen = $parse(attrs.isOpen);
       setIsOpen = getIsOpen.assign;
       scope.$watch(
         function watchIsOpen() { return getIsOpen(scope.$parent); },
         function updateOpen(value) { scope.isOpen = value; }
       );
       
       scope.isOpen = getIsOpen ? getIsOpen(scope.$parent) : false;
     }
     scope.$watch('isOpen', function(value) {
       if ( value ) {
         accordionCtrl.closeOthers(scope);
       }
       if ( setIsOpen ) {
         setIsOpen(scope.$parent, value);
       }
     });
   }
 };

}])

// Use accordion-heading below an accordion-group to provide a heading containing HTML // <accordion-group> // <accordion-heading>Heading containing HTML - <img src="..."></accordion-heading> // </accordion-group> .directive('accordionHeading', function() {

 return {
   restrict: 'EA',
   transclude: true,   // Grab the contents to be used as the heading
   template: ,       // In effect remove this element!
   replace: true,
   require: '^accordionGroup',
   compile: function(element, attr, transclude) {
     return function link(scope, element, attr, accordionGroupCtrl) {
       // Pass the heading to the accordion-group controller
       // so that it can be transcluded into the right place in the template
       // [The second parameter to transclude causes the elements to be cloned so that they work in ng-repeat]
       accordionGroupCtrl.setHeading(transclude(scope, function() {}));
     };
   }
 };

})

// Use in the accordion-group template to indicate where you want the heading to be transcluded // You must provide the property on the accordion-group controller that will hold the transcluded element

//
//
<a ... accordion-transclude="heading">...</a>

// ...

//

.directive('accordionTransclude', function() {

 return {
   require: '^accordionGroup',
   link: function(scope, element, attr, controller) {
     scope.$watch(function() { return controller[attr.accordionTransclude]; }, function(heading) {
       if ( heading ) {
         element.html();
         element.append(heading);
       }
     });
   }
 };

});

angular.module("ui.bootstrap.alert", []).directive('alert', function () {

 return {
   restrict:'EA',
   templateUrl:'template/alert/alert.html',
   transclude:true,
   replace:true,
   scope: {
     type: '=',
     close: '&'
   },
   link: function(scope, iElement, iAttrs, controller) {
     scope.closeable = "close" in iAttrs;
   }
 };

});

angular.module('ui.bootstrap.bindHtml', [])

 .directive('bindHtmlUnsafe', function () {
   return function (scope, element, attr) {
     element.addClass('ng-binding').data('$binding', attr.bindHtmlUnsafe);
     scope.$watch(attr.bindHtmlUnsafe, function bindHtmlUnsafeWatchAction(value) {
       element.html(value || );
     });
   };
 });

angular.module('ui.bootstrap.buttons', [])

 .constant('buttonConfig', {
   activeClass:'active',
   toggleEvent:'click'
 })
 .directive('btnRadio', ['buttonConfig', function (buttonConfig) {
 var activeClass = buttonConfig.activeClass || 'active';
 var toggleEvent = buttonConfig.toggleEvent || 'click';
 return {
   require:'ngModel',
   link:function (scope, element, attrs, ngModelCtrl) {
     //model -> UI
     ngModelCtrl.$render = function () {
       element.toggleClass(activeClass, angular.equals(ngModelCtrl.$modelValue, scope.$eval(attrs.btnRadio)));
     };
     //ui->model
     element.bind(toggleEvent, function () {
       if (!element.hasClass(activeClass)) {
         scope.$apply(function () {
           ngModelCtrl.$setViewValue(scope.$eval(attrs.btnRadio));
           ngModelCtrl.$render();
         });
       }
     });
   }
 };

}])

 .directive('btnCheckbox', ['buttonConfig', function (buttonConfig) {
 var activeClass = buttonConfig.activeClass || 'active';
 var toggleEvent = buttonConfig.toggleEvent || 'click';
 return {
   require:'ngModel',
   link:function (scope, element, attrs, ngModelCtrl) {
     function getTrueValue() {
       var trueValue = scope.$eval(attrs.btnCheckboxTrue);
       return angular.isDefined(trueValue) ? trueValue : true;
     }
     function getFalseValue() {
       var falseValue = scope.$eval(attrs.btnCheckboxFalse);
       return angular.isDefined(falseValue) ? falseValue : false;
     }
     //model -> UI
     ngModelCtrl.$render = function () {
       element.toggleClass(activeClass, angular.equals(ngModelCtrl.$modelValue, getTrueValue()));
     };
     //ui->model
     element.bind(toggleEvent, function () {
       scope.$apply(function () {
         ngModelCtrl.$setViewValue(element.hasClass(activeClass) ? getFalseValue() : getTrueValue());
         ngModelCtrl.$render();
       });
     });
   }
 };

}]); /**

  • @ngdoc overview
  • @name ui.bootstrap.carousel
  • @description
  • AngularJS version of an image carousel.
  • /

angular.module('ui.bootstrap.carousel', ['ui.bootstrap.transition']) .controller('CarouselController', ['$scope', '$timeout', '$transition', '$q', function ($scope, $timeout, $transition, $q) {

 var self = this,
   slides = self.slides = [],
   currentIndex = -1,
   currentTimeout, isPlaying;
 self.currentSlide = null;
 /* direction: "prev" or "next" */
 self.select = function(nextSlide, direction) {
   var nextIndex = slides.indexOf(nextSlide);
   //Decide direction if it's not given
   if (direction === undefined) {
     direction = nextIndex > currentIndex ? "next" : "prev";
   }
   if (nextSlide && nextSlide !== self.currentSlide) {
     if ($scope.$currentTransition) {
       $scope.$currentTransition.cancel();
       //Timeout so ng-class in template has time to fix classes for finished slide
       $timeout(goNext);
     } else {
       goNext();
     }
   }
   function goNext() {
     //If we have a slide to transition from and we have a transition type and we're allowed, go
     if (self.currentSlide && angular.isString(direction) && !$scope.noTransition && nextSlide.$element) {
       //We shouldn't do class manip in here, but it's the same weird thing bootstrap does. need to fix sometime
       nextSlide.$element.addClass(direction);
       var reflow = nextSlide.$element[0].offsetWidth; //force reflow
       //Set all other slides to stop doing their stuff for the new transition
       angular.forEach(slides, function(slide) {
         angular.extend(slide, {direction: , entering: false, leaving: false, active: false});
       });
       angular.extend(nextSlide, {direction: direction, active: true, entering: true});
       angular.extend(self.currentSlide||{}, {direction: direction, leaving: true});
       $scope.$currentTransition = $transition(nextSlide.$element, {});
       //We have to create new pointers inside a closure since next & current will change
       (function(next,current) {
         $scope.$currentTransition.then(
           function(){ transitionDone(next, current); },
           function(){ transitionDone(next, current); }
         );
       }(nextSlide, self.currentSlide));
     } else {
       transitionDone(nextSlide, self.currentSlide);
     }
     self.currentSlide = nextSlide;
     currentIndex = nextIndex;
     //every time you change slides, reset the timer
     restartTimer();
   }
   function transitionDone(next, current) {
     angular.extend(next, {direction: , active: true, leaving: false, entering: false});
     angular.extend(current||{}, {direction: , active: false, leaving: false, entering: false});
     $scope.$currentTransition = null;
   }
 };
 /* Allow outside people to call indexOf on slides array */
 self.indexOfSlide = function(slide) {
   return slides.indexOf(slide);
 };
 $scope.next = function() {
   var newIndex = (currentIndex + 1) % slides.length;
   //Prevent this user-triggered transition from occurring if there is already one in progress
   if (!$scope.$currentTransition) {
     return self.select(slides[newIndex], 'next');
   }
 };
 $scope.prev = function() {
   var newIndex = currentIndex - 1 < 0 ? slides.length - 1 : currentIndex - 1;
   //Prevent this user-triggered transition from occurring if there is already one in progress
   if (!$scope.$currentTransition) {
     return self.select(slides[newIndex], 'prev');
   }
 };
 $scope.select = function(slide) {
   self.select(slide);
 };
 $scope.isActive = function(slide) {
    return self.currentSlide === slide;
 };
 $scope.slides = function() {
   return slides;
 };
 $scope.$watch('interval', restartTimer);
 function restartTimer() {
   if (currentTimeout) {
     $timeout.cancel(currentTimeout);
   }
   function go() {
     if (isPlaying) {
       $scope.next();
       restartTimer();
     } else {
       $scope.pause();
     }
   }
   var interval = +$scope.interval;
   if (!isNaN(interval) && interval>=0) {
     currentTimeout = $timeout(go, interval);
   }
 }
 $scope.play = function() {
   if (!isPlaying) {
     isPlaying = true;
     restartTimer();
   }
 };
 $scope.pause = function() {
   if (!$scope.noPause) {
     isPlaying = false;
     if (currentTimeout) {
       $timeout.cancel(currentTimeout);
     }
   }
 };
 self.addSlide = function(slide, element) {
   slide.$element = element;
   slides.push(slide);
   //if this is the first slide or the slide is set to active, select it
   if(slides.length === 1 || slide.active) {
     self.select(slides[slides.length-1]);
     if (slides.length == 1) {
       $scope.play();
     }
   } else {
     slide.active = false;
   }
 };
 self.removeSlide = function(slide) {
   //get the index of the slide inside the carousel
   var index = slides.indexOf(slide);
   slides.splice(index, 1);
   if (slides.length > 0 && slide.active) {
     if (index >= slides.length) {
       self.select(slides[index-1]);
     } else {
       self.select(slides[index]);
     }
   } else if (currentIndex > index) {
     currentIndex--;
   }
 };

}])

/**

* @ngdoc directive
* @name ui.bootstrap.carousel.directive:carousel
* @restrict EA
*
* @description
* Carousel is the outer container for a set of image 'slides' to showcase.
*
* @param {number=} interval The time, in milliseconds, that it will take the carousel to go to the next slide.
* @param {boolean=} noTransition Whether to disable transitions on the carousel.
* @param {boolean=} noPause Whether to disable pausing on the carousel (by default, the carousel interval pauses on hover).
*
* @example

<example module="ui.bootstrap">

 <file name="index.html">
   <carousel>
     <slide>
       <img src="http://placekitten.com/150/150" style="margin:auto;">
     </slide>
     <slide>
       <img src="http://placekitten.com/100/150" style="margin:auto;">
     </slide>
   </carousel>
 </file>
 <file name="demo.css">
   .carousel-indicators {
     top: auto;
     bottom: 15px;
   }
 </file>

</example>

*/

.directive('carousel', [function() {

 return {
   restrict: 'EA',
   transclude: true,
   replace: true,
   controller: 'CarouselController',
   require: 'carousel',
   templateUrl: 'template/carousel/carousel.html',
   scope: {
     interval: '=',
     noTransition: '=',
     noPause: '='
   }
 };

}])

/**

* @ngdoc directive
* @name ui.bootstrap.carousel.directive:slide
* @restrict EA
*
* @description
* Creates a slide inside a {@link ui.bootstrap.carousel.directive:carousel carousel}.  Must be placed as a child of a carousel element.
*
* @param {boolean=} active Model binding, whether or not this slide is currently active.
*
* @example

<example module="ui.bootstrap">

 <file name="index.html">
 <carousel>
   <slide ng-repeat="slide in slides" active="slide.active">
     <img ng-src="Template:Slide.image" style="margin:auto;">
   </slide>
 </carousel>
  • <button class="btn btn-mini" ng-class="{'btn-info': !slide.active, 'btn-success': slide.active}" ng-disabled="slide.active" ng-click="slide.active = true">select</button> Template:$index: Template:Slide.text
     <a class="btn" ng-click="addSlide()">Add Slide</a>
     Interval, in milliseconds: <input type="number" ng-model="myInterval">
     
Enter a negative number to stop the interval.
 </file>
 <file name="script.js">

function CarouselDemoCtrl($scope) {

 $scope.myInterval = 5000;
 var slides = $scope.slides = [];
 $scope.addSlide = function() {
   var newWidth = 200 + ((slides.length + (25 * slides.length)) % 150);
   slides.push({
     image: 'http://placekitten.com/' + newWidth + '/200',
     text: ['More','Extra','Lots of','Surplus'][slides.length % 4] + ' '
       ['Cats', 'Kittys', 'Felines', 'Cutes'][slides.length % 4]
   });
 };
 for (var i=0; i<4; i++) $scope.addSlide();

}

 </file>
 <file name="demo.css">
   .carousel-indicators {
     top: auto;
     bottom: 15px;
   }
 </file>

</example>

  • /

.directive('slide', ['$parse', function($parse) {

 return {
   require: '^carousel',
   restrict: 'EA',
   transclude: true,
   replace: true,
   templateUrl: 'template/carousel/slide.html',
   scope: {
   },
   link: function (scope, element, attrs, carouselCtrl) {
     //Set up optional 'active' = binding
     if (attrs.active) {
       var getActive = $parse(attrs.active);
       var setActive = getActive.assign;
       var lastValue = scope.active = getActive(scope.$parent);
       scope.$watch(function parentActiveWatch() {
         var parentActive = getActive(scope.$parent);
         if (parentActive !== scope.active) {
           // we are out of sync and need to copy
           if (parentActive !== lastValue) {
             // parent changed and it has precedence
             lastValue = scope.active = parentActive;
           } else {
             // if the parent can be assigned then do so
             setActive(scope.$parent, parentActive = lastValue = scope.active);
           }
         }
         return parentActive;
       });
     }
     carouselCtrl.addSlide(scope, element);
     //when the scope is destroyed then remove the slide from the current slides array
     scope.$on('$destroy', function() {
       carouselCtrl.removeSlide(scope);
     });
     scope.$watch('active', function(active) {
       if (active) {
         carouselCtrl.select(scope);
       }
     });
   }
 };

}]);

angular.module('ui.bootstrap.position', [])

/**

* A set of utility methods that can be use to retrieve position of DOM elements.
* It is meant to be used where we need to absolute-position DOM elements in
* relation to other, existing elements (this is the case for tooltips, popovers,
* typeahead suggestions etc.).
*/
 .factory('$position', ['$document', '$window', function ($document, $window) {
   function getStyle(el, cssprop) {
     if (el.currentStyle) { //IE
       return el.currentStyle[cssprop];
     } else if ($window.getComputedStyle) {
       return $window.getComputedStyle(el)[cssprop];
     }
     // finally try and get inline style
     return el.style[cssprop];
   }
   /**
    * Checks if a given element is statically positioned
    * @param element - raw DOM element
    */
   function isStaticPositioned(element) {
     return (getStyle(element, "position") || 'static' ) === 'static';
   }
   /**
    * returns the closest, non-statically positioned parentOffset of a given element
    * @param element
    */
   var parentOffsetEl = function (element) {
     var docDomEl = $document[0];
     var offsetParent = element.offsetParent || docDomEl;
     while (offsetParent && offsetParent !== docDomEl && isStaticPositioned(offsetParent) ) {
       offsetParent = offsetParent.offsetParent;
     }
     return offsetParent || docDomEl;
   };
   return {
     /**
      * Provides read-only equivalent of jQuery's position function:
      * http://api.jquery.com/position/
      */
     position: function (element) {
       var elBCR = this.offset(element);
       var offsetParentBCR = { top: 0, left: 0 };
       var offsetParentEl = parentOffsetEl(element[0]);
       if (offsetParentEl != $document[0]) {
         offsetParentBCR = this.offset(angular.element(offsetParentEl));
         offsetParentBCR.top += offsetParentEl.clientTop - offsetParentEl.scrollTop;
         offsetParentBCR.left += offsetParentEl.clientLeft - offsetParentEl.scrollLeft;
       }
       return {
         width: element.prop('offsetWidth'),
         height: element.prop('offsetHeight'),
         top: elBCR.top - offsetParentBCR.top,
         left: elBCR.left - offsetParentBCR.left
       };
     },
     /**
      * Provides read-only equivalent of jQuery's offset function:
      * http://api.jquery.com/offset/
      */
     offset: function (element) {
       var boundingClientRect = element[0].getBoundingClientRect();
       return {
         width: element.prop('offsetWidth'),
         height: element.prop('offsetHeight'),
         top: boundingClientRect.top + ($window.pageYOffset || $document[0].body.scrollTop || $document[0].documentElement.scrollTop),
         left: boundingClientRect.left + ($window.pageXOffset || $document[0].body.scrollLeft  || $document[0].documentElement.scrollLeft)
       };
     }
   };
 }]);

angular.module('ui.bootstrap.datepicker', ['ui.bootstrap.position'])

.constant('datepickerConfig', {

 dayFormat: 'dd',
 monthFormat: 'MMMM',
 yearFormat: 'yyyy',
 dayHeaderFormat: 'EEE',
 dayTitleFormat: 'MMMM yyyy',
 monthTitleFormat: 'yyyy',
 showWeeks: true,
 startingDay: 0,
 yearRange: 20,
 minDate: null,
 maxDate: null

})

.controller('DatepickerController', ['$scope', '$attrs', 'dateFilter', 'datepickerConfig', function($scope, $attrs, dateFilter, dtConfig) {

 var format = {
   day:        getValue($attrs.dayFormat,        dtConfig.dayFormat),
   month:      getValue($attrs.monthFormat,      dtConfig.monthFormat),
   year:       getValue($attrs.yearFormat,       dtConfig.yearFormat),
   dayHeader:  getValue($attrs.dayHeaderFormat,  dtConfig.dayHeaderFormat),
   dayTitle:   getValue($attrs.dayTitleFormat,   dtConfig.dayTitleFormat),
   monthTitle: getValue($attrs.monthTitleFormat, dtConfig.monthTitleFormat)
 },
 startingDay = getValue($attrs.startingDay,      dtConfig.startingDay),
 yearRange =   getValue($attrs.yearRange,        dtConfig.yearRange);
 this.minDate = dtConfig.minDate ? new Date(dtConfig.minDate) : null;
 this.maxDate = dtConfig.maxDate ? new Date(dtConfig.maxDate) : null;
 function getValue(value, defaultValue) {
   return angular.isDefined(value) ? $scope.$parent.$eval(value) : defaultValue;
 }
 function getDaysInMonth( year, month ) {
   return new Date(year, month, 0).getDate();
 }
 function getDates(startDate, n) {
   var dates = new Array(n);
   var current = startDate, i = 0;
   while (i < n) {
     dates[i++] = new Date(current);
     current.setDate( current.getDate() + 1 );
   }
   return dates;
 }
 function makeDate(date, format, isSelected, isSecondary) {
   return { date: date, label: dateFilter(date, format), selected: !!isSelected, secondary: !!isSecondary };
 }
 this.modes = [
   {
     name: 'day',
     getVisibleDates: function(date, selected) {
       var year = date.getFullYear(), month = date.getMonth(), firstDayOfMonth = new Date(year, month, 1);
       var difference = startingDay - firstDayOfMonth.getDay(),
       numDisplayedFromPreviousMonth = (difference > 0) ? 7 - difference : - difference,
       firstDate = new Date(firstDayOfMonth), numDates = 0;
       if ( numDisplayedFromPreviousMonth > 0 ) {
         firstDate.setDate( - numDisplayedFromPreviousMonth + 1 );
         numDates += numDisplayedFromPreviousMonth; // Previous
       }
       numDates += getDaysInMonth(year, month + 1); // Current
       numDates += (7 - numDates % 7) % 7; // Next
       var days = getDates(firstDate, numDates), labels = new Array(7);
       for (var i = 0; i < numDates; i ++) {
         var dt = new Date(days[i]);
         days[i] = makeDate(dt, format.day, (selected && selected.getDate() === dt.getDate() && selected.getMonth() === dt.getMonth() && selected.getFullYear() === dt.getFullYear()), dt.getMonth() !== month);
       }
       for (var j = 0; j < 7; j++) {
         labels[j] = dateFilter(days[j].date, format.dayHeader);
       }
       return { objects: days, title: dateFilter(date, format.dayTitle), labels: labels };
     },
     compare: function(date1, date2) {
       return (new Date( date1.getFullYear(), date1.getMonth(), date1.getDate() ) - new Date( date2.getFullYear(), date2.getMonth(), date2.getDate() ) );
     },
     split: 7,
     step: { months: 1 }
   },
   {
     name: 'month',
     getVisibleDates: function(date, selected) {
       var months = new Array(12), year = date.getFullYear();
       for ( var i = 0; i < 12; i++ ) {
         var dt = new Date(year, i, 1);
         months[i] = makeDate(dt, format.month, (selected && selected.getMonth() === i && selected.getFullYear() === year));
       }
       return { objects: months, title: dateFilter(date, format.monthTitle) };
     },
     compare: function(date1, date2) {
       return new Date( date1.getFullYear(), date1.getMonth() ) - new Date( date2.getFullYear(), date2.getMonth() );
     },
     split: 3,
     step: { years: 1 }
   },
   {
     name: 'year',
     getVisibleDates: function(date, selected) {
       var years = new Array(yearRange), year = date.getFullYear(), startYear = parseInt((year - 1) / yearRange, 10) * yearRange + 1;
       for ( var i = 0; i < yearRange; i++ ) {
         var dt = new Date(startYear + i, 0, 1);
         years[i] = makeDate(dt, format.year, (selected && selected.getFullYear() === dt.getFullYear()));
       }
       return { objects: years, title: [years[0].label, years[yearRange - 1].label].join(' - ') };
     },
     compare: function(date1, date2) {
       return date1.getFullYear() - date2.getFullYear();
     },
     split: 5,
     step: { years: yearRange }
   }
 ];
 this.isDisabled = function(date, mode) {
   var currentMode = this.modes[mode || 0];
   return ((this.minDate && currentMode.compare(date, this.minDate) < 0) || (this.maxDate && currentMode.compare(date, this.maxDate) > 0) || ($scope.dateDisabled && $scope.dateDisabled({date: date, mode: currentMode.name})));
 };

}])

.directive( 'datepicker', ['dateFilter', '$parse', 'datepickerConfig', '$log', function (dateFilter, $parse, datepickerConfig, $log) {

 return {
   restrict: 'EA',
   replace: true,
   templateUrl: 'template/datepicker/datepicker.html',
   scope: {
     dateDisabled: '&'
   },
   require: ['datepicker', '?^ngModel'],
   controller: 'DatepickerController',
   link: function(scope, element, attrs, ctrls) {
     var datepickerCtrl = ctrls[0], ngModel = ctrls[1];
     if (!ngModel) {
       return; // do nothing if no ng-model
     }
     // Configuration parameters
     var mode = 0, selected = new Date(), showWeeks = datepickerConfig.showWeeks;
     if (attrs.showWeeks) {
       scope.$parent.$watch($parse(attrs.showWeeks), function(value) {
         showWeeks = !! value;
         updateShowWeekNumbers();
       });
     } else {
       updateShowWeekNumbers();
     }
     if (attrs.min) {
       scope.$parent.$watch($parse(attrs.min), function(value) {
         datepickerCtrl.minDate = value ? new Date(value) : null;
         refill();
       });
     }
     if (attrs.max) {
       scope.$parent.$watch($parse(attrs.max), function(value) {
         datepickerCtrl.maxDate = value ? new Date(value) : null;
         refill();
       });
     }
     function updateShowWeekNumbers() {
       scope.showWeekNumbers = mode === 0 && showWeeks;
     }
     // Split array into smaller arrays
     function split(arr, size) {
       var arrays = [];
       while (arr.length > 0) {
         arrays.push(arr.splice(0, size));
       }
       return arrays;
     }
     function refill( updateSelected ) {
       var date = null, valid = true;
       if ( ngModel.$modelValue ) {
         date = new Date( ngModel.$modelValue );
         if ( isNaN(date) ) {
           valid = false;
           $log.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.');
         } else if ( updateSelected ) {
           selected = date;
         }
       }
       ngModel.$setValidity('date', valid);
       var currentMode = datepickerCtrl.modes[mode], data = currentMode.getVisibleDates(selected, date);
       angular.forEach(data.objects, function(obj) {
         obj.disabled = datepickerCtrl.isDisabled(obj.date, mode);
       });
       ngModel.$setValidity('date-disabled', (!date || !datepickerCtrl.isDisabled(date)));
       scope.rows = split(data.objects, currentMode.split);
       scope.labels = data.labels || [];
       scope.title = data.title;
     }
     function setMode(value) {
       mode = value;
       updateShowWeekNumbers();
       refill();
     }
     ngModel.$render = function() {
       refill( true );
     };
     scope.select = function( date ) {
       if ( mode === 0 ) {
         var dt = new Date( ngModel.$modelValue );
         dt.setFullYear( date.getFullYear(), date.getMonth(), date.getDate() );
         ngModel.$setViewValue( dt );
         refill( true );
       } else {
         selected = date;
         setMode( mode - 1 );
       }
     };
     scope.move = function(direction) {
       var step = datepickerCtrl.modes[mode].step;
       selected.setMonth( selected.getMonth() + direction * (step.months || 0) );
       selected.setFullYear( selected.getFullYear() + direction * (step.years || 0) );
       refill();
     };
     scope.toggleMode = function() {
       setMode( (mode + 1) % datepickerCtrl.modes.length );
     };
     scope.getWeekNumber = function(row) {
       return ( mode === 0 && scope.showWeekNumbers && row.length === 7 ) ? getISO8601WeekNumber(row[0].date) : null;
     };
     function getISO8601WeekNumber(date) {
       var checkDate = new Date(date);
       checkDate.setDate(checkDate.getDate() + 4 - (checkDate.getDay() || 7)); // Thursday
       var time = checkDate.getTime();
       checkDate.setMonth(0); // Compare with Jan 1
       checkDate.setDate(1);
       return Math.floor(Math.round((time - checkDate) / 86400000) / 7) + 1;
     }
   }
 };

}])

.constant('datepickerPopupConfig', {

 dateFormat: 'yyyy-MM-dd',
 closeOnDateSelection: true

})

.directive('datepickerPopup', ['$compile', '$parse', '$document', '$position', 'dateFilter', 'datepickerPopupConfig', function ($compile, $parse, $document, $position, dateFilter, datepickerPopupConfig) {

 return {
   restrict: 'EA',
   require: 'ngModel',
   link: function(originalScope, element, attrs, ngModel) {
     var closeOnDateSelection = angular.isDefined(attrs.closeOnDateSelection) ? scope.$eval(attrs.closeOnDateSelection) : datepickerPopupConfig.closeOnDateSelection;
     var dateFormat = attrs.datepickerPopup || datepickerPopupConfig.dateFormat;
    // create a child scope for the datepicker directive so we are not polluting original scope
     var scope = originalScope.$new();
     originalScope.$on('$destroy', function() {
       scope.$destroy();
     });
     var getIsOpen, setIsOpen;
     if ( attrs.isOpen ) {
       getIsOpen = $parse(attrs.isOpen);
       setIsOpen = getIsOpen.assign;
       originalScope.$watch(getIsOpen, function updateOpen(value) {
         scope.isOpen = !! value;
       });
     }
     scope.isOpen = getIsOpen ? getIsOpen(originalScope) : false; // Initial state
     function setOpen( value ) {
       if (setIsOpen) {
         setIsOpen(originalScope, !!value);
       } else {
         scope.isOpen = !!value;
       }
     }
     var documentClickBind = function(event) {
       if (scope.isOpen && event.target !== element[0]) {
         scope.$apply(function() {
           setOpen(false);
         });
       }
     };
     var elementFocusBind = function() {
       scope.$apply(function() {
         setOpen( true );
       });
     };
     // popup element used to display calendar
     var popupEl = angular.element('<datepicker-popup-wrap><datepicker></datepicker></datepicker-popup-wrap>');
     popupEl.attr({
       'ng-model': 'date',
       'ng-change': 'dateSelection()'
     });
     var datepickerEl = popupEl.find('datepicker');
     if (attrs.datepickerOptions) {
       datepickerEl.attr(angular.extend({}, originalScope.$eval(attrs.datepickerOptions)));
     }
     // TODO: reverse from dateFilter string to Date object
     function parseDate(viewValue) {
       if (!viewValue) {
         ngModel.$setValidity('date', true);
         return null;
       } else if (angular.isDate(viewValue)) {
         ngModel.$setValidity('date', true);
         return viewValue;
       } else if (angular.isString(viewValue)) {
         var date = new Date(viewValue);
         if (isNaN(date)) {
           ngModel.$setValidity('date', false);
           return undefined;
         } else {
           ngModel.$setValidity('date', true);
           return date;
         }
       } else {
         ngModel.$setValidity('date', false);
         return undefined;
       }
     }
     ngModel.$parsers.unshift(parseDate);
     // Inner change
     scope.dateSelection = function() {
       ngModel.$setViewValue(scope.date);
       ngModel.$render();
       if (closeOnDateSelection) {
         setOpen( false );
       }
     };
     element.bind('input change keyup', function() {
       scope.$apply(function() {
         updateCalendar();
       });
     });
     // Outter change
     ngModel.$render = function() {
       var date = ngModel.$viewValue ? dateFilter(ngModel.$viewValue, dateFormat) : ;
       element.val(date);
       updateCalendar();
     };
     function updateCalendar() {
       scope.date = ngModel.$modelValue;
       updatePosition();
     }
     function addWatchableAttribute(attribute, scopeProperty, datepickerAttribute) {
       if (attribute) {
         originalScope.$watch($parse(attribute), function(value){
           scope[scopeProperty] = value;
         });
         datepickerEl.attr(datepickerAttribute || scopeProperty, scopeProperty);
       }
     }
     addWatchableAttribute(attrs.min, 'min');
     addWatchableAttribute(attrs.max, 'max');
     if (attrs.showWeeks) {
       addWatchableAttribute(attrs.showWeeks, 'showWeeks', 'show-weeks');
     } else {
       scope.showWeeks = true;
       datepickerEl.attr('show-weeks', 'showWeeks');
     }
     if (attrs.dateDisabled) {
       datepickerEl.attr('date-disabled', attrs.dateDisabled);
     }
     function updatePosition() {
       scope.position = $position.position(element);
       scope.position.top = scope.position.top + element.prop('offsetHeight');
     }
     var documentBindingInitialized = false, elementFocusInitialized = false;
     scope.$watch('isOpen', function(value) {
       if (value) {
         updatePosition();
         $document.bind('click', documentClickBind);
         if(elementFocusInitialized) {
           element.unbind('focus', elementFocusBind);
         }
         element[0].focus();
         documentBindingInitialized = true;
       } else {
         if(documentBindingInitialized) {
           $document.unbind('click', documentClickBind);
         }
         element.bind('focus', elementFocusBind);
         elementFocusInitialized = true;
       }
       if ( setIsOpen ) {
         setIsOpen(originalScope, value);
       }
     });
     var $setModelValue = $parse(attrs.ngModel).assign;
     scope.today = function() {
       $setModelValue(originalScope, new Date());
     };
     scope.clear = function() {
       $setModelValue(originalScope, null);
     };
     element.after($compile(popupEl)(scope));
   }
 };

}])

.directive('datepickerPopupWrap', [function() {

 return {
   restrict:'E',
   replace: true,
   transclude: true,
   templateUrl: 'template/datepicker/popup.html',
   link:function (scope, element, attrs) {
     element.bind('click', function(event) {
       event.preventDefault();
       event.stopPropagation();
     });
   }
 };

}]);

/*

* dropdownToggle - Provides dropdown menu functionality in place of bootstrap js
* @restrict class or attribute
* @example:
  • \n" + " <a ng-click=\"select()\" tab-heading-transclude>Template:Heading</a>\n" + "</li>\n" + ""); }]); angular.module("template/tabs/tabs.html", []).run(["$templateCache", function($templateCache) { $templateCache.put("template/tabs/tabs.html", "
    \n" + " \n" + "
    \n" + "
    \n" +
       "");
    

    }]);

    angular.module("template/tabs/tabset-titles.html", []).run(["$templateCache", function($templateCache) {

     $templateCache.put("template/tabs/tabset-titles.html",
    
    "
      \n" + "
    \n" +
       "");
    

    }]);

    angular.module("template/tabs/tabset.html", []).run(["$templateCache", function($templateCache) {

     $templateCache.put("template/tabs/tabset.html",
       "\n" +
    
    "
    \n" + "
    \n" + "
    \n" + "
    \n" + "
    \n" + "
    \n" + "
    \n" + "
    \n" +
       "");
    

    }]);

    angular.module("template/timepicker/timepicker.html", []).run(["$templateCache", function($templateCache) {

     $templateCache.put("template/timepicker/timepicker.html",
    
    "\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "
    <a ng-click=\"incrementHours()\" class=\"btn btn-link\"></a> <a ng-click=\"incrementMinutes()\" class=\"btn btn-link\"></a>
    <input type=\"text\" ng-model=\"hours\" ng-change=\"updateHours()\" class=\"span1 text-center\" ng-mousewheel=\"incrementHours()\" ng-readonly=\"readonlyInput\" maxlength=\"2\" />:<input type=\"text\" ng-model=\"minutes\" ng-change=\"updateMinutes()\" class=\"span1 text-center\" ng-readonly=\"readonlyInput\" maxlength=\"2\"><button type=\"button\" ng-click=\"toggleMeridian()\" class=\"btn text-center\">Template:Meridian</button>
    <a ng-click=\"decrementHours()\" class=\"btn btn-link\"></a> <a ng-click=\"decrementMinutes()\" class=\"btn btn-link\"></a>
    ");

    }]);

    angular.module("template/typeahead/typeahead-match.html", []).run(["$templateCache", function($templateCache) {

     $templateCache.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($templateCache) {

     $templateCache.put("template/typeahead/typeahead-popup.html",
    
    "
      \n" + "
    • \n" + " <typeahead-match index=\"$index\" match=\"match\" query=\"query\" template-url=\"templateUrl\"></typeahead-match>\n" + "
    • \n" + "
    ");

    }]);

    angular.module("template/typeahead/typeahead.html", []).run(["$templateCache", function($templateCache) {

     $templateCache.put("template/typeahead/typeahead.html",
    
    "
      \n" + "
    • \n" + " <a tabindex=\"-1\" ng-click=\"selectMatch($index)\" ng-bind-html-unsafe=\"match.label | typeaheadHighlight:query\"></a>\n" + "
    • \n" + "
    ");

    }]);