<!DOCTYPE html>
<html ng-app="app">

<head>

    <link href="//netdna.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet" />
    <link href="//maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css" rel="stylesheet" />
    <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.5.3/angular.js"></script>
    <script src="//code.angularjs.org/1.4.8/angular-sanitize.js"></script>
    <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.5.3/angular-animate.js"></script>
    <script src="//angular-ui.github.io/bootstrap/ui-bootstrap-tpls-1.3.3.js"></script>
    
    <!--<link href="spinner.css" rel="stylesheet" />-->
    
    <script src="app.js"></script>
    <script src="popup.service.min.js"></script>
</head>

<body>
    <div ng-controller="appCtrl as vm" class="fluid panel-body">
        <h2>Dialoge Box using UI Bootstrap</h2>
        <h5>Inspired by <a href="http://ionicframework.com/docs/v1/api/service/$ionicPopup/" target="_blank">Ionic Popup</a></h5>
        <div style=" padding-bottom: 10px;">
           <strong class="text-danger mark">Launch in full window(or extend size of this window) to see the modal size effect.</strong>
        </div>
        <div>
            <button class="btn btn-info" ng-click="vm.showConfirmWithOptions()">
                Complex Confirm
            </button>
            <button class="btn btn-primary" ng-click="vm.showConfirm()">
                Simple Confirm
            </button>
             <button class="btn btn-success" ng-click="vm.showAlert()">
                Alert
            </button>
            <button class="btn btn-success" ng-click="vm.showAndClose()">
                Alert (Close after 2  Sec)
            </button>
            <button class="btn btn-warning" ng-click="vm.showAndCloseSpinner()">
                Spinner started with Generic Method (Close after 2  Sec)
            </button>
            <button class="btn btn-warning" ng-click="vm.showAndCloseSpinnerGlobal()">
                Simple Spinner (Close after 2  Sec)
            </button>

        </div>

        <!--<button class="button button-dark" ng-click="vm.showPopup()">-->
        <!--      show-->
        <!--  </button>-->
        <form>
            <div class="form-group">
                <label for="text1">Message Body </label>
                <input type="text" class="form-control" id="text1" ng-model="vm.body">
            </div>
            <div class="form-group">
                <label for="text2">Title </label>
                <input type="text" class="form-control" id="text2" ng-model="vm.title">
            </div>
            <div class="form-group">
                <label for="text3">Sub Title </label>
                <input type="text" class="form-control" id="text3" ng-model="vm.subTitle">
            </div>
            <div class="alert alert-success">
                All input supports html snippet as well!!
            </div>
        </form>
        <hr>
    </div>
</body>

</html>
angular.module('app', ['ngAnimate', 'ui.bootstrap', 'ngSanitize', 'popup'])
    .controller('appCtrl', ['$scope', '$injector', appCtrl])
    .config(['PopupSvcProvider', function(PopupSvcProvider) { //optionally configure popup svc

        PopupSvcProvider.setDefaults({
            //okText: 'Dismiss'
        });
    }]);

