");
- el.addClass(clazz);
- return el;
- }
-
- // The `Dialog` class represents a modal dialog. The dialog class can be invoked by providing an options object
- // containing at lest template or templateUrl and controller:
- //
- // var d = new Dialog({templateUrl: 'foo.html', controller: 'BarController'});
- //
- // Dialogs can also be created using templateUrl and controller as distinct arguments:
- //
- // var d = new Dialog('path/to/dialog.html', MyDialogController);
- function Dialog(opts) {
-
- var self = this, options = this.options = angular.extend({}, defaults, globalOptions, opts);
- this._open = false;
-
- this.backdropEl = createElement(options.backdropClass);
- if(options.backdropFade){
- this.backdropEl.addClass(options.transitionClass);
- this.backdropEl.removeClass(options.triggerClass);
- }
-
- this.modalEl = createElement(options.dialogClass);
- if(options.dialogFade){
- this.modalEl.addClass(options.transitionClass);
- this.modalEl.removeClass(options.triggerClass);
- }
- this.modalEl.css("display", "block"); /* FIXME: For BS 3.x support */
-
- this.handledEscapeKey = function(e) {
- if (e.which === 27) {
- self.close();
- e.preventDefault();
- self.$scope.$apply();
- }
- };
-
- this.handleBackDropClick = function(e) {
- self.close();
- e.preventDefault();
- self.$scope.$apply();
- };
-
- this.handleLocationChange = function() {
- self.close();
- };
- }
-
- // The `isOpen()` method returns wether the dialog is currently visible.
- Dialog.prototype.isOpen = function(){
- return this._open;
- };
-
- // The `open(templateUrl, controller)` method opens the dialog.
- // Use the `templateUrl` and `controller` arguments if specifying them at dialog creation time is not desired.
- Dialog.prototype.open = function(templateUrl, controller){
- var self = this, options = this.options;
-
- if(templateUrl){
- options.templateUrl = templateUrl;
- }
- if(controller){
- options.controller = controller;
- }
-
- if(!(options.template || options.templateUrl)) {
- throw new Error('Dialog.open expected template or templateUrl, neither found. Use options or open method to specify them.');
- }
-
- this._loadResolves().then(function(locals) {
- var $scope = locals.$scope = self.$scope = locals.$scope ? locals.$scope : $rootScope.$new();
-
- self.modalEl.html(locals.$template);
-
- if (self.options.controller) {
- var ctrl = $controller(self.options.controller, locals);
- self.modalEl.children().data('ngControllerController', ctrl);
- }
-
- $compile(self.modalEl)($scope);
- self._addElementsToDom();
-
- // trigger tranisitions
- setTimeout(function(){
- if(self.options.dialogFade){ self.modalEl.addClass(self.options.triggerClass); }
- if(self.options.backdropFade){ self.backdropEl.addClass(self.options.triggerClass); }
- });
-
- self._bindEvents();
- });
-
- this.deferred = $q.defer();
- return this.deferred.promise;
- };
-
- // closes the dialog and resolves the promise returned by the `open` method with the specified result.
- Dialog.prototype.close = function(result){
- var self = this;
- var fadingElements = this._getFadingElements();
-
- if(fadingElements.length > 0){
- for (var i = fadingElements.length - 1; i >= 0; i--) {
- $transition(fadingElements[i], removeTriggerClass).then(onCloseComplete);
- }
- return;
- }
-
- this._onCloseComplete(result);
-
- function removeTriggerClass(el){
- el.removeClass(self.options.triggerClass);
- }
-
- function onCloseComplete(){
- if(self._open){
- self._onCloseComplete(result);
- }
- }
- };
-
- Dialog.prototype._getFadingElements = function(){
- var elements = [];
- if(this.options.dialogFade){
- elements.push(this.modalEl);
- }
- if(this.options.backdropFade){
- elements.push(this.backdropEl);
- }
-
- return elements;
- };
-
- Dialog.prototype._bindEvents = function() {
- if(this.options.keyboard){ body.bind('keydown', this.handledEscapeKey); }
- if(this.options.backdrop && this.options.backdropClick){ this.backdropEl.bind('click', this.handleBackDropClick); }
- };
-
- Dialog.prototype._unbindEvents = function() {
- if(this.options.keyboard){ body.unbind('keydown', this.handledEscapeKey); }
- if(this.options.backdrop && this.options.backdropClick){ this.backdropEl.unbind('click', this.handleBackDropClick); }
- };
-
- Dialog.prototype._onCloseComplete = function(result) {
- this._removeElementsFromDom();
- this._unbindEvents();
-
- this.deferred.resolve(result);
- };
-
- Dialog.prototype._addElementsToDom = function(){
- body.append(this.modalEl);
-
- if(this.options.backdrop) {
- if (activeBackdrops.value === 0) {
- body.append(this.backdropEl);
- }
- activeBackdrops.value++;
- }
-
- this._open = true;
- };
-
- Dialog.prototype._removeElementsFromDom = function(){
- this.modalEl.remove();
-
- if(this.options.backdrop) {
- activeBackdrops.value--;
- if (activeBackdrops.value === 0) {
- this.backdropEl.remove();
- }
- }
- this._open = false;
- };
-
- // Loads all `options.resolve` members to be used as locals for the controller associated with the dialog.
- Dialog.prototype._loadResolves = function(){
- var values = [], keys = [], templatePromise, self = this;
-
- if (this.options.template) {
- templatePromise = $q.when(this.options.template);
- } else if (this.options.templateUrl) {
- templatePromise = $http.get(this.options.templateUrl, {cache:$templateCache})
- .then(function(response) { return response.data; });
- }
-
- angular.forEach(this.options.resolve || [], function(value, key) {
- keys.push(key);
- values.push(angular.isString(value) ? $injector.get(value) : $injector.invoke(value));
- });
-
- keys.push('$template');
- values.push(templatePromise);
-
- return $q.all(values).then(function(values) {
- var locals = {};
- angular.forEach(values, function(value, index) {
- locals[keys[index]] = value;
- });
- locals.dialog = self;
- return locals;
- });
- };
-
- // The actual `$dialog` service that is injected in controllers.
- return {
- // Creates a new `Dialog` with the specified options.
- dialog: function(opts){
- return new Dialog(opts);
- },
- // creates a new `Dialog` tied to the default message box template and controller.
- //
- // Arguments `title` and `message` are rendered in the modal header and body sections respectively.
- // The `buttons` array holds an object with the following members for each button to include in the
- // modal footer section:
- //
- // * `result`: the result to pass to the `close` method of the dialog when the button is clicked
- // * `label`: the label of the button
- // * `cssClass`: additional css class(es) to apply to the button for styling
- messageBox: function(title, message, buttons){
- return new Dialog({templateUrl: 'template/dialog/message.html', controller: 'MessageBoxController', resolve:
- {model: function() {
- return {
- title: title,
- message: message,
- buttons: buttons
- };
- }
- }});
- }
- };
- }];
-});
-
-/*
- * dropdownToggle - Provides dropdown menu functionality in place of bootstrap js
- * @restrict class or attribute
- * @example:
-
- My Dropdown Menu
-
-
- */
-
-angular.module('ui.bootstrap.dropdownToggle', []).directive('dropdownToggle', ['$document', '$location', function ($document, $location) {
- var openElement = null,
- closeMenu = angular.noop;
- return {
- restrict: 'CA',
- link: function(scope, element, attrs) {
- scope.$watch('$location.path', function() { closeMenu(); });
- element.parent().bind('click', function() { closeMenu(); });
- element.bind('click', function (event) {
-
- var elementWasOpen = (element === openElement);
-
- event.preventDefault();
- event.stopPropagation();
-
- if (!!openElement) {
- closeMenu();
- }
-
- if (!elementWasOpen) {
- element.parent().addClass('open');
- openElement = element;
- closeMenu = function (event) {
- if (event) {
- event.preventDefault();
- event.stopPropagation();
- }
- $document.unbind('click', closeMenu);
- element.parent().removeClass('open');
- closeMenu = angular.noop;
- openElement = null;
- };
- $document.bind('click', closeMenu);
- }
- });
- }
- };
-}]);
-angular.module('ui.bootstrap.modal', ['ui.bootstrap.dialog'])
-.directive('modal', ['$parse', '$dialog', function($parse, $dialog) {
- return {
- restrict: 'EA',
- terminal: true,
- link: function(scope, elm, attrs) {
- var opts = angular.extend({}, scope.$eval(attrs.uiOptions || attrs.bsOptions || attrs.options));
- var shownExpr = attrs.modal || attrs.show;
- var setClosed;
-
- // Create a dialog with the template as the contents of the directive
- // Add the current scope as the resolve in order to make the directive scope as a dialog controller scope
- opts = angular.extend(opts, {
- template: elm.html(),
- resolve: { $scope: function() { return scope; } }
- });
- var dialog = $dialog.dialog(opts);
-
- elm.remove();
-
- if (attrs.close) {
- setClosed = function() {
- $parse(attrs.close)(scope);
- };
- } else {
- setClosed = function() {
- if (angular.isFunction($parse(shownExpr).assign)) {
- $parse(shownExpr).assign(scope, false);
- }
- };
- }
-
- scope.$watch(shownExpr, function(isShown, oldShown) {
- if (isShown) {
- dialog.open().then(function(){
- setClosed();
- });
- } else {
- //Make sure it is not opened
- if (dialog.isOpen()){
- dialog.close();
- }
- }
- });
- }
- };
-}]);
-angular.module('ui.bootstrap.pagination', [])
-
-.controller('PaginationController', ['$scope', function (scope) {
-
- scope.noPrevious = function() {
- return scope.currentPage === 1;
- };
- scope.noNext = function() {
- return scope.currentPage === scope.numPages;
- };
-
- scope.isActive = function(page) {
- return scope.currentPage === page;
- };
-
- scope.selectPage = function(page) {
- if ( ! scope.isActive(page) && page > 0 && page <= scope.numPages) {
- scope.currentPage = page;
- scope.onSelectPage({ page: page });
- }
- };
-}])
-
-.constant('paginationConfig', {
- boundaryLinks: false,
- directionLinks: true,
- firstText: 'First',
- previousText: 'Previous',
- nextText: 'Next',
- lastText: 'Last',
- rotate: true
-})
-
-.directive('pagination', ['paginationConfig', function(paginationConfig) {
- return {
- restrict: 'EA',
- scope: {
- numPages: '=',
- currentPage: '=',
- maxSize: '=',
- onSelectPage: '&'
- },
- controller: 'PaginationController',
- templateUrl: 'template/pagination/pagination.html',
- replace: true,
- link: function(scope, element, attrs) {
-
- // Setup configuration parameters
- var boundaryLinks = angular.isDefined(attrs.boundaryLinks) ? scope.$eval(attrs.boundaryLinks) : paginationConfig.boundaryLinks;
- var directionLinks = angular.isDefined(attrs.directionLinks) ? scope.$eval(attrs.directionLinks) : paginationConfig.directionLinks;
- var firstText = angular.isDefined(attrs.firstText) ? scope.$parent.$eval(attrs.firstText) : paginationConfig.firstText;
- var previousText = angular.isDefined(attrs.previousText) ? scope.$parent.$eval(attrs.previousText) : paginationConfig.previousText;
- var nextText = angular.isDefined(attrs.nextText) ? scope.$parent.$eval(attrs.nextText) : paginationConfig.nextText;
- var lastText = angular.isDefined(attrs.lastText) ? scope.$parent.$eval(attrs.lastText) : paginationConfig.lastText;
- var rotate = angular.isDefined(attrs.rotate) ? scope.$eval(attrs.rotate) : paginationConfig.rotate;
-
- // Create page object used in template
- function makePage(number, text, isActive, isDisabled) {
- return {
- number: number,
- text: text,
- active: isActive,
- disabled: isDisabled
- };
- }
-
- scope.$watch('numPages + currentPage + maxSize', function() {
- scope.pages = [];
-
- // Default page limits
- var startPage = 1, endPage = scope.numPages;
- var isMaxSized = ( angular.isDefined(scope.maxSize) && scope.maxSize < scope.numPages );
-
- // recompute if maxSize
- if ( isMaxSized ) {
- if ( rotate ) {
- // Current page is displayed in the middle of the visible ones
- startPage = Math.max(scope.currentPage - Math.floor(scope.maxSize/2), 1);
- endPage = startPage + scope.maxSize - 1;
-
- // Adjust if limit is exceeded
- if (endPage > scope.numPages) {
- endPage = scope.numPages;
- startPage = endPage - scope.maxSize + 1;
- }
- } else {
- // Visible pages are paginated with maxSize
- startPage = ((Math.ceil(scope.currentPage / scope.maxSize) - 1) * scope.maxSize) + 1;
-
- // Adjust last page if limit is exceeded
- endPage = Math.min(startPage + scope.maxSize - 1, scope.numPages);
- }
- }
-
- // Add page number links
- for (var number = startPage; number <= endPage; number++) {
- var page = makePage(number, number, scope.isActive(number), false);
- scope.pages.push(page);
- }
-
- // Add links to move between page sets
- if ( isMaxSized && ! rotate ) {
- if ( startPage > 1 ) {
- var previousPageSet = makePage(startPage - 1, '...', false, false);
- scope.pages.unshift(previousPageSet);
- }
-
- if ( endPage < scope.numPages ) {
- var nextPageSet = makePage(endPage + 1, '...', false, false);
- scope.pages.push(nextPageSet);
- }
- }
-
- // Add previous & next links
- if (directionLinks) {
- var previousPage = makePage(scope.currentPage - 1, previousText, false, scope.noPrevious());
- scope.pages.unshift(previousPage);
-
- var nextPage = makePage(scope.currentPage + 1, nextText, false, scope.noNext());
- scope.pages.push(nextPage);
- }
-
- // Add first & last links
- if (boundaryLinks) {
- var firstPage = makePage(1, firstText, false, scope.noPrevious());
- scope.pages.unshift(firstPage);
-
- var lastPage = makePage(scope.numPages, lastText, false, scope.noNext());
- scope.pages.push(lastPage);
- }
-
- if ( scope.currentPage > scope.numPages ) {
- scope.selectPage(scope.numPages);
- }
- });
- }
- };
-}])
-
-.constant('pagerConfig', {
- previousText: '« Previous',
- nextText: 'Next »',
- align: true
-})
-
-.directive('pager', ['pagerConfig', function(config) {
- return {
- restrict: 'EA',
- scope: {
- numPages: '=',
- currentPage: '=',
- onSelectPage: '&'
- },
- controller: 'PaginationController',
- templateUrl: 'template/pagination/pager.html',
- replace: true,
- link: function(scope, element, attrs, paginationCtrl) {
-
- // Setup configuration parameters
- var previousText = angular.isDefined(attrs.previousText) ? scope.$parent.$eval(attrs.previousText) : config.previousText;
- var nextText = angular.isDefined(attrs.nextText) ? scope.$parent.$eval(attrs.nextText) : config.nextText;
- var align = angular.isDefined(attrs.align) ? scope.$parent.$eval(attrs.align) : config.align;
-
- // Create page object used in template
- function makePage(number, text, isDisabled, isPrevious, isNext) {
- return {
- number: number,
- text: text,
- disabled: isDisabled,
- previous: ( align && isPrevious ),
- next: ( align && isNext )
- };
- }
-
- scope.$watch('numPages + currentPage', function() {
- scope.pages = [];
-
- // Add previous & next links
- var previousPage = makePage(scope.currentPage - 1, previousText, scope.noPrevious(), true, false);
- scope.pages.unshift(previousPage);
-
- var nextPage = makePage(scope.currentPage + 1, nextText, scope.noNext(), false, true);
- scope.pages.push(nextPage);
-
- if ( scope.currentPage > scope.numPages ) {
- scope.selectPage(scope.numPages);
- }
- });
- }
- };
-}]);
-
-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) {
-
- var mouseX, mouseY;
-
- $document.bind('mousemove', function mouseMoved(event) {
- mouseX = event.pageX;
- mouseY = event.pageY;
- });
-
- 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;
- offsetParentBCR.left += offsetParentEl.clientLeft;
- }
-
- 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),
- left: boundingClientRect.left + ($window.pageXOffset || $document[0].body.scrollLeft)
- };
- },
-
- /**
- * Provides the coordinates of the mouse
- */
- mouse: function () {
- return {x: mouseX, y: mouseY};
- }
- };
- }]);
-
-/**
- * The following features are still outstanding: animation as a
- * function, placement as a function, inside, support for more triggers than
- * just mouse enter/leave, html tooltips, and selector delegation.
- */
-angular.module( 'ui.bootstrap.tooltip', [ 'ui.bootstrap.position' ] )
-
-/**
- * The $tooltip service creates tooltip- and popover-like directives as well as
- * houses global options for them.
- */
-.provider( '$tooltip', function () {
- // The default options tooltip and popover.
- var defaultOptions = {
- placement: 'top',
- animation: true,
- popupDelay: 0
- };
-
- // Default hide triggers for each show trigger
- var triggerMap = {
- 'mouseenter': 'mouseleave',
- 'click': 'click',
- 'focus': 'blur'
- };
-
- // The options specified to the provider globally.
- var globalOptions = {};
-
- /**
- * `options({})` allows global configuration of all tooltips in the
- * application.
- *
- * var app = angular.module( 'App', ['ui.bootstrap.tooltip'], function( $tooltipProvider ) {
- * // place tooltips left instead of top by default
- * $tooltipProvider.options( { placement: 'left' } );
- * });
- */
- this.options = function( value ) {
- angular.extend( globalOptions, value );
- };
-
- /**
- * This allows you to extend the set of trigger mappings available. E.g.:
- *
- * $tooltipProvider.setTriggers( 'openTrigger': 'closeTrigger' );
- */
- this.setTriggers = function setTriggers ( triggers ) {
- angular.extend( triggerMap, triggers );
- };
-
- /**
- * This is a helper function for translating camel-case to snake-case.
- */
- function snake_case(name){
- var regexp = /[A-Z]/g;
- var separator = '-';
- return name.replace(regexp, function(letter, pos) {
- return (pos ? separator : '') + letter.toLowerCase();
- });
- }
-
- /**
- * Returns the actual instance of the $tooltip service.
- * TODO support multiple triggers
- */
- this.$get = [ '$window', '$compile', '$timeout', '$parse', '$document', '$position', '$interpolate', function ( $window, $compile, $timeout, $parse, $document, $position, $interpolate ) {
- return function $tooltip ( type, prefix, defaultTriggerShow ) {
- var options = angular.extend( {}, defaultOptions, globalOptions );
-
- /**
- * Returns an object of show and hide triggers.
- *
- * If a trigger is supplied,
- * it is used to show the tooltip; otherwise, it will use the `trigger`
- * option passed to the `$tooltipProvider.options` method; else it will
- * default to the trigger supplied to this directive factory.
- *
- * The hide trigger is based on the show trigger. If the `trigger` option
- * was passed to the `$tooltipProvider.options` method, it will use the
- * mapped trigger from `triggerMap` or the passed trigger if the map is
- * undefined; otherwise, it uses the `triggerMap` value of the show
- * trigger; else it will just use the show trigger.
- */
- function setTriggers ( trigger ) {
- var show, hide;
-
- show = trigger || options.trigger || defaultTriggerShow;
- if ( angular.isDefined ( options.trigger ) ) {
- hide = triggerMap[options.trigger] || show;
- } else {
- hide = triggerMap[show] || show;
- }
-
- return {
- show: show,
- hide: hide
- };
- }
-
- var directiveName = snake_case( type );
- var triggers = setTriggers( undefined );
-
- var startSym = $interpolate.startSymbol();
- var endSym = $interpolate.endSymbol();
- var template =
- '<'+ directiveName +'-popup '+
- 'title="'+startSym+'tt_title'+endSym+'" '+
- 'content="'+startSym+'tt_content'+endSym+'" '+
- 'placement="'+startSym+'tt_placement'+endSym+'" '+
- 'animation="tt_animation()" '+
- 'is-open="tt_isOpen"'+
- '>'+
- ''+ directiveName +'-popup>';
-
- return {
- restrict: 'EA',
- scope: true,
- link: function link ( scope, element, attrs ) {
- var tooltip = $compile( template )( scope );
- var transitionTimeout;
- var popupTimeout;
- var $body;
- var appendToBody = angular.isDefined( options.appendToBody ) ? options.appendToBody : false;
-
- // By default, the tooltip is not open.
- // TODO add ability to start tooltip opened
- scope.tt_isOpen = false;
-
- function toggleTooltipBind () {
- if ( ! scope.tt_isOpen ) {
- showTooltipBind();
- } else {
- hideTooltipBind();
- }
- }
-
- // Show the tooltip with delay if specified, otherwise show it immediately
- function showTooltipBind() {
- if ( scope.tt_popupDelay ) {
- popupTimeout = $timeout( show, scope.tt_popupDelay );
- } else {
- scope.$apply( show );
- }
- }
-
- function hideTooltipBind () {
- scope.$apply(function () {
- hide();
- });
- }
-
- // Show the tooltip popup element.
- function show() {
- var position,
- ttWidth,
- ttHeight,
- ttPosition;
-
- // Don't show empty tooltips.
- if ( ! scope.tt_content ) {
- return;
- }
-
- // If there is a pending remove transition, we must cancel it, lest the
- // tooltip be mysteriously removed.
- if ( transitionTimeout ) {
- $timeout.cancel( transitionTimeout );
- }
-
- // Set the initial positioning.
- tooltip.css({ top: 0, left: 0, display: 'block' });
-
- // Now we add it to the DOM because need some info about it. But it's not
- // visible yet anyway.
- if ( appendToBody ) {
- $body = $body || $document.find( 'body' );
- $body.append( tooltip );
- } else {
- element.after( tooltip );
- }
-
- // Get the position of the directive element.
- position = options.appendToBody ? $position.offset( element ) : $position.position( element );
-
- // Get the height and width of the tooltip so we can center it.
- ttWidth = tooltip.prop( 'offsetWidth' );
- ttHeight = tooltip.prop( 'offsetHeight' );
-
- // Calculate the tooltip's top and left coordinates to center it with
- // this directive.
- switch ( scope.tt_placement ) {
- case 'mouse':
- var mousePos = $position.mouse();
- ttPosition = {
- top: mousePos.y,
- left: mousePos.x
- };
- break;
- case 'right':
- ttPosition = {
- top: position.top + position.height / 2 - ttHeight / 2,
- left: position.left + position.width
- };
- break;
- case 'bottom':
- ttPosition = {
- top: position.top + position.height,
- left: position.left + position.width / 2 - ttWidth / 2
- };
- break;
- case 'left':
- ttPosition = {
- top: position.top + position.height / 2 - ttHeight / 2,
- left: position.left - ttWidth
- };
- break;
- default:
- ttPosition = {
- top: position.top - ttHeight,
- left: position.left + position.width / 2 - ttWidth / 2
- };
- break;
- }
-
- ttPosition.top += 'px';
- ttPosition.left += 'px';
-
- // Now set the calculated positioning.
- tooltip.css( ttPosition );
-
- // And show the tooltip.
- scope.tt_isOpen = true;
- }
-
- // Hide the tooltip popup element.
- function hide() {
- // First things first: we don't show it anymore.
- scope.tt_isOpen = false;
-
- //if tooltip is going to be shown after delay, we must cancel this
- $timeout.cancel( popupTimeout );
-
- // And now we remove it from the DOM. However, if we have animation, we
- // need to wait for it to expire beforehand.
- // FIXME: this is a placeholder for a port of the transitions library.
- if ( angular.isDefined( scope.tt_animation ) && scope.tt_animation() ) {
- transitionTimeout = $timeout( function () { tooltip.remove(); }, 500 );
- } else {
- tooltip.remove();
- }
- }
-
- /**
- * Observe the relevant attributes.
- */
- attrs.$observe( type, function ( val ) {
- scope.tt_content = val;
- });
-
- attrs.$observe( prefix+'Title', function ( val ) {
- scope.tt_title = val;
- });
-
- attrs.$observe( prefix+'Placement', function ( val ) {
- scope.tt_placement = angular.isDefined( val ) ? val : options.placement;
- });
-
- attrs.$observe( prefix+'Animation', function ( val ) {
- scope.tt_animation = angular.isDefined( val ) ? $parse( val ) : function(){ return options.animation; };
- });
-
- attrs.$observe( prefix+'PopupDelay', function ( val ) {
- var delay = parseInt( val, 10 );
- scope.tt_popupDelay = ! isNaN(delay) ? delay : options.popupDelay;
- });
-
- attrs.$observe( prefix+'Trigger', function ( val ) {
- element.unbind( triggers.show );
- element.unbind( triggers.hide );
-
- triggers = setTriggers( val );
-
- if ( triggers.show === triggers.hide ) {
- element.bind( triggers.show, toggleTooltipBind );
- } else {
- element.bind( triggers.show, showTooltipBind );
- element.bind( triggers.hide, hideTooltipBind );
- }
- });
-
- attrs.$observe( prefix+'AppendToBody', function ( val ) {
- appendToBody = angular.isDefined( val ) ? $parse( val )( scope ) : appendToBody;
- });
-
- // if a tooltip is attached to we need to remove it on
- // location change as its parent scope will probably not be destroyed
- // by the change.
- if ( appendToBody ) {
- scope.$on('$locationChangeSuccess', function closeTooltipOnLocationChangeSuccess () {
- if ( scope.tt_isOpen ) {
- hide();
- }
- });
- }
-
- // Make sure tooltip is destroyed and removed.
- scope.$on('$destroy', function onDestroyTooltip() {
- if ( scope.tt_isOpen ) {
- hide();
- } else {
- tooltip.remove();
- }
- });
- }
- };
- };
- }];
-})
-
-.directive( 'tooltipPopup', function () {
- return {
- restrict: 'E',
- replace: true,
- scope: { content: '@', placement: '@', animation: '&', isOpen: '&' },
- templateUrl: 'template/tooltip/tooltip-popup.html'
- };
-})
-
-.directive( 'tooltip', [ '$tooltip', function ( $tooltip ) {
- return $tooltip( 'tooltip', 'tooltip', 'mouseenter' );
-}])
-
-.directive( 'tooltipHtmlUnsafePopup', function () {
- return {
- restrict: 'E',
- replace: true,
- scope: { content: '@', placement: '@', animation: '&', isOpen: '&' },
- templateUrl: 'template/tooltip/tooltip-html-unsafe-popup.html'
- };
-})
-
-.directive( 'tooltipHtmlUnsafe', [ '$tooltip', function ( $tooltip ) {
- return $tooltip( 'tooltipHtmlUnsafe', 'tooltip', 'mouseenter' );
-}]);
-
-/**
- * The following features are still outstanding: popup delay, animation as a
- * function, placement as a function, inside, support for more triggers than
- * just mouse enter/leave, html popovers, and selector delegatation.
- */
-angular.module( 'ui.bootstrap.popover', [ 'ui.bootstrap.tooltip' ] )
-.directive( 'popoverPopup', function () {
- return {
- restrict: 'EA',
- replace: true,
- scope: { title: '@', content: '@', placement: '@', animation: '&', isOpen: '&' },
- templateUrl: 'template/popover/popover.html'
- };
-})
-.directive( 'popover', [ '$compile', '$timeout', '$parse', '$window', '$tooltip', function ( $compile, $timeout, $parse, $window, $tooltip ) {
- return $tooltip( 'popover', 'popover', 'click' );
-}]);
-
-
-angular.module('ui.bootstrap.progressbar', ['ui.bootstrap.transition'])
-
-.constant('progressConfig', {
- animate: true,
- autoType: false,
- stackedTypes: ['success', 'info', 'warning', 'danger']
-})
-
-.controller('ProgressBarController', ['$scope', '$attrs', 'progressConfig', function($scope, $attrs, progressConfig) {
-
- // Whether bar transitions should be animated
- var animate = angular.isDefined($attrs.animate) ? $scope.$eval($attrs.animate) : progressConfig.animate;
- var autoType = angular.isDefined($attrs.autoType) ? $scope.$eval($attrs.autoType) : progressConfig.autoType;
- var stackedTypes = angular.isDefined($attrs.stackedTypes) ? $scope.$eval('[' + $attrs.stackedTypes + ']') : progressConfig.stackedTypes;
-
- // Create bar object
- this.makeBar = function(newBar, oldBar, index) {
- var newValue = (angular.isObject(newBar)) ? newBar.value : (newBar || 0);
- var oldValue = (angular.isObject(oldBar)) ? oldBar.value : (oldBar || 0);
- var type = (angular.isObject(newBar) && angular.isDefined(newBar.type)) ? newBar.type : (autoType) ? getStackedType(index || 0) : null;
-
- return {
- from: oldValue,
- to: newValue,
- type: type,
- animate: animate
- };
- };
-
- function getStackedType(index) {
- return stackedTypes[index];
- }
-
- this.addBar = function(bar) {
- $scope.bars.push(bar);
- $scope.totalPercent += bar.to;
- };
-
- this.clearBars = function() {
- $scope.bars = [];
- $scope.totalPercent = 0;
- };
- this.clearBars();
-}])
-
-.directive('progress', function() {
- return {
- restrict: 'EA',
- replace: true,
- controller: 'ProgressBarController',
- scope: {
- value: '=percent',
- onFull: '&',
- onEmpty: '&'
- },
- templateUrl: 'template/progressbar/progress.html',
- link: function(scope, element, attrs, controller) {
- scope.$watch('value', function(newValue, oldValue) {
- controller.clearBars();
-
- if (angular.isArray(newValue)) {
- // Stacked progress bar
- for (var i=0, n=newValue.length; i < n; i++) {
- controller.addBar(controller.makeBar(newValue[i], oldValue[i], i));
- }
- } else {
- // Simple bar
- controller.addBar(controller.makeBar(newValue, oldValue));
- }
- }, true);
-
- // Total percent listeners
- scope.$watch('totalPercent', function(value) {
- if (value >= 100) {
- scope.onFull();
- } else if (value <= 0) {
- scope.onEmpty();
- }
- }, true);
- }
- };
-})
-
-.directive('progressbar', ['$transition', function($transition) {
- return {
- restrict: 'EA',
- replace: true,
- scope: {
- width: '=',
- old: '=',
- type: '=',
- animate: '='
- },
- templateUrl: 'template/progressbar/bar.html',
- link: function(scope, element) {
- scope.$watch('width', function(value) {
- if (scope.animate) {
- element.css('width', scope.old + '%');
- $transition(element, {width: value + '%'});
- } else {
- element.css('width', value + '%');
- }
- });
- }
- };
-}]);
-angular.module('ui.bootstrap.rating', [])
-
-.constant('ratingConfig', {
- max: 5
-})
-
-.directive('rating', ['ratingConfig', '$parse', function(ratingConfig, $parse) {
- return {
- restrict: 'EA',
- scope: {
- value: '='
- },
- templateUrl: 'template/rating/rating.html',
- replace: true,
- link: function(scope, element, attrs) {
-
- var maxRange = angular.isDefined(attrs.max) ? scope.$eval(attrs.max) : ratingConfig.max;
-
- scope.range = [];
- for (var i = 1; i <= maxRange; i++) {
- scope.range.push(i);
- }
-
- scope.rate = function(value) {
- if ( ! scope.readonly ) {
- scope.value = value;
- }
- };
-
- scope.enter = function(value) {
- if ( ! scope.readonly ) {
- scope.val = value;
- }
- };
-
- scope.reset = function() {
- scope.val = angular.copy(scope.value);
- };
- scope.reset();
-
- scope.$watch('value', function(value) {
- scope.val = value;
- });
-
- scope.readonly = false;
- if (attrs.readonly) {
- scope.$parent.$watch($parse(attrs.readonly), function(value) {
- scope.readonly = !!value;
- });
- }
- }
- };
-}]);
-
-/**
- * @ngdoc overview
- * @name ui.bootstrap.tabs
- *
- * @description
- * AngularJS version of the tabs directive.
- */
-
-angular.module('ui.bootstrap.tabs', [])
-
-.directive('tabs', function() {
- return function() {
- throw new Error("The `tabs` directive is deprecated, please migrate to `tabset`. Instructions can be found at http://github.com/angular-ui/bootstrap/tree/master/CHANGELOG.md");
- };
-})
-
-.controller('TabsetController', ['$scope', '$element',
-function TabsetCtrl($scope, $element) {
- var ctrl = this,
- tabs = ctrl.tabs = $scope.tabs = [];
-
- ctrl.select = function(tab) {
- angular.forEach(tabs, function(tab) {
- tab.active = false;
- });
- tab.active = true;
- };
-
- ctrl.addTab = function addTab(tab) {
- tabs.push(tab);
- if (tabs.length == 1) {
- ctrl.select(tab);
- }
- };
-
- ctrl.removeTab = function removeTab(tab) {
- var index = tabs.indexOf(tab);
- //Select a new tab if the tab to be removed is selected
- if (tab.active && tabs.length > 1) {
- //If this is the last tab, select the previous tab. else, the next tab.
- var newActiveIndex = index == tabs.length - 1 ? index - 1 : index + 1;
- ctrl.select(tabs[newActiveIndex]);
- }
- tabs.splice(index, 1);
- };
-}])
-
-/**
- * @ngdoc directive
- * @name ui.bootstrap.tabs.directive:tabset
- * @restrict EA
- *
- * @description
- * Tabset is the outer container for the tabs directive
- *
- * @param {boolean=} vertical Whether or not to use vertical styling for the tabs.
- *
- * @example
-
-
-
- First Content!
- Second Content!
-
-
-
- First Vertical Content!
- Second Vertical Content!
-
-
-
- */
-.directive('tabset', function() {
- return {
- restrict: 'EA',
- transclude: true,
- scope: {},
- controller: 'TabsetController',
- templateUrl: 'template/tabs/tabset.html',
- link: function(scope, element, attrs) {
- scope.vertical = angular.isDefined(attrs.vertical) ? scope.$eval(attrs.vertical) : false;
- scope.type = angular.isDefined(attrs.type) ? scope.$parent.$eval(attrs.type) : 'tabs';
- }
- };
-})
-
-/**
- * @ngdoc directive
- * @name ui.bootstrap.tabs.directive:tab
- * @restrict EA
- *
- * @param {string=} heading The visible heading, or title, of the tab. Set HTML headings with {@link ui.bootstrap.tabs.directive:tabHeading tabHeading}.
- * @param {string=} select An expression to evaluate when the tab is selected.
- * @param {boolean=} active A binding, telling whether or not this tab is selected.
- * @param {boolean=} disabled A binding, telling whether or not this tab is disabled.
- *
- * @description
- * Creates a tab with a heading and content. Must be placed within a {@link ui.bootstrap.tabs.directive:tabset tabset}.
- *
- * @example
-
-
-
-
-
-
-
- First Tab
-
- Alert me!
- Second Tab, with alert callback and html heading!
-
-
- {{item.content}}
-
-
-
-
-
- function TabsDemoCtrl($scope) {
- $scope.items = [
- { title:"Dynamic Title 1", content:"Dynamic Item 0" },
- { title:"Dynamic Title 2", content:"Dynamic Item 1", disabled: true }
- ];
-
- $scope.alertMe = function() {
- setTimeout(function() {
- alert("You've selected the alert tab!");
- });
- };
- };
-
-
- */
-
-/**
- * @ngdoc directive
- * @name ui.bootstrap.tabs.directive:tabHeading
- * @restrict EA
- *
- * @description
- * Creates an HTML heading for a {@link ui.bootstrap.tabs.directive:tab tab}. Must be placed as a child of a tab element.
- *
- * @example
-
-
-
-
- HTML in my titles?!
- And some content, too!
-
-
- Icon heading?!?
- That's right.
-
-
-
-
- */
-.directive('tab', ['$parse', '$http', '$templateCache', '$compile',
-function($parse, $http, $templateCache, $compile) {
- return {
- require: '^tabset',
- restrict: 'EA',
- replace: true,
- templateUrl: 'template/tabs/tab.html',
- transclude: true,
- scope: {
- heading: '@',
- onSelect: '&select' //This callback is called in contentHeadingTransclude
- //once it inserts the tab's content into the dom
- },
- controller: function() {
- //Empty controller so other directives can require being 'under' a tab
- },
- compile: function(elm, attrs, transclude) {
- return function postLink(scope, elm, attrs, tabsetCtrl) {
- var getActive, setActive;
- scope.active = false; // default value
- if (attrs.active) {
- getActive = $parse(attrs.active);
- setActive = getActive.assign;
- scope.$parent.$watch(getActive, function updateActive(value) {
- if ( !!value && scope.disabled ) {
- setActive(scope.$parent, false); // Prevent active assignment
- } else {
- scope.active = !!value;
- }
- });
- } else {
- setActive = getActive = angular.noop;
- }
-
- scope.$watch('active', function(active) {
- setActive(scope.$parent, active);
- if (active) {
- tabsetCtrl.select(scope);
- scope.onSelect();
- }
- });
-
- scope.disabled = false;
- if ( attrs.disabled ) {
- scope.$parent.$watch($parse(attrs.disabled), function(value) {
- scope.disabled = !! value;
- });
- }
-
- scope.select = function() {
- if ( ! scope.disabled ) {
- scope.active = true;
- }
- };
-
- tabsetCtrl.addTab(scope);
- scope.$on('$destroy', function() {
- tabsetCtrl.removeTab(scope);
- });
- //If the tabset sets this tab to active, set the parent scope's active
- //binding too. We do this so the watch for the parent's initial active
- //value won't overwrite what is initially set by the tabset
- if (scope.active) {
- setActive(scope.$parent, true);
- }
-
- //Transclude the collection of sibling elements. Use forEach to find
- //the heading if it exists. We don't use a directive for tab-heading
- //because it is problematic. Discussion @ http://git.io/MSNPwQ
- transclude(scope.$parent, function(clone) {
- //Look at every element in the clone collection. If it's tab-heading,
- //mark it as that. If it's not tab-heading, mark it as tab contents
- var contents = [], heading;
- angular.forEach(clone, function(el) {
- //See if it's a tab-heading attr or element directive
- //First make sure it's a normal element, one that has a tagName
- if (el.tagName &&
- (el.hasAttribute("tab-heading") ||
- el.hasAttribute("data-tab-heading") ||
- el.tagName.toLowerCase() == "tab-heading" ||
- el.tagName.toLowerCase() == "data-tab-heading"
- )) {
- heading = el;
- } else {
- contents.push(el);
- }
- });
- //Share what we found on the scope, so our tabHeadingTransclude and
- //tabContentTransclude directives can find out what the heading and
- //contents are.
- if (heading) {
- scope.headingElement = angular.element(heading);
- }
- scope.contentElement = angular.element(contents);
- });
- };
- }
- };
-}])
-
-.directive('tabHeadingTransclude', [function() {
- return {
- restrict: 'A',
- require: '^tab',
- link: function(scope, elm, attrs, tabCtrl) {
- scope.$watch('headingElement', function updateHeadingElement(heading) {
- if (heading) {
- elm.html('');
- elm.append(heading);
- }
- });
- }
- };
-}])
-
-.directive('tabContentTransclude', ['$parse', function($parse) {
- return {
- restrict: 'A',
- require: '^tabset',
- link: function(scope, elm, attrs, tabsetCtrl) {
- scope.$watch($parse(attrs.tabContentTransclude), function(tab) {
- elm.html('');
- if (tab) {
- elm.append(tab.contentElement);
- }
- });
- }
- };
-}])
-
-;
-
-
-angular.module('ui.bootstrap.timepicker', [])
-
-.filter('pad', function() {
- return function(input) {
- if ( angular.isDefined(input) && input.toString().length < 2 ) {
- input = '0' + input;
- }
- return input;
- };
-})
-
-.constant('timepickerConfig', {
- hourStep: 1,
- minuteStep: 1,
- showMeridian: true,
- meridians: ['AM', 'PM'],
- readonlyInput: false,
- mousewheel: true
-})
-
-.directive('timepicker', ['padFilter', '$parse', 'timepickerConfig', function (padFilter, $parse, timepickerConfig) {
- return {
- restrict: 'EA',
- require:'ngModel',
- replace: true,
- templateUrl: 'template/timepicker/timepicker.html',
- scope: {
- model: '=ngModel'
- },
- link: function(scope, element, attrs, ngModelCtrl) {
- var selected = new Date(), meridians = timepickerConfig.meridians;
-
- var hourStep = timepickerConfig.hourStep;
- if (attrs.hourStep) {
- scope.$parent.$watch($parse(attrs.hourStep), function(value) {
- hourStep = parseInt(value, 10);
- });
- }
-
- var minuteStep = timepickerConfig.minuteStep;
- if (attrs.minuteStep) {
- scope.$parent.$watch($parse(attrs.minuteStep), function(value) {
- minuteStep = parseInt(value, 10);
- });
- }
-
- // 12H / 24H mode
- scope.showMeridian = timepickerConfig.showMeridian;
- if (attrs.showMeridian) {
- scope.$parent.$watch($parse(attrs.showMeridian), function(value) {
- scope.showMeridian = !! value;
-
- if ( ! scope.model ) {
- // Reset
- var dt = new Date( selected );
- var hours = getScopeHours();
- if (angular.isDefined( hours )) {
- dt.setHours( hours );
- }
- scope.model = new Date( dt );
- } else {
- refreshTemplate();
- }
- });
- }
-
- // Get scope.hours in 24H mode if valid
- function getScopeHours ( ) {
- var hours = parseInt( scope.hours, 10 );
- var valid = ( scope.showMeridian ) ? (hours > 0 && hours < 13) : (hours >= 0 && hours < 24);
- if ( !valid ) {
- return;
- }
-
- if ( scope.showMeridian ) {
- if ( hours === 12 ) {
- hours = 0;
- }
- if ( scope.meridian === meridians[1] ) {
- hours = hours + 12;
- }
- }
- return hours;
- }
-
- // Input elements
- var inputs = element.find('input');
- var hoursInputEl = inputs.eq(0), minutesInputEl = inputs.eq(1);
-
- // Respond on mousewheel spin
- var mousewheel = (angular.isDefined(attrs.mousewheel)) ? scope.$eval(attrs.mousewheel) : timepickerConfig.mousewheel;
- if ( mousewheel ) {
-
- var isScrollingUp = function(e) {
- if (e.originalEvent) {
- e = e.originalEvent;
- }
- return (e.detail || e.wheelDelta > 0);
- };
-
- hoursInputEl.bind('mousewheel', function(e) {
- scope.$apply( (isScrollingUp(e)) ? scope.incrementHours() : scope.decrementHours() );
- e.preventDefault();
- });
-
- minutesInputEl.bind('mousewheel', function(e) {
- scope.$apply( (isScrollingUp(e)) ? scope.incrementMinutes() : scope.decrementMinutes() );
- e.preventDefault();
- });
- }
-
- var keyboardChange = false;
- scope.readonlyInput = (angular.isDefined(attrs.readonlyInput)) ? scope.$eval(attrs.readonlyInput) : timepickerConfig.readonlyInput;
- if ( ! scope.readonlyInput ) {
- scope.updateHours = function() {
- var hours = getScopeHours();
-
- if ( angular.isDefined(hours) ) {
- keyboardChange = 'h';
- if ( scope.model === null ) {
- scope.model = new Date( selected );
- }
- scope.model.setHours( hours );
- } else {
- scope.model = null;
- scope.validHours = false;
- }
- };
-
- hoursInputEl.bind('blur', function(e) {
- if ( scope.validHours && scope.hours < 10) {
- scope.$apply( function() {
- scope.hours = padFilter( scope.hours );
- });
- }
- });
-
- scope.updateMinutes = function() {
- var minutes = parseInt(scope.minutes, 10);
- if ( minutes >= 0 && minutes < 60 ) {
- keyboardChange = 'm';
- if ( scope.model === null ) {
- scope.model = new Date( selected );
- }
- scope.model.setMinutes( minutes );
- } else {
- scope.model = null;
- scope.validMinutes = false;
- }
- };
-
- minutesInputEl.bind('blur', function(e) {
- if ( scope.validMinutes && scope.minutes < 10 ) {
- scope.$apply( function() {
- scope.minutes = padFilter( scope.minutes );
- });
- }
- });
- } else {
- scope.updateHours = angular.noop;
- scope.updateMinutes = angular.noop;
- }
-
- scope.$watch( function getModelTimestamp() {
- return +scope.model;
- }, function( timestamp ) {
- if ( !isNaN( timestamp ) && timestamp > 0 ) {
- selected = new Date( timestamp );
- refreshTemplate();
- }
- });
-
- function refreshTemplate() {
- var hours = selected.getHours();
- if ( scope.showMeridian ) {
- // Convert 24 to 12 hour system
- hours = ( hours === 0 || hours === 12 ) ? 12 : hours % 12;
- }
- scope.hours = ( keyboardChange === 'h' ) ? hours : padFilter(hours);
- scope.validHours = true;
-
- var minutes = selected.getMinutes();
- scope.minutes = ( keyboardChange === 'm' ) ? minutes : padFilter(minutes);
- scope.validMinutes = true;
-
- scope.meridian = ( scope.showMeridian ) ? (( selected.getHours() < 12 ) ? meridians[0] : meridians[1]) : '';
-
- keyboardChange = false;
- }
-
- function addMinutes( minutes ) {
- var dt = new Date( selected.getTime() + minutes * 60000 );
- if ( dt.getDate() !== selected.getDate()) {
- dt.setDate( dt.getDate() - 1 );
- }
- selected.setTime( dt.getTime() );
- scope.model = new Date( selected );
- }
-
- scope.incrementHours = function() {
- addMinutes( hourStep * 60 );
- };
- scope.decrementHours = function() {
- addMinutes( - hourStep * 60 );
- };
- scope.incrementMinutes = function() {
- addMinutes( minuteStep );
- };
- scope.decrementMinutes = function() {
- addMinutes( - minuteStep );
- };
- scope.toggleMeridian = function() {
- addMinutes( 12 * 60 * (( selected.getHours() < 12 ) ? 1 : -1) );
- };
- }
- };
-}]);
-angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position'])
-
-/**
- * A helper service that can parse typeahead's syntax (string provided by users)
- * Extracted to a separate service for ease of unit testing
- */
- .factory('typeaheadParser', ['$parse', function ($parse) {
-
- // 00000111000000000000022200000000000000003333333333333330000000000044000
- var TYPEAHEAD_REGEXP = /^\s*(.*?)(?:\s+as\s+(.*?))?\s+for\s+(?:([\$\w][\$\w\d]*))\s+in\s+(.*)$/;
-
- return {
- parse:function (input) {
-
- var match = input.match(TYPEAHEAD_REGEXP), modelMapper, viewMapper, source;
- if (!match) {
- throw new Error(
- "Expected typeahead specification in form of '_modelValue_ (as _label_)? for _item_ in _collection_'" +
- " but got '" + input + "'.");
- }
-
- return {
- itemName:match[3],
- source:$parse(match[4]),
- viewMapper:$parse(match[2] || match[1]),
- modelMapper:$parse(match[1])
- };
- }
- };
-}])
-
- .directive('typeahead', ['$compile', '$parse', '$q', '$timeout', '$document', '$position', 'typeaheadParser', function ($compile, $parse, $q, $timeout, $document, $position, typeaheadParser) {
-
- var HOT_KEYS = [9, 13, 27, 38, 40];
-
- return {
- require:'ngModel',
- link:function (originalScope, element, attrs, modelCtrl) {
-
- var selected;
-
- //minimal no of characters that needs to be entered before typeahead kicks-in
- var minSearch = originalScope.$eval(attrs.typeaheadMinLength) || 1;
-
- //minimal wait time after last character typed before typehead kicks-in
- var waitTime = originalScope.$eval(attrs.typeaheadWaitMs) || 0;
-
- //expressions used by typeahead
- var parserResult = typeaheadParser.parse(attrs.typeahead);
-
- //should it restrict model values to the ones selected from the popup only?
- var isEditable = originalScope.$eval(attrs.typeaheadEditable) !== false;
-
- var isLoadingSetter = $parse(attrs.typeaheadLoading).assign || angular.noop;
-
- var onSelectCallback = $parse(attrs.typeaheadOnSelect);
-
- //pop-up element used to display matches
- var popUpEl = angular.element('
');
- popUpEl.attr({
- matches: 'matches',
- active: 'activeIdx',
- select: 'select(activeIdx)',
- query: 'query',
- position: 'position'
- });
-
- //create a child scope for the typeahead directive so we are not polluting original scope
- //with typeahead-specific data (matches, query etc.)
- var scope = originalScope.$new();
- originalScope.$on('$destroy', function(){
- scope.$destroy();
- });
-
- var resetMatches = function() {
- scope.matches = [];
- scope.activeIdx = -1;
- };
-
- var getMatchesAsync = function(inputValue) {
-
- var locals = {$viewValue: inputValue};
- isLoadingSetter(originalScope, true);
- $q.when(parserResult.source(scope, locals)).then(function(matches) {
-
- //it might happen that several async queries were in progress if a user were typing fast
- //but we are interested only in responses that correspond to the current view value
- if (inputValue === modelCtrl.$viewValue) {
- if (matches.length > 0) {
-
- scope.activeIdx = 0;
- scope.matches.length = 0;
-
- //transform labels
- for(var i=0; i
= minSearch) {
- if (waitTime > 0) {
- if (timeoutId) {
- $timeout.cancel(timeoutId);//cancel previous timeout
- }
- timeoutId = $timeout(function () {
- getMatchesAsync(inputValue);
- }, waitTime);
- } else {
- getMatchesAsync(inputValue);
- }
- }
- }
-
- return isEditable ? inputValue : undefined;
- });
-
- modelCtrl.$render = function () {
- var locals = {};
- locals[parserResult.itemName] = selected || modelCtrl.$viewValue;
- element.val(parserResult.viewMapper(scope, locals) || modelCtrl.$viewValue);
- selected = undefined;
- };
-
- scope.select = function (activeIdx) {
- //called from within the $digest() cycle
- var locals = {};
- var model, item;
- locals[parserResult.itemName] = item = selected = scope.matches[activeIdx].model;
-
- model = parserResult.modelMapper(scope, locals);
- modelCtrl.$setViewValue(model);
- modelCtrl.$render();
- onSelectCallback(scope, {
- $item: item,
- $model: model,
- $label: parserResult.viewMapper(scope, locals)
- });
-
- element[0].focus();
- };
-
- //bind keyboard events: arrows up(38) / down(40), enter(13) and tab(9), esc(27)
- element.bind('keydown', function (evt) {
-
- //typeahead is open and an "interesting" key was pressed
- if (scope.matches.length === 0 || HOT_KEYS.indexOf(evt.which) === -1) {
- return;
- }
-
- evt.preventDefault();
-
- if (evt.which === 40) {
- scope.activeIdx = (scope.activeIdx + 1) % scope.matches.length;
- scope.$digest();
-
- } else if (evt.which === 38) {
- scope.activeIdx = (scope.activeIdx ? scope.activeIdx : scope.matches.length) - 1;
- scope.$digest();
-
- } else if (evt.which === 13 || evt.which === 9) {
- scope.$apply(function () {
- scope.select(scope.activeIdx);
- });
-
- } else if (evt.which === 27) {
- evt.stopPropagation();
-
- resetMatches();
- scope.$digest();
- }
- });
-
- $document.bind('click', function(){
- resetMatches();
- scope.$digest();
- });
-
- element.after($compile(popUpEl)(scope));
- }
- };
-
-}])
-
- .directive('typeaheadPopup', function () {
- return {
- restrict:'E',
- scope:{
- matches:'=',
- query:'=',
- active:'=',
- position:'=',
- select:'&'
- },
- replace:true,
- templateUrl:'template/typeahead/typeahead.html',
- link:function (scope, element, attrs) {
-
- scope.isOpen = function () {
- return scope.matches.length > 0;
- };
-
- scope.isActive = function (matchIdx) {
- return scope.active == matchIdx;
- };
-
- scope.selectActive = function (matchIdx) {
- scope.active = matchIdx;
- };
-
- scope.selectMatch = function (activeIdx) {
- scope.select({activeIdx:activeIdx});
- };
- }
- };
- })
-
- .filter('typeaheadHighlight', function() {
-
- function escapeRegexp(queryToEscape) {
- return queryToEscape.replace(/([.?*+^$[\]\\(){}|-])/g, "\\$1");
- }
-
- return function(matchItem, query) {
- return query ? matchItem.replace(new RegExp(escapeRegexp(query), 'gi'), '$&') : query;
- };
- });
-
-angular.module("template/accordion/accordion-group.html", []).run(["$templateCache", function($templateCache) {
- $templateCache.put("template/accordion/accordion-group.html",
- "\n" +
- "
\n" +
- "
\n" +
- "
");
-}]);
-
-angular.module("template/accordion/accordion.html", []).run(["$templateCache", function($templateCache) {
- $templateCache.put("template/accordion/accordion.html",
- "");
-}]);
-
-angular.module("template/alert/alert.html", []).run(["$templateCache", function($templateCache) {
- $templateCache.put("template/alert/alert.html",
- "\n" +
- "
\n" +
- "
\n" +
- "
\n" +
- "");
-}]);
-
-angular.module("template/carousel/carousel.html", []).run(["$templateCache", function($templateCache) {
- $templateCache.put("template/carousel/carousel.html",
- "\n" +
- "
1\">\n" +
- " \n" +
- "
\n" +
- "
\n" +
- "
1\">‹\n" +
- "
1\">›\n" +
- "
\n" +
- "");
-}]);
-
-angular.module("template/carousel/slide.html", []).run(["$templateCache", function($templateCache) {
- $templateCache.put("template/carousel/slide.html",
- "\n" +
- "");
-}]);
-
-angular.module("template/datepicker/datepicker.html", []).run(["$templateCache", function($templateCache) {
- $templateCache.put("template/datepicker/datepicker.html",
- "\n" +
- " \n" +
- " \n" +
- " | \n" +
- " | \n" +
- " | \n" +
- "
\n" +
- " 0\">\n" +
- " # | \n" +
- " {{label}} | \n" +
- "
\n" +
- " \n" +
- " \n" +
- " \n" +
- " {{ getWeekNumber(row) }} | \n" +
- " \n" +
- " \n" +
- " | \n" +
- "
\n" +
- " \n" +
- "
\n" +
- "");
-}]);
-
-angular.module("template/dialog/message.html", []).run(["$templateCache", function($templateCache) {
- $templateCache.put("template/dialog/message.html",
- "\n" +
- "
\n" +
- "\n" +
- "
\n" +
- "\n" +
- "
\n" +
- "
\n" +
- "");
-}]);
-
-angular.module("template/modal/backdrop.html", []).run(["$templateCache", function($templateCache) {
- $templateCache.put("template/modal/backdrop.html",
- "");
-}]);
-
-angular.module("template/modal/window.html", []).run(["$templateCache", function($templateCache) {
- $templateCache.put("template/modal/window.html",
- "");
-}]);
-
-angular.module("template/pagination/pager.html", []).run(["$templateCache", function($templateCache) {
- $templateCache.put("template/pagination/pager.html",
- "\n" +
- "");
-}]);
-
-angular.module("template/pagination/pagination.html", []).run(["$templateCache", function($templateCache) {
- $templateCache.put("template/pagination/pagination.html",
- "\n" +
- "");
-}]);
-
-angular.module("template/tooltip/tooltip-html-unsafe-popup.html", []).run(["$templateCache", function($templateCache) {
- $templateCache.put("template/tooltip/tooltip-html-unsafe-popup.html",
- "\n" +
- "");
-}]);
-
-angular.module("template/tooltip/tooltip-popup.html", []).run(["$templateCache", function($templateCache) {
- $templateCache.put("template/tooltip/tooltip-popup.html",
- "\n" +
- "");
-}]);
-
-angular.module("template/popover/popover.html", []).run(["$templateCache", function($templateCache) {
- $templateCache.put("template/popover/popover.html",
- "\n" +
- "
\n" +
- "\n" +
- "
\n" +
- "
\n" +
- "
\n" +
- "
\n" +
- "
\n" +
- "");
-}]);
-
-angular.module("template/progressbar/bar.html", []).run(["$templateCache", function($templateCache) {
- $templateCache.put("template/progressbar/bar.html",
- "");
-}]);
-
-angular.module("template/progressbar/progress.html", []).run(["$templateCache", function($templateCache) {
- $templateCache.put("template/progressbar/progress.html",
- "");
-}]);
-
-angular.module("template/rating/rating.html", []).run(["$templateCache", function($templateCache) {
- $templateCache.put("template/rating/rating.html",
- "\n" +
- " val}\">\n" +
- "\n" +
- "");
-}]);
-
-angular.module("template/tabs/pane.html", []).run(["$templateCache", function($templateCache) {
- $templateCache.put("template/tabs/pane.html",
- "\n" +
- "");
-}]);
-
-angular.module("template/tabs/tab.html", []).run(["$templateCache", function($templateCache) {
- $templateCache.put("template/tabs/tab.html",
- "\n" +
- " {{heading}}\n" +
- "\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.html", []).run(["$templateCache", function($templateCache) {
- $templateCache.put("template/tabs/tabset.html",
- "\n" +
- "\n" +
- "
\n" +
- "
\n" +
- "
\n" +
- "
\n" +
- "
\n" +
- "
\n" +
- "");
-}]);
-
-angular.module("template/timepicker/timepicker.html", []).run(["$templateCache", function($templateCache) {
- $templateCache.put("template/timepicker/timepicker.html",
- "");
-}]);
-
-angular.module("template/typeahead/typeahead.html", []).run(["$templateCache", function($templateCache) {
- $templateCache.put("template/typeahead/typeahead.html",
- "\n" +
- " - \n" +
- " \n" +
- "
\n" +
- "
");
-}]);