| angular.module("ui.bootstrap", ["ui.bootstrap.tpls", "ui.bootstrap.buttons","ui.bootstrap.transition","ui.bootstrap.dialog"]); |
| angular.module("ui.bootstrap.tpls", ["template/dialog/message.html"]); |
| 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) { |
| |
| var trueValue = scope.$eval(attrs.btnCheckboxTrue); |
| var falseValue = scope.$eval(attrs.btnCheckboxFalse); |
| |
| trueValue = angular.isDefined(trueValue) ? trueValue : true; |
| falseValue = angular.isDefined(falseValue) ? falseValue : false; |
| |
| //model -> UI |
| ngModelCtrl.$render = function () { |
| element.toggleClass(activeClass, angular.equals(ngModelCtrl.$modelValue, trueValue)); |
| }; |
| |
| //ui->model |
| element.bind(toggleEvent, function () { |
| scope.$apply(function () { |
| ngModelCtrl.$setViewValue(element.hasClass(activeClass) ? falseValue : trueValue); |
| ngModelCtrl.$render(); |
| }); |
| }); |
| } |
| }; |
| }]); |
| 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; |
| }]); |
| |
| // The `$dialogProvider` can be used to configure global defaults for your |
| // `$dialog` service. |
| var dialogModule = angular.module('ui.bootstrap.dialog', ['ui.bootstrap.transition']); |
| |
| dialogModule.controller('MessageBoxController', ['$scope', 'dialog', 'model', function($scope, dialog, model){ |
| $scope.title = model.title; |
| $scope.message = model.message; |
| $scope.buttons = model.buttons; |
| $scope.close = function(res){ |
| dialog.close(res); |
| }; |
| }]); |
| |
| dialogModule.provider("$dialog", function(){ |
| |
| // The default options for all dialogs. |
| var defaults = { |
| backdrop: true, |
| dialogClass: 'modal', |
| backdropClass: 'modal-backdrop', |
| transitionClass: 'fade', |
| triggerClass: 'in', |
| resolve:{}, |
| backdropFade: false, |
| dialogFade:false, |
| keyboard: true, // close with esc key |
| backdropClick: true // only in conjunction with backdrop=true |
| /* other options: template, templateUrl, controller */ |
| }; |
| |
| var globalOptions = {}; |
| |
| var activeBackdrops = {value : 0}; |
| |
| // The `options({})` allows global configuration of all dialogs in the application. |
| // |
| // var app = angular.module('App', ['ui.bootstrap.dialog'], function($dialogProvider){ |
| // // don't close dialog when backdrop is clicked by default |
| // $dialogProvider.options({backdropClick: false}); |
| // }); |
| this.options = function(value){ |
| globalOptions = value; |
| }; |
| |
| // Returns the actual `$dialog` service that is injected in controllers |
| this.$get = ["$http", "$document", "$compile", "$rootScope", "$controller", "$templateCache", "$q", "$transition", "$injector", |
| function ($http, $document, $compile, $rootScope, $controller, $templateCache, $q, $transition, $injector) { |
| |
| var body = $document.find('body'); |
| |
| function createElement(clazz) { |
| var el = angular.element("<div>"); |
| 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.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 |
| }; |
| } |
| }}); |
| } |
| }; |
| }]; |
| }); |
| |
| angular.module("template/dialog/message.html", []).run(["$templateCache", function($templateCache) { |
| $templateCache.put("template/dialog/message.html", |
| "<div class=\"modal-header\">\n" + |
| " <h1>{{ title }}</h1>\n" + |
| "</div>\n" + |
| "<div class=\"modal-body\">\n" + |
| " <p>{{ message }}</p>\n" + |
| "</div>\n" + |
| "<div class=\"modal-footer\">\n" + |
| " <button ng-repeat=\"btn in buttons\" ng-click=\"close(btn.result)\" class=btn ng-class=\"btn.cssClass\">{{ btn.label }}</button>\n" + |
| "</div>\n" + |
| ""); |
| }]); |