function appCtrl($scope, $injector) {
    var vm = this;

    vm.body = "<span class='text-success'>Hey!!</span>Modal width automatically adjust as per Message body length when you simply pass the string";
    vm.title = "Attention!";
    vm.subTitle = "You bet";

    var PopupSvc = $injector.get('PopupSvc');

    vm.showAlert = function() {
        var modal = PopupSvc.alert(vm.body);
        modal.then(function(response) {
            vm.response = response;
        });
    };

    vm.showAndClose = function() {
        var modal = PopupSvc.alert(vm.body);
        window.setTimeout(modal.close, 2000);
    };
    
    vm.showAndCloseSpinner = function() {
        var modal = PopupSvc.spin('Saving...');
        window.setTimeout(modal.close, 2000);
    };
    
    vm.showAndCloseSpinnerGlobal = function() {
        PopupSvc.spin('');
        window.setTimeout(PopupSvc.stopSpin, 2000);
    };

    vm.showConfirm = function() {
        PopupSvc.confirm(vm.body).then(function(response) {
            vm.response = response;
        });
    };

    vm.showConfirmWithOptions = function() {
        PopupSvc.confirm({
            title: vm.title,
            subTitle: vm.subTitle,
            body: vm.body,
            okText: 'Agree',
            cancelClass: 'btn-danger'
        }).then(function(response) {
            vm.response = response;
        });
    };
}
(function() {
    /* popup.service.js v1.1 by Amitesh Kumar*/
    angular.module('popup', [])
        .provider('PopupSvc', PopupProviderFunction);

    function PopupProviderFunction() {

        var DEFAULTS = {
            //title, subTitle and body are not a part of configuration
            // title: '', // String (optional). The title of the popup.
            // subTitle: '', // String (optional). The sub-title of the popup. only applicable when title provided
            // body: '', //String or html template to place in the popup body.

            okText: 'OK', //text for OK button
            okClass: 'btn-info', //class(es) to be added to OK button; e.g 'btn-info btn-small'
            cancelText: 'Cancel', //not applicable for alert
            cancelClass: 'btn-secondary',

            headerClass: 'text-center', //class to be added to bootstrap modal-header
            bodyClass: '', //class for bootstrap modal-body
            footerClass: '', //class for bootstrap modal-footer

            //Below are the three uibModal related properties, see uibModal Bootstrap documentation for details

            backdrop: 'static', //Controls presence of a backdrop. Allowed values: true (default), false (no backdrop), 'static' (disables modal closing by click on the backdrop).

            keyboard: false, //Indicates whether the dialog should be closable by hitting the ESC key.
            size: 'sm', //modal or popup size, default is small

            /*NOTE: Below are the app level configuration applicable when input parameter is string. It can be set during angular config phase.
             */

            showStringAs: 'body', //it will display the text as modal body(left aligned smaller font text). Other value is 'title' (center aligned h5 element)
            enableDynamicSize: true, //show medium size popup when input string extends the below character limit
            extendSizeCharLength: 300
        };

        this.setDefaults = function(userDefaults) {
            angular.extend(DEFAULTS, userDefaults);
        };

        this.$get = ["$uibModal", function($uibModal) {
            return new PopupSvc(DEFAULTS, $uibModal);
        }];

    }

    function PopupSvc(DEFAULTS, $uibModal) {
        /* jshint -W043 */
        var POPUP_TEMPLATE = '\
        <div>\
            <div class="modal-header" ng-class="vm.headerClass" ng-if="vm.title" style="border:none; padding-bottom: 0;">\
                <h3 class="modal-title" ng-bind-html="vm.title"></h3>\
                <h5 class="modal-sub-title" ng-bind-html="vm.subTitle" ng-if="vm.subTitle"></h5>\
            </div>\
            <div class="modal-body" ng-if="vm.body" ng-class="vm.bodyClass">\
                <div ng-bind-html="vm.body"></div>\
            </div>\
            <div class="modal-footer" ng-class="vm.footerClass" style="text-align: center; padding-top: 10px; padding-bottom: 15px; border: none">\
                <div xclass="btn-group">\
                    <button ng-repeat="button in vm.buttons" ng-click="vm.onButtonClick(button, $index)" style="min-width: 70px;" class="btn" ng-class="button.className" ng-bind-html="button.text"></button>\
                </div>\
            </div>\
        </div>';

        var SPINNER_TEMPLATE = '<div><i class="fa fa-spinner fa-spin fa-2x"></i><span style="margin-left:5px; font-size: 1.3em">{{vm.title}}</span></div>';
        var _spinnerModals = [];
        
        _addDirectiveStyle();
        
        return {
            alert: _alert,
            confirm: _confirm,
            spin: _spin,
            stopSpin: _stopSpin,
            stopAll: _stopAll
                //prompt: _prompt,
                //show: _show//generic one which can show multiple buttons
        };
        
        function _addDirectiveStyle(){
            //minified spinner.css
            angular.element(document).find('head').prepend('<style type="text/css">@charset "UTF-8";.modal-spinner .modal-content{padding:20px;border-radius:5px;background-color:rgba(0,0,0,.8);color:#fff;text-align:center;text-overflow:ellipsis;font-size:15px}.modal-spinner-text .modal-content{text-align:left}.modal-spinner .modal-dialog{position:absolute;left:0;right:0;top:0;bottom:0;margin:auto;width:80px;height:300px}.modal-spinner-text .modal-dialog{text-align:left;width:250px}</style>');
        }

        function _alert(opts) {
            return _showPopup(extend([{
                text: opts.okText || DEFAULTS.okText,
                className: opts.okClass || DEFAULTS.okClass,
                handler: function() {
                    return true;
                }
            }], opts || {}));
        }

        function _confirm(opts) {
            return _showPopup(extend([{
                text: opts.cancelText || DEFAULTS.cancelText,
                className: opts.cancelClass || DEFAULTS.cancelClass,
                handler: function() {
                    return false;
                }
            }, {
                text: opts.okText || DEFAULTS.okText,
                className: opts.okClass || DEFAULTS.okClass,
                handler: function() {
                    return true;
                }
            }], opts || {}));
        }

        function _spin(title) { //by default spinner is global
            var windowClass = 'modal-spinner';
            if (title) {
                windowClass += ' modal-spinner-text';
            }

            var modal = _showPopup({
                title: title,
                backdrop: 'static',
                keyboard: false,
                windowClass: windowClass,
                isSpinner: true
            });
            var modalClose = modal.close;
            var index = _spinnerModals.length;
            _spinnerModals.push(modal);
            modal.close = function(calledViaStop) {
                modalClose();
                if(!calledViaStop){//i.e from UI some one kept the reference of modal and called modal.close()
                    _spinnerModals.splice(index, 1);
                }
            };

            return modal;
        }

        function _stopSpin() {
            if (!_spinnerModals.length) {
                return;
            }
            var spinnerModal = _spinnerModals.pop();
            spinnerModal.close(true);
        }

        function _stopAll() {
            var spinnerModal;
            while (_spinnerModals.length) {
                spinnerModal = _spinnerModals.pop();
                spinnerModal.close(true);
            }
        }

        function extend(buttons, userOpts) {
            var options = angular.copy(DEFAULTS);
            userOpts = userOpts || 'You there?';
            if (typeof userOpts === 'string') {
                var obj = {};
                obj[DEFAULTS.showStringAs] = userOpts;
                if (DEFAULTS.enableDynamicSize && userOpts.length > DEFAULTS.extendSizeCharLength) {
                    obj.size = 'md';
                }
                userOpts = obj;
            }
            options.buttons = buttons;
            return angular.extend(options, userOpts);
        }


        function _showPopup(options) {
            var modalInstance = $uibModal.open({
                template: options.isSpinner ? SPINNER_TEMPLATE : POPUP_TEMPLATE,
                size: options.size,
                backdrop: options.backdrop,
                keyboard: options.keyboard,
                windowClass: options.windowClass,
                bindToController: true,
                controllerAs: 'vm',
                controller: ['$injector', 'options', ModelController],
                resolve: {
                    options: function() {
                        return options;
                    }
                }
            });

            var obj = modalInstance.result; //returned promise
            obj.close = modalInstance.close; //augment close method
            return obj;
        }

        function ModelController($injector, options) {
            var vm = this;
            sanitizeInput(options);
            angular.extend(vm, options);

            vm.onButtonClick = function(button, $index) {
                var response = button.handler(); //true for alert,  true and false for confirm
                vm.$close(response);
            };

            function sanitizeInput(options) {
                if ($injector.has('$sanitize')) {
                    return; //no need to sanitize
                }

                var $sce = $injector.get('$sce');

                options.title = $sce.trustAsHtml(options.title);
                if (options.isSpinner) {
                    return;
                }

                options.subTitle = $sce.trustAsHtml(options.subTitle);
                options.body = $sce.trustAsHtml(options.body);
                options.buttons.forEach(function(button, index, buttons) {
                    button.text = $sce.trustAsHtml(button.text);
                });
            }
        }
    }
})();
/*! popup.service.min.js v1.1 by Amitesh Kumar, updated 2017-10-20. Visit  https://github.com/amiteshhh/utilities/angular/popup */
!function(){function t(){var t={okText:"OK",okClass:"btn-info",cancelText:"Cancel",cancelClass:"btn-secondary",headerClass:"text-center",bodyClass:"",footerClass:"",backdrop:"static",keyboard:!1,size:"sm",showStringAs:"body",enableDynamicSize:!0,extendSizeCharLength:300};this.setDefaults=function(n){angular.extend(t,n)},this.$get=["$uibModal",function(e){return new n(t,e)}]}function n(t,n){function e(){angular.element(document).find("head").prepend('<style type="text/css">@charset "UTF-8";.modal-spinner .modal-content{padding:20px;border-radius:5px;background-color:rgba(0,0,0,.8);color:#fff;text-align:center;text-overflow:ellipsis;font-size:15px}.modal-spinner-text .modal-content{text-align:left}.modal-spinner .modal-dialog{position:absolute;left:0;right:0;top:0;bottom:0;margin:auto;width:80px;height:300px}.modal-spinner-text .modal-dialog{text-align:left;width:250px}</style>')}function o(n){return d(r([{text:n.okText||t.okText,className:n.okClass||t.okClass,handler:function(){return!0}}],n||{}))}function s(n){return d(r([{text:n.cancelText||t.cancelText,className:n.cancelClass||t.cancelClass,handler:function(){return!1}},{text:n.okText||t.okText,className:n.okClass||t.okClass,handler:function(){return!0}}],n||{}))}function a(t){var n="modal-spinner";t&&(n+=" modal-spinner-text");var e=d({title:t,backdrop:"static",keyboard:!1,windowClass:n,isSpinner:!0}),o=e.close,s=m.length;return m.push(e),e.close=function(t){o(),t||m.splice(s,1)},e}function l(){if(m.length){var t=m.pop();t.close(!0)}}function i(){for(var t;m.length;)t=m.pop(),t.close(!0)}function r(n,e){var o=angular.copy(t);if(e=e||"You there?","string"==typeof e){var s={};s[t.showStringAs]=e,t.enableDynamicSize&&e.length>t.extendSizeCharLength&&(s.size="md"),e=s}return o.buttons=n,angular.extend(o,e)}function d(t){var e=n.open({template:t.isSpinner?p:u,size:t.size,backdrop:t.backdrop,keyboard:t.keyboard,windowClass:t.windowClass,bindToController:!0,controllerAs:"vm",controller:["$injector","options",c],resolve:{options:function(){return t}}}),o=e.result;return o.close=e.close,o}function c(t,n){function e(n){if(!t.has("$sanitize")){var e=t.get("$sce");n.title=e.trustAsHtml(n.title),n.isSpinner||(n.subTitle=e.trustAsHtml(n.subTitle),n.body=e.trustAsHtml(n.body),n.buttons.forEach(function(t,n,o){t.text=e.trustAsHtml(t.text)}))}}var o=this;e(n),angular.extend(o,n),o.onButtonClick=function(t,n){var e=t.handler();o.$close(e)}}var u='        <div>            <div class="modal-header" ng-class="vm.headerClass" ng-if="vm.title" style="border:none; padding-bottom: 0;">                <h3 class="modal-title" ng-bind-html="vm.title"></h3>                <h5 class="modal-sub-title" ng-bind-html="vm.subTitle" ng-if="vm.subTitle"></h5>            </div>            <div class="modal-body" ng-if="vm.body" ng-class="vm.bodyClass">                <div ng-bind-html="vm.body"></div>            </div>            <div class="modal-footer" ng-class="vm.footerClass" style="text-align: center; padding-top: 10px; padding-bottom: 15px; border: none">                <div xclass="btn-group">                    <button ng-repeat="button in vm.buttons" ng-click="vm.onButtonClick(button, $index)" style="min-width: 70px;" class="btn" ng-class="button.className" ng-bind-html="button.text"></button>                </div>            </div>        </div>',p='<div><i class="fa fa-spinner fa-spin fa-2x"></i><span style="margin-left:5px; font-size: 1.3em">{{vm.title}}</span></div>',m=[];return e(),{alert:o,confirm:s,spin:a,stopSpin:l,stopAll:i}}angular.module("popup",[]).provider("PopupSvc",t)}();
# Angular Bootstrap Popup


