2 * angular-dialog-service - A service to handle common dialog types in a web application. Built on top of Angular-Bootstrap's modal
4 * @author Michael Conroy, michael.e.conroy@gmail.com
5 * @license MIT, http://www.opensource.org/licenses/MIT
9 //== Translate Substitute Module =============================================//
12 * For those not using Angular-Translate (pascalprecht.translate), this will sub
13 * in for it so we don't have to include Angular-Translate if we don't want to.
16 var translateSubMod = angular.module('translate.sub',[]);
20 * Sets up a $translateProvider service to use in your module's config
21 * function. $translate.Provider syntax is the same as Angular-Translate,
22 * use $translate.Provider.translations(lang,obj) to change the defaults
23 * for modal button, header and message text.
25 translateSubMod.provider('$translate',[function(){
26 var _translations = []; // object of key/value translation pairs
27 var _current = 'en-US'; // default language
31 * Set the internal object of translation key/value pairs.
33 this.translations = function(lang,obj){
34 if(angular.isDefined(lang) && angular.isDefined(obj)){
35 _translations[lang] = angular.copy(obj);
38 }; // end translations
40 this.$get = [function(){
44 * Retrieve the translation for the given key, if key not found
45 * return an empty string.
46 * Example: $translate.instant('DIALOGS_OK');
48 instant : function(what){
49 if(angular.isDefined(what) && angular.isDefined(_translations[_current][what]))
50 return _translations[_current][what];
57 }]); // end $translate
61 * For use in an Angular template.
62 * Example: {{"DIALOGS_CLOSE" | translate}}
64 translateSubMod.filter('translate',['$translate',function($translate){
65 return function(what){
66 return $translate.instant(what);
68 }]); // end translate / translate.sub
69 //== Controllers =============================================================//
71 var ctrlrs; // will be dialogs.controllers module
73 // determine if Angular-Translate is available, if not use the substitute
75 angular.module('pascalprecht.translate'); // throws error if module not loaded
76 // console.log('Dialogs (Angular-Translate): OK');
78 // dialogs.controllers: module declaration
79 ctrlrs = angular.module('dialogs.controllers',['ui.bootstrap.modal','pascalprecht.translate']);
81 // console.log('Dialogs: (Angular-Translate): ' + err.message);
82 // console.log('Dialogs: Attempting to use translate.sub module.');
84 // dialogs.controllers: module declaration
85 ctrlrs = angular.module('dialogs.controllers',['ui.bootstrap.modal','translate.sub']);
88 // angular.module('dialogs.controllers',['ui.bootstrap.modal','pascalprecht.translate'])
91 * Error Dialog Controller
93 ctrlrs.controller('errorDialogCtrl',['$scope','$uibModalInstance','$translate','data',function($scope,$uibModalInstance,$translate,data){
94 //-- Variables -----//
96 $scope.header = (angular.isDefined(data.header)) ? data.header : $translate.instant('DIALOGS_ERROR');
97 $scope.msg = (angular.isDefined(data.msg)) ? data.msg : $translate.instant('DIALOGS_ERROR_MSG');
98 $scope.icon = (angular.isDefined(data.fa) && angular.equals(data.fa,true)) ? 'fa fa-warning' : 'glyphicon glyphicon-warning-sign';
102 $scope.close = function(){
103 $uibModalInstance.close();
106 }]); // end ErrorDialogCtrl
109 * Wait Dialog Controller
111 ctrlrs.controller('waitDialogCtrl',['$scope','$uibModalInstance','$translate','$timeout','data',function($scope,$uibModalInstance,$translate,$timeout,data){
112 //-- Variables -----//
114 $scope.header = (angular.isDefined(data.header)) ? data.header : $translate.instant('DIALOGS_PLEASE_WAIT_ELIPS');
115 $scope.msg = (angular.isDefined(data.msg)) ? data.msg : $translate.instant('DIALOGS_PLEASE_WAIT_MSG');
116 $scope.progress = (angular.isDefined(data.progress)) ? data.progress : 100;
117 $scope.icon = (angular.isDefined(data.fa) && angular.equals(data.fa,true)) ? 'fa fa-clock-o' : 'glyphicon glyphicon-time';
119 //-- Listeners -----//
121 // Note: used $timeout instead of $scope.$apply() because I was getting a $$nextSibling error
124 $scope.$on('dialogs.wait.complete',function(){
125 $timeout(function(){ $uibModalInstance.close(); $scope.$destroy(); });
126 }); // end on(dialogs.wait.complete)
128 // update the dialog's message
129 $scope.$on('dialogs.wait.message',function(evt,args){
130 $scope.msg = (angular.isDefined(args.msg)) ? args.msg : $scope.msg;
131 }); // end on(dialogs.wait.message)
133 // update the dialog's progress (bar) and/or message
134 $scope.$on('dialogs.wait.progress',function(evt,args){
135 $scope.msg = (angular.isDefined(args.msg)) ? args.msg : $scope.msg;
136 $scope.progress = (angular.isDefined(args.progress)) ? args.progress : $scope.progress;
137 }); // end on(dialogs.wait.progress)
141 $scope.getProgress = function(){
142 return {'width': $scope.progress + '%'};
143 }; // end getProgress
145 }]); // end WaitDialogCtrl
148 * Notify Dialog Controller
150 ctrlrs.controller('notifyDialogCtrl',['$scope','$uibModalInstance','$translate','data',function($scope,$uibModalInstance,$translate,data){
151 //-- Variables -----//
153 $scope.header = (angular.isDefined(data.header)) ? data.header : $translate.instant('DIALOGS_NOTIFICATION');
154 $scope.msg = (angular.isDefined(data.msg)) ? data.msg : $translate.instant('DIALOGS_NOTIFICATION_MSG');
155 $scope.icon = (angular.isDefined(data.fa) && angular.equals(data.fa,true)) ? 'fa fa-info' : 'glyphicon glyphicon-info-sign';
159 $scope.close = function(){
160 $uibModalInstance.close();
163 }]); // end WaitDialogCtrl
166 * Confirm Dialog Controller
168 ctrlrs.controller('confirmDialogCtrl',['$scope','$uibModalInstance','$translate','data',function($scope,$uibModalInstance,$translate,data){
169 //-- Variables -----//
171 $scope.header = (angular.isDefined(data.header)) ? data.header : $translate.instant('DIALOGS_CONFIRMATION');
172 $scope.msg = (angular.isDefined(data.msg)) ? data.msg : $translate.instant('DIALOGS_CONFIRMATION_MSG');
173 $scope.icon = (angular.isDefined(data.fa) && angular.equals(data.fa,true)) ? 'fa fa-check' : 'glyphicon glyphicon-check';
177 $scope.no = function(){
178 $uibModalInstance.dismiss('no');
181 $scope.yes = function(){
182 $uibModalInstance.close('yes');
184 }]); // end ConfirmDialogCtrl / dialogs.controllers
185 //== Services ================================================================//
187 angular.module('dialogs.services',['ui.bootstrap.modal','dialogs.controllers'])
189 .provider('dialogs',[function(){
190 var _b = true; // backdrop
191 var _k = true; // keyboard
192 var _w = 'dialogs-default'; // windowClass
193 var _bdc = 'dialogs-backdrop-default'; // backdropClass
194 var _copy = true; // controls use of angular.copy
195 var _wTmpl = null; // window template
196 var _wSize = 'lg'; // large modal window default
197 var _animation = false; // true/false to use animation
199 var _fa = false; // fontawesome flag
201 var _setOpts = function(opts){
204 _opts.kb = (angular.isDefined(opts.keyboard)) ? !!opts.keyboard : _k; // values: true,false
205 _opts.bd = (angular.isDefined(opts.backdrop)) ? opts.backdrop : _b; // values: 'static',true,false
206 _opts.bdc = (angular.isDefined(opts.backdropClass)) ? opts.backdropClass : _bdc; // additional CSS class(es) to be added to the modal backdrop
207 _opts.ws = (angular.isDefined(opts.size) && ((opts.size === 'sm') || (opts.size === 'lg') || (opts.size === 'md'))) ? opts.size : _wSize; // values: 'sm', 'lg', 'md'
208 _opts.wc = (angular.isDefined(opts.windowClass)) ? opts.windowClass : _w; // additional CSS class(es) to be added to a modal window
209 _opts.anim = (angular.isDefined(opts.animation)) ? !!opts.animation : _animation; // values: true,false
216 * Sets the use of the modal backdrop. Either to have one or not and
217 * whether or not it responds to mouse clicks ('static' sets the
218 * backdrop to true and does not respond to mouse clicks).
220 * @param val mixed (true, false, 'static')
222 this.useBackdrop = function(val){ // possible values : true, false, 'static'
223 if(angular.isDefined(val))
225 }; // end useStaticBackdrop
230 * Sets the use of the ESC (escape) key to close modal windows.
234 this.useEscClose = function(val){ // possible values : true, false
235 if(angular.isDefined(val))
236 _k = (!angular.equals(val,0) && !angular.equals(val,'false') && !angular.equals(val,'no') && !angular.equals(val,null) && !angular.equals(val,false)) ? true : false;
237 }; // end useESCClose
242 * Sets the additional CSS window class of the modal window template.
246 this.useClass = function(val){
247 if(angular.isDefined(val))
254 * Determines the use of angular.copy when sending data to the modal controller.
258 this.useCopy = function(val){
259 if(angular.isDefined(val))
260 _copy = (!angular.equals(val,0) && !angular.equals(val,'false') && !angular.equals(val,'no') && !angular.equals(val,null) && !angular.equals(val,false)) ? true : false;
264 * Set Window Template
266 * Sets a path to a template to use overriding modal's window template.
270 this.setWindowTmpl = function(val){
271 if(angular.isDefined(val))
273 }; // end setWindowTmpl
278 * Sets the modal size to use (sm,lg,md)
280 * @param val string (sm,lg,md)
282 this.setSize = function(val){
283 if(angular.isDefined(val))
284 _wSize = (angular.equals(val,'sm') || angular.equals(val,'lg') || angular.equals(val,'md')) ? val : _wSize;
290 * Sets the use of animations to true
292 this.useAnimation = function(){
294 }; // end useAnimation
299 * Sets Font-Awesome flag to true and substitutes font-awesome icons for
300 * Bootstrap's glyphicons.
302 this.useFontAwesome = function(){
304 }; // end useFontAwesome
307 this.$get = ['$uibModal',function ($uibModal){
313 * @param header string
317 error : function(header,msg,opts){
318 opts = _setOpts(opts);
320 return $uibModal.open({
321 templateUrl : '/dialogs/error.html',
322 controller : 'errorDialogCtrl',
324 backdropClass: opts.bdc,
326 windowClass: opts.wc,
328 animation: opts.anim,
332 header : angular.copy(header),
333 msg : angular.copy(msg),
338 }); // end modal.open
344 * @param header string
346 * @param progress int
349 wait : function(header,msg,progress,opts){
350 opts = _setOpts(opts);
352 return $uibModal.open({
353 templateUrl : '/dialogs/wait.html',
354 controller : 'waitDialogCtrl',
356 backdropClass: opts.bdc,
358 windowClass: opts.wc,
360 animation: opts.anim,
364 header : angular.copy(header),
365 msg : angular.copy(msg),
366 progress : angular.copy(progress),
371 }); // end modal.open
377 * @param header string
381 notify : function(header,msg,opts){
382 opts = _setOpts(opts);
384 return $uibModal.open({
385 templateUrl : '/dialogs/notify.html',
386 controller : 'notifyDialogCtrl',
388 backdropClass: opts.bdc,
390 windowClass: opts.wc,
392 animation: opts.anim,
396 header : angular.copy(header),
397 msg : angular.copy(msg),
402 }); // end modal.open
408 * @param header string
412 confirm : function(header,msg,opts){
413 opts = _setOpts(opts);
415 return $uibModal.open({
416 templateUrl : '/dialogs/confirm.html',
417 controller : 'confirmDialogCtrl',
419 backdropClass: opts.bdc,
421 windowClass: opts.wc,
423 animation: opts.anim,
427 header : angular.copy(header),
428 msg : angular.copy(msg),
433 }); // end modal.open
437 * Create Custom Dialog
440 * @param ctrlr string
444 create : function(url,ctrlr,data,opts,ctrlAs){
445 var copy = (opts && angular.isDefined(opts.copy)) ? opts.copy : _copy;
446 opts = _setOpts(opts);
448 return $uibModal.open({
451 controllerAs : ctrlAs,
454 backdropClass: opts.bdc,
455 windowClass: opts.wc,
457 animation: opts.anim,
461 return angular.copy(data);
466 }); // end modal.open
472 }]); // end provider dialogs
473 //== Dialogs.Main Module =====================================================//
476 * Include this module 'dialogs.main' in your module's dependency list where you
477 * intend to use it. Then inject the 'dialogs' service in your controllers that
481 angular.module('dialogs.main',['dialogs.services','ngSanitize']) // requires angular-sanitize.min.js (ngSanitize) //code.angularjs.org/1.2.1/angular-sanitize.min.js
483 .config(['$translateProvider','dialogsProvider',function($translateProvider,dialogsProvider){
485 * if Angular-Translate is not loaded, use the translate substitute
486 * module and create default translations to use as default modal texts
489 angular.module('pascalprecht.translate');
491 // console.log('Dialogs: Creating default translations for use without Angular-Translate.');
493 // This will set default modal buttons, header and message text
494 $translateProvider.translations('en-US',{
495 DIALOGS_ERROR: "Error",
496 DIALOGS_ERROR_MSG: "An unknown error has occurred.",
497 DIALOGS_CLOSE: "Close",
498 DIALOGS_PLEASE_WAIT: "Please Wait",
499 DIALOGS_PLEASE_WAIT_ELIPS: "Please Wait...",
500 DIALOGS_PLEASE_WAIT_MSG: "Waiting on operation to complete.",
501 DIALOGS_PERCENT_COMPLETE: "% Complete",
502 DIALOGS_NOTIFICATION: "Notification",
503 DIALOGS_NOTIFICATION_MSG: "Unknown application notification.",
504 DIALOGS_CONFIRMATION: "Confirmation",
505 DIALOGS_CONFIRMATION_MSG: "Confirmation required.",
513 * Attempt to ascertain if page is using Font Awesome instead of the
514 * regular Bootstrap Icons. If you are changing the stylesheet name or
515 * not including it from a CDN or have included Font-Awesome as a
516 * concatentation of CSS sheets together, then you will have to manually
517 * set Font-Awesome usage in your Angular Module's config by including
518 * the $dialogsProvider and calling the method $dialogsProvider.useFontAwesome().
521 var _sheets = document.styleSheets;
524 for(var i = (_sheets.length - 1);i >= 0;i--){
528 if(!_sheets[i].disabled){
529 // check href of style sheet first
530 if(_sheets[i].href !== null)
531 _matches = _sheets[i].href.match(/font\-*awesome/i);
533 if(angular.isArray(_matches)){
534 dialogsProvider.useFontAwesome();
535 break; // done, leave the style sheet for loop
537 // try to find css rule .fa, in case style sheet has been concatenated
538 _rules = _sheets[i].cssRules;
539 for(var x = (_rules.length - 1);x >= 0;x--){
540 if(typeof(_rules[x].selectorText) === 'string' && _rules[x].selectorText.toLowerCase() === '.fa'){
541 dialogsProvider.useFontAwesome();
542 break sheetLoop; // done, exit both for loops
546 } // end if(disabled)
549 // console.log('Error Message: ' + err);
553 // Add default templates via $templateCache
554 .run(['$templateCache','$interpolate',function($templateCache,$interpolate){
556 // get interpolation symbol (possible that someone may have changed it in their application instead of using '{{}}')
557 var startSym = $interpolate.startSymbol();
558 var endSym = $interpolate.endSymbol();
560 $templateCache.put('/dialogs/error.html','<div class="modal-header dialog-header-error"><button type="button" class="close" ng-click="close()">×</button><h4 class="modal-title text-danger"><span class="'+startSym+'icon'+endSym+'"></span> <span ng-bind-html="header"></span></h4></div><div class="modal-body text-danger" ng-bind-html="msg"></div><div class="modal-footer"><button type="button" class="btn btn-default" ng-click="close()">'+startSym+'"DIALOGS_CLOSE" | translate'+endSym+'</button></div>');
561 $templateCache.put('/dialogs/wait.html','<div class="modal-header dialog-header-wait"><h4 class="modal-title"><span class="'+startSym+'icon'+endSym+'"></span> '+startSym+'header'+endSym+'</h4></div><div class="modal-body"><p ng-bind-html="msg"></p><div class="progress progress-striped active"><div class="progress-bar progress-bar-info" ng-style="getProgress()"></div><span class="sr-only">'+startSym+'progress'+endSym+''+startSym+'"DIALOGS_PERCENT_COMPLETE" | translate'+endSym+'</span></div></div>');
562 $templateCache.put('/dialogs/notify.html','<div class="modal-header dialog-header-notify"><button type="button" class="close" ng-click="close()" class="pull-right">×</button><h4 class="modal-title text-info"><span class="'+startSym+'icon'+endSym+'"></span> '+startSym+'header'+endSym+'</h4></div><div class="modal-body text-info" ng-bind-html="msg"></div><div class="modal-footer"><button type="button" class="btn btn-primary" ng-click="close()">'+startSym+'"DIALOGS_OK" | translate'+endSym+'</button></div>');
563 $templateCache.put('/dialogs/confirm.html','<div class="modal-header dialog-header-confirm"><button type="button" class="close" ng-click="no()">×</button><h4 class="modal-title"><span class="'+startSym+'icon'+endSym+'"></span> '+startSym+'header'+endSym+'</h4></div><div class="modal-body" ng-bind-html="msg"></div><div class="modal-footer"><button type="button" class="btn btn-default" ng-click="yes()">'+startSym+'"DIALOGS_YES" | translate'+endSym+'</button><button type="button" class="btn btn-primary" ng-click="no()">'+startSym+'"DIALOGS_NO" | translate'+endSym+'</button></div>');
564 }]); // end run / dialogs.main