A simple and elegant angular service inspired by [ionic popup](http://ionicframework.com/docs/v1/api/service/$ionicPopup) to show loader/spinner and popup(or dialogue) window for `alert` and `confirm` with custom content and look.

## Dialoge Box
PopupSvc exposes two methods `alert` and `confirm` which takes one parameter either string/html template or an [option](#advance-usage) object for customized look and feel.
These methods returns [promise](https://docs.angularjs.org/api/ng/service/$q) which is resolved when the popup is dismissed. It also returns `close` method to programmatically close it.

## Spinner

Use below methods to display a loader or spinner as overlay driven by Bootstrap modal window

    1. spin : Show a spinner/loader
    2. stopSpin: Stop the spinner


## Dependency

[Angular](https://code.angularjs.org/1.5.3/docs/api) (tested on v1.5.3)

[Angular UI Bootstrap](http://angular-ui.github.io/bootstrap/versioned-docs/1.3.3/) (tested on v1.3.3)

> PopupSvc should work fine with any Angular/UI Bootstrap version supporting [ControllerAs](https://johnpapa.net/angularjss-controller-as-and-the-vm-variable/) syntax.

## Demo

Check out the demo at [Plunker](https://embed.plnkr.co/SNhye1/)


## Dialoge Usage 

```javascript
angular.module('myApp', ['popup']);//add popup module dependency
angular.controller('myCtrl', ['PopupSvc', function(PopupSvc){//Inject the PopupSvc service into your controller

    //1. Basic Usage
    PopupSvc.alert("<strong>Hey!</strong> How you doing");//html string
    PopupSvc.confirm("Are you sure?");//normal text

    //2. Reacting on popup dismissal/button click
    var popup = PopupSvc.alert("Hey! How you doing");
    popup.then(function(){
        console.log('Alert dismissed');
    });

    PopupSvc.confirm("Hey! How you doing").then(function(response){
        if(response){
            console.log('Primary/OK button clicked');
        }else{
            console.log('Secondary/Cancel button clicked');
        }    
    });

    //Programmatically closing the popup using `close` method
    //close method must be executed in next tick as popup creation is asynchronous
    var popup = PopupSvc.alert("Hey! How you doing");
    window.setTimeout(popup.close, 3000);

    //3. Customized popup
    var popupOption = {//only fewer options here
        title: 'Confirm',
        subTitle: '<span style="color: red;">Are you sure?</span>',
        body: 'Operation can not be reverted',
        okText: 'Delete',
        okClass: 'btn-danger',
        size: 'md'//show a medium size modal popup
    };
    PopupSvc.confirm(popupOption);
}]);

```
## Spinner Usage 

```javascript
PopupSvc.spin();//spinner without leading text
PopupSvc.spin('Saving...');//spinner with leading text
PopupSvc.stopSpin();//stop and remove the last spinner

//Advance usage to stop the required spinner in case of multiple spinner showing at a time
var modal = PopupSvc.spin();//will create the spinner and return modal reference to stop it
modal.close();//stop or close the spinner
```


## Install

Download the script file directly from Github.

https://raw.githubusercontent.com/amiteshhh/angular-uib-popup/master/popup.service.min.js

Add `script` reference to your html then add `popup` module as angular module dependency. Now you can use `PopupSvc` service.


## Advance Usage

To have a custom look and feel (e.g button texts etc.) use below option as a parameter to `alert` or `confirm`. All of the option properties are optional. Of course you will provide value for `body` or `title` to render some text.


> popup is created using bootstrap $uibModal hence there are few properties related to that as well

```javascript
{
    title: '', // String. The title of the popup.
    subTitle: '', // String. The sub-title of the popup. only applicable when title provided
    body: '',//String to place in the popup body.

    okText: 'OK',//text for OK button
    okClass: 'btn-info',//class(es) to be added to OK button; e.g 'btn-info btn-small'
    cancelText: 'Cancel',//not applicable for alert
    cancelClass: 'btn-secondary',
    
    headerClass: 'text-center',//class to be added to bootstrap modal-header
    bodyClass: '',//class for bootstrap modal-body
    footerClass: '',//class for bootstrap modal-footer

    //Below are the three uibModal related properties, see uibModal Bootstrap documentation for details

    backdrop: 'static',//Controls presence of a backdrop. Allowed values: true (default), false (no backdrop), 'static' (disables modal closing by click on the backdrop).

    keyboard: false,//Indicates whether the dialog should be closable by hitting the ESC key.
    size: 'sm',//modal or popup size, default is small

    /*NOTE: Below are the app level configuration applicable when input parameter is string. It can be set during angular config phase.
    */

    showStringAs: 'body',//it will display the text as modal body(left aligned smaller font text). Other value is 'title' (center aligned h5 element)
    enableDynamicSize: true,//show medium size popup when input string extends the below character limit
    extendSizeCharLength: 300
};

```

> Sice all applicable string inputs can contains `html` tags therefore you can easily control the style.


## Default Configuration

You can easily configure the default popup option for the application during config phase using `PopupSvcProvider` as below.


```javascript
angular.module('myApp', ['popup'])
    .config(['PopupSvcProvider', function(PopupSvcProvider){
         PopupSvcProvider.setDefaults({
            okText: 'Dismiss',//Now instead of 'OK' popup will show button with text 'Dismiss' 
            okClass: 'btn-primary',
            cancelText: 'Close',
            keyboard: true//popup can be closed now with ESC key.
        });
    }]);
```
However the option parameter will still take precedence over app level configuration.

## License

MIT
/* Below css already include in directive and therefore not required separately*/
.modal-spinner .modal-content {
    padding: 20px;
    border-radius: 5px;
    background-color: rgba(0, 0, 0, 0.8);
    color: #fff;
    text-align: center;
    text-overflow: ellipsis;
    font-size: 15px;
}
.modal-spinner-text .modal-content{
    text-align: left;
}

.modal-spinner .modal-dialog {
    position: absolute;
    left: 0;
    right: 0;
    top: 0;
    bottom: 0;
    margin: auto;
    width: 80px;
    height: 300px;
}

.modal-spinner-text .modal-dialog{
    text-align: left;
    width: 250px;